@metalabel/dfos-protocol 0.0.2 → 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.
@@ -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;
@@ -124,12 +138,12 @@ declare const decodeMultikey: (multibase: string) => {
124
138
  */
125
139
  declare const deriveChainIdentifier: (cidBytes: Uint8Array, prefix: string) => string;
126
140
  /**
127
- * Derive a bare entity identifier from CID bytes
141
+ * Derive a bare content identifier from CID bytes
128
142
  *
129
143
  * Returns the raw 22-char hash with no prefix. Applications may add
130
144
  * their own prefix for routing (e.g., post_xxxx) — that's semantic sugar.
131
145
  */
132
- declare const deriveEntityId: (cidBytes: Uint8Array) => string;
146
+ declare const deriveContentId: (cidBytes: Uint8Array) => string;
133
147
 
134
148
  /**
135
149
  * Sign an identity operation as a JWS and derive the operation CID
@@ -156,8 +170,8 @@ declare const verifyIdentityChain: (input: {
156
170
  }) => Promise<VerifiedIdentity>;
157
171
 
158
172
  interface VerifiedContentChain {
159
- /** Entity identifier — bare 22-char hash derived from genesis CID */
160
- entityId: string;
173
+ /** Content identifier — bare 22-char hash derived from genesis CID */
174
+ contentId: string;
161
175
  /** CID of the genesis operation */
162
176
  genesisCID: string;
163
177
  /** CID of the most recent operation */
@@ -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, deriveEntityId, 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,
@@ -7,15 +8,21 @@ import {
7
8
  VerifiedIdentity,
8
9
  decodeMultikey,
9
10
  deriveChainIdentifier,
10
- deriveEntityId,
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-LWC4PWGZ.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,
@@ -24,10 +31,15 @@ export {
24
31
  VerifiedIdentity,
25
32
  decodeMultikey,
26
33
  deriveChainIdentifier,
27
- deriveEntityId,
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";
@@ -127,7 +140,7 @@ var deriveChainIdentifier = (cidBytes, prefix) => {
127
140
  const id = generateIdNoPrefix({ seed: cidBytes });
128
141
  return `${prefix}:${id}`;
129
142
  };
130
- var deriveEntityId = (cidBytes) => {
143
+ var deriveContentId = (cidBytes) => {
131
144
  return generateIdNoPrefix({ seed: cidBytes });
132
145
  };
133
146
 
@@ -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`);
@@ -289,7 +305,7 @@ var signContentOperation = async (input) => {
289
305
  var verifyContentChain = async (input) => {
290
306
  if (input.log.length === 0) throw new Error("log must have at least one operation");
291
307
  const state = {
292
- entityId: null,
308
+ contentId: null,
293
309
  genesisCID: null,
294
310
  headCID: null,
295
311
  isDeleted: false,
@@ -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 });
@@ -339,7 +364,7 @@ var verifyContentChain = async (input) => {
339
364
  }
340
365
  if (idx === 0) {
341
366
  state.genesisCID = operationCID;
342
- state.entityId = deriveEntityId(encoded.cid.bytes);
367
+ state.contentId = deriveContentId(encoded.cid.bytes);
343
368
  }
344
369
  state.headCID = operationCID;
345
370
  state.previousCID = operationCID;
@@ -358,7 +383,7 @@ var verifyContentChain = async (input) => {
358
383
  }
359
384
  }
360
385
  return {
361
- entityId: state.entityId,
386
+ contentId: state.contentId,
362
387
  genesisCID: state.genesisCID,
363
388
  headCID: state.headCID,
364
389
  isDeleted: state.isDeleted,
@@ -367,19 +392,164 @@ 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,
378
543
  decodeMultikey,
379
544
  deriveChainIdentifier,
380
- deriveEntityId,
545
+ deriveContentId,
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, deriveEntityId, encodeEd25519Multikey, signContentOperation, signIdentityOperation, verifyContentChain, verifyIdentityChain } from './chain/index.js';
3
- export { ChainStore, EntityOperationsParams, EntityOperationsResponse, IdentityOperationsParams, IdentityOperationsResponse, OperationEntry, PaginationParams, RegistryError, ResolveEntityResponse, 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';