@metalabel/dfos-protocol 0.0.3 → 0.1.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 CHANGED
@@ -15,17 +15,17 @@ npm install @metalabel/dfos-protocol
15
15
  import { verifyContentChain, verifyIdentityChain } from '@metalabel/dfos-protocol/chain';
16
16
  // Crypto primitives
17
17
  import { createJws, dagCborCanonicalEncode, verifyJws } from '@metalabel/dfos-protocol/crypto';
18
- // Reference registry server
19
- import { createRegistryServer } from '@metalabel/dfos-protocol/registry';
18
+ // Merkle trees
19
+ import { buildMerkleTree, verifyMerkleProof } from '@metalabel/dfos-protocol/merkle';
20
20
  ```
21
21
 
22
22
  ## Subpath Exports
23
23
 
24
- | Export | Description |
25
- | ----------------------------------- | ------------------------------------------------------------------- |
26
- | `@metalabel/dfos-protocol/chain` | Identity and content chain signing, verification, multikey encoding |
27
- | `@metalabel/dfos-protocol/crypto` | Ed25519, JWS, JWT, dag-cbor, base64url, ID generation |
28
- | `@metalabel/dfos-protocol/registry` | Hono-based registry server, in-memory store, Zod schemas |
24
+ | Export | Description |
25
+ | --------------------------------- | ----------------------------------------------------------------------- |
26
+ | `@metalabel/dfos-protocol/chain` | Identity and content chain signing, verification, beacons, countersigns |
27
+ | `@metalabel/dfos-protocol/crypto` | Ed25519, JWS, JWT, dag-cbor, base64url, ID generation |
28
+ | `@metalabel/dfos-protocol/merkle` | SHA-256 binary merkle tree, inclusion proofs |
29
29
 
30
30
  ## Specifications
31
31
 
@@ -33,9 +33,7 @@ import { createRegistryServer } from '@metalabel/dfos-protocol/registry';
33
33
  | -------------------------------------- | -------------------------------------------------------------- |
34
34
  | [PROTOCOL.md](./PROTOCOL.md) | Core protocol — chains, signatures, verification, test vectors |
35
35
  | [DID-METHOD.md](./DID-METHOD.md) | W3C DID method specification for `did:dfos` |
36
- | [CONTENT-MODEL.md](./CONTENT-MODEL.md) | Standard content schemas (post, profile, media) |
37
- | [REGISTRY-API.md](./REGISTRY-API.md) | HTTP API for chain storage and resolution |
38
- | [openapi.yaml](./openapi.yaml) | OpenAPI 3.1 machine-readable registry spec |
36
+ | [CONTENT-MODEL.md](./CONTENT-MODEL.md) | Standard content schemas (post, profile, manifest) |
39
37
 
40
38
  ## Examples
41
39
 
@@ -77,24 +77,38 @@ type VerifiedIdentity = z.infer<typeof VerifiedIdentity>;
77
77
  declare const ContentOperation: z.ZodDiscriminatedUnion<[z.ZodObject<{
78
78
  version: z.ZodLiteral<1>;
79
79
  type: z.ZodLiteral<"create">;
80
+ did: z.ZodString;
80
81
  documentCID: z.ZodString;
82
+ baseDocumentCID: z.ZodNullable<z.ZodString>;
81
83
  createdAt: z.ZodISODateTime;
82
84
  note: z.ZodNullable<z.ZodString>;
83
85
  }, z.core.$strict>, z.ZodObject<{
84
86
  version: z.ZodLiteral<1>;
85
87
  type: z.ZodLiteral<"update">;
88
+ did: z.ZodString;
86
89
  previousOperationCID: z.ZodString;
87
90
  documentCID: z.ZodNullable<z.ZodString>;
91
+ baseDocumentCID: z.ZodNullable<z.ZodString>;
88
92
  createdAt: z.ZodISODateTime;
89
93
  note: z.ZodNullable<z.ZodString>;
90
94
  }, z.core.$strict>, z.ZodObject<{
91
95
  version: z.ZodLiteral<1>;
92
96
  type: z.ZodLiteral<"delete">;
97
+ did: z.ZodString;
93
98
  previousOperationCID: z.ZodString;
94
99
  createdAt: z.ZodISODateTime;
95
100
  note: z.ZodNullable<z.ZodString>;
96
101
  }, z.core.$strict>], "type">;
97
102
  type ContentOperation = z.infer<typeof ContentOperation>;
103
+ /** Beacon: floating signed merkle root announcement */
104
+ declare const BeaconPayload: z.ZodObject<{
105
+ version: z.ZodLiteral<1>;
106
+ type: z.ZodLiteral<"beacon">;
107
+ did: z.ZodString;
108
+ merkleRoot: z.ZodString;
109
+ createdAt: z.ZodISODateTime;
110
+ }, z.core.$strict>;
111
+ type BeaconPayload = z.infer<typeof BeaconPayload>;
98
112
 
99
113
  /** Ed25519 public key multicodec value */
100
114
  declare const ED25519_PUB_MULTICODEC = 237;
@@ -193,4 +207,82 @@ declare const verifyContentChain: (input: {
193
207
  resolveKey: (kid: string) => Promise<Uint8Array>;
194
208
  }) => Promise<VerifiedContentChain>;
195
209
 
196
- export { ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, type Signer, type VerifiedContentChain, VerifiedIdentity, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, signContentOperation, signIdentityOperation, verifyContentChain, verifyIdentityChain };
210
+ /**
211
+ * Sign a beacon announcement as a JWS
212
+ */
213
+ declare const signBeacon: (input: {
214
+ payload: BeaconPayload;
215
+ signer: Signer;
216
+ kid: string;
217
+ }) => Promise<{
218
+ jwsToken: string;
219
+ beaconCID: string;
220
+ }>;
221
+ interface VerifiedBeacon {
222
+ payload: BeaconPayload;
223
+ beaconCID: string;
224
+ }
225
+ /**
226
+ * Verify a beacon JWS — signature, CID, payload schema, clock skew
227
+ */
228
+ declare const verifyBeacon: (input: {
229
+ jwsToken: string;
230
+ resolveKey: (kid: string) => Promise<Uint8Array>;
231
+ /** Current time for clock skew check (defaults to Date.now()) */
232
+ now?: number;
233
+ }) => Promise<VerifiedBeacon>;
234
+
235
+ /**
236
+ * Sign an existing content operation as a countersignature (witness JWS)
237
+ *
238
+ * The witness signs the same payload as the author, producing a different
239
+ * JWS token with their own kid. The CID is identical because the payload
240
+ * is identical.
241
+ */
242
+ declare const signCountersignature: (input: {
243
+ /** The original operation payload (must include did of the author) */
244
+ operationPayload: ContentOperation;
245
+ /** Witness signer */
246
+ signer: Signer;
247
+ /** Witness kid — DID URL of the witness (must differ from payload.did) */
248
+ kid: string;
249
+ }) => Promise<{
250
+ jwsToken: string;
251
+ operationCID: string;
252
+ }>;
253
+ interface VerifiedCountersignature {
254
+ operationCID: string;
255
+ /** The DID that authored the operation (payload.did) */
256
+ authorDID: string;
257
+ /** The DID that witnessed the operation (kid DID) */
258
+ witnessDID: string;
259
+ }
260
+ /**
261
+ * Verify a countersignature JWS against an expected operation CID
262
+ *
263
+ * Checks: valid signature, CID matches, kid DID differs from payload did
264
+ */
265
+ declare const verifyCountersignature: (input: {
266
+ jwsToken: string;
267
+ expectedCID: string;
268
+ resolveKey: (kid: string) => Promise<Uint8Array>;
269
+ }) => Promise<VerifiedCountersignature>;
270
+ interface VerifiedBeaconCountersignature {
271
+ beaconCID: string;
272
+ /** The DID that controls the beacon (payload.did) */
273
+ controllerDID: string;
274
+ /** The DID that witnessed the beacon (kid DID) */
275
+ witnessDID: string;
276
+ }
277
+ /**
278
+ * Verify a beacon countersignature JWS against an expected beacon CID
279
+ *
280
+ * Checks: valid signature, CID matches, kid DID differs from payload did
281
+ */
282
+ declare const verifyBeaconCountersignature: (input: {
283
+ jwsToken: string;
284
+ expectedCID: string;
285
+ resolveKey: (kid: string) => Promise<Uint8Array>;
286
+ }) => Promise<VerifiedBeaconCountersignature>;
287
+
288
+ export { BeaconPayload, ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, type Signer, type VerifiedBeacon, type VerifiedBeaconCountersignature, type VerifiedContentChain, type VerifiedCountersignature, VerifiedIdentity, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, signBeacon, signContentOperation, signCountersignature, signIdentityOperation, verifyBeacon, verifyBeaconCountersignature, verifyContentChain, verifyCountersignature, verifyIdentityChain };
@@ -1,4 +1,5 @@
1
1
  import {
2
+ BeaconPayload,
2
3
  ContentOperation,
3
4
  ED25519_PRIV_MULTICODEC,
4
5
  ED25519_PUB_MULTICODEC,
@@ -9,13 +10,19 @@ import {
9
10
  deriveChainIdentifier,
10
11
  deriveContentId,
11
12
  encodeEd25519Multikey,
13
+ signBeacon,
12
14
  signContentOperation,
15
+ signCountersignature,
13
16
  signIdentityOperation,
17
+ verifyBeacon,
18
+ verifyBeaconCountersignature,
14
19
  verifyContentChain,
20
+ verifyCountersignature,
15
21
  verifyIdentityChain
16
- } from "../chunk-VEBMLR37.js";
22
+ } from "../chunk-ASGEXSVT.js";
17
23
  import "../chunk-ZXXP5W5N.js";
18
24
  export {
25
+ BeaconPayload,
19
26
  ContentOperation,
20
27
  ED25519_PRIV_MULTICODEC,
21
28
  ED25519_PUB_MULTICODEC,
@@ -26,8 +33,13 @@ export {
26
33
  deriveChainIdentifier,
27
34
  deriveContentId,
28
35
  encodeEd25519Multikey,
36
+ signBeacon,
29
37
  signContentOperation,
38
+ signCountersignature,
30
39
  signIdentityOperation,
40
+ verifyBeacon,
41
+ verifyBeaconCountersignature,
31
42
  verifyContentChain,
43
+ verifyCountersignature,
32
44
  verifyIdentityChain
33
45
  };
@@ -13,6 +13,7 @@ var MAX_PUBLIC_KEY_MULTIBASE = 128;
13
13
  var MAX_CID = 256;
14
14
  var MAX_NOTE = 256;
15
15
  var MAX_KEYS_PER_ROLE = 16;
16
+ var MAX_DID = 256;
16
17
  var MultikeyPublicKey = z.strictObject({
17
18
  id: z.string().max(MAX_KEY_ID),
18
19
  type: z.literal("Multikey"),
@@ -49,7 +50,7 @@ var IdentityOperation = z.discriminatedUnion("type", [
49
50
  IdentityDelete
50
51
  ]);
51
52
  var VerifiedIdentity = z.strictObject({
52
- did: z.string(),
53
+ did: z.string().max(MAX_DID),
53
54
  isDeleted: z.boolean(),
54
55
  authKeys: z.array(MultikeyPublicKey).max(MAX_KEYS_PER_ROLE),
55
56
  assertKeys: z.array(MultikeyPublicKey).max(MAX_KEYS_PER_ROLE),
@@ -58,21 +59,26 @@ var VerifiedIdentity = z.strictObject({
58
59
  var ContentCreate = z.strictObject({
59
60
  version: z.literal(1),
60
61
  type: z.literal("create"),
62
+ did: z.string().max(MAX_DID),
61
63
  documentCID: CIDString,
64
+ baseDocumentCID: CIDString.nullable(),
62
65
  createdAt: Iso8601,
63
66
  note: z.string().max(MAX_NOTE).nullable()
64
67
  });
65
68
  var ContentUpdate = z.strictObject({
66
69
  version: z.literal(1),
67
70
  type: z.literal("update"),
71
+ did: z.string().max(MAX_DID),
68
72
  previousOperationCID: CIDString,
69
73
  documentCID: CIDString.nullable(),
74
+ baseDocumentCID: CIDString.nullable(),
70
75
  createdAt: Iso8601,
71
76
  note: z.string().max(MAX_NOTE).nullable()
72
77
  });
73
78
  var ContentDelete = z.strictObject({
74
79
  version: z.literal(1),
75
80
  type: z.literal("delete"),
81
+ did: z.string().max(MAX_DID),
76
82
  previousOperationCID: CIDString,
77
83
  createdAt: Iso8601,
78
84
  note: z.string().max(MAX_NOTE).nullable()
@@ -82,6 +88,13 @@ var ContentOperation = z.discriminatedUnion("type", [
82
88
  ContentUpdate,
83
89
  ContentDelete
84
90
  ]);
91
+ var BeaconPayload = z.strictObject({
92
+ version: z.literal(1),
93
+ type: z.literal("beacon"),
94
+ did: z.string().max(MAX_DID),
95
+ merkleRoot: z.string().regex(/^[0-9a-f]{64}$/),
96
+ createdAt: Iso8601
97
+ });
85
98
 
86
99
  // src/chain/multikey.ts
87
100
  import { base58btc } from "multiformats/bases/base58";
@@ -164,6 +177,9 @@ var verifyIdentityChain = async (input) => {
164
177
  throw new Error(`log[${idx}]: ${messages}`);
165
178
  }
166
179
  const op = result.data;
180
+ if (decoded.header.typ !== "did:dfos:identity-op") {
181
+ throw new Error(`log[${idx}]: invalid typ: ${decoded.header.typ}`);
182
+ }
167
183
  if (state.isDeleted) throw new Error(`log[${idx}]: cannot modify a deleted identity`);
168
184
  if (idx === 0 && op.type !== "create") {
169
185
  throw new Error(`log[${idx}]: first operation must be create`);
@@ -306,6 +322,9 @@ var verifyContentChain = async (input) => {
306
322
  throw new Error(`log[${idx}]: ${messages}`);
307
323
  }
308
324
  const op = result.data;
325
+ if (decoded.header.typ !== "did:dfos:content-op") {
326
+ throw new Error(`log[${idx}]: invalid typ: ${decoded.header.typ}`);
327
+ }
309
328
  if (state.isDeleted) throw new Error(`log[${idx}]: cannot extend a deleted chain`);
310
329
  if (idx === 0 && op.type !== "create") {
311
330
  throw new Error(`log[${idx}]: first operation must be create`);
@@ -323,6 +342,12 @@ var verifyContentChain = async (input) => {
323
342
  }
324
343
  }
325
344
  const kid = decoded.header.kid;
345
+ const hashIdx = kid.indexOf("#");
346
+ if (hashIdx < 0) throw new Error(`log[${idx}]: kid must be a DID URL`);
347
+ const kidDid = kid.substring(0, hashIdx);
348
+ if (kidDid !== op.did) {
349
+ throw new Error(`log[${idx}]: kid DID does not match operation did`);
350
+ }
326
351
  const publicKey = await input.resolveKey(kid);
327
352
  try {
328
353
  verifyJws({ token: jwsToken, publicKey });
@@ -367,11 +392,151 @@ var verifyContentChain = async (input) => {
367
392
  };
368
393
  };
369
394
 
395
+ // src/chain/beacon.ts
396
+ var signBeacon = async (input) => {
397
+ const encoded = await dagCborCanonicalEncode(input.payload);
398
+ const beaconCID = encoded.cid.toString();
399
+ const jwsToken = await createJws({
400
+ header: { alg: "EdDSA", typ: "did:dfos:beacon", kid: input.kid, cid: beaconCID },
401
+ payload: input.payload,
402
+ sign: input.signer
403
+ });
404
+ return { jwsToken, beaconCID };
405
+ };
406
+ var MAX_FUTURE_MS = 5 * 60 * 1e3;
407
+ var verifyBeacon = async (input) => {
408
+ const decoded = decodeJwsUnsafe(input.jwsToken);
409
+ if (!decoded) throw new Error("failed to decode beacon JWS");
410
+ const result = BeaconPayload.safeParse(decoded.payload);
411
+ if (!result.success) {
412
+ const messages = result.error.issues.map((e) => e.message).join(", ");
413
+ throw new Error(`invalid beacon payload: ${messages}`);
414
+ }
415
+ const payload = result.data;
416
+ if (decoded.header.typ !== "did:dfos:beacon") {
417
+ throw new Error(`invalid beacon typ: ${decoded.header.typ}`);
418
+ }
419
+ const kid = decoded.header.kid;
420
+ const hashIdx = kid.indexOf("#");
421
+ if (hashIdx < 0) throw new Error("beacon kid must be a DID URL");
422
+ const kidDid = kid.substring(0, hashIdx);
423
+ if (kidDid !== payload.did) {
424
+ throw new Error("beacon kid DID does not match payload did");
425
+ }
426
+ const publicKey = await input.resolveKey(kid);
427
+ try {
428
+ verifyJws({ token: input.jwsToken, publicKey });
429
+ } catch {
430
+ throw new Error("invalid beacon signature");
431
+ }
432
+ const encoded = await dagCborCanonicalEncode(payload);
433
+ const beaconCID = encoded.cid.toString();
434
+ if (!decoded.header.cid) throw new Error("missing cid in beacon header");
435
+ if (decoded.header.cid !== beaconCID) throw new Error("beacon cid mismatch");
436
+ const now = input.now ?? Date.now();
437
+ const beaconTime = new Date(payload.createdAt).getTime();
438
+ if (beaconTime > now + MAX_FUTURE_MS) {
439
+ throw new Error("beacon createdAt is too far in the future");
440
+ }
441
+ return { payload, beaconCID };
442
+ };
443
+
444
+ // src/chain/countersign.ts
445
+ var signCountersignature = async (input) => {
446
+ const encoded = await dagCborCanonicalEncode(input.operationPayload);
447
+ const operationCID = encoded.cid.toString();
448
+ const jwsToken = await createJws({
449
+ header: { alg: "EdDSA", typ: "did:dfos:content-op", kid: input.kid, cid: operationCID },
450
+ payload: input.operationPayload,
451
+ sign: input.signer
452
+ });
453
+ return { jwsToken, operationCID };
454
+ };
455
+ var verifyCountersignature = async (input) => {
456
+ const decoded = decodeJwsUnsafe(input.jwsToken);
457
+ if (!decoded) throw new Error("failed to decode countersignature JWS");
458
+ if (decoded.header.typ !== "did:dfos:content-op") {
459
+ throw new Error(`invalid countersignature typ: ${decoded.header.typ}`);
460
+ }
461
+ const result = ContentOperation.safeParse(decoded.payload);
462
+ if (!result.success) {
463
+ const messages = result.error.issues.map((e) => e.message).join(", ");
464
+ throw new Error(`invalid operation payload: ${messages}`);
465
+ }
466
+ const op = result.data;
467
+ const encoded = await dagCborCanonicalEncode(op);
468
+ const operationCID = encoded.cid.toString();
469
+ if (operationCID !== input.expectedCID) {
470
+ throw new Error("countersignature CID does not match expected CID");
471
+ }
472
+ if (decoded.header.cid !== operationCID) {
473
+ throw new Error("countersignature header cid mismatch");
474
+ }
475
+ const kid = decoded.header.kid;
476
+ const publicKey = await input.resolveKey(kid);
477
+ try {
478
+ verifyJws({ token: input.jwsToken, publicKey });
479
+ } catch {
480
+ throw new Error("invalid countersignature");
481
+ }
482
+ const hashIdx = kid.indexOf("#");
483
+ if (hashIdx < 0) throw new Error("countersignature kid must be a DID URL");
484
+ const witnessDID = kid.substring(0, hashIdx);
485
+ if (witnessDID === op.did) {
486
+ throw new Error("countersignature kid DID must differ from operation did (not a witness)");
487
+ }
488
+ return {
489
+ operationCID,
490
+ authorDID: op.did,
491
+ witnessDID
492
+ };
493
+ };
494
+ var verifyBeaconCountersignature = async (input) => {
495
+ const decoded = decodeJwsUnsafe(input.jwsToken);
496
+ if (!decoded) throw new Error("failed to decode beacon countersignature JWS");
497
+ if (decoded.header.typ !== "did:dfos:beacon") {
498
+ throw new Error(`invalid beacon countersignature typ: ${decoded.header.typ}`);
499
+ }
500
+ const result = BeaconPayload.safeParse(decoded.payload);
501
+ if (!result.success) {
502
+ const messages = result.error.issues.map((e) => e.message).join(", ");
503
+ throw new Error(`invalid beacon payload: ${messages}`);
504
+ }
505
+ const beacon = result.data;
506
+ const encoded = await dagCborCanonicalEncode(beacon);
507
+ const beaconCID = encoded.cid.toString();
508
+ if (beaconCID !== input.expectedCID) {
509
+ throw new Error("beacon countersignature CID does not match expected CID");
510
+ }
511
+ if (decoded.header.cid !== beaconCID) {
512
+ throw new Error("beacon countersignature header cid mismatch");
513
+ }
514
+ const kid = decoded.header.kid;
515
+ const publicKey = await input.resolveKey(kid);
516
+ try {
517
+ verifyJws({ token: input.jwsToken, publicKey });
518
+ } catch {
519
+ throw new Error("invalid beacon countersignature");
520
+ }
521
+ const hashIdx = kid.indexOf("#");
522
+ if (hashIdx < 0) throw new Error("beacon countersignature kid must be a DID URL");
523
+ const witnessDID = kid.substring(0, hashIdx);
524
+ if (witnessDID === beacon.did) {
525
+ throw new Error("beacon countersignature kid DID must differ from beacon did (not a witness)");
526
+ }
527
+ return {
528
+ beaconCID,
529
+ controllerDID: beacon.did,
530
+ witnessDID
531
+ };
532
+ };
533
+
370
534
  export {
371
535
  MultikeyPublicKey,
372
536
  IdentityOperation,
373
537
  VerifiedIdentity,
374
538
  ContentOperation,
539
+ BeaconPayload,
375
540
  ED25519_PUB_MULTICODEC,
376
541
  ED25519_PRIV_MULTICODEC,
377
542
  encodeEd25519Multikey,
@@ -381,5 +546,10 @@ export {
381
546
  signIdentityOperation,
382
547
  verifyIdentityChain,
383
548
  signContentOperation,
384
- verifyContentChain
549
+ verifyContentChain,
550
+ signBeacon,
551
+ verifyBeacon,
552
+ signCountersignature,
553
+ verifyCountersignature,
554
+ verifyBeaconCountersignature
385
555
  };
@@ -0,0 +1,99 @@
1
+ // src/merkle/tree.ts
2
+ var sha256 = async (data) => {
3
+ const buf = await crypto.subtle.digest("SHA-256", data);
4
+ return new Uint8Array(buf);
5
+ };
6
+ var toHex = (bytes) => {
7
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
8
+ };
9
+ var hexToBytes = (hex) => {
10
+ const bytes = new Uint8Array(hex.length / 2);
11
+ for (let i = 0; i < hex.length; i += 2) {
12
+ bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
13
+ }
14
+ return bytes;
15
+ };
16
+ var concat = (a, b) => {
17
+ const result = new Uint8Array(a.length + b.length);
18
+ result.set(a, 0);
19
+ result.set(b, a.length);
20
+ return result;
21
+ };
22
+ var hashLeaf = async (contentId) => {
23
+ return sha256(new TextEncoder().encode(contentId));
24
+ };
25
+ var hashInterior = async (left, right) => {
26
+ return sha256(concat(left, right));
27
+ };
28
+ var buildMerkleTree = async (contentIds) => {
29
+ const sorted = [...new Set(contentIds)].sort();
30
+ if (sorted.length === 0) return { root: null, leafCount: 0 };
31
+ let level = await Promise.all(sorted.map(hashLeaf));
32
+ while (level.length > 1) {
33
+ const nextLevel = [];
34
+ for (let i = 0; i < level.length; i += 2) {
35
+ if (i + 1 < level.length) {
36
+ nextLevel.push(await hashInterior(level[i], level[i + 1]));
37
+ } else {
38
+ nextLevel.push(level[i]);
39
+ }
40
+ }
41
+ level = nextLevel;
42
+ }
43
+ return { root: toHex(level[0]), leafCount: sorted.length };
44
+ };
45
+
46
+ // src/merkle/proof.ts
47
+ var generateMerkleProof = async (contentIds, targetId) => {
48
+ const sorted = [...new Set(contentIds)].sort();
49
+ const targetIdx = sorted.indexOf(targetId);
50
+ if (targetIdx < 0) return null;
51
+ const { root } = await buildMerkleTree(sorted);
52
+ if (!root) return null;
53
+ const leaves = await Promise.all(sorted.map(hashLeaf));
54
+ const path = [];
55
+ let level = leaves;
56
+ let idx = targetIdx;
57
+ while (level.length > 1) {
58
+ const nextLevel = [];
59
+ const nextIdx = Math.floor(idx / 2);
60
+ for (let i = 0; i < level.length; i += 2) {
61
+ if (i + 1 < level.length) {
62
+ if (i === idx || i + 1 === idx) {
63
+ const siblingIdx = i === idx ? i + 1 : i;
64
+ path.push({
65
+ hash: toHex(level[siblingIdx]),
66
+ position: siblingIdx < idx ? "left" : "right"
67
+ });
68
+ }
69
+ const interior = await sha256(concat(level[i], level[i + 1]));
70
+ nextLevel.push(interior);
71
+ } else {
72
+ nextLevel.push(level[i]);
73
+ }
74
+ }
75
+ level = nextLevel;
76
+ idx = nextIdx;
77
+ }
78
+ return { contentId: targetId, root, path };
79
+ };
80
+ var verifyMerkleProof = async (proof) => {
81
+ let current = await hashLeaf(proof.contentId);
82
+ for (const step of proof.path) {
83
+ const sibling = hexToBytes(step.hash);
84
+ if (step.position === "left") {
85
+ current = await sha256(concat(sibling, current));
86
+ } else {
87
+ current = await sha256(concat(current, sibling));
88
+ }
89
+ }
90
+ return toHex(current) === proof.root;
91
+ };
92
+
93
+ export {
94
+ hexToBytes,
95
+ hashLeaf,
96
+ buildMerkleTree,
97
+ generateMerkleProof,
98
+ verifyMerkleProof
99
+ };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  export { JwsHeader, JwsVerificationError, JwtClaims, JwtCreateOptions, JwtHeader, JwtVerificationError, JwtVerifyOptions, PrefixedID, base64urlDecode, base64urlEncode, createJws, createJwt, createNewEd25519Keypair, dagCborCanonicalEncode, decodeJwsUnsafe, decodeJwtUnsafe, generateId, generateIdNoPrefix, importEd25519Keypair, isCanonicallyEqual, isValidEd25519Signature, isValidId, normalizedId, parseDagCborCID, signPayloadEd25519, verifyJws, verifyJwt } from './crypto/index.js';
2
- export { ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, Signer, VerifiedContentChain, VerifiedIdentity, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, signContentOperation, signIdentityOperation, verifyContentChain, verifyIdentityChain } from './chain/index.js';
3
- export { ChainStore, ContentOperationsParams, ContentOperationsResponse, IdentityOperationsParams, IdentityOperationsResponse, OperationEntry, PaginationParams, RegistryError, ResolveContentResponse, ResolveIdentityResponse, ResolveOperationResponse, StoredChain, SubmitContentChainRequest, SubmitContentChainResponse, SubmitIdentityChainRequest, SubmitIdentityChainResponse, createRegistryServer } from './registry/index.js';
2
+ export { BeaconPayload, ContentOperation, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, MultikeyPublicKey, Signer, VerifiedBeacon, VerifiedBeaconCountersignature, VerifiedContentChain, VerifiedCountersignature, VerifiedIdentity, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, signBeacon, signContentOperation, signCountersignature, signIdentityOperation, verifyBeacon, verifyBeaconCountersignature, verifyContentChain, verifyCountersignature, verifyIdentityChain } from './chain/index.js';
3
+ export { MerkleProof, buildMerkleTree, generateMerkleProof, hashLeaf, hexToBytes, verifyMerkleProof } from './merkle/index.js';
4
4
  import 'multiformats';
5
5
  import 'multiformats/cid';
6
6
  import 'zod';
7
- import 'hono/types';
8
- import 'hono';
package/dist/index.js CHANGED
@@ -1,20 +1,5 @@
1
1
  import {
2
- ChainStore,
3
- ContentOperationsParams,
4
- ContentOperationsResponse,
5
- IdentityOperationsParams,
6
- IdentityOperationsResponse,
7
- RegistryError,
8
- ResolveContentResponse,
9
- ResolveIdentityResponse,
10
- ResolveOperationResponse,
11
- SubmitContentChainRequest,
12
- SubmitContentChainResponse,
13
- SubmitIdentityChainRequest,
14
- SubmitIdentityChainResponse,
15
- createRegistryServer
16
- } from "./chunk-U6DANYPT.js";
17
- import {
2
+ BeaconPayload,
18
3
  ContentOperation,
19
4
  ED25519_PRIV_MULTICODEC,
20
5
  ED25519_PUB_MULTICODEC,
@@ -25,11 +10,16 @@ import {
25
10
  deriveChainIdentifier,
26
11
  deriveContentId,
27
12
  encodeEd25519Multikey,
13
+ signBeacon,
28
14
  signContentOperation,
15
+ signCountersignature,
29
16
  signIdentityOperation,
17
+ verifyBeacon,
18
+ verifyBeaconCountersignature,
30
19
  verifyContentChain,
20
+ verifyCountersignature,
31
21
  verifyIdentityChain
32
- } from "./chunk-VEBMLR37.js";
22
+ } from "./chunk-ASGEXSVT.js";
33
23
  import {
34
24
  JwsVerificationError,
35
25
  JwtVerificationError,
@@ -53,34 +43,29 @@ import {
53
43
  verifyJws,
54
44
  verifyJwt
55
45
  } from "./chunk-ZXXP5W5N.js";
46
+ import {
47
+ buildMerkleTree,
48
+ generateMerkleProof,
49
+ hashLeaf,
50
+ hexToBytes,
51
+ verifyMerkleProof
52
+ } from "./chunk-E5CFQG2B.js";
56
53
  export {
57
- ChainStore,
54
+ BeaconPayload,
58
55
  ContentOperation,
59
- ContentOperationsParams,
60
- ContentOperationsResponse,
61
56
  ED25519_PRIV_MULTICODEC,
62
57
  ED25519_PUB_MULTICODEC,
63
58
  IdentityOperation,
64
- IdentityOperationsParams,
65
- IdentityOperationsResponse,
66
59
  JwsVerificationError,
67
60
  JwtVerificationError,
68
61
  MultikeyPublicKey,
69
- RegistryError,
70
- ResolveContentResponse,
71
- ResolveIdentityResponse,
72
- ResolveOperationResponse,
73
- SubmitContentChainRequest,
74
- SubmitContentChainResponse,
75
- SubmitIdentityChainRequest,
76
- SubmitIdentityChainResponse,
77
62
  VerifiedIdentity,
78
63
  base64urlDecode,
79
64
  base64urlEncode,
65
+ buildMerkleTree,
80
66
  createJws,
81
67
  createJwt,
82
68
  createNewEd25519Keypair,
83
- createRegistryServer,
84
69
  dagCborCanonicalEncode,
85
70
  decodeJwsUnsafe,
86
71
  decodeJwtUnsafe,
@@ -90,17 +75,26 @@ export {
90
75
  encodeEd25519Multikey,
91
76
  generateId,
92
77
  generateIdNoPrefix,
78
+ generateMerkleProof,
79
+ hashLeaf,
80
+ hexToBytes,
93
81
  importEd25519Keypair,
94
82
  isCanonicallyEqual,
95
83
  isValidEd25519Signature,
96
84
  isValidId,
97
85
  normalizedId,
98
86
  parseDagCborCID,
87
+ signBeacon,
99
88
  signContentOperation,
89
+ signCountersignature,
100
90
  signIdentityOperation,
101
91
  signPayloadEd25519,
92
+ verifyBeacon,
93
+ verifyBeaconCountersignature,
102
94
  verifyContentChain,
95
+ verifyCountersignature,
103
96
  verifyIdentityChain,
104
97
  verifyJws,
105
- verifyJwt
98
+ verifyJwt,
99
+ verifyMerkleProof
106
100
  };