@metalabel/dfos-protocol 0.50.0 → 0.52.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-j-kHtFvG.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-j-kHtFvG.js';
3
3
  import 'zod';
4
4
 
5
5
  /** Ed25519 public key multicodec value */
@@ -22,6 +22,17 @@ declare const decodeMultikey: (multibase: string) => {
22
22
  keyBytes: Uint8Array;
23
23
  codec: number;
24
24
  };
25
+ /**
26
+ * Decode a Multikey multibase string that MUST carry an Ed25519 PUBLIC key
27
+ *
28
+ * `decodeMultikey` also accepts the `ed25519-priv` prefix and reports it in
29
+ * `codec`, which a caller that destructures only `{ keyBytes }` silently
30
+ * discards — so a private-key-tagged multibase string would verify as if it were
31
+ * a public key, where the Go reference (DecodeMultikey) accepts the 0xed01
32
+ * prefix and nothing else. This is the decode every signature path takes, so the
33
+ * codec assertion cannot be forgotten at the next call site.
34
+ */
35
+ declare const decodeEd25519PublicMultikey: (multibase: string) => Uint8Array;
25
36
 
26
37
  /**
27
38
  * Derive a prefixed chain identifier from CID bytes
@@ -54,13 +65,26 @@ declare const signIdentityOperation: (input: {
54
65
  * Verify a log of JWS identity operations and derive the identity
55
66
  *
56
67
  * 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
68
+ * folds the possession proofs alongside. Returns the verified identity state:
69
+ * EFFECTIVE key arrays, with the declared arrays and the void memberships
59
70
  * beside them.
71
+ *
72
+ * THE BASIS SELECTS WHICH STATE COMES BACK, never how much of the log is
73
+ * verified. With `asOf` set, the whole log is walked and validated exactly as
74
+ * without it, and the state returned is the one the last operation dated at or
75
+ * before the basis folds to — the identity's state as of that instant
76
+ * (PROTOCOL, Time basis). The comparison is byte-wise on the `createdAt`
77
+ * strings, per PROTOCOL's Comparison basis. A basis earlier than genesis has no
78
+ * state to name and throws, and so does an empty basis.
60
79
  */
61
80
  declare const verifyIdentityChain: (input: {
62
81
  didPrefix: string;
63
82
  log: string[];
83
+ /**
84
+ * Basis time, in the `createdAt` grammar. Omitted = the chain head, which is
85
+ * the state as of now.
86
+ */
87
+ asOf?: string;
64
88
  }) => Promise<VerifiedIdentity>;
65
89
  /**
66
90
  * Verify a single new operation against already-verified identity state
@@ -70,10 +94,13 @@ declare const verifyIdentityChain: (input: {
70
94
  * verified genesis). This function performs one signature verification and one
71
95
  * state transition — constant time regardless of chain length.
72
96
  *
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.
97
+ * KEY-ID CONSISTENCY IS CHECKED HERE, on the same terms as the full walk. The
98
+ * trusted state carries `seenKeys` the id-to-material binding the walk already
99
+ * enforced so an operation that re-points an existing key id at new material
100
+ * is REJECTED on this path exactly as a replay of the same two operations would
101
+ * reject it. Without that the fast path would accept a chain its own
102
+ * re-verification refuses, and a relay's linear path is the path almost every
103
+ * operation takes.
77
104
  *
78
105
  * THE POSSESSION FOLD RUNS HERE TOO, and it needs both halves of the trusted
79
106
  * state: the EFFECTIVE arrays (to know what an introduction is a transition out
@@ -164,8 +191,11 @@ declare const signContentOperation: (input: {
164
191
  */
165
192
  declare const verifyContentChain: (input: {
166
193
  log: string[];
167
- /** Resolve a kid (DID URL) to the raw Ed25519 public key bytes */
168
- resolveKey: (kid: string) => Promise<Uint8Array>;
194
+ /**
195
+ * Resolve a kid (DID URL) to the raw Ed25519 public key bytes, in the signing
196
+ * identity's state as of `basis` — each operation's own `createdAt`.
197
+ */
198
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
169
199
  /**
170
200
  * Enforce creator-sovereignty authorization. When true, non-creator signers
171
201
  * must include a DFOS credential in the operation's `authorization` field
@@ -173,10 +203,11 @@ declare const verifyContentChain: (input: {
173
203
  */
174
204
  enforceAuthorization?: boolean;
175
205
  /**
176
- * Resolve a DID to a VerifiedIdentity. Required when `enforceAuthorization`
177
- * is true, as credential verification needs identity resolution.
206
+ * Resolve a DID to a ResolvedIdentity as of `basis`. Required when
207
+ * `enforceAuthorization` is true, as credential verification needs identity
208
+ * resolution.
178
209
  */
179
- resolveIdentity?: (did: string) => Promise<VerifiedIdentity | undefined>;
210
+ resolveIdentity?: (did: string, basis?: string) => Promise<ResolvedIdentity | undefined>;
180
211
  /**
181
212
  * Check whether a credential (leaf or parent) has been revoked. Called with
182
213
  * `asOfUnix` = each operation's own `createdAt`, so a fold of committed
@@ -198,12 +229,15 @@ declare const verifyContentExtensionFromTrustedState: (input: {
198
229
  lastCreatedAt: string;
199
230
  /** The new JWS operation to verify */
200
231
  newOp: string;
201
- /** Resolve a kid (DID URL) to the raw Ed25519 public key bytes */
202
- resolveKey: (kid: string) => Promise<Uint8Array>;
232
+ /**
233
+ * Resolve a kid (DID URL) to the raw Ed25519 public key bytes, in the signing
234
+ * identity's state as of `basis` — the new operation's own `createdAt`.
235
+ */
236
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
203
237
  /** Enforce creator-sovereignty authorization (see verifyContentChain) */
204
238
  enforceAuthorization?: boolean;
205
- /** Resolve a DID to a VerifiedIdentity. Required when enforceAuthorization is true. */
206
- resolveIdentity?: (did: string) => Promise<VerifiedIdentity | undefined>;
239
+ /** Resolve a DID to a ResolvedIdentity as of `basis`. Required when enforceAuthorization is true. */
240
+ resolveIdentity?: (did: string, basis?: string) => Promise<ResolvedIdentity | undefined>;
207
241
  /**
208
242
  * Check whether a credential (leaf or parent) has been revoked. Called with
209
243
  * `asOfUnix` = the new operation's own `createdAt`. See `RevocationChecker`.
@@ -242,10 +276,15 @@ declare const signCountersignature: (input: {
242
276
  * Checks: valid signature, CID integrity, payload schema. Does NOT check
243
277
  * whether the target exists or whether the witness differs from the target
244
278
  * author — those are relay-level semantic checks.
279
+ *
280
+ * The countersignature's own `createdAt` is the basis: a committed statement
281
+ * resolves its signer in the state that held when it was signed (PROTOCOL, Time
282
+ * basis).
245
283
  */
246
284
  declare const verifyCountersignature: (input: {
247
285
  jwsToken: string;
248
- resolveKey: (kid: string) => Promise<Uint8Array>;
286
+ /** Resolve a kid to key bytes in the signer's state as of `basis`. */
287
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
249
288
  }) => Promise<VerifiedCountersignature>;
250
289
 
251
290
  interface VerifiedArtifact {
@@ -267,10 +306,14 @@ declare const signArtifact: (input: {
267
306
  }>;
268
307
  /**
269
308
  * Verify an artifact JWS — signature, CID, payload schema, size limit
309
+ *
310
+ * The artifact's own `createdAt` is the basis: a committed statement resolves
311
+ * its signer in the state that held when it was signed (PROTOCOL, Time basis).
270
312
  */
271
313
  declare const verifyArtifact: (input: {
272
314
  jwsToken: string;
273
- resolveKey: (kid: string) => Promise<Uint8Array>;
315
+ /** Resolve a kid to key bytes in the signer's state as of `basis`. */
316
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
274
317
  }) => Promise<VerifiedArtifact>;
275
318
 
276
319
  interface VerifiedRevocation {
@@ -299,10 +342,14 @@ declare const signRevocation: (input: {
299
342
  }>;
300
343
  /**
301
344
  * Verify a revocation JWS — signature, CID, payload schema, signer match
345
+ *
346
+ * The revocation's own `createdAt` is the basis: a committed statement resolves
347
+ * its signer in the state that held when it was signed (PROTOCOL, Time basis).
302
348
  */
303
349
  declare const verifyRevocation: (input: {
304
350
  jwsToken: string;
305
- resolveKey: (kid: string) => Promise<Uint8Array>;
351
+ /** Resolve a kid to key bytes in the signer's state as of `basis`. */
352
+ resolveKey: (kid: string, basis?: string) => Promise<Uint8Array>;
306
353
  }) => Promise<VerifiedRevocation>;
307
354
 
308
355
  interface VerifiedCreditClaim {
@@ -322,7 +369,7 @@ interface VerifiedCreditClaim {
322
369
  claimCID: string;
323
370
  }
324
371
  /**
325
- * The four states a `credits[]` entry resolves to (see `specs/CREDITS.md`).
372
+ * The four states a `credits[]` entry resolves to (see `specs/CONTENT-MODEL.md`).
326
373
  *
327
374
  * `invalid` and `unverifiable` are deliberately distinct and MUST NOT be
328
375
  * collapsed: `invalid` means "checked and failed" (a positive signal that
@@ -493,7 +540,8 @@ declare const buildSignRequest: (input: {
493
540
  requestCID: string;
494
541
  }>;
495
542
  /**
496
- * Verify a sign-request envelope in SIGNING.md's exact 1–9 order.
543
+ * Verify a sign-request envelope in RELAY.md, Envelope verification's exact 1–9
544
+ * order.
497
545
  *
498
546
  * `resolveIdentity` supplies CURRENT identity state. A missing identity or
499
547
  * resolver failure is `unverifiable`; a deleted identity, missing current key,
@@ -512,4 +560,4 @@ declare const assertCanonicalSignRequestPayload: (payloadTyp: string, payloadByt
512
560
  subject: string;
513
561
  }) => void;
514
562
 
515
- export { type AnchorKind, ArtifactPayload, ContentOperation, CountersignPayload, type CreditClaimFailureReason, CreditClaimVerifyError, type CreditEntry, type CreditEntryState, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, RECOGNIZED_SERVICE_TYPES, ServiceEntry, type SignRequestFailureReason, SignRequestVerifyError, Signer, type VerifiedArtifact, type VerifiedContentChain, type VerifiedCountersignature, type VerifiedCreditClaim, type VerifiedCreditEntry, VerifiedIdentity, type VerifiedRevocation, type 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 };
563
+ export { type AnchorKind, ArtifactPayload, ContentOperation, CountersignPayload, type CreditClaimFailureReason, CreditClaimVerifyError, type CreditEntry, type CreditEntryState, ED25519_PRIV_MULTICODEC, ED25519_PUB_MULTICODEC, IdentityOperation, RECOGNIZED_SERVICE_TYPES, ServiceEntry, type SignRequestFailureReason, SignRequestVerifyError, Signer, type VerifiedArtifact, type VerifiedContentChain, type VerifiedCountersignature, type VerifiedCreditClaim, type VerifiedCreditEntry, VerifiedIdentity, type VerifiedRevocation, type VerifiedSignRequest, anchorsByLabel, assertCanonicalSignRequestPayload, assertServicesWithinCap, buildSignRequest, classifyAnchor, decodeEd25519PublicMultikey, decodeMultikey, deriveChainIdentifier, deriveContentId, encodeEd25519Multikey, isRecognizedServiceType, relayEndpoints, signArtifact, signContentOperation, signCountersignature, signCreditClaim, signIdentityOperation, signRevocation, verifyArtifact, verifyContentChain, verifyContentExtensionFromTrustedState, verifyCountersignature, verifyCreditClaim, verifyCreditEntry, verifyIdentityChain, verifyIdentityExtensionFromTrustedState, verifyRevocation, verifySignRequest };
@@ -52,16 +52,17 @@ import {
52
52
  verifyIdentityExtensionFromTrustedState,
53
53
  verifyRevocation,
54
54
  verifySignRequest
55
- } from "../chunk-JGFFADPR.js";
56
- import "../chunk-MHSBBYDO.js";
57
- import "../chunk-JVSC67DC.js";
55
+ } from "../chunk-OU2DPHCN.js";
56
+ import "../chunk-IV3TIYKP.js";
57
+ import "../chunk-QRCOMLAP.js";
58
58
  import {
59
59
  ED25519_PRIV_MULTICODEC,
60
60
  ED25519_PUB_MULTICODEC,
61
+ decodeEd25519PublicMultikey,
61
62
  decodeMultikey,
62
63
  encodeEd25519Multikey
63
- } from "../chunk-IDVYITX7.js";
64
- import "../chunk-4LG2GEB2.js";
64
+ } from "../chunk-F7RHI2CK.js";
65
+ import "../chunk-QTJZGXMH.js";
65
66
  export {
66
67
  ARTIFACT_CID_ANCHOR_RE,
67
68
  ArtifactPayload,
@@ -97,6 +98,7 @@ export {
97
98
  assertServicesWithinCap,
98
99
  buildSignRequest,
99
100
  classifyAnchor,
101
+ decodeEd25519PublicMultikey,
100
102
  decodeMultikey,
101
103
  deriveChainIdentifier,
102
104
  deriveContentId,
@@ -36,10 +36,18 @@ var decodeMultikey = (multibase) => {
36
36
  `unsupported multikey codec: [0x${bytes[0]?.toString(16)}, 0x${bytes[1]?.toString(16)}]`
37
37
  );
38
38
  };
39
+ var decodeEd25519PublicMultikey = (multibase) => {
40
+ const { keyBytes, codec } = decodeMultikey(multibase);
41
+ if (codec !== ED25519_PUB_MULTICODEC) {
42
+ throw new Error("publicKeyMultibase is not an Ed25519 public key");
43
+ }
44
+ return keyBytes;
45
+ };
39
46
 
40
47
  export {
41
48
  ED25519_PUB_MULTICODEC,
42
49
  ED25519_PRIV_MULTICODEC,
43
50
  encodeEd25519Multikey,
44
- decodeMultikey
51
+ decodeMultikey,
52
+ decodeEd25519PublicMultikey
45
53
  };
@@ -1,6 +1,6 @@
1
1
  import {
2
- decodeMultikey
3
- } from "./chunk-IDVYITX7.js";
2
+ decodeEd25519PublicMultikey
3
+ } from "./chunk-F7RHI2CK.js";
4
4
  import {
5
5
  assertJwsProfile,
6
6
  base64urlDecode,
@@ -10,7 +10,7 @@ import {
10
10
  decodeJwsUnsafe,
11
11
  sha256,
12
12
  verifyJws
13
- } from "./chunk-4LG2GEB2.js";
13
+ } from "./chunk-QTJZGXMH.js";
14
14
 
15
15
  // src/credentials/schemas.ts
16
16
  import { z } from "zod";
@@ -355,7 +355,10 @@ var verifyProofEnvelope = async (input, shape, resolvePresenter) => {
355
355
  const key = state.keys.find((candidate) => candidate.id === presenterKeyId);
356
356
  if (!key) throw invalidProof(`${shape.label} signing key is not a current key of the presenter`);
357
357
  try {
358
- verifyJws({ token: input.proof, publicKey: decodeMultikey(key.publicKeyMultibase).keyBytes });
358
+ verifyJws({
359
+ token: input.proof,
360
+ publicKey: decodeEd25519PublicMultikey(key.publicKeyMultibase)
361
+ });
359
362
  } catch (err) {
360
363
  throw invalidProof(err instanceof Error ? err.message : `invalid ${shape.label} signature`);
361
364
  }
@@ -370,20 +373,26 @@ var isDependencyMissing = (err) => err instanceof Error && err.dependencyMissing
370
373
  var carryDependencyMissing = (cause, wrapped) => isDependencyMissing(cause) ? markDependencyMissing(wrapped) : wrapped;
371
374
 
372
375
  // src/credentials/dfos-credential.ts
373
- var resolveKeyFromIdentity = (identity, kid) => {
376
+ var resolveKeyFromIdentity = (identity, kid, determinate) => {
374
377
  const hashIdx = kid.indexOf("#");
375
378
  if (hashIdx < 0) throw new CredentialVerificationError("kid must be a DID URL");
376
379
  const keyId = kid.substring(hashIdx + 1);
377
380
  const allKeys = [...identity.authKeys, ...identity.assertKeys, ...identity.controllerKeys];
378
381
  const key = allKeys.find((k) => k.id === keyId);
379
382
  if (!key) {
380
- throw markDependencyMissing(
381
- new CredentialVerificationError(`key ${keyId} not found on identity ${identity.did}`)
383
+ const miss = new CredentialVerificationError(
384
+ `unknown key ${keyId} on identity ${identity.did}`
382
385
  );
386
+ throw determinate ? miss : markDependencyMissing(miss);
383
387
  }
384
- const { keyBytes } = decodeMultikey(key.publicKeyMultibase);
388
+ const keyBytes = decodeEd25519PublicMultikey(key.publicKeyMultibase);
385
389
  return keyBytes;
386
390
  };
391
+ var basisUnixSeconds = (basis) => {
392
+ const ms = Date.parse(basis);
393
+ if (Number.isNaN(ms)) throw new CredentialVerificationError(`invalid basis time: ${basis}`);
394
+ return Math.floor(ms / 1e3);
395
+ };
387
396
  var createDFOSCredential = async (options) => {
388
397
  const kid = `${options.issuerDID}#${options.keyId}`;
389
398
  const now = options.iat ?? Math.floor(Date.now() / 1e3);
@@ -435,7 +444,7 @@ var verifyDFOSCredential = async (jwsToken, options) => {
435
444
  if (kidDid !== payload.iss) {
436
445
  throw new CredentialVerificationError("credential kid DID does not match iss");
437
446
  }
438
- const identity = await options.resolveIdentity(payload.iss);
447
+ const identity = await options.resolveIdentity(payload.iss, options.basis);
439
448
  if (!identity) {
440
449
  throw markDependencyMissing(
441
450
  new CredentialVerificationError(`issuer identity not found: ${payload.iss}`)
@@ -444,13 +453,13 @@ var verifyDFOSCredential = async (jwsToken, options) => {
444
453
  if (identity.isDeleted) {
445
454
  throw new CredentialVerificationError(`issuer identity is deleted: ${payload.iss}`);
446
455
  }
447
- const publicKey = resolveKeyFromIdentity(identity, kid);
456
+ const publicKey = resolveKeyFromIdentity(identity, kid, identity.basisDeterminate === true);
448
457
  try {
449
458
  verifyJws({ token: jwsToken, publicKey });
450
459
  } catch {
451
460
  throw new CredentialVerificationError("invalid credential signature");
452
461
  }
453
- const encoded = await dagCborCanonicalEncode(payload);
462
+ const encoded = await dagCborCanonicalEncode(decoded.payload);
454
463
  const credentialCID = encoded.cid.toString();
455
464
  if (!decoded.header.cid) {
456
465
  throw new CredentialVerificationError("missing cid in credential header");
@@ -458,11 +467,8 @@ var verifyDFOSCredential = async (jwsToken, options) => {
458
467
  if (decoded.header.cid !== credentialCID) {
459
468
  throw new CredentialVerificationError("credential cid mismatch");
460
469
  }
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) {
470
+ const basisSeconds = options.basis !== void 0 ? basisUnixSeconds(options.basis) : options.nowUnix ?? Math.floor(Date.now() / 1e3);
471
+ if (payload.exp <= basisSeconds) {
466
472
  throw new CredentialVerificationError("credential expired");
467
473
  }
468
474
  return {
@@ -478,6 +484,7 @@ var verifyDFOSCredential = async (jwsToken, options) => {
478
484
  };
479
485
  var verifyDelegationChain = async (credential, options) => {
480
486
  const chain = [credential];
487
+ const revocationBasis = options.basis !== void 0 ? basisUnixSeconds(options.basis) : void 0;
481
488
  let current = credential;
482
489
  const maxDepth = 16;
483
490
  for (let depth = 0; depth < maxDepth; depth++) {
@@ -496,10 +503,11 @@ var verifyDelegationChain = async (credential, options) => {
496
503
  }
497
504
  const parent = await verifyDFOSCredential(current.prf[0], {
498
505
  resolveIdentity: options.resolveIdentity,
499
- ...options.now !== void 0 ? { now: options.now } : {}
506
+ ...options.basis !== void 0 ? { basis: options.basis } : {},
507
+ ...options.nowUnix !== void 0 ? { nowUnix: options.nowUnix } : {}
500
508
  });
501
509
  if (options.isRevoked) {
502
- const revoked = await options.isRevoked(parent.iss, parent.credentialCID, options.asOfUnix);
510
+ const revoked = await options.isRevoked(parent.iss, parent.credentialCID, revocationBasis);
503
511
  if (revoked) {
504
512
  throw new CredentialVerificationError("parent credential in delegation chain is revoked");
505
513
  }
@@ -529,8 +537,9 @@ var parseResource = (resource) => {
529
537
  if (colonIdx < 0) return null;
530
538
  return { type: resource.substring(0, colonIdx), id: resource.substring(colonIdx + 1) };
531
539
  };
540
+ var ASCII_TRIM_RE = /^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g;
532
541
  var parseActions = (action) => new Set(
533
- action.split(",").map((a) => a.trim()).filter((a) => a !== "")
542
+ action.split(",").map((a) => a.replace(ASCII_TRIM_RE, "")).filter((a) => a !== "")
534
543
  );
535
544
  var isAttenuated = (parentAtt, childAtt) => {
536
545
  return childAtt.every((childEntry) => {