@metalabel/dfos-protocol 0.50.0 → 0.51.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
@@ -38,17 +38,14 @@ import { signKeyProof, verifyKeyProof } from '@metalabel/dfos-protocol/key-proof
38
38
 
39
39
  ## Specifications
40
40
 
41
- | Document | Description |
42
- | ------------------------------------------------ | --------------------------------------------------------------------------------- |
43
- | [PROTOCOL.md](../../specs/PROTOCOL.md) | Core protocol — chains, signatures, verification, test vectors |
44
- | [DID-METHOD.md](../../specs/DID-METHOD.md) | W3C DID method specification for `did:dfos` |
45
- | [CONTENT-MODEL.md](../../specs/CONTENT-MODEL.md) | Standard content schemas (post, profile) |
46
- | [CREDENTIALS.md](../../specs/CREDENTIALS.md) | UCAN-style authorization credentials for the DFOS protocol |
47
- | [CREDITS.md](../../specs/CREDITS.md) | Verifiable attribution for DFOS content |
48
- | [SIGNING.md](../../specs/SIGNING.md) | A transport-agnostic way for one party to ask another to produce a DFOS signature |
49
- | [SIWD.md](../../specs/SIWD.md) | Sign In With DFOS — cryptographic identity verification for third-party apps |
50
- | [API-AUTH.md](../../specs/API-AUTH.md) | Proof-of-possession authentication for credential-gated HTTP APIs |
51
- | [KEY-PROOF.md](../../specs/KEY-PROOF.md) | Challenge-bound, single-shot proof that a candidate key is held |
41
+ | Document | Description |
42
+ | ------------------------------------------------ | ------------------------------------------------------------------------------- |
43
+ | [PROTOCOL.md](../../specs/PROTOCOL.md) | Core protocol — chains, signatures, key possession, verification, test vectors |
44
+ | [DID-METHOD.md](../../specs/DID-METHOD.md) | W3C DID method specification for `did:dfos` |
45
+ | [CONTENT-MODEL.md](../../specs/CONTENT-MODEL.md) | Standard content schemas (post, profile) and verifiable attribution |
46
+ | [CREDENTIALS.md](../../specs/CREDENTIALS.md) | UCAN-style authorization credentials for the DFOS protocol |
47
+ | [RELAY.md](../../specs/RELAY.md) | The relay HTTP surface, including the sign-request envelope and signing mailbox |
48
+ | [INTEGRATIONS.md](../../specs/INTEGRATIONS.md) | Sign in, API authentication, origin binding, and key ceremonies |
52
49
 
53
50
  Release history lives at https://github.com/metalabel/dfos/releases.
54
51
 
@@ -1,5 +1,5 @@
1
- import { I as IdentityOperation, u as Signer, w as VerifiedIdentity, S as ServiceEntry, c as ContentOperation, R as RevocationChecker, d as CountersignPayload, a as ArtifactPayload } from '../dfos-credential-BtiYPqBT.js';
2
- export { A as ARTIFACT_CID_ANCHOR_RE, C as CONTENT_ID_ANCHOR_RE, f as CreditClaimPayload, g as DeclaredKeyState, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, r as RevocationPayload, s as ServicesArray, t as SignRequestPayload, x as VoidKeyMembership, F as parseProtocolTimestampUnix } from '../dfos-credential-BtiYPqBT.js';
1
+ import { I as IdentityOperation, v as Signer, x as VerifiedIdentity, S as ServiceEntry, c as ContentOperation, R as ResolvedIdentity, r as RevocationChecker, d as CountersignPayload, a as ArtifactPayload } from '../dfos-credential-CtG0_n4P.js';
2
+ export { A as ARTIFACT_CID_ANCHOR_RE, C as CONTENT_ID_ANCHOR_RE, f as CreditClaimPayload, g as DeclaredKeyState, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, s as RevocationPayload, t as ServicesArray, u as SignRequestPayload, y as VoidKeyMembership, G as parseProtocolTimestampUnix } from '../dfos-credential-CtG0_n4P.js';
3
3
  import 'zod';
4
4
 
5
5
  /** Ed25519 public key multicodec value */
@@ -54,13 +54,26 @@ declare const signIdentityOperation: (input: {
54
54
  * Verify a log of JWS identity operations and derive the identity
55
55
  *
56
56
  * Walks the chain from genesis, verifying signatures and chain integrity, and
57
- * folds the possession proofs alongside. Returns the final verified identity
58
- * state: EFFECTIVE key arrays, with the declared arrays and the void memberships
57
+ * folds the possession proofs alongside. Returns the verified identity state:
58
+ * EFFECTIVE key arrays, with the declared arrays and the void memberships
59
59
  * beside them.
60
+ *
61
+ * THE BASIS SELECTS WHICH STATE COMES BACK, never how much of the log is
62
+ * verified. With `asOf` set, the whole log is walked and validated exactly as
63
+ * without it, and the state returned is the one the last operation dated at or
64
+ * before the basis folds to — the identity's state as of that instant
65
+ * (PROTOCOL, Time basis). The comparison is byte-wise on the `createdAt`
66
+ * strings, per PROTOCOL's Comparison basis. A basis earlier than genesis has no
67
+ * state to name and throws, and so does an empty basis.
60
68
  */
61
69
  declare const verifyIdentityChain: (input: {
62
70
  didPrefix: string;
63
71
  log: string[];
72
+ /**
73
+ * Basis time, in the `createdAt` grammar. Omitted = the chain head, which is
74
+ * the state as of now.
75
+ */
76
+ asOf?: string;
64
77
  }) => Promise<VerifiedIdentity>;
65
78
  /**
66
79
  * Verify a single new operation against already-verified identity state
@@ -70,10 +83,13 @@ declare const verifyIdentityChain: (input: {
70
83
  * verified genesis). This function performs one signature verification and one
71
84
  * state transition — constant time regardless of chain length.
72
85
  *
73
- * Note: key-ID consistency across the full chain history is NOT checked here.
74
- * That invariant is established during genesis verification and maintained by
75
- * the protocol's key consistency rules. Periodic full re-verification can
76
- * audit this property.
86
+ * KEY-ID CONSISTENCY IS CHECKED HERE, on the same terms as the full walk. The
87
+ * trusted state carries `seenKeys` the id-to-material binding the walk already
88
+ * enforced so an operation that re-points an existing key id at new material
89
+ * is REJECTED on this path exactly as a replay of the same two operations would
90
+ * reject it. Without that the fast path would accept a chain its own
91
+ * re-verification refuses, and a relay's linear path is the path almost every
92
+ * operation takes.
77
93
  *
78
94
  * THE POSSESSION FOLD RUNS HERE TOO, and it needs both halves of the trusted
79
95
  * state: the EFFECTIVE arrays (to know what an introduction is a transition out
@@ -164,8 +180,11 @@ declare const signContentOperation: (input: {
164
180
  */
165
181
  declare const verifyContentChain: (input: {
166
182
  log: string[];
167
- /** Resolve a kid (DID URL) to the raw Ed25519 public key bytes */
168
- resolveKey: (kid: string) => Promise<Uint8Array>;
183
+ /**
184
+ * Resolve a kid (DID URL) to the raw Ed25519 public key bytes, in the signing
185
+ * identity's state as of `basis` — each operation's own `createdAt`.
186
+ */
187
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
169
188
  /**
170
189
  * Enforce creator-sovereignty authorization. When true, non-creator signers
171
190
  * must include a DFOS credential in the operation's `authorization` field
@@ -173,10 +192,11 @@ declare const verifyContentChain: (input: {
173
192
  */
174
193
  enforceAuthorization?: boolean;
175
194
  /**
176
- * Resolve a DID to a VerifiedIdentity. Required when `enforceAuthorization`
177
- * is true, as credential verification needs identity resolution.
195
+ * Resolve a DID to a ResolvedIdentity as of `basis`. Required when
196
+ * `enforceAuthorization` is true, as credential verification needs identity
197
+ * resolution.
178
198
  */
179
- resolveIdentity?: (did: string) => Promise<VerifiedIdentity | undefined>;
199
+ resolveIdentity?: (did: string, basis?: string) => Promise<ResolvedIdentity | undefined>;
180
200
  /**
181
201
  * Check whether a credential (leaf or parent) has been revoked. Called with
182
202
  * `asOfUnix` = each operation's own `createdAt`, so a fold of committed
@@ -198,12 +218,15 @@ declare const verifyContentExtensionFromTrustedState: (input: {
198
218
  lastCreatedAt: string;
199
219
  /** The new JWS operation to verify */
200
220
  newOp: string;
201
- /** Resolve a kid (DID URL) to the raw Ed25519 public key bytes */
202
- resolveKey: (kid: string) => Promise<Uint8Array>;
221
+ /**
222
+ * Resolve a kid (DID URL) to the raw Ed25519 public key bytes, in the signing
223
+ * identity's state as of `basis` — the new operation's own `createdAt`.
224
+ */
225
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
203
226
  /** Enforce creator-sovereignty authorization (see verifyContentChain) */
204
227
  enforceAuthorization?: boolean;
205
- /** Resolve a DID to a VerifiedIdentity. Required when enforceAuthorization is true. */
206
- resolveIdentity?: (did: string) => Promise<VerifiedIdentity | undefined>;
228
+ /** Resolve a DID to a ResolvedIdentity as of `basis`. Required when enforceAuthorization is true. */
229
+ resolveIdentity?: (did: string, basis?: string) => Promise<ResolvedIdentity | undefined>;
207
230
  /**
208
231
  * Check whether a credential (leaf or parent) has been revoked. Called with
209
232
  * `asOfUnix` = the new operation's own `createdAt`. See `RevocationChecker`.
@@ -242,10 +265,15 @@ declare const signCountersignature: (input: {
242
265
  * Checks: valid signature, CID integrity, payload schema. Does NOT check
243
266
  * whether the target exists or whether the witness differs from the target
244
267
  * author — those are relay-level semantic checks.
268
+ *
269
+ * The countersignature's own `createdAt` is the basis: a committed statement
270
+ * resolves its signer in the state that held when it was signed (PROTOCOL, Time
271
+ * basis).
245
272
  */
246
273
  declare const verifyCountersignature: (input: {
247
274
  jwsToken: string;
248
- resolveKey: (kid: string) => Promise<Uint8Array>;
275
+ /** Resolve a kid to key bytes in the signer's state as of `basis`. */
276
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
249
277
  }) => Promise<VerifiedCountersignature>;
250
278
 
251
279
  interface VerifiedArtifact {
@@ -267,10 +295,14 @@ declare const signArtifact: (input: {
267
295
  }>;
268
296
  /**
269
297
  * Verify an artifact JWS — signature, CID, payload schema, size limit
298
+ *
299
+ * The artifact's own `createdAt` is the basis: a committed statement resolves
300
+ * its signer in the state that held when it was signed (PROTOCOL, Time basis).
270
301
  */
271
302
  declare const verifyArtifact: (input: {
272
303
  jwsToken: string;
273
- resolveKey: (kid: string) => Promise<Uint8Array>;
304
+ /** Resolve a kid to key bytes in the signer's state as of `basis`. */
305
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
274
306
  }) => Promise<VerifiedArtifact>;
275
307
 
276
308
  interface VerifiedRevocation {
@@ -299,10 +331,14 @@ declare const signRevocation: (input: {
299
331
  }>;
300
332
  /**
301
333
  * Verify a revocation JWS — signature, CID, payload schema, signer match
334
+ *
335
+ * The revocation's own `createdAt` is the basis: a committed statement resolves
336
+ * its signer in the state that held when it was signed (PROTOCOL, Time basis).
302
337
  */
303
338
  declare const verifyRevocation: (input: {
304
339
  jwsToken: string;
305
- resolveKey: (kid: string) => Promise<Uint8Array>;
340
+ /** Resolve a kid to key bytes in the signer's state as of `basis`. */
341
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
306
342
  }) => Promise<VerifiedRevocation>;
307
343
 
308
344
  interface VerifiedCreditClaim {
@@ -322,7 +358,7 @@ interface VerifiedCreditClaim {
322
358
  claimCID: string;
323
359
  }
324
360
  /**
325
- * The four states a `credits[]` entry resolves to (see `specs/CREDITS.md`).
361
+ * The four states a `credits[]` entry resolves to (see `specs/CONTENT-MODEL.md`).
326
362
  *
327
363
  * `invalid` and `unverifiable` are deliberately distinct and MUST NOT be
328
364
  * collapsed: `invalid` means "checked and failed" (a positive signal that
@@ -52,8 +52,8 @@ import {
52
52
  verifyIdentityExtensionFromTrustedState,
53
53
  verifyRevocation,
54
54
  verifySignRequest
55
- } from "../chunk-JGFFADPR.js";
56
- import "../chunk-MHSBBYDO.js";
55
+ } from "../chunk-LMAPBTDW.js";
56
+ import "../chunk-T2YR5KWQ.js";
57
57
  import "../chunk-JVSC67DC.js";
58
58
  import {
59
59
  ED25519_PRIV_MULTICODEC,
@@ -6,7 +6,7 @@ import {
6
6
  matchesResource,
7
7
  verifyDFOSCredential,
8
8
  verifyDelegationChain
9
- } from "./chunk-MHSBBYDO.js";
9
+ } from "./chunk-T2YR5KWQ.js";
10
10
  import {
11
11
  KEY_ADD_JWS_TYP,
12
12
  KEY_ROLES,
@@ -102,7 +102,7 @@ var IdentityUpdate = z.looseObject({
102
102
  createdAt: Iso8601,
103
103
  /**
104
104
  * POSSESSION PROOFS FOR THE KEYS THIS OPERATION INTRODUCES — compact key-proof
105
- * JWS strings (specs/KEY-PROOF.md), each signed by the key it speaks for.
105
+ * JWS strings (specs/PROTOCOL.md § Key possession), each signed by the key it speaks for.
106
106
  *
107
107
  * Optional, and optional in the CID-neutral sense: omitting it encodes
108
108
  * identically to an operation that never had it (undefined strips under
@@ -188,7 +188,31 @@ var VerifiedIdentity = z.strictObject({
188
188
  * re-deriving it from the raw log under a declared-state rule that quietly
189
189
  * disagrees with this one.
190
190
  */
191
- provedKeys: DeclaredKeyState.optional()
191
+ provedKeys: DeclaredKeyState.optional(),
192
+ /**
193
+ * HAS-EVER-BEEN-DECLARED, as a key-id-to-material binding: one entry per key
194
+ * id the chain has ever written, carrying the Multikey that id went in with,
195
+ * in the order the walk first saw it.
196
+ *
197
+ * A KEY ID IS BOUND TO ONE KEY FOR THE LIFE OF A CHAIN. The full walk enforces
198
+ * that chain-wide and rejects the operation that breaks it — the id is a
199
+ * stable name, so re-pointing it at new material would silently re-aim every
200
+ * artifact that ever referenced it. The incremental extension verifier has no
201
+ * log to re-scan, so it needs the binding handed to it, and this member is how
202
+ * it arrives: with it, the fast path returns the SAME verdict as a full replay
203
+ * on the same operation, which is the property that keeps a relay's linear
204
+ * path from accepting a chain its own re-verification would refuse.
205
+ *
206
+ * Broader than `declared` and than `provedKeys` on purpose. The rule covers
207
+ * every id the chain wrote, including one declared into a void membership and
208
+ * later dropped — neither of the other two members remembers that id, and it
209
+ * is exactly the id an attacker would reuse.
210
+ *
211
+ * Optional because a caller may hand the extension verifier a hand-built or
212
+ * older state. Absent, the binding is read off `declared` and `provedKeys`,
213
+ * which is complete for any chain that never dropped an unproved key id.
214
+ */
215
+ seenKeys: z.array(MultikeyPublicKey).optional()
192
216
  });
193
217
  var ContentCreate = z.looseObject({
194
218
  version: z.literal(1),
@@ -333,7 +357,9 @@ var unionProved = (into, next) => {
333
357
  return merged;
334
358
  };
335
359
  var foldEffectiveKeyState = (input) => {
336
- const carried = (role, key) => input.priorEffective[ROLE_ARRAY[role]].some((prior) => prior.id === key.id);
360
+ const carried = (role, key) => input.priorEffective[ROLE_ARRAY[role]].some(
361
+ (prior) => prior.id === key.id && prior.type === key.type && prior.publicKeyMultibase === key.publicKeyMultibase
362
+ );
337
363
  const introduced = /* @__PURE__ */ new Map();
338
364
  for (const role of KEY_ROLES) {
339
365
  for (const key of input.declared[ROLE_ARRAY[role]]) {
@@ -379,6 +405,26 @@ var foldEffectiveKeyState = (input) => {
379
405
  }
380
406
  return { effective, voidKeys };
381
407
  };
408
+ var admitKeyMaterial = (index, keys) => {
409
+ for (const key of keys) {
410
+ const existing = index.get(key.id);
411
+ if (!existing) {
412
+ index.set(key.id, key);
413
+ } else if (existing.publicKeyMultibase !== key.publicKeyMultibase || existing.type !== key.type) {
414
+ throw new Error(`key ${key.id} type or public key inconsistency`);
415
+ }
416
+ }
417
+ };
418
+ var keyMaterialIndex = (keys) => {
419
+ const index = /* @__PURE__ */ new Map();
420
+ for (const key of keys) if (!index.has(key.id)) index.set(key.id, key);
421
+ return index;
422
+ };
423
+ var flatKeys = (state) => [
424
+ ...state.authKeys,
425
+ ...state.assertKeys,
426
+ ...state.controllerKeys
427
+ ];
382
428
  var assertSingleKeyGenesis = (op) => {
383
429
  if (op.authKeys.length !== 1 || op.assertKeys.length !== 1 || op.controllerKeys.length !== 1) {
384
430
  throw new Error("create must declare exactly one key in each of auth, assert and controller");
@@ -407,6 +453,7 @@ var signIdentityOperation = async (input) => {
407
453
  };
408
454
  var verifyIdentityChain = async (input) => {
409
455
  if (input.log.length === 0) throw new Error("log must have at least one operation");
456
+ if (input.asOf === "") throw new Error("basis must not be empty");
410
457
  const state = {
411
458
  did: void 0,
412
459
  isDeleted: false,
@@ -422,6 +469,7 @@ var verifyIdentityChain = async (input) => {
422
469
  services: [],
423
470
  seenKeys: /* @__PURE__ */ new Map()
424
471
  };
472
+ let asOfState;
425
473
  for (const [idx, jwsToken] of input.log.entries()) {
426
474
  const decoded = decodeJwsUnsafe(jwsToken);
427
475
  if (!decoded) throw new Error(`log[${idx}]: failed to decode JWS`);
@@ -472,19 +520,15 @@ var verifyIdentityChain = async (input) => {
472
520
  }
473
521
  }
474
522
  if (op.type === "create" || op.type === "update") {
475
- const incomingKeys = [...op.authKeys, ...op.assertKeys, ...op.controllerKeys];
476
- const currentKeys = [
477
- ...state.declared.authKeys,
478
- ...state.declared.assertKeys,
479
- ...state.declared.controllerKeys
480
- ];
481
- for (const k of [...currentKeys, ...incomingKeys]) {
482
- const existing = state.seenKeys.get(k.id);
483
- if (!existing) {
484
- state.seenKeys.set(k.id, k);
485
- } else if (existing.publicKeyMultibase !== k.publicKeyMultibase || existing.type !== k.type) {
486
- throw new Error(`log[${idx}]: key ${k.id} type or public key inconsistency`);
487
- }
523
+ try {
524
+ admitKeyMaterial(state.seenKeys, [
525
+ ...flatKeys(state.declared),
526
+ ...op.authKeys,
527
+ ...op.assertKeys,
528
+ ...op.controllerKeys
529
+ ]);
530
+ } catch (e) {
531
+ throw new Error(`log[${idx}]: ${e.message}`);
488
532
  }
489
533
  [op.authKeys, op.assertKeys, op.controllerKeys].forEach((keys) => {
490
534
  const set = new Set(keys.map((k) => k.id));
@@ -578,8 +622,26 @@ var verifyIdentityChain = async (input) => {
578
622
  state.isDeleted = false;
579
623
  break;
580
624
  }
625
+ if (input.asOf !== void 0 && op.createdAt <= input.asOf) {
626
+ asOfState = {
627
+ did,
628
+ isDeleted: state.isDeleted,
629
+ authKeys: state.effective.authKeys,
630
+ assertKeys: state.effective.assertKeys,
631
+ controllerKeys: state.effective.controllerKeys,
632
+ services: state.services,
633
+ declared: state.declared,
634
+ voidKeys: state.voidKeys,
635
+ provedKeys: state.provedKeys,
636
+ seenKeys: [...state.seenKeys.values()]
637
+ };
638
+ }
581
639
  }
582
640
  if (!state.did) throw new Error("did is not set");
641
+ if (input.asOf !== void 0) {
642
+ if (!asOfState) throw new Error(`identity has no state as of ${input.asOf}`);
643
+ return asOfState;
644
+ }
583
645
  return {
584
646
  did: state.did,
585
647
  isDeleted: state.isDeleted,
@@ -590,7 +652,10 @@ var verifyIdentityChain = async (input) => {
590
652
  services: state.services,
591
653
  declared: state.declared,
592
654
  voidKeys: state.voidKeys,
593
- provedKeys: state.provedKeys
655
+ provedKeys: state.provedKeys,
656
+ // The id-to-material binding the walk enforced, handed on so an incremental
657
+ // extension of this state can enforce the identical rule without the log.
658
+ seenKeys: [...state.seenKeys.values()]
594
659
  };
595
660
  };
596
661
  var verifyIdentityExtensionFromTrustedState = async (input) => {
@@ -598,6 +663,10 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
598
663
  const priorEffective = keyStateOf(currentState);
599
664
  const priorDeclared = currentState.declared ?? priorEffective;
600
665
  const priorProved = currentState.provedKeys ?? priorEffective;
666
+ const seenKeys = keyMaterialIndex(
667
+ currentState.seenKeys ?? [...flatKeys(priorDeclared), ...flatKeys(priorProved)]
668
+ );
669
+ const priorSeenKeys = [...seenKeys.values()];
601
670
  const decoded = decodeJwsUnsafe(newOp);
602
671
  if (!decoded) throw new Error("failed to decode JWS");
603
672
  const result = IdentityOperation.safeParse(decoded.payload);
@@ -653,6 +722,7 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
653
722
  throw new Error("invalid signature");
654
723
  }
655
724
  if (op.type === "update") {
725
+ admitKeyMaterial(seenKeys, [...op.authKeys, ...op.assertKeys, ...op.controllerKeys]);
656
726
  [op.authKeys, op.assertKeys, op.controllerKeys].forEach((keys) => {
657
727
  const set = new Set(keys.map((k) => k.id));
658
728
  if (set.size !== keys.length) {
@@ -682,7 +752,8 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
682
752
  services: op.services ?? [],
683
753
  declared,
684
754
  voidKeys: folded.voidKeys,
685
- provedKeys: unionProved(priorProved, folded.effective)
755
+ provedKeys: unionProved(priorProved, folded.effective),
756
+ seenKeys: [...seenKeys.values()]
686
757
  };
687
758
  }
688
759
  // delete and restore introduce nothing, so both key states — and the void
@@ -697,7 +768,8 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
697
768
  services: currentState.services,
698
769
  declared: priorDeclared,
699
770
  voidKeys: currentState.voidKeys ?? [],
700
- provedKeys: priorProved
771
+ provedKeys: priorProved,
772
+ seenKeys: priorSeenKeys
701
773
  };
702
774
  case "restore":
703
775
  return {
@@ -709,7 +781,8 @@ var verifyIdentityExtensionFromTrustedState = async (input) => {
709
781
  services: currentState.services,
710
782
  declared: priorDeclared,
711
783
  voidKeys: currentState.voidKeys ?? [],
712
- provedKeys: priorProved
784
+ provedKeys: priorProved,
785
+ seenKeys: priorSeenKeys
713
786
  };
714
787
  }
715
788
  })();
@@ -750,7 +823,7 @@ var verifyOperationAuthorization = async (input) => {
750
823
  const opCreatedAtUnix = Math.floor(new Date(input.createdAt).getTime() / 1e3);
751
824
  const credential = await verifyDFOSCredential(input.authorization, {
752
825
  resolveIdentity: input.resolveIdentity,
753
- now: opCreatedAtUnix
826
+ basis: input.createdAt
754
827
  });
755
828
  if (input.isRevoked && await input.isRevoked(credential.iss, credential.credentialCID, opCreatedAtUnix)) {
756
829
  throw new Error("credential is revoked");
@@ -758,8 +831,8 @@ var verifyOperationAuthorization = async (input) => {
758
831
  await verifyDelegationChain(credential, {
759
832
  resolveIdentity: input.resolveIdentity,
760
833
  rootDID: input.creatorDID,
761
- now: opCreatedAtUnix,
762
- ...input.isRevoked ? { isRevoked: input.isRevoked, asOfUnix: opCreatedAtUnix } : {}
834
+ basis: input.createdAt,
835
+ ...input.isRevoked ? { isRevoked: input.isRevoked } : {}
763
836
  });
764
837
  if (credential.aud !== "*" && credential.aud !== input.operationDID) {
765
838
  throw new Error(
@@ -821,7 +894,7 @@ var verifyContentChain = async (input) => {
821
894
  if (kidDid !== op.did) {
822
895
  throw new Error(`log[${idx}]: kid DID does not match operation did`);
823
896
  }
824
- const publicKey = await input.resolveKey(kid);
897
+ const publicKey = await input.resolveKey(kid, op.createdAt);
825
898
  try {
826
899
  verifyJws({ token: jwsToken, publicKey });
827
900
  } catch {
@@ -931,7 +1004,7 @@ var verifyContentExtensionFromTrustedState = async (input) => {
931
1004
  if (kidDid !== op.did) {
932
1005
  throw new Error("kid DID does not match operation did");
933
1006
  }
934
- const publicKey = await resolveKey(kid);
1007
+ const publicKey = await resolveKey(kid, op.createdAt);
935
1008
  try {
936
1009
  verifyJws({ token: newOp, publicKey });
937
1010
  } catch {
@@ -1009,7 +1082,7 @@ var verifyCountersignature = async (input) => {
1009
1082
  if (kidDid !== payload.did) {
1010
1083
  throw new Error("countersignature kid DID does not match payload did");
1011
1084
  }
1012
- const publicKey = await input.resolveKey(kid);
1085
+ const publicKey = await input.resolveKey(kid, payload.createdAt);
1013
1086
  try {
1014
1087
  verifyJws({ token: input.jwsToken, publicKey });
1015
1088
  } catch {
@@ -1062,7 +1135,7 @@ var verifyArtifact = async (input) => {
1062
1135
  if (kidDid !== payload.did) {
1063
1136
  throw new Error("artifact kid DID does not match payload did");
1064
1137
  }
1065
- const publicKey = await input.resolveKey(kid);
1138
+ const publicKey = await input.resolveKey(kid, payload.createdAt);
1066
1139
  try {
1067
1140
  verifyJws({ token: input.jwsToken, publicKey });
1068
1141
  } catch {
@@ -1119,7 +1192,7 @@ var verifyRevocation = async (input) => {
1119
1192
  if (kidDid !== payload.did) {
1120
1193
  throw new Error("revocation kid DID does not match payload did");
1121
1194
  }
1122
- const publicKey = await input.resolveKey(kid);
1195
+ const publicKey = await input.resolveKey(kid, payload.createdAt);
1123
1196
  try {
1124
1197
  verifyJws({ token: input.jwsToken, publicKey });
1125
1198
  } catch {
@@ -370,20 +370,26 @@ var isDependencyMissing = (err) => err instanceof Error && err.dependencyMissing
370
370
  var carryDependencyMissing = (cause, wrapped) => isDependencyMissing(cause) ? markDependencyMissing(wrapped) : wrapped;
371
371
 
372
372
  // src/credentials/dfos-credential.ts
373
- var resolveKeyFromIdentity = (identity, kid) => {
373
+ var resolveKeyFromIdentity = (identity, kid, determinate) => {
374
374
  const hashIdx = kid.indexOf("#");
375
375
  if (hashIdx < 0) throw new CredentialVerificationError("kid must be a DID URL");
376
376
  const keyId = kid.substring(hashIdx + 1);
377
377
  const allKeys = [...identity.authKeys, ...identity.assertKeys, ...identity.controllerKeys];
378
378
  const key = allKeys.find((k) => k.id === keyId);
379
379
  if (!key) {
380
- throw markDependencyMissing(
381
- new CredentialVerificationError(`key ${keyId} not found on identity ${identity.did}`)
380
+ const miss = new CredentialVerificationError(
381
+ `unknown key ${keyId} on identity ${identity.did}`
382
382
  );
383
+ throw determinate ? miss : markDependencyMissing(miss);
383
384
  }
384
385
  const { keyBytes } = decodeMultikey(key.publicKeyMultibase);
385
386
  return keyBytes;
386
387
  };
388
+ var basisUnixSeconds = (basis) => {
389
+ const ms = Date.parse(basis);
390
+ if (Number.isNaN(ms)) throw new CredentialVerificationError(`invalid basis time: ${basis}`);
391
+ return Math.floor(ms / 1e3);
392
+ };
387
393
  var createDFOSCredential = async (options) => {
388
394
  const kid = `${options.issuerDID}#${options.keyId}`;
389
395
  const now = options.iat ?? Math.floor(Date.now() / 1e3);
@@ -435,7 +441,7 @@ var verifyDFOSCredential = async (jwsToken, options) => {
435
441
  if (kidDid !== payload.iss) {
436
442
  throw new CredentialVerificationError("credential kid DID does not match iss");
437
443
  }
438
- const identity = await options.resolveIdentity(payload.iss);
444
+ const identity = await options.resolveIdentity(payload.iss, options.basis);
439
445
  if (!identity) {
440
446
  throw markDependencyMissing(
441
447
  new CredentialVerificationError(`issuer identity not found: ${payload.iss}`)
@@ -444,7 +450,7 @@ var verifyDFOSCredential = async (jwsToken, options) => {
444
450
  if (identity.isDeleted) {
445
451
  throw new CredentialVerificationError(`issuer identity is deleted: ${payload.iss}`);
446
452
  }
447
- const publicKey = resolveKeyFromIdentity(identity, kid);
453
+ const publicKey = resolveKeyFromIdentity(identity, kid, identity.basisDeterminate === true);
448
454
  try {
449
455
  verifyJws({ token: jwsToken, publicKey });
450
456
  } catch {
@@ -458,11 +464,8 @@ var verifyDFOSCredential = async (jwsToken, options) => {
458
464
  if (decoded.header.cid !== credentialCID) {
459
465
  throw new CredentialVerificationError("credential cid mismatch");
460
466
  }
461
- const now = options.now ?? Math.floor(Date.now() / 1e3);
462
- if (payload.iat > now) {
463
- throw new CredentialVerificationError("credential not yet valid (iat is in the future)");
464
- }
465
- if (payload.exp <= now) {
467
+ const basisSeconds = options.basis !== void 0 ? basisUnixSeconds(options.basis) : options.nowUnix ?? Math.floor(Date.now() / 1e3);
468
+ if (payload.exp <= basisSeconds) {
466
469
  throw new CredentialVerificationError("credential expired");
467
470
  }
468
471
  return {
@@ -478,6 +481,7 @@ var verifyDFOSCredential = async (jwsToken, options) => {
478
481
  };
479
482
  var verifyDelegationChain = async (credential, options) => {
480
483
  const chain = [credential];
484
+ const revocationBasis = options.basis !== void 0 ? basisUnixSeconds(options.basis) : void 0;
481
485
  let current = credential;
482
486
  const maxDepth = 16;
483
487
  for (let depth = 0; depth < maxDepth; depth++) {
@@ -496,10 +500,11 @@ var verifyDelegationChain = async (credential, options) => {
496
500
  }
497
501
  const parent = await verifyDFOSCredential(current.prf[0], {
498
502
  resolveIdentity: options.resolveIdentity,
499
- ...options.now !== void 0 ? { now: options.now } : {}
503
+ ...options.basis !== void 0 ? { basis: options.basis } : {},
504
+ ...options.nowUnix !== void 0 ? { nowUnix: options.nowUnix } : {}
500
505
  });
501
506
  if (options.isRevoked) {
502
- const revoked = await options.isRevoked(parent.iss, parent.credentialCID, options.asOfUnix);
507
+ const revoked = await options.isRevoked(parent.iss, parent.credentialCID, revocationBasis);
503
508
  if (revoked) {
504
509
  throw new CredentialVerificationError("parent credential in delegation chain is revoked");
505
510
  }
@@ -1,4 +1,4 @@
1
- export { b as Attenuation, e as CredentialVerificationError, D as DFOSCredentialPayload, i as MAX_CREDENTIAL_SIZE, R as RevocationChecker, V as VerifiedDFOSCredential, v as VerifiedDelegationChain, y as createDFOSCredential, z as decodeDFOSCredentialUnsafe, B as isAttenuated, E as matchesResource, G as verifyDFOSCredential, H as verifyDelegationChain } from '../dfos-credential-BtiYPqBT.js';
1
+ export { b as Attenuation, e as CredentialVerificationError, D as DFOSCredentialPayload, i as MAX_CREDENTIAL_SIZE, R as ResolvedIdentity, r as RevocationChecker, V as VerifiedDFOSCredential, w as VerifiedDelegationChain, z as createDFOSCredential, B as decodeDFOSCredentialUnsafe, E as isAttenuated, F as matchesResource, H as verifyDFOSCredential, J as verifyDelegationChain } from '../dfos-credential-CtG0_n4P.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -34,7 +34,7 @@ import {
34
34
  verifyDelegationChain,
35
35
  verifyIdentityProofEnvelope,
36
36
  verifyRequestProofEnvelope
37
- } from "../chunk-MHSBBYDO.js";
37
+ } from "../chunk-T2YR5KWQ.js";
38
38
  import "../chunk-IDVYITX7.js";
39
39
  import "../chunk-4LG2GEB2.js";
40
40
  export {
@@ -294,6 +294,11 @@ declare const VerifiedIdentity: z.ZodObject<{
294
294
  publicKeyMultibase: z.ZodString;
295
295
  }, z.core.$loose>>;
296
296
  }, z.core.$strict>>;
297
+ seenKeys: z.ZodOptional<z.ZodArray<z.ZodObject<{
298
+ id: z.ZodString;
299
+ type: z.ZodLiteral<"Multikey">;
300
+ publicKeyMultibase: z.ZodString;
301
+ }, z.core.$loose>>>;
297
302
  }, z.core.$strict>;
298
303
  type VerifiedIdentity = z.infer<typeof VerifiedIdentity>;
299
304
  declare const ContentOperation: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -377,7 +382,7 @@ declare const MAX_CREDIT_CLAIM_SIZE = 4096;
377
382
  * Credit claim: a claimant's signed assertion that it holds a named role on a
378
383
  * content chain. A document-plane artifact — it is NOT gossiped and relays are not
379
384
  * credit-claim aware; a claim travels inside the document bytes that embed it (see
380
- * `specs/CREDITS.md`).
385
+ * `specs/CONTENT-MODEL.md`).
381
386
  *
382
387
  * `contentId` is the binder — the STABLE 31-char content chain id, never a
383
388
  * documentCID or a chain head CID. Binding to the chain (not a document) is what
@@ -500,6 +505,24 @@ interface VerifiedDFOSCredential {
500
505
  /** kid from the JWS header */
501
506
  signerKeyId: string;
502
507
  }
508
+ /**
509
+ * The identity a resolver answers with, plus the one fact about that answer only
510
+ * the resolver knows: whether it is FINAL for the basis it was asked about.
511
+ *
512
+ * A resolver whose copy of the chain runs past the basis answers determinately,
513
+ * and a key missing from that state is a verdict. A resolver answering from a
514
+ * copy that ends at or before the basis does not: an operation dated at or
515
+ * before the basis can still arrive and add the key, so the miss is a dependency
516
+ * miss the caller retries. Absent is the retryable reading, which is the safe
517
+ * default: only a resolver that can say its answer is final gets a verdict.
518
+ */
519
+ type ResolvedIdentity = VerifiedIdentity & {
520
+ /**
521
+ * True when no operation dated at or before the basis can still arrive and
522
+ * change this key state.
523
+ */
524
+ basisDeterminate?: boolean;
525
+ };
503
526
  interface VerifiedDelegationChain {
504
527
  /** The leaf credential */
505
528
  credential: VerifiedDFOSCredential;
@@ -512,8 +535,8 @@ interface VerifiedDelegationChain {
512
535
  * Check whether a credential (leaf or any parent) has been revoked.
513
536
  *
514
537
  * `asOfUnix` selects WHICH question is being asked, and the two are different
515
- * decisions: **acceptance is a freshness decision; verification of committed
516
- * history is a validity decision.**
538
+ * decisions: **acceptance is a freshness decision; verification at a basis is a
539
+ * validity decision.**
517
540
  *
518
541
  * - **Omitted, or `<= 0` (timeless)** — "is this credential revoked as far as you
519
542
  * know right now?". The freshness question. Used by acceptance gates: relay
@@ -524,10 +547,10 @@ interface VerifiedDelegationChain {
524
547
  * 1970) therefore gets the stricter answer in both languages.
525
548
  * - **Positive (as-of)** — "was this credential already revoked at `asOfUnix`?".
526
549
  * The validity question. Return true only if a revocation exists AND its
527
- * signed `createdAt` is ≤ `asOfUnix`. Used when verifying operations already
528
- * committed to a chain, where `asOfUnix` is the operation's own `createdAt`.
529
- * A revocation signed AFTER an operation does not invalidate it — see
530
- * CREDENTIALS.md "Revocation Scope".
550
+ * signed `createdAt` is ≤ `asOfUnix`. Used when verifying an artifact against
551
+ * a basis, where `asOfUnix` is the basis in integer Unix seconds. A revocation
552
+ * signed AFTER the basis does not invalidate the artifact — see CREDENTIALS.md
553
+ * "Revocation against the basis".
531
554
  *
532
555
  * An implementation that ignores `asOfUnix` degrades to the timeless answer,
533
556
  * which is always the stricter (safe) direction — it can only reject history
@@ -559,13 +582,35 @@ declare const createDFOSCredential: (options: {
559
582
  /**
560
583
  * Verify a DFOS credential — signature, schema, expiry, CID integrity
561
584
  *
585
+ * Every check runs against ONE basis time (PROTOCOL, Time basis): the issuer's
586
+ * key must be effective in the identity's state as of the basis, and `exp` must
587
+ * be strictly greater than the basis in integer Unix seconds. `iat` is
588
+ * informational and gates nothing.
589
+ *
562
590
  * Does NOT verify the delegation chain. Use `verifyDelegationChain` for full
563
591
  * chain verification including attenuation enforcement.
564
592
  */
565
593
  declare const verifyDFOSCredential: (jwsToken: string, options: {
566
- resolveIdentity: (did: string) => Promise<VerifiedIdentity | undefined>;
567
- /** Current time in seconds (defaults to Date.now() / 1000) */
568
- now?: number;
594
+ /**
595
+ * Resolve a DID to its verified identity state AS OF `basis`. Called with
596
+ * the basis this verification runs at, or with none when the presentation is
597
+ * ephemeral and the answer is head state. A resolver that knows its answer
598
+ * is final for the basis says so on the result (`ResolvedIdentity`).
599
+ */
600
+ resolveIdentity: (did: string, basis?: string) => Promise<ResolvedIdentity | undefined>;
601
+ /**
602
+ * The basis time, in the `createdAt` grammar — the operation's own
603
+ * `createdAt` for a credential carried inline in a committed operation.
604
+ * Omitted for an ephemeral presentation, where the basis is now.
605
+ */
606
+ basis?: string;
607
+ /**
608
+ * The ephemeral clock in integer Unix seconds, for a caller that carries its
609
+ * own. It reaches `exp` and nothing else: key resolution on the ephemeral
610
+ * path is head state, which is what "the basis is now" means. Ignored when
611
+ * `basis` is present, and defaults to `floor(Date.now() / 1000)`.
612
+ */
613
+ nowUnix?: number;
569
614
  }) => Promise<VerifiedDFOSCredential>;
570
615
  /**
571
616
  * Verify a full delegation chain — walk `prf`, confirm monotonic attenuation,
@@ -578,23 +623,29 @@ declare const verifyDFOSCredential: (jwsToken: string, options: {
578
623
  *
579
624
  * The chain terminates when a credential has `prf: []` (root credential). The
580
625
  * root credential's `iss` must equal `rootDID`.
626
+ *
627
+ * ONE BASIS FOR THE WHOLE WALK. Every hop — each parent's signing key, each
628
+ * `exp`, each revocation — resolves against the same basis the leaf did, so a
629
+ * chain either held at that instant or it did not.
581
630
  */
582
631
  declare const verifyDelegationChain: (credential: VerifiedDFOSCredential, options: {
583
- resolveIdentity: (did: string) => Promise<VerifiedIdentity | undefined>;
632
+ /** Resolve a DID to its verified identity state as of `basis`. */
633
+ resolveIdentity: (did: string, basis?: string) => Promise<ResolvedIdentity | undefined>;
584
634
  /** The expected root authority DID (e.g., content chain creator) */
585
635
  rootDID: string;
586
- /** Current time in seconds (defaults to Date.now() / 1000) */
587
- now?: number;
588
636
  /** Check if a credential has been revoked (checked at every level of the chain) */
589
637
  isRevoked?: RevocationChecker;
590
638
  /**
591
- * As-of basis for the revocation check, unix seconds. Kept SEPARATE from
592
- * `now` (the expiry basis) on purpose: expiry and revocation are two
593
- * different decisions, and a caller evaluating expiry against a deterministic
594
- * basis does not automatically want history-relative revocation. Omitted =
595
- * timeless revocation (current knowledge). See `RevocationChecker`.
639
+ * The basis time, in the `createdAt` grammar. Omitted for an ephemeral
640
+ * presentation: `exp` runs against the wall clock and revocation is asked
641
+ * timelessly, which is the stricter direction (see `RevocationChecker`).
642
+ */
643
+ basis?: string;
644
+ /**
645
+ * The ephemeral clock in integer Unix seconds, threaded to every hop's `exp`
646
+ * and nothing else. Ignored when `basis` is present.
596
647
  */
597
- asOfUnix?: number;
648
+ nowUnix?: number;
598
649
  }) => Promise<VerifiedDelegationChain>;
599
650
  /**
600
651
  * Check if `childAtt` is a valid attenuation of `parentAtt`
@@ -639,4 +690,4 @@ declare class CredentialVerificationError extends Error {
639
690
  constructor(message: string);
640
691
  }
641
692
 
642
- export { ARTIFACT_CID_ANCHOR_RE as A, isAttenuated as B, CONTENT_ID_ANCHOR_RE as C, DFOSCredentialPayload as D, matchesResource as E, parseProtocolTimestampUnix as F, verifyDFOSCredential as G, verifyDelegationChain as H, IdentityOperation as I, MAX_ARTIFACT_PAYLOAD_SIZE as M, type RevocationChecker as R, ServiceEntry as S, type VerifiedDFOSCredential as V, ArtifactPayload as a, Attenuation as b, ContentOperation as c, CountersignPayload as d, CredentialVerificationError as e, CreditClaimPayload as f, DeclaredKeyState as g, Iso8601 as h, MAX_CREDENTIAL_SIZE as i, MAX_CREDIT_CLAIM_SIZE as j, MAX_KEY_PROOFS as k, MAX_OPERATION_SIZE as l, MAX_SERVICES_ENTRIES as m, MAX_SERVICES_PAYLOAD_SIZE as n, MAX_SIGN_REQUEST_PAYLOAD_SIZE as o, MAX_SIGN_REQUEST_SIZE as p, MultikeyPublicKey as q, RevocationPayload as r, ServicesArray as s, SignRequestPayload as t, type Signer as u, type VerifiedDelegationChain as v, VerifiedIdentity as w, VoidKeyMembership as x, createDFOSCredential as y, decodeDFOSCredentialUnsafe as z };
693
+ export { ARTIFACT_CID_ANCHOR_RE as A, decodeDFOSCredentialUnsafe as B, CONTENT_ID_ANCHOR_RE as C, DFOSCredentialPayload as D, isAttenuated as E, matchesResource as F, parseProtocolTimestampUnix as G, verifyDFOSCredential as H, IdentityOperation as I, verifyDelegationChain as J, MAX_ARTIFACT_PAYLOAD_SIZE as M, type ResolvedIdentity as R, ServiceEntry as S, type VerifiedDFOSCredential as V, ArtifactPayload as a, Attenuation as b, ContentOperation as c, CountersignPayload as d, CredentialVerificationError as e, CreditClaimPayload as f, DeclaredKeyState as g, Iso8601 as h, MAX_CREDENTIAL_SIZE as i, MAX_CREDIT_CLAIM_SIZE as j, MAX_KEY_PROOFS as k, MAX_OPERATION_SIZE as l, MAX_SERVICES_ENTRIES as m, MAX_SERVICES_PAYLOAD_SIZE as n, MAX_SIGN_REQUEST_PAYLOAD_SIZE as o, MAX_SIGN_REQUEST_SIZE as p, MultikeyPublicKey as q, type RevocationChecker as r, RevocationPayload as s, ServicesArray as t, SignRequestPayload as u, type Signer as v, type VerifiedDelegationChain as w, VerifiedIdentity as x, VoidKeyMembership as y, createDFOSCredential as z };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { JwsHeader, JwsVerificationError, JwtClaims, JwtCreateOptions, JwtHeader, JwtVerificationError, JwtVerifyOptions, PrefixedID, assertJwsProfile, base64urlDecode, base64urlEncode, createJws, createJwt, createNewEd25519Keypair, dagCborCanonicalEncode, decodeJwsUnsafe, decodeJwtUnsafe, generateId, generateIdNoPrefix, importEd25519Keypair, isCanonicallyEqual, isValidEd25519Signature, isValidId, normalizedId, parseDagCborCID, sha256, signPayloadEd25519, verifyJws, verifyJwt } from './crypto/index.js';
2
- export { A as ARTIFACT_CID_ANCHOR_RE, a as ArtifactPayload, b as Attenuation, C as CONTENT_ID_ANCHOR_RE, c as ContentOperation, d as CountersignPayload, e as CredentialVerificationError, f as CreditClaimPayload, D as DFOSCredentialPayload, g as DeclaredKeyState, I as IdentityOperation, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, i as MAX_CREDENTIAL_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, R as RevocationChecker, r as RevocationPayload, S as ServiceEntry, s as ServicesArray, t as SignRequestPayload, u as Signer, V as VerifiedDFOSCredential, v as VerifiedDelegationChain, w as VerifiedIdentity, x as VoidKeyMembership, y as createDFOSCredential, z as decodeDFOSCredentialUnsafe, B as isAttenuated, E as matchesResource, F as parseProtocolTimestampUnix, G as verifyDFOSCredential, H as verifyDelegationChain } from './dfos-credential-BtiYPqBT.js';
2
+ export { A as ARTIFACT_CID_ANCHOR_RE, a as ArtifactPayload, b as Attenuation, C as CONTENT_ID_ANCHOR_RE, c as ContentOperation, d as CountersignPayload, e as CredentialVerificationError, f as CreditClaimPayload, D as DFOSCredentialPayload, g as DeclaredKeyState, I as IdentityOperation, h as Iso8601, M as MAX_ARTIFACT_PAYLOAD_SIZE, i as MAX_CREDENTIAL_SIZE, j as MAX_CREDIT_CLAIM_SIZE, k as MAX_KEY_PROOFS, l as MAX_OPERATION_SIZE, m as MAX_SERVICES_ENTRIES, n as MAX_SERVICES_PAYLOAD_SIZE, o as MAX_SIGN_REQUEST_PAYLOAD_SIZE, p as MAX_SIGN_REQUEST_SIZE, q as MultikeyPublicKey, R as ResolvedIdentity, r as RevocationChecker, s as RevocationPayload, S as ServiceEntry, t as ServicesArray, u as SignRequestPayload, v as Signer, V as VerifiedDFOSCredential, w as VerifiedDelegationChain, x as VerifiedIdentity, y as VoidKeyMembership, z as createDFOSCredential, B as decodeDFOSCredentialUnsafe, E as isAttenuated, F as matchesResource, G as parseProtocolTimestampUnix, H as verifyDFOSCredential, J as verifyDelegationChain } from './dfos-credential-CtG0_n4P.js';
3
3
  export { AnchorKind, CreditClaimFailureReason, CreditClaimVerifyError, CreditEntry, CreditEntryState, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, RECOGNIZED_SERVICE_TYPES, SignRequestFailureReason, SignRequestVerifyError, VerifiedArtifact, VerifiedContentChain, VerifiedCountersignature, VerifiedCreditClaim, VerifiedCreditEntry, VerifiedRevocation, VerifiedSignRequest, anchorsByLabel, assertCanonicalSignRequestPayload, assertServicesWithinCap, buildSignRequest, classifyAnchor, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, isRecognizedServiceType, relayEndpoints, signArtifact, signContentOperation, signCountersignature, signCreditClaim, signIdentityOperation, signRevocation, verifyArtifact, verifyContentChain, verifyContentExtensionFromTrustedState, verifyCountersignature, verifyCreditClaim, verifyCreditEntry, verifyIdentityChain, verifyIdentityExtensionFromTrustedState, verifyRevocation, verifySignRequest } from './chain/index.js';
4
4
  export { ApiRequestVerifyError, DEFAULT_PROOF_SKEW_SECONDS, DEFAULT_PROOF_WINDOW_SECONDS, DFOS_AUTH_SCHEME, EMPTY_BODY_SHA256, IDENTITY_PROOF_JWS_TYP, IdentityProofPayload, MAX_BODY_BYTES, MAX_PROOF_FRESHNESS_SPAN_SECONDS, MAX_REQUEST_PROOF_SIZE, ParsedProofPayload, ProofEnvelopeInput, ProofExtraMembers, ProofPresenterState, REQUEST_PROOF_JWS_TYP, RequestProofFailurePhase, RequestProofFailureReason, RequestProofPayload, ResolveProofPresenter, SignApiIdentityRequestInput, SignApiRequestInput, VerifiedProofEnvelope, apiIdentitySigningInput, apiRequestSigningInput, assertProofVerifierConfig, buildApiAuthHeaders, buildApiIdentityHeaders, canonicalExtraMembers, invalidProof, misconfiguredProof, parseDfosAuthorization, sha256BodyHash, signApiIdentityRequest, signApiRequest, unverifiableProof, verifyIdentityProofEnvelope, verifyRequestProofEnvelope } from './credentials/index.js';
5
5
  export { DEFAULT_KEY_PROOF_SKEW_SECONDS, KEY_ADD_JWS_TYP, KEY_ROLES, KeyProofFailureReason, KeyProofPayload, KeyProofVerifyError, KeyRole, MAX_KEY_PROOF_SIZE, SignKeyProofInput, VerifiedKeyProof, VerifyChainKeyProofOptions, VerifyKeyProofOptions, isCanonicalRoleSet, keyProofSigningInput, keyWordFingerprint, parseRoleSet, roleSetCovers, serializeRoleSet, signKeyProof, unsafeKeyProofSubject, verifyChainKeyProof, verifyKeyProof } from './key-proof/index.js';
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  verifyIdentityExtensionFromTrustedState,
53
53
  verifyRevocation,
54
54
  verifySignRequest
55
- } from "./chunk-JGFFADPR.js";
55
+ } from "./chunk-LMAPBTDW.js";
56
56
  import {
57
57
  ApiRequestVerifyError,
58
58
  Attenuation,
@@ -92,7 +92,7 @@ import {
92
92
  verifyDelegationChain,
93
93
  verifyIdentityProofEnvelope,
94
94
  verifyRequestProofEnvelope
95
- } from "./chunk-MHSBBYDO.js";
95
+ } from "./chunk-T2YR5KWQ.js";
96
96
  import {
97
97
  DEFAULT_KEY_PROOF_SKEW_SECONDS,
98
98
  KEY_ADD_JWS_TYP,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metalabel/dfos-protocol",
3
- "version": "0.50.0",
3
+ "version": "0.51.0",
4
4
  "type": "module",
5
5
  "description": "DFOS Protocol — Ed25519 signed chain primitives, services, credentials, and verification",
6
6
  "license": "MIT",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://schemas.dfos.com/credit-claim/v1",
4
4
  "title": "Credit Claim",
5
- "description": "The payload of a did:dfos:credit-claim JWS — a claimant's own signed assertion that it holds a named role on a content chain. Unlike post/v1, profile/v1, and index/v1, this is NOT a document committed to a chain by CID: it is an envelope payload carried inside document bytes, in the claim field of a credits entry (see $defs/creditEntry). Attribution, not authorization — a credit claim grants nothing. Unknown top-level fields are deliberately NOT rejected here (no additionalProperties: false, unlike the document schemas): this is a wire payload under the protocol's MUST-ignore-unknown rule, and the CID commits to the exact bytes, so a verifier that stripped unknown keys would fail its own CID check. Full semantics, the two-way bind, and the four verification states: https://protocol.dfos.com/credits",
5
+ "description": "The payload of a did:dfos:credit-claim JWS — a claimant's own signed assertion that it holds a named role on a content chain. Unlike post/v1, profile/v1, and index/v1, this is NOT a document committed to a chain by CID: it is an envelope payload carried inside document bytes, in the claim field of a credits entry (see $defs/creditEntry). Attribution, not authorization — a credit claim grants nothing. Unknown top-level fields are deliberately NOT rejected here (no additionalProperties: false, unlike the document schemas): this is a wire payload under the protocol's MUST-ignore-unknown rule, and the CID commits to the exact bytes, so a verifier that stripped unknown keys would fail its own CID check. Full semantics, the two-way bind, and the four verification states: https://protocol.dfos.com/content-model#credits",
6
6
  "type": "object",
7
7
  "required": ["version", "type", "contentId", "did", "role", "createdAt"],
8
8
  "properties": {
@@ -43,7 +43,7 @@
43
43
  "items": {
44
44
  "$ref": "#/$defs/credit"
45
45
  },
46
- "description": "Ordered authorship credits. Array order is display order; the first entry is the primary author. Omit entirely for unattributed content. A bare entry is the assertion tier of the authorship lattice — the operation signer asserts it; a credited DID upgrades its credit to proof by adding a credit claim in the entry's claim field (see https://protocol.dfos.com/credits)."
46
+ "description": "Ordered authorship credits. Array order is display order; the first entry is the primary author. Omit entirely for unattributed content. A bare entry is the assertion tier of the authorship lattice — the operation signer asserts it; a credited DID upgrades its credit to proof by adding a credit claim in the entry's claim field (see https://protocol.dfos.com/content-model#credits)."
47
47
  }
48
48
  },
49
49
  "additionalProperties": false,