@metalabel/dfos-web-relay 0.3.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 ADDED
@@ -0,0 +1,561 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: DFOS Web Relay
4
+ version: 0.1.0
5
+ description: |
6
+ HTTP relay for the DFOS protocol. Receives, verifies, stores, and serves
7
+ identity chains, content chains, beacons, countersignatures, and content blobs.
8
+
9
+ Two data planes:
10
+ - **Proof plane** (public): signed chain operations, beacons, countersignatures
11
+ - **Content plane** (authenticated): raw content blobs gated by DID auth tokens and VC-JWT credentials
12
+
13
+ servers:
14
+ - url: http://localhost:3000
15
+ description: Local development
16
+
17
+ paths:
18
+ /.well-known/dfos-relay:
19
+ get:
20
+ operationId: getRelayMetadata
21
+ summary: Relay metadata
22
+ description: Returns the relay's DID, protocol identifier, and version.
23
+ tags: [Meta]
24
+ responses:
25
+ '200':
26
+ description: Relay metadata
27
+ content:
28
+ application/json:
29
+ schema:
30
+ type: object
31
+ required: [did, protocol, version]
32
+ properties:
33
+ did:
34
+ type: string
35
+ description: The relay's DID
36
+ example: 'did:dfos:e3vvtck42d4eacdnzvtrn6'
37
+ protocol:
38
+ type: string
39
+ enum: [dfos-web-relay]
40
+ version:
41
+ type: string
42
+ example: '0.1.0'
43
+
44
+ /operations:
45
+ post:
46
+ operationId: ingestOperations
47
+ summary: Submit operations for ingestion
48
+ description: |
49
+ Accept a batch of JWS tokens — identity operations, content operations,
50
+ beacons, and countersignatures. The relay classifies, dependency-sorts,
51
+ verifies, and stores each token.
52
+ tags: [Proof Plane]
53
+ requestBody:
54
+ required: true
55
+ content:
56
+ application/json:
57
+ schema:
58
+ type: object
59
+ required: [operations]
60
+ properties:
61
+ operations:
62
+ type: array
63
+ minItems: 1
64
+ maxItems: 100
65
+ items:
66
+ type: string
67
+ description: JWS compact serialization token
68
+ responses:
69
+ '200':
70
+ description: Ingestion results
71
+ content:
72
+ application/json:
73
+ schema:
74
+ type: object
75
+ required: [results]
76
+ properties:
77
+ results:
78
+ type: array
79
+ items:
80
+ $ref: '#/components/schemas/IngestionResult'
81
+ '400':
82
+ $ref: '#/components/responses/BadRequest'
83
+
84
+ /operations/{cid}:
85
+ get:
86
+ operationId: getOperation
87
+ summary: Get an operation by CID
88
+ tags: [Proof Plane]
89
+ parameters:
90
+ - name: cid
91
+ in: path
92
+ required: true
93
+ schema:
94
+ type: string
95
+ description: CIDv1 of the operation
96
+ responses:
97
+ '200':
98
+ description: Operation details
99
+ content:
100
+ application/json:
101
+ schema:
102
+ $ref: '#/components/schemas/StoredOperation'
103
+ '404':
104
+ $ref: '#/components/responses/NotFound'
105
+
106
+ /operations/{cid}/countersignatures:
107
+ get:
108
+ operationId: getCountersignatures
109
+ summary: Get countersignatures for an operation
110
+ tags: [Proof Plane]
111
+ parameters:
112
+ - name: cid
113
+ in: path
114
+ required: true
115
+ schema:
116
+ type: string
117
+ responses:
118
+ '200':
119
+ description: Countersignatures
120
+ content:
121
+ application/json:
122
+ schema:
123
+ type: object
124
+ required: [operationCID, countersignatures]
125
+ properties:
126
+ operationCID:
127
+ type: string
128
+ countersignatures:
129
+ type: array
130
+ items:
131
+ type: string
132
+ description: JWS compact serialization (witness signature)
133
+ '404':
134
+ $ref: '#/components/responses/NotFound'
135
+
136
+ /identities/{did}:
137
+ get:
138
+ operationId: getIdentityChain
139
+ summary: Get an identity chain by DID
140
+ tags: [Proof Plane]
141
+ parameters:
142
+ - name: did
143
+ in: path
144
+ required: true
145
+ schema:
146
+ type: string
147
+ description: 'DID of the identity (e.g., did:dfos:e3vvtck42d4eacdnzvtrn6)'
148
+ responses:
149
+ '200':
150
+ description: Identity chain state and log
151
+ content:
152
+ application/json:
153
+ schema:
154
+ $ref: '#/components/schemas/IdentityChainResponse'
155
+ '404':
156
+ $ref: '#/components/responses/NotFound'
157
+
158
+ /content/{contentId}:
159
+ get:
160
+ operationId: getContentChain
161
+ summary: Get a content chain by content ID
162
+ tags: [Proof Plane]
163
+ parameters:
164
+ - name: contentId
165
+ in: path
166
+ required: true
167
+ schema:
168
+ type: string
169
+ description: Content identifier (22-char hash)
170
+ responses:
171
+ '200':
172
+ description: Content chain state and log
173
+ content:
174
+ application/json:
175
+ schema:
176
+ $ref: '#/components/schemas/ContentChainResponse'
177
+ '404':
178
+ $ref: '#/components/responses/NotFound'
179
+
180
+ /content/{contentId}/blob:
181
+ put:
182
+ operationId: uploadBlob
183
+ summary: Upload a content blob
184
+ description: |
185
+ Upload raw bytes for a document referenced by a content chain.
186
+ Requires auth token. Caller must be the chain creator.
187
+ tags: [Content Plane]
188
+ security:
189
+ - BearerAuth: []
190
+ parameters:
191
+ - name: contentId
192
+ in: path
193
+ required: true
194
+ schema:
195
+ type: string
196
+ - name: X-Document-CID
197
+ in: header
198
+ required: true
199
+ schema:
200
+ type: string
201
+ description: The documentCID this blob corresponds to
202
+ requestBody:
203
+ required: true
204
+ content:
205
+ application/octet-stream:
206
+ schema:
207
+ type: string
208
+ format: binary
209
+ responses:
210
+ '200':
211
+ description: Blob stored
212
+ content:
213
+ application/json:
214
+ schema:
215
+ type: object
216
+ required: [status, contentId, documentCID]
217
+ properties:
218
+ status:
219
+ type: string
220
+ enum: [stored]
221
+ contentId:
222
+ type: string
223
+ documentCID:
224
+ type: string
225
+ '400':
226
+ $ref: '#/components/responses/BadRequest'
227
+ '401':
228
+ $ref: '#/components/responses/Unauthorized'
229
+ '403':
230
+ $ref: '#/components/responses/Forbidden'
231
+ '404':
232
+ $ref: '#/components/responses/NotFound'
233
+
234
+ get:
235
+ operationId: downloadBlob
236
+ summary: Download a content blob at head
237
+ description: |
238
+ Download the raw bytes of the current document at chain head.
239
+ Requires auth token. Non-creators must present a DFOSContentRead credential.
240
+ tags: [Content Plane]
241
+ security:
242
+ - BearerAuth: []
243
+ parameters:
244
+ - name: contentId
245
+ in: path
246
+ required: true
247
+ schema:
248
+ type: string
249
+ - name: X-Credential
250
+ in: header
251
+ required: false
252
+ schema:
253
+ type: string
254
+ description: DFOSContentRead VC-JWT (required for non-creators)
255
+ responses:
256
+ '200':
257
+ description: Blob data
258
+ headers:
259
+ X-Document-CID:
260
+ schema:
261
+ type: string
262
+ description: The documentCID of the returned blob
263
+ content:
264
+ application/octet-stream:
265
+ schema:
266
+ type: string
267
+ format: binary
268
+ '401':
269
+ $ref: '#/components/responses/Unauthorized'
270
+ '403':
271
+ $ref: '#/components/responses/Forbidden'
272
+ '404':
273
+ $ref: '#/components/responses/NotFound'
274
+
275
+ /content/{contentId}/blob/{ref}:
276
+ get:
277
+ operationId: downloadBlobAtRef
278
+ summary: Download a content blob at a specific operation
279
+ description: |
280
+ Download the raw bytes of the document committed by a specific operation.
281
+ The ref parameter is an operation CID within the chain.
282
+ tags: [Content Plane]
283
+ security:
284
+ - BearerAuth: []
285
+ parameters:
286
+ - name: contentId
287
+ in: path
288
+ required: true
289
+ schema:
290
+ type: string
291
+ - name: ref
292
+ in: path
293
+ required: true
294
+ schema:
295
+ type: string
296
+ description: Operation CID within the chain
297
+ - name: X-Credential
298
+ in: header
299
+ required: false
300
+ schema:
301
+ type: string
302
+ description: DFOSContentRead VC-JWT (required for non-creators)
303
+ responses:
304
+ '200':
305
+ description: Blob data
306
+ headers:
307
+ X-Document-CID:
308
+ schema:
309
+ type: string
310
+ content:
311
+ application/octet-stream:
312
+ schema:
313
+ type: string
314
+ format: binary
315
+ '401':
316
+ $ref: '#/components/responses/Unauthorized'
317
+ '403':
318
+ $ref: '#/components/responses/Forbidden'
319
+ '404':
320
+ $ref: '#/components/responses/NotFound'
321
+
322
+ /countersignatures/{cid}:
323
+ get:
324
+ operationId: getCountersignaturesByCID
325
+ summary: Get countersignatures for any CID (operation or beacon)
326
+ tags: [Proof Plane]
327
+ parameters:
328
+ - name: cid
329
+ in: path
330
+ required: true
331
+ schema:
332
+ type: string
333
+ description: CIDv1 of the operation or beacon
334
+ responses:
335
+ '200':
336
+ description: Countersignatures
337
+ content:
338
+ application/json:
339
+ schema:
340
+ type: object
341
+ required: [cid, countersignatures]
342
+ properties:
343
+ cid:
344
+ type: string
345
+ countersignatures:
346
+ type: array
347
+ items:
348
+ type: string
349
+ description: JWS compact serialization (witness signature)
350
+ '404':
351
+ $ref: '#/components/responses/NotFound'
352
+
353
+ /beacons/{did}:
354
+ get:
355
+ operationId: getBeacon
356
+ summary: Get the latest beacon for a DID
357
+ tags: [Proof Plane]
358
+ parameters:
359
+ - name: did
360
+ in: path
361
+ required: true
362
+ schema:
363
+ type: string
364
+ responses:
365
+ '200':
366
+ description: Latest beacon
367
+ content:
368
+ application/json:
369
+ schema:
370
+ $ref: '#/components/schemas/BeaconResponse'
371
+ '404':
372
+ $ref: '#/components/responses/NotFound'
373
+
374
+ components:
375
+ securitySchemes:
376
+ BearerAuth:
377
+ type: http
378
+ scheme: bearer
379
+ description: DID-signed auth token JWT
380
+
381
+ schemas:
382
+ IngestionResult:
383
+ type: object
384
+ required: [cid, status]
385
+ properties:
386
+ cid:
387
+ type: string
388
+ description: CID of the operation
389
+ status:
390
+ type: string
391
+ enum: [accepted, rejected]
392
+ error:
393
+ type: string
394
+ description: Error message if rejected
395
+ kind:
396
+ type: string
397
+ enum: [identity-op, content-op, beacon, countersig, beacon-countersig]
398
+ chainId:
399
+ type: string
400
+ description: Chain identifier (DID or contentId)
401
+
402
+ StoredOperation:
403
+ type: object
404
+ required: [cid, jwsToken, chainType, chainId]
405
+ properties:
406
+ cid:
407
+ type: string
408
+ jwsToken:
409
+ type: string
410
+ chainType:
411
+ type: string
412
+ enum: [identity, content]
413
+ chainId:
414
+ type: string
415
+
416
+ IdentityChainResponse:
417
+ type: object
418
+ required: [did, log, state]
419
+ properties:
420
+ did:
421
+ type: string
422
+ log:
423
+ type: array
424
+ items:
425
+ type: string
426
+ description: Ordered JWS tokens from genesis to head
427
+ state:
428
+ type: object
429
+ required: [did, isDeleted, authKeys, assertKeys, controllerKeys]
430
+ properties:
431
+ did:
432
+ type: string
433
+ isDeleted:
434
+ type: boolean
435
+ authKeys:
436
+ type: array
437
+ items:
438
+ $ref: '#/components/schemas/MultikeyPublicKey'
439
+ assertKeys:
440
+ type: array
441
+ items:
442
+ $ref: '#/components/schemas/MultikeyPublicKey'
443
+ controllerKeys:
444
+ type: array
445
+ items:
446
+ $ref: '#/components/schemas/MultikeyPublicKey'
447
+
448
+ ContentChainResponse:
449
+ type: object
450
+ required: [contentId, genesisCID, log, state]
451
+ properties:
452
+ contentId:
453
+ type: string
454
+ genesisCID:
455
+ type: string
456
+ log:
457
+ type: array
458
+ items:
459
+ type: string
460
+ state:
461
+ type: object
462
+ required:
463
+ [contentId, genesisCID, headCID, isDeleted, currentDocumentCID, length, creatorDID]
464
+ properties:
465
+ contentId:
466
+ type: string
467
+ genesisCID:
468
+ type: string
469
+ headCID:
470
+ type: string
471
+ isDeleted:
472
+ type: boolean
473
+ currentDocumentCID:
474
+ type: string
475
+ nullable: true
476
+ length:
477
+ type: integer
478
+ creatorDID:
479
+ type: string
480
+
481
+ BeaconResponse:
482
+ type: object
483
+ required: [did, jwsToken, beaconCID, payload]
484
+ properties:
485
+ did:
486
+ type: string
487
+ jwsToken:
488
+ type: string
489
+ beaconCID:
490
+ type: string
491
+ payload:
492
+ type: object
493
+ required: [version, type, did, merkleRoot, createdAt]
494
+ properties:
495
+ version:
496
+ type: integer
497
+ enum: [1]
498
+ type:
499
+ type: string
500
+ enum: [beacon]
501
+ did:
502
+ type: string
503
+ merkleRoot:
504
+ type: string
505
+ pattern: '^[0-9a-f]{64}$'
506
+ createdAt:
507
+ type: string
508
+ format: date-time
509
+
510
+ MultikeyPublicKey:
511
+ type: object
512
+ required: [id, type, publicKeyMultibase]
513
+ properties:
514
+ id:
515
+ type: string
516
+ type:
517
+ type: string
518
+ enum: [Multikey]
519
+ publicKeyMultibase:
520
+ type: string
521
+
522
+ Error:
523
+ type: object
524
+ required: [error]
525
+ properties:
526
+ error:
527
+ type: string
528
+
529
+ responses:
530
+ BadRequest:
531
+ description: Invalid request
532
+ content:
533
+ application/json:
534
+ schema:
535
+ $ref: '#/components/schemas/Error'
536
+ NotFound:
537
+ description: Resource not found
538
+ content:
539
+ application/json:
540
+ schema:
541
+ $ref: '#/components/schemas/Error'
542
+ Unauthorized:
543
+ description: Authentication required
544
+ content:
545
+ application/json:
546
+ schema:
547
+ $ref: '#/components/schemas/Error'
548
+ Forbidden:
549
+ description: Insufficient permissions
550
+ content:
551
+ application/json:
552
+ schema:
553
+ $ref: '#/components/schemas/Error'
554
+
555
+ tags:
556
+ - name: Meta
557
+ description: Relay metadata and discovery
558
+ - name: Proof Plane
559
+ description: Public routes for signed chain operations, beacons, and countersignatures
560
+ - name: Content Plane
561
+ description: Authenticated routes for content blob storage and retrieval
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@metalabel/dfos-web-relay",
3
+ "version": "0.3.0",
4
+ "type": "module",
5
+ "description": "DFOS Web Relay — verifying HTTP relay for identity chains, content chains, beacons, and content blobs",
6
+ "license": "MIT",
7
+ "author": "Metalabel <hello@metalabel.com> (https://metalabel.com)",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/metalabel/dfos.git",
11
+ "directory": "packages/dfos-web-relay"
12
+ },
13
+ "homepage": "https://protocol.dfos.com",
14
+ "keywords": [
15
+ "dfos",
16
+ "relay",
17
+ "web-relay",
18
+ "hono",
19
+ "ed25519",
20
+ "jws",
21
+ "did",
22
+ "verifiable",
23
+ "identity",
24
+ "content-addressing",
25
+ "metalabel"
26
+ ],
27
+ "exports": {
28
+ ".": {
29
+ "import": "./dist/index.js",
30
+ "types": "./dist/index.d.ts"
31
+ }
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "RELAY.md",
36
+ "openapi.yaml",
37
+ "LICENSE",
38
+ "README.md"
39
+ ],
40
+ "dependencies": {
41
+ "hono": "^4.12.5",
42
+ "zod": "^4.3.6"
43
+ },
44
+ "peerDependencies": {
45
+ "@metalabel/dfos-protocol": "^0.3.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^24.10.4",
49
+ "tsup": "^8.5.1",
50
+ "vitest": "^4.0.18",
51
+ "@metalabel/dfos-protocol": "0.3.0"
52
+ },
53
+ "scripts": {
54
+ "build": "tsup",
55
+ "clean": "rm -rf dist",
56
+ "typecheck": "tsc --noEmit",
57
+ "test": "vitest run"
58
+ }
59
+ }