@metalabel/dfos-web-relay 0.11.0 → 0.13.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/README.md +12 -12
- package/dist/index.d.ts +10 -0
- package/dist/index.js +24 -14
- package/openapi.yaml +19 -14
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -38,18 +38,18 @@ serve({ port: 4444 });
|
|
|
38
38
|
|
|
39
39
|
## Routes
|
|
40
40
|
|
|
41
|
-
| Method | Path
|
|
42
|
-
| ------ |
|
|
43
|
-
| `GET` | `/.well-known/dfos-relay`
|
|
44
|
-
| `POST` | `/operations`
|
|
45
|
-
| `GET` | `/identities/:did`
|
|
46
|
-
| `GET` | `/content/:contentId`
|
|
47
|
-
| `GET` | `/operations/:cid`
|
|
48
|
-
| `GET` | `/countersignatures/:cid`
|
|
49
|
-
| `GET` | `/operations/:cid/countersignatures`
|
|
50
|
-
| `PUT` | `/content/:contentId/blob/:operationCID`
|
|
51
|
-
| `GET` | `/content/:contentId/blob`
|
|
52
|
-
| `GET` | `/content/:contentId/blob/:ref`
|
|
41
|
+
| Method | Path | Description |
|
|
42
|
+
| ------ | --------------------------------------------- | ----------------------------------------------------------- |
|
|
43
|
+
| `GET` | `/.well-known/dfos-relay` | Relay metadata (DID, protocol version) |
|
|
44
|
+
| `POST` | `/proof/v1/operations` | Submit signed operations (identity, content, countersig) |
|
|
45
|
+
| `GET` | `/proof/v1/identities/:did` | Get identity chain state and operation log |
|
|
46
|
+
| `GET` | `/proof/v1/content/:contentId` | Get content chain state and operation log |
|
|
47
|
+
| `GET` | `/proof/v1/operations/:cid` | Get a single operation by CID |
|
|
48
|
+
| `GET` | `/proof/v1/countersignatures/:cid` | Get countersignatures for an operation |
|
|
49
|
+
| `GET` | `/proof/v1/operations/:cid/countersignatures` | Same as above (alias) |
|
|
50
|
+
| `PUT` | `/content/:contentId/blob/:operationCID` | Upload blob (auth required) |
|
|
51
|
+
| `GET` | `/content/:contentId/blob` | Download blob at head (standing auth, or auth + credential) |
|
|
52
|
+
| `GET` | `/content/:contentId/blob/:ref` | Download blob at specific operation ref |
|
|
53
53
|
|
|
54
54
|
## Blob Authorization
|
|
55
55
|
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ interface RelayOptions {
|
|
|
17
17
|
content?: boolean;
|
|
18
18
|
/** Whether the global operation log is enabled (default: true) */
|
|
19
19
|
log?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Whether this relay accepts writes (default: true). When false, it is a LITE
|
|
22
|
+
* pull-only proof node: POST /proof/v1/operations is rejected (501), so neither
|
|
23
|
+
* client writes nor peer gossip-in are accepted. The node still ingests by
|
|
24
|
+
* PULLING from peers (syncFromPeers polls their /log).
|
|
25
|
+
*/
|
|
26
|
+
write?: boolean;
|
|
20
27
|
/** Peer relay configurations */
|
|
21
28
|
peers?: PeerConfig[];
|
|
22
29
|
/** Injected peer client — if omitted, a default HTTP implementation is used */
|
|
@@ -405,16 +412,19 @@ declare const createKeyResolver: (store: RelayStore) => (kid: string) => Promise
|
|
|
405
412
|
*/
|
|
406
413
|
declare const createHistoricalIdentityResolver: (store: RelayStore) => (did: string) => Promise<{
|
|
407
414
|
authKeys: {
|
|
415
|
+
[x: string]: unknown;
|
|
408
416
|
id: string;
|
|
409
417
|
type: "Multikey";
|
|
410
418
|
publicKeyMultibase: string;
|
|
411
419
|
}[];
|
|
412
420
|
assertKeys: {
|
|
421
|
+
[x: string]: unknown;
|
|
413
422
|
id: string;
|
|
414
423
|
type: "Multikey";
|
|
415
424
|
publicKeyMultibase: string;
|
|
416
425
|
}[];
|
|
417
426
|
controllerKeys: {
|
|
427
|
+
[x: string]: unknown;
|
|
418
428
|
id: string;
|
|
419
429
|
type: "Multikey";
|
|
420
430
|
publicKeyMultibase: string;
|
package/dist/index.js
CHANGED
|
@@ -934,6 +934,9 @@ var bootstrapWithKeyMaterial = async (store, params) => {
|
|
|
934
934
|
return { did, profileArtifactJws };
|
|
935
935
|
};
|
|
936
936
|
|
|
937
|
+
// src/types.ts
|
|
938
|
+
var PROOF_BASE_PATH = "/proof/v1";
|
|
939
|
+
|
|
937
940
|
// src/peer-client.ts
|
|
938
941
|
var createHttpPeerClient = () => {
|
|
939
942
|
const fetchJSON = async (url) => {
|
|
@@ -947,7 +950,7 @@ var createHttpPeerClient = () => {
|
|
|
947
950
|
};
|
|
948
951
|
return {
|
|
949
952
|
async getIdentityLog(peerUrl, did, params) {
|
|
950
|
-
const url = new URL(
|
|
953
|
+
const url = new URL(`${PROOF_BASE_PATH}/identities/${encodeURIComponent(did)}/log`, peerUrl);
|
|
951
954
|
if (params?.after) url.searchParams.set("after", params.after);
|
|
952
955
|
if (params?.limit) url.searchParams.set("limit", String(params.limit));
|
|
953
956
|
const data = await fetchJSON(url.toString());
|
|
@@ -955,7 +958,10 @@ var createHttpPeerClient = () => {
|
|
|
955
958
|
return { entries: data.entries, cursor: data.cursor ?? null };
|
|
956
959
|
},
|
|
957
960
|
async getContentLog(peerUrl, contentId, params) {
|
|
958
|
-
const url = new URL(
|
|
961
|
+
const url = new URL(
|
|
962
|
+
`${PROOF_BASE_PATH}/content/${encodeURIComponent(contentId)}/log`,
|
|
963
|
+
peerUrl
|
|
964
|
+
);
|
|
959
965
|
if (params?.after) url.searchParams.set("after", params.after);
|
|
960
966
|
if (params?.limit) url.searchParams.set("limit", String(params.limit));
|
|
961
967
|
const data = await fetchJSON(url.toString());
|
|
@@ -963,7 +969,7 @@ var createHttpPeerClient = () => {
|
|
|
963
969
|
return { entries: data.entries, cursor: data.cursor ?? null };
|
|
964
970
|
},
|
|
965
971
|
async getOperationLog(peerUrl, params) {
|
|
966
|
-
const url = new URL(
|
|
972
|
+
const url = new URL(`${PROOF_BASE_PATH}/log`, peerUrl);
|
|
967
973
|
if (params?.after) url.searchParams.set("after", params.after);
|
|
968
974
|
if (params?.limit) url.searchParams.set("limit", String(params.limit));
|
|
969
975
|
const data = await fetchJSON(url.toString());
|
|
@@ -972,7 +978,7 @@ var createHttpPeerClient = () => {
|
|
|
972
978
|
},
|
|
973
979
|
async submitOperations(peerUrl, operations) {
|
|
974
980
|
try {
|
|
975
|
-
const res = await fetch(new URL(
|
|
981
|
+
const res = await fetch(new URL(`${PROOF_BASE_PATH}/operations`, peerUrl).toString(), {
|
|
976
982
|
method: "POST",
|
|
977
983
|
headers: { "Content-Type": "application/json" },
|
|
978
984
|
body: JSON.stringify({ operations })
|
|
@@ -1173,6 +1179,7 @@ var createRelay = async (options) => {
|
|
|
1173
1179
|
const { store } = options;
|
|
1174
1180
|
const contentEnabled = options.content !== false;
|
|
1175
1181
|
const logEnabled = options.log !== false;
|
|
1182
|
+
const writeEnabled = options.write !== false;
|
|
1176
1183
|
const maxAuthTokenTTLSeconds = options.maxAuthTokenTTLSeconds ?? DEFAULT_MAX_AUTH_TOKEN_TTL_SECONDS;
|
|
1177
1184
|
const peers = options.peers ?? [];
|
|
1178
1185
|
const peerClient = options.peerClient;
|
|
@@ -1235,14 +1242,17 @@ var createRelay = async (options) => {
|
|
|
1235
1242
|
version: RELAY_VERSION,
|
|
1236
1243
|
capabilities: {
|
|
1237
1244
|
proof: true,
|
|
1245
|
+
write: writeEnabled,
|
|
1238
1246
|
content: contentEnabled,
|
|
1239
|
-
documents: contentEnabled,
|
|
1240
1247
|
log: logEnabled
|
|
1241
1248
|
},
|
|
1242
1249
|
profile: profileArtifactJws
|
|
1243
1250
|
});
|
|
1244
1251
|
});
|
|
1245
|
-
app.post(
|
|
1252
|
+
app.post(`${PROOF_BASE_PATH}/operations`, async (c) => {
|
|
1253
|
+
if (!writeEnabled) {
|
|
1254
|
+
return c.json({ error: "this relay is pull-only; writes are disabled" }, 501);
|
|
1255
|
+
}
|
|
1246
1256
|
if (exceedsBodyCap(c.req.header("content-length"))) {
|
|
1247
1257
|
return c.json({ error: "request body too large" }, 413);
|
|
1248
1258
|
}
|
|
@@ -1259,7 +1269,7 @@ var createRelay = async (options) => {
|
|
|
1259
1269
|
const results = await ingestWithGossip(parsed.data.operations);
|
|
1260
1270
|
return c.json({ results });
|
|
1261
1271
|
});
|
|
1262
|
-
app.get(
|
|
1272
|
+
app.get(`${PROOF_BASE_PATH}/operations/:cid`, async (c) => {
|
|
1263
1273
|
const cid = c.req.param("cid");
|
|
1264
1274
|
const op = await store.getOperation(cid);
|
|
1265
1275
|
if (!op) return c.json({ error: "not found" }, 404);
|
|
@@ -1270,7 +1280,7 @@ var createRelay = async (options) => {
|
|
|
1270
1280
|
chainId: op.chainId
|
|
1271
1281
|
});
|
|
1272
1282
|
});
|
|
1273
|
-
app.get(
|
|
1283
|
+
app.get(`${PROOF_BASE_PATH}/identities/:did/log`, async (c) => {
|
|
1274
1284
|
const did = c.req.param("did");
|
|
1275
1285
|
const chain = await store.getIdentityChain(did);
|
|
1276
1286
|
if (!chain) return c.json({ error: "not found" }, 404);
|
|
@@ -1289,7 +1299,7 @@ var createRelay = async (options) => {
|
|
|
1289
1299
|
const cursor = page.length === limit ? page[page.length - 1].cid : null;
|
|
1290
1300
|
return c.json({ entries: page, cursor });
|
|
1291
1301
|
});
|
|
1292
|
-
app.get(
|
|
1302
|
+
app.get(`${PROOF_BASE_PATH}/identities/:did{.+}`, async (c) => {
|
|
1293
1303
|
const did = c.req.param("did");
|
|
1294
1304
|
let chain = await store.getIdentityChain(did);
|
|
1295
1305
|
if (!chain && readThroughPeers.length > 0 && peerClient) {
|
|
@@ -1316,7 +1326,7 @@ var createRelay = async (options) => {
|
|
|
1316
1326
|
state: chain.state
|
|
1317
1327
|
});
|
|
1318
1328
|
});
|
|
1319
|
-
app.get(
|
|
1329
|
+
app.get(`${PROOF_BASE_PATH}/content/:contentId/log`, async (c) => {
|
|
1320
1330
|
const contentId = c.req.param("contentId");
|
|
1321
1331
|
const chain = await store.getContentChain(contentId);
|
|
1322
1332
|
if (!chain) return c.json({ error: "not found" }, 404);
|
|
@@ -1370,7 +1380,7 @@ var createRelay = async (options) => {
|
|
|
1370
1380
|
nextCursor: result.cursor
|
|
1371
1381
|
});
|
|
1372
1382
|
});
|
|
1373
|
-
app.get(
|
|
1383
|
+
app.get(`${PROOF_BASE_PATH}/content/:contentId`, async (c) => {
|
|
1374
1384
|
const contentId = c.req.param("contentId");
|
|
1375
1385
|
let chain = await store.getContentChain(contentId);
|
|
1376
1386
|
if (!chain && readThroughPeers.length > 0 && peerClient) {
|
|
@@ -1398,7 +1408,7 @@ var createRelay = async (options) => {
|
|
|
1398
1408
|
state: chain.state
|
|
1399
1409
|
});
|
|
1400
1410
|
});
|
|
1401
|
-
app.get(
|
|
1411
|
+
app.get(`${PROOF_BASE_PATH}/countersignatures/:cid`, async (c) => {
|
|
1402
1412
|
const cid = c.req.param("cid");
|
|
1403
1413
|
const op = await store.getOperation(cid);
|
|
1404
1414
|
if (!op) {
|
|
@@ -1409,14 +1419,14 @@ var createRelay = async (options) => {
|
|
|
1409
1419
|
const countersigs = await store.getCountersignatures(cid);
|
|
1410
1420
|
return c.json({ operationCID: cid, countersignatures: countersigs });
|
|
1411
1421
|
});
|
|
1412
|
-
app.get(
|
|
1422
|
+
app.get(`${PROOF_BASE_PATH}/operations/:cid/countersignatures`, async (c) => {
|
|
1413
1423
|
const cid = c.req.param("cid");
|
|
1414
1424
|
const op = await store.getOperation(cid);
|
|
1415
1425
|
if (!op) return c.json({ error: "not found" }, 404);
|
|
1416
1426
|
const countersigs = await store.getCountersignatures(cid);
|
|
1417
1427
|
return c.json({ operationCID: cid, countersignatures: countersigs });
|
|
1418
1428
|
});
|
|
1419
|
-
app.get(
|
|
1429
|
+
app.get(`${PROOF_BASE_PATH}/log`, async (c) => {
|
|
1420
1430
|
if (!logEnabled) return c.json({ error: "global log not available" }, 501);
|
|
1421
1431
|
const afterParam = c.req.query("after");
|
|
1422
1432
|
const limit = parseLimit(c.req.query("limit"), 100, 1e3);
|
package/openapi.yaml
CHANGED
|
@@ -33,7 +33,7 @@ paths:
|
|
|
33
33
|
did:
|
|
34
34
|
type: string
|
|
35
35
|
description: The relay's DID
|
|
36
|
-
example: 'did:dfos:
|
|
36
|
+
example: 'did:dfos:cnnnft9f8a2rn938d6nkz38r847v2kr'
|
|
37
37
|
protocol:
|
|
38
38
|
type: string
|
|
39
39
|
enum: [dfos-web-relay]
|
|
@@ -42,11 +42,14 @@ paths:
|
|
|
42
42
|
example: '0.8.0'
|
|
43
43
|
capabilities:
|
|
44
44
|
type: object
|
|
45
|
-
required: [proof, content, documents, log]
|
|
45
|
+
required: [proof, write, content, documents, log]
|
|
46
46
|
properties:
|
|
47
47
|
proof:
|
|
48
48
|
type: boolean
|
|
49
49
|
description: Always true — a relay without proof plane is not a relay
|
|
50
|
+
write:
|
|
51
|
+
type: boolean
|
|
52
|
+
description: Whether the relay accepts writes via POST /proof/v1/operations (false = lite pull-only node)
|
|
50
53
|
content:
|
|
51
54
|
type: boolean
|
|
52
55
|
description: Whether the relay supports content plane (blob upload/download)
|
|
@@ -55,12 +58,12 @@ paths:
|
|
|
55
58
|
description: Whether the relay serves the documents endpoint
|
|
56
59
|
log:
|
|
57
60
|
type: boolean
|
|
58
|
-
description: Whether the global operation log is available (GET /log)
|
|
61
|
+
description: Whether the global operation log is available (GET /proof/v1/log)
|
|
59
62
|
profile:
|
|
60
63
|
type: string
|
|
61
64
|
description: The relay's profile artifact as a compact JWS token
|
|
62
65
|
|
|
63
|
-
/operations:
|
|
66
|
+
/proof/v1/operations:
|
|
64
67
|
post:
|
|
65
68
|
operationId: ingestOperations
|
|
66
69
|
summary: Submit operations for ingestion
|
|
@@ -99,8 +102,10 @@ paths:
|
|
|
99
102
|
$ref: '#/components/schemas/IngestionResult'
|
|
100
103
|
'400':
|
|
101
104
|
$ref: '#/components/responses/BadRequest'
|
|
105
|
+
'501':
|
|
106
|
+
description: Writes disabled — relay is a lite pull-only node (capabilities.write is false)
|
|
102
107
|
|
|
103
|
-
/operations/{cid}:
|
|
108
|
+
/proof/v1/operations/{cid}:
|
|
104
109
|
get:
|
|
105
110
|
operationId: getOperation
|
|
106
111
|
summary: Get an operation by CID
|
|
@@ -122,7 +127,7 @@ paths:
|
|
|
122
127
|
'404':
|
|
123
128
|
$ref: '#/components/responses/NotFound'
|
|
124
129
|
|
|
125
|
-
/operations/{cid}/countersignatures:
|
|
130
|
+
/proof/v1/operations/{cid}/countersignatures:
|
|
126
131
|
get:
|
|
127
132
|
operationId: getCountersignatures
|
|
128
133
|
summary: Get countersignatures for an operation
|
|
@@ -152,7 +157,7 @@ paths:
|
|
|
152
157
|
'404':
|
|
153
158
|
$ref: '#/components/responses/NotFound'
|
|
154
159
|
|
|
155
|
-
/identities/{did}:
|
|
160
|
+
/proof/v1/identities/{did}:
|
|
156
161
|
get:
|
|
157
162
|
operationId: getIdentityChain
|
|
158
163
|
summary: Get an identity chain by DID
|
|
@@ -163,7 +168,7 @@ paths:
|
|
|
163
168
|
required: true
|
|
164
169
|
schema:
|
|
165
170
|
type: string
|
|
166
|
-
description: 'DID of the identity (e.g., did:dfos:
|
|
171
|
+
description: 'DID of the identity (e.g., did:dfos:cnnnft9f8a2rn938d6nkz38r847v2kr)'
|
|
167
172
|
responses:
|
|
168
173
|
'200':
|
|
169
174
|
description: Identity chain state and log
|
|
@@ -174,7 +179,7 @@ paths:
|
|
|
174
179
|
'404':
|
|
175
180
|
$ref: '#/components/responses/NotFound'
|
|
176
181
|
|
|
177
|
-
/content/{contentId}:
|
|
182
|
+
/proof/v1/content/{contentId}:
|
|
178
183
|
get:
|
|
179
184
|
operationId: getContentChain
|
|
180
185
|
summary: Get a content chain by content ID
|
|
@@ -414,9 +419,9 @@ paths:
|
|
|
414
419
|
'404':
|
|
415
420
|
$ref: '#/components/responses/NotFound'
|
|
416
421
|
'501':
|
|
417
|
-
description:
|
|
422
|
+
description: Content plane not enabled (the documents endpoint is part of the content plane)
|
|
418
423
|
|
|
419
|
-
/log:
|
|
424
|
+
/proof/v1/log:
|
|
420
425
|
get:
|
|
421
426
|
operationId: getLog
|
|
422
427
|
summary: Paginated global log of all accepted operations
|
|
@@ -472,7 +477,7 @@ paths:
|
|
|
472
477
|
'501':
|
|
473
478
|
description: Global log capability not enabled
|
|
474
479
|
|
|
475
|
-
/identities/{did}/log:
|
|
480
|
+
/proof/v1/identities/{did}/log:
|
|
476
481
|
get:
|
|
477
482
|
operationId: getIdentityLog
|
|
478
483
|
summary: Paginated log of identity chain operations
|
|
@@ -525,7 +530,7 @@ paths:
|
|
|
525
530
|
'404':
|
|
526
531
|
$ref: '#/components/responses/NotFound'
|
|
527
532
|
|
|
528
|
-
/content/{contentId}/log:
|
|
533
|
+
/proof/v1/content/{contentId}/log:
|
|
529
534
|
get:
|
|
530
535
|
operationId: getContentLog
|
|
531
536
|
summary: Paginated log of content chain operations
|
|
@@ -578,7 +583,7 @@ paths:
|
|
|
578
583
|
'404':
|
|
579
584
|
$ref: '#/components/responses/NotFound'
|
|
580
585
|
|
|
581
|
-
/countersignatures/{cid}:
|
|
586
|
+
/proof/v1/countersignatures/{cid}:
|
|
582
587
|
get:
|
|
583
588
|
operationId: getCountersignaturesByCID
|
|
584
589
|
summary: Get countersignatures for an operation CID
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metalabel/dfos-web-relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DFOS Web Relay — verifying HTTP relay for identity chains, content chains, and content blobs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"zod": "^4.4.3"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@metalabel/dfos-protocol": "^0.
|
|
48
|
+
"@metalabel/dfos-protocol": "^0.13.0"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@types/node": "^24.10.4",
|
|
52
52
|
"tsup": "^8.5.1",
|
|
53
53
|
"tsx": "^4.22.4",
|
|
54
54
|
"vitest": "^4.1.8",
|
|
55
|
-
"@metalabel/dfos-protocol": "0.
|
|
55
|
+
"@metalabel/dfos-protocol": "0.13.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup",
|