@metalabel/dfos-web-relay 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/openapi.yaml CHANGED
@@ -28,7 +28,7 @@ paths:
28
28
  application/json:
29
29
  schema:
30
30
  type: object
31
- required: [did, protocol, version]
31
+ required: [did, protocol, version, proof, content, log, profile]
32
32
  properties:
33
33
  did:
34
34
  type: string
@@ -40,6 +40,18 @@ paths:
40
40
  version:
41
41
  type: string
42
42
  example: '0.1.0'
43
+ proof:
44
+ type: boolean
45
+ description: Always true — a relay without proof plane is not a relay
46
+ content:
47
+ type: boolean
48
+ description: Whether the relay supports content plane (blob upload/download)
49
+ log:
50
+ type: boolean
51
+ description: Whether the global operation log is available (GET /log)
52
+ profile:
53
+ type: string
54
+ description: The relay's profile artifact as a compact JWS token
43
55
 
44
56
  /operations:
45
57
  post:
@@ -388,7 +400,7 @@ components:
388
400
  description: CID of the operation
389
401
  status:
390
402
  type: string
391
- enum: [accepted, rejected]
403
+ enum: [new, duplicate, rejected]
392
404
  error:
393
405
  type: string
394
406
  description: Error message if rejected
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metalabel/dfos-web-relay",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "DFOS Web Relay — verifying HTTP relay for identity chains, content chains, beacons, and content blobs",
6
6
  "license": "MIT",
@@ -36,7 +36,6 @@
36
36
  },
37
37
  "files": [
38
38
  "dist",
39
- "RELAY.md",
40
39
  "openapi.yaml",
41
40
  "LICENSE",
42
41
  "README.md"
@@ -46,14 +45,14 @@
46
45
  "zod": "^4.3.6"
47
46
  },
48
47
  "peerDependencies": {
49
- "@metalabel/dfos-protocol": "^0.4.0"
48
+ "@metalabel/dfos-protocol": "^0.6.0"
50
49
  },
51
50
  "devDependencies": {
52
51
  "@types/node": "^24.10.4",
53
52
  "tsup": "^8.5.1",
54
53
  "tsx": "^4.20.3",
55
54
  "vitest": "^4.1.0",
56
- "@metalabel/dfos-protocol": "0.5.0"
55
+ "@metalabel/dfos-protocol": "0.6.0"
57
56
  },
58
57
  "scripts": {
59
58
  "build": "tsup",
package/RELAY.md DELETED
@@ -1,457 +0,0 @@
1
- # DFOS Web Relay
2
-
3
- An HTTP relay for the DFOS protocol — receives, verifies, stores, and serves identity chains, content chains, artifacts, beacons, countersignatures, and content blobs.
4
-
5
- This spec is under active review. Discuss it in the [clear.txt](https://clear.dfos.com) space on DFOS.
6
-
7
- [Source](https://github.com/metalabel/dfos/tree/main/packages/dfos-web-relay) · [npm](https://www.npmjs.com/package/@metalabel/dfos-web-relay) · [Protocol](https://protocol.dfos.com)
8
-
9
- ---
10
-
11
- ## Philosophy
12
-
13
- The DFOS protocol defines signed chain primitives — identity and content chains, beacons, credentials — but says nothing about transport. A web relay is the HTTP layer that carries these primitives between participants.
14
-
15
- Relays are not authorities. They verify what they receive and serve what they've verified, but they don't issue identity, grant permissions, or define content semantics. Any relay implementing the same verification rules produces the same acceptance/rejection decisions for the same operations. Clients can replicate their data across multiple relays without coordination.
16
-
17
- A relay is a library, not a service. `createRelay()` returns a portable Hono application that any runtime can host — Node.js, Cloudflare Workers, Deno, Bun, a Docker container, a Raspberry Pi. The consumer provides a storage backend and configuration. The relay handles verification and HTTP semantics.
18
-
19
- ---
20
-
21
- ## Two Planes
22
-
23
- The relay serves two distinct planes of data with different access models:
24
-
25
- ### Proof Plane (public)
26
-
27
- Signed chain operations, artifacts, beacons, and countersignatures. These are cryptographic proofs — anyone can verify them with a public key. The proof plane gossips freely: relays push operations to peers, peers verify and store independently.
28
-
29
- All proof plane routes are unauthenticated. The operations themselves carry their own authentication (Ed25519 signatures).
30
-
31
- ### Content Plane (private)
32
-
33
- Raw content blobs — the actual documents that content chains commit to via `documentCID`. The content plane never gossips. Blobs are stored by the relay that received them and served only to authorized readers.
34
-
35
- Content plane access requires two credentials:
36
-
37
- - **Auth token**: A DID-signed JWT proving the caller controls an identity (AuthN)
38
- - **Read credential** (for non-creators): A `DFOSContentRead` VC-JWT issued by the content creator, granting the caller read access (AuthZ)
39
-
40
- The content creator (the DID that signed the genesis content operation) can always read their own blobs with just an auth token.
41
-
42
- Content plane support is optional per relay. When disabled (`content: false` in the well-known response), all content plane routes return **501 Not Implemented** — not 404 (resource doesn't exist), but 501 (capability not supported).
43
-
44
- ---
45
-
46
- ## Operation Ingestion
47
-
48
- All proof plane operations enter through a single endpoint: `POST /operations`. The request body is an array of JWS tokens — identity operations, content operations, artifacts, beacons, and countersignatures can be mixed freely in the same batch.
49
-
50
- ### Classification
51
-
52
- Each token is classified by its JWS `typ` header:
53
-
54
- | `typ` header | Classification |
55
- | ---------------------- | ------------------------ |
56
- | `did:dfos:identity-op` | Identity chain operation |
57
- | `did:dfos:content-op` | Content chain operation |
58
- | `did:dfos:beacon` | Beacon announcement |
59
- | `did:dfos:artifact` | Artifact |
60
- | `did:dfos:countersign` | Countersignature |
61
-
62
- Each operation type has its own `typ` header. Classification is unambiguous — no DID comparison needed.
63
-
64
- ### Dependency Sort
65
-
66
- Within a batch, operations are sorted by dependency priority before processing:
67
-
68
- 1. **Identity operations** — must be processed first so their keys are available
69
- 2. **Beacons and artifacts** — reference identity keys for signature verification
70
- 3. **Content operations** — reference identity keys, may have chain dependencies
71
- 4. **Countersignatures** — reference identity keys and existing operations (target must exist)
72
-
73
- Within each priority level, genesis operations (no `previousOperationCID`) are processed before extensions. This ensures that a single batch can bootstrap an entire identity-and-content lifecycle — including chained create + update operations — without multiple round trips.
74
-
75
- ### Verification
76
-
77
- Each operation is verified against the relay's stored state:
78
-
79
- - **Identity operations**: Extension operations are verified against the relay's current trusted state using O(1) extension verification — the trusted head state plus the new operation is sufficient. Genesis operations verify the single-operation chain. The relay uses `verifyIdentityChain()` / `verifyIdentityExtensionFromTrustedState()` from the protocol library
80
- - **Content operations**: Extension operations are verified against trusted state with `enforceAuthorization: true`. Non-creator signers must include a `DFOSContentWrite` VC-JWT. The relay uses `verifyContentChain()` / `verifyContentExtensionFromTrustedState()` from the protocol library
81
- - **Artifacts**: Signature is verified against the signing DID's current identity state. CID integrity is checked. Payload must conform to the declared `$schema`. CBOR-encoded payload must not exceed 16384 bytes
82
- - **Beacons**: Signature, CID integrity, and clock skew are verified. Replace-on-newer: only the most recent beacon per DID is retained
83
- - **Countersignatures**: Two-phase verification. Protocol-level (stateless): signature, CID integrity, payload schema. Relay-level (stateful): target CID must exist in the relay, witness DID must differ from the target's author DID, one countersign per witness per target
84
-
85
- ### Fork Policy
86
-
87
- First-seen-wins. If a chain head has already advanced past the `previousOperationCID` referenced by an incoming operation, the new operation is rejected. The relay does not attempt to resolve forks — the first valid extension wins.
88
-
89
- Duplicate submissions (same operation CID, same JWS token) are silently accepted (idempotent). Submissions with the same CID but a different JWS token are rejected — since Ed25519 is deterministic, a different token for the same payload means a different signing key, which is either a self-countersign attempt or an unauthorized re-sign.
90
-
91
- Duplicate countersignatures (same witness DID, same target CID) MUST be deduplicated — one countersign per witness per target. The relay MUST NOT store multiple attestations from the same witness for the same target. Resubmission SHOULD return `accepted` (idempotent).
92
-
93
- ### Deletion Semantics
94
-
95
- Deletion means the identity stops being an active participant. Historical operations remain verifiable — keys persist in state for signature verification — but no new acts flow from a deleted identity.
96
-
97
- Specifically:
98
-
99
- - **Identity operations after deletion**: Rejected. A deleted identity chain is sealed.
100
- - **Content operations after deletion**: Rejected. A deleted content chain is sealed.
101
- - **Beacons from deleted identities**: Rejected. A deleted identity MUST NOT publish new beacons.
102
- - **Artifacts from deleted identities**: Rejected. A deleted identity MUST NOT publish new artifacts.
103
- - **Credentials from deleted issuers**: Rejected. Identity deletion revokes all authority, including outstanding `DFOSContentRead` and `DFOSContentWrite` credentials issued by the deleted identity. Credentials that were valid at time of issuance cease to be honored once the issuer is deleted.
104
- - **Countersignatures from deleted witnesses**: Rejected. A deleted identity MUST NOT publish new countersignatures. Countersignatures on operations by deleted authors are still accepted — deletion of the target's author does not prevent other identities from attesting.
105
-
106
- Self-countersignatures — where the witness DID matches the target's author DID — are rejected at the relay level. A countersignature's semantic is "a distinct witness attests." The protocol-level verifier is stateless and does not enforce this; the relay resolves the target's author and rejects self-attestation.
107
-
108
- ### Result Ordering
109
-
110
- Ingestion results are returned in the same order as the input `operations` array, regardless of internal processing order. `results[i]` corresponds to `operations[i]`.
111
-
112
- ---
113
-
114
- ## Artifacts
115
-
116
- Artifacts are standalone signed inline documents — immutable, CID-addressable proof plane primitives. Unlike chain operations which extend a sequence, an artifact is a single signed statement with no predecessor or successor.
117
-
118
- ### Payload
119
-
120
- ```json
121
- {
122
- "version": 1,
123
- "type": "artifact",
124
- "did": "did:dfos:...",
125
- "content": {
126
- "$schema": "https://schemas.dfos.com/profile/v1",
127
- "name": "My Relay",
128
- "description": "A relay for the dark forest"
129
- },
130
- "createdAt": "2026-03-25T00:00:00.000Z"
131
- }
132
- ```
133
-
134
- The `content` object MUST include a `$schema` string that identifies the artifact's schema. The schema acts as a discriminator — consumers use it to determine how to interpret the artifact's content. Schema names are free-form strings (no protocol-level registry). Communities may establish conventions for well-known schemas.
135
-
136
- ### Constraints
137
-
138
- - **JWS `typ` header**: `did:dfos:artifact`
139
- - **Max payload size**: 16384 bytes CBOR-encoded. This is a protocol constant — not configurable per relay
140
- - **Immutability**: Once ingested, an artifact is never updated or replaced. To "update" an artifact's content, publish a new artifact
141
- - **CID-addressable**: Each artifact is addressed by the CID of its CBOR-encoded payload
142
-
143
- ### Verification
144
-
145
- 1. JWS signature verification against the signing DID's current key state
146
- 2. CID integrity — the payload CID matches the computed CID from the raw payload bytes
147
- 3. Payload schema validation — the payload conforms to the artifact structure (`version`, `type`, `did`, `content` with `$schema`, `createdAt`)
148
- 4. Size limit — CBOR-encoded payload does not exceed 16384 bytes
149
-
150
- ---
151
-
152
- ## Countersignatures
153
-
154
- A countersignature is a standalone witness attestation — a signed statement that references a target operation by CID. Unlike the original operation primitives (which carry the data itself), a countersign is pure attestation: "I, witness W, attest to operation X."
155
-
156
- ### Payload
157
-
158
- ```json
159
- {
160
- "version": 1,
161
- "type": "countersign",
162
- "did": "did:dfos:witness...",
163
- "targetCID": "bafy...",
164
- "createdAt": "2026-03-25T00:00:00.000Z"
165
- }
166
- ```
167
-
168
- ### Properties
169
-
170
- - **JWS `typ` header**: `did:dfos:countersign`
171
- - **Own CID**: Each countersign has its own CID, distinct from the target. This avoids the ambiguity of multiple JWS tokens sharing the same CID
172
- - **Stateless verification**: Signature + CID integrity + payload schema. No relay state required to verify the cryptographic validity of a countersign
173
- - **Composable**: The `targetCID` can reference any CID-addressable operation — content ops, beacons, artifacts, identity ops, even other countersigns
174
- - **Immutable**: Once published, a countersign is permanent
175
-
176
- ### Relay-Level Checks
177
-
178
- The relay enforces semantic rules beyond cryptographic validity:
179
-
180
- 1. **Target exists**: The `targetCID` must reference an operation already stored in the relay
181
- 2. **Witness ≠ author**: The countersign's `did` (witness) must differ from the target operation's author DID
182
- 3. **Deduplication**: One countersign per witness per target. If the same witness submits a second countersign for the same target, the relay accepts idempotently
183
- 4. **Deleted witness rejection**: Countersigns from deleted identities are rejected
184
-
185
- ---
186
-
187
- ## Relay Identity
188
-
189
- Every relay has a DID that resolves on its own proof plane. The relay DID serves as:
190
-
191
- - **Auth token audience**: Auth tokens are scoped to a specific relay via the JWT `aud` claim, preventing cross-relay token replay
192
- - **Peer identity**: When relays gossip proof plane data to each other, the relay DID identifies the peer
193
- - **Self-proof anchor**: The relay's identity chain lives in its own store, verifiable by anyone querying the relay
194
-
195
- ### Relay Profile
196
-
197
- The relay MUST publish a profile artifact signed by its own DID using the HEAD key state. The profile artifact uses the `https://schemas.dfos.com/profile/v1` schema:
198
-
199
- ```json
200
- {
201
- "$schema": "https://schemas.dfos.com/profile/v1",
202
- "name": "edge.relay.dfos.com",
203
- "description": "Cloudflare edge relay for the DFOS network",
204
- "image": { "id": "relay-avatar", "uri": "https://cdn.example.com/avatar.png" },
205
- "operator": "Metalabel",
206
- "motd": "Welcome to the dark forest"
207
- }
208
- ```
209
-
210
- All fields are optional except `name`, which SHOULD be present. The `image.uri` field is any valid URI (operator choice — CDN, content-plane reference, or any resolvable URL). The profile JWS token is inlined in the well-known response — self-proving, no extra fetch needed.
211
-
212
- ### Well-Known Endpoint (`GET /.well-known/dfos-relay`)
213
-
214
- Returns relay metadata. All fields are required — `profile` is the relay's proof of DID controllership (an artifact JWS signed by the relay DID's controller key).
215
-
216
- ```json
217
- {
218
- "did": "did:dfos:edgerelay0000000000000",
219
- "protocol": "dfos-web-relay",
220
- "version": "0.1.0",
221
- "proof": true,
222
- "content": false,
223
- "profile": "eyJhbGciOiJFZERTQSIs..."
224
- }
225
- ```
226
-
227
- | Field | Type | Description |
228
- | ---------- | ------- | -------------------------------------------------------------------------- |
229
- | `did` | string | The relay's DID, resolvable on this relay's proof plane |
230
- | `protocol` | string | Protocol identifier, always `"dfos-web-relay"` |
231
- | `version` | string | Relay protocol version (semver) |
232
- | `proof` | boolean | MUST be `true`. A relay without proof plane capability is not a relay |
233
- | `content` | boolean | Whether the relay supports content plane (blob upload/download) |
234
- | `profile` | string | The relay's profile artifact as a compact JWS token — self-proving payload |
235
-
236
- `proof: false` is not a valid value. A compliant relay always serves the proof plane.
237
-
238
- ---
239
-
240
- ## Operation Log
241
-
242
- The relay maintains a global append-only operation log. Every successfully ingested operation (identity ops, content ops, artifacts, beacons, countersignatures) is appended to the log in ingestion order.
243
-
244
- ### Global Log (`GET /log?after={cid}&limit=N`)
245
-
246
- Returns log entries starting after the given CID cursor.
247
-
248
- ```json
249
- {
250
- "entries": [
251
- {
252
- "cid": "bafy...",
253
- "jwsToken": "eyJhbGciOiJFZERTQSIs...",
254
- "kind": "identity-op",
255
- "chainId": "did:dfos:..."
256
- },
257
- {
258
- "cid": "bafy...",
259
- "jwsToken": "eyJhbGciOiJFZERTQSIs...",
260
- "kind": "artifact",
261
- "chainId": "did:dfos:..."
262
- }
263
- ],
264
- "cursor": "bafy..."
265
- }
266
- ```
267
-
268
- | Field | Type | Description |
269
- | -------------------- | ------------ | -------------------------------------------------------------------------------- |
270
- | `entries[].cid` | string | Operation CID |
271
- | `entries[].jwsToken` | string | The full compact JWS token — makes the log self-contained for sync |
272
- | `entries[].kind` | string | Operation kind: `identity-op`, `content-op`, `beacon`, `artifact`, `countersign` |
273
- | `entries[].chainId` | string | DID (identity/beacon/artifact), contentId (content), or targetCID (countersign) |
274
- | `cursor` | string\|null | CID to pass as `after` for the next page. `null` means caught up |
275
-
276
- Parameters:
277
-
278
- - **`after`** (optional): CID cursor. Omit to start from the beginning of the log
279
- - **`limit`** (optional): Max entries to return. Default: 100. Max: 1000
280
-
281
- Pagination is forward-only. The log is ordered by ingestion time. JWS tokens are included in every entry because proof-plane JWS payloads are bounded (chain operations and artifacts have finite size), keeping the log self-contained — a syncing peer can replay the log without separate fetches.
282
-
283
- ### Per-Chain Logs
284
-
285
- Identity and content chains expose their own log views with the same cursor-based pagination:
286
-
287
- - `GET /identities/:did/log?after={cid}&limit=N`
288
- - `GET /content/:contentId/log?after={cid}&limit=N`
289
-
290
- Same cursor-based pagination parameters as the global log. Per-chain log entries include `{ cid, jwsToken }` — the chain-specific subset of the global log entry shape. Returns operations belonging to that chain in chain order.
291
-
292
- ---
293
-
294
- ## Identity and Content State
295
-
296
- State endpoints return projected state — the computed result of replaying the chain — without embedding the full operation log.
297
-
298
- ### Identity State (`GET /identities/:did`)
299
-
300
- ```json
301
- {
302
- "did": "did:dfos:abc123...",
303
- "headCID": "bafy...",
304
- "state": {
305
- "did": "did:dfos:abc123...",
306
- "isDeleted": false,
307
- "authKeys": [...],
308
- "assertKeys": [...],
309
- "controllerKeys": [...]
310
- }
311
- }
312
- ```
313
-
314
- ### Content State (`GET /content/:contentId`)
315
-
316
- ```json
317
- {
318
- "contentId": "abc123...",
319
- "genesisCID": "bafy...",
320
- "headCID": "bafy...",
321
- "state": {
322
- "contentId": "abc123...",
323
- "genesisCID": "bafy...",
324
- "headCID": "bafy...",
325
- "isDeleted": false,
326
- "currentDocumentCID": "bafy...",
327
- "length": 1,
328
- "creatorDID": "did:dfos:..."
329
- }
330
- }
331
- ```
332
-
333
- Chain history is available via the per-chain log routes described above.
334
-
335
- ---
336
-
337
- ## Content Plane Access
338
-
339
- ### Blob Upload (`PUT /content/:contentId/blob/:operationCID`)
340
-
341
- The upload path mirrors the download path — the operation CID identifies which operation's document is being uploaded.
342
-
343
- Requirements:
344
-
345
- - Valid auth token (Bearer header)
346
- - The operation CID must reference an operation in this content chain that has a `documentCID`
347
- - The authenticated DID must be either the chain creator OR the signer of the referenced operation (enabling delegated uploads)
348
- - The uploaded bytes must hash to the operation's `documentCID` (dag-cbor + sha-256 verification)
349
-
350
- Blobs are stored by `(creatorDID, documentCID)` — always keyed to the chain creator regardless of who uploads. If multiple content chains by the same creator reference the same document, the blob is shared (deduplication).
351
-
352
- ### Blob Download (`GET /content/:contentId/blob[/:ref]`)
353
-
354
- Requirements:
355
-
356
- - Valid auth token (Bearer header)
357
- - If the caller is the chain creator: no further credentials needed
358
- - If the caller is not the creator: must present a `DFOSContentRead` VC-JWT in the `X-Credential` header, issued by the creator to the caller
359
-
360
- The optional `:ref` parameter selects which operation's document to return:
361
-
362
- - `head` (default): the current document at chain head
363
- - An operation CID: the document committed by that specific operation
364
-
365
- ---
366
-
367
- ## Key Resolution
368
-
369
- The relay uses two key resolution strategies:
370
-
371
- - **Historical resolver** (for chain re-verification): searches all keys that have ever appeared in an identity chain's log, including rotated-out keys. This is necessary because re-verifying a full content chain from genesis must resolve keys from operations signed before a key rotation.
372
- - **Current-state resolver** (for live authentication): only resolves keys in the identity's current state. After a key rotation, the old key immediately stops working for auth tokens and credentials. This prevents a compromised rotated-out key from being used to authenticate new requests.
373
-
374
- ---
375
-
376
- ## Storage Interface
377
-
378
- The relay delegates persistence to a `RelayStore` interface. Implementations handle how data is stored — the relay handles what to store and when.
379
-
380
- ```typescript
381
- interface RelayStore {
382
- getOperation(cid: string): Promise<StoredOperation | undefined>;
383
- putOperation(op: StoredOperation): Promise<void>;
384
-
385
- getIdentityChain(did: string): Promise<StoredIdentityChain | undefined>;
386
- putIdentityChain(chain: StoredIdentityChain): Promise<void>;
387
-
388
- getContentChain(contentId: string): Promise<StoredContentChain | undefined>;
389
- putContentChain(chain: StoredContentChain): Promise<void>;
390
-
391
- getBeacon(did: string): Promise<StoredBeacon | undefined>;
392
- putBeacon(beacon: StoredBeacon): Promise<void>;
393
-
394
- getBlob(key: BlobKey): Promise<Uint8Array | undefined>;
395
- putBlob(key: BlobKey, data: Uint8Array): Promise<void>;
396
-
397
- getCountersignatures(operationCID: string): Promise<string[]>;
398
- addCountersignature(operationCID: string, jwsToken: string): Promise<void>;
399
-
400
- appendToLog(entry: LogEntry): Promise<void>;
401
- readLog(params: { after?: string; limit: number }): Promise<LogPage>;
402
- }
403
- ```
404
-
405
- The `appendToLog` / `readLog` pair supports both the global log and per-chain log queries. The store implementation determines how to scope queries (e.g., by filtering on `chainId`).
406
-
407
- The package includes `MemoryRelayStore` for development and testing. Production deployments would implement the interface over Postgres, SQLite, D1, or any durable store.
408
-
409
- ---
410
-
411
- ## Quick Start
412
-
413
- ```typescript
414
- import { createRelay, MemoryRelayStore } from '@metalabel/dfos-web-relay';
415
-
416
- // JIT mode — generates relay identity + profile artifact at startup
417
- const relay = await createRelay({
418
- store: new MemoryRelayStore(),
419
- });
420
-
421
- // Or provide a pre-created identity (production)
422
- const relay = await createRelay({
423
- store: new MemoryRelayStore(),
424
- identity: { did: 'did:dfos:myrelay...', profileArtifactJws: '...' },
425
- });
426
-
427
- // Mount on any Hono-compatible runtime
428
- export default relay;
429
- ```
430
-
431
- The returned Hono app exposes:
432
-
433
- | Method | Path | Plane | Auth |
434
- | ------ | ------------------------------------ | ------- | ----------------------- |
435
- | `GET` | `/.well-known/dfos-relay` | meta | none |
436
- | `POST` | `/operations` | proof | none |
437
- | `GET` | `/operations/:cid` | proof | none |
438
- | `GET` | `/operations/:cid/countersignatures` | proof | none |
439
- | `GET` | `/countersignatures/:cid` | proof | none |
440
- | `GET` | `/identities/:did` | proof | none |
441
- | `GET` | `/identities/:did/log` | proof | none |
442
- | `GET` | `/content/:contentId` | proof | none |
443
- | `GET` | `/content/:contentId/log` | proof | none |
444
- | `GET` | `/beacons/:did` | proof | none |
445
- | `GET` | `/log` | proof | none |
446
- | `PUT` | `/content/:contentId/blob/:opCID` | content | auth token |
447
- | `GET` | `/content/:contentId/blob[/:ref]` | content | auth token + credential |
448
-
449
- ---
450
-
451
- ## What's Deferred
452
-
453
- - **Peer gossip**: Proactive push of proof plane operations to other relays
454
- - **Rate limiting / anti-spam**: Operational concern, not protocol concern
455
- - **Docker/CF reference deployments**: Focus on the core library first
456
- - **Blob size limits**: No enforcement yet — production deployments should add limits at the middleware layer
457
- - **Artifact `$schema` registry**: Schema names are free-form strings for now — no formal registry or validation beyond structural checks