@proof-holdings/delegation-verifier 0.1.0 → 0.2.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
@@ -1,8 +1,7 @@
1
1
  # @proof-holdings/delegation-verifier
2
2
 
3
3
  Verify a **Proof of Delegation**: a signed statement that the controller of a domain authorized
4
- a specific agent artifact — an npm package, a container, an endpoint — for a set of capability
5
- scopes.
4
+ a specific agent artifact — an npm package, a container, an endpoint.
6
5
 
7
6
  You do not need an account with anyone to use this. It runs against public surfaces only, and
8
7
  it has **no built-in trust list**: you name the issuers you accept.
@@ -18,8 +17,8 @@ Zero runtime dependencies. Node ≥ 18 (or any runtime with WebCrypto, `fetch` a
18
17
 
19
18
  A verified delegation says exactly one thing:
20
19
 
21
- > The controller of `principal` authorized `delegate` for these scopes, and the issuer checked
22
- > that control when it signed.
20
+ > The controller of `principal` authorized `delegate`, and the issuer checked that control when
21
+ > it signed.
23
22
 
24
23
  It is **not** a statement that the artifact is safe, audited, maintained, or endorsed, and it is
25
24
  not a review of the business behind the domain. Do not render it as a generic "verified" badge.
@@ -41,7 +40,6 @@ const result = await verifyPublishedDelegation(serverCard, {
41
40
  // The artifact YOU resolved — the package you are installing, the endpoint you are calling.
42
41
  delegate: { type: 'purl', value: 'pkg:npm/postmark-mcp' },
43
42
  expectedPrincipal: 'postmarkapp.com',
44
- requiredScopes: ['send-email'],
45
43
  });
46
44
 
47
45
  if (result.valid) {
@@ -85,21 +83,29 @@ presented as *"X says it authorized this"*, never as *verified*.
85
83
  1. **Trust policy** — the token's `iss` must be one you named. No trust list, no verification.
86
84
  2. **Signature** — RS256/ES256 against the issuer's JWKS. The algorithm is chosen by the
87
85
  verifier, never read from the token, so `alg: none` and HMAC forgeries die before any key is
88
- fetched. Key sets are cached; an unknown `kid` triggers at most one refetch per 30s.
86
+ fetched. Key sets are cached; an unknown `kid` triggers at most one refetch per 30s. The
87
+ signing key's purpose, window and tombstone are checked here too — see
88
+ [Key rotation](#key-rotation-and-the-key-set-cache).
89
89
  3. **Claims** — `token_type`, schema version, `exp`/`iat` (5s tolerance by default), and the
90
90
  required claim set.
91
91
  4. **Principal** — pinned to the domain you expect, when you supply one. See above: without it
92
92
  the answer says who *claims* the artifact, not that the right party did.
93
93
  5. **Delegate** — canonicalized and compared byte-for-byte with the identity you resolved.
94
- 6. **Status** — two channels, both authenticated:
94
+ 6. **Status** — two channels, both authenticated, asked in order:
95
95
  - the IETF Token Status List the token points at (`status.status_list`), whose uri is
96
96
  refused unless it belongs to your trusted issuer, and whose signature is checked against
97
- the same key set;
98
- - the issuer's status endpoint, which is the only thing that can report the **cascade**: a
99
- delegation is worth no more than the domain control proof beneath it, and a revoked domain
100
- proof does not flip the delegation's own status-list bit.
101
-
102
- Status checking is **fail-closed**: if either channel cannot answer, the result is
97
+ the same key set. A readable `valid`, `revoked` or `suspended` slot is the answer, and the
98
+ check stops there. An issuer whose validity cascades folds a revoked or suspended domain
99
+ control proof beneath the delegation into this same slot (proof.holdings does);
100
+ - the issuer's status endpoint, asked only when the list gives no answer: the token carries
101
+ no status-list claim, or the list cannot be read, is not the trusted issuer's, or holds no
102
+ valid/revoked/suspended value for the token's slot. One thing
103
+ only the endpoint can report is an **expiry** inherited from the domain control proof, which
104
+ a list slot never encodes. A delegation whose own slot and own `exp` both read fine is
105
+ therefore not re-checked for it — an accepted residual, explained in
106
+ [Why the status endpoint is not always called](#why-the-status-endpoint-is-not-always-called-and-what-that-leaves-uncovered).
107
+
108
+ Status checking is **fail-closed**: if neither channel gives an answer, the result is
103
109
  `status_unavailable`, not "valid". Pass `statusCheck: 'skip'` to check signature and claims
104
110
  only — the result then carries `statusChecked: false`, which means *revocation was not
105
111
  checked*, never *not revoked*.
@@ -121,6 +127,56 @@ than before.
121
127
  `POST <statusEndpoint>` with `{"proof_token": "<jwt>"}` answering JSON with at least
122
128
  `{"valid": boolean}` and optionally `{"reason": "revoked" | "suspended" | ...}`.
123
129
 
130
+ ## Key rotation and the key-set cache
131
+
132
+ An issuer's JWKS may carry several keys at once. Beside the standard members, a key may declare
133
+ `proof_purpose` (`proof`, `delegation` or `status-list`), a window (`not_before`, `not_after`,
134
+ `max_token_lifetime` — integer seconds) or `proof_legacy: true`; revoked keys are listed in a
135
+ top-level `proof_tombstones: [{ "kid", "revoked_at" }]`. A key set without these members verifies
136
+ exactly as before. Once any key carries `proof_purpose`:
137
+
138
+ - a delegation must be signed by a `delegation` key or a legacy one (`wrong_key_purpose`);
139
+ - its `iat` must lie in the key's window, ± 5 minutes (`outside_key_window`), and its `exp` no later
140
+ than `iat + max_token_lifetime` (`token_lifetime_exceeded`) — legacy keys skip both;
141
+ - a token without `kid` is tried against legacy keys only.
142
+
143
+ The status list's own signing key is held to the same rules as a `status-list` key; a list signed by
144
+ a key that fails them counts as unreadable, exactly like a list that could not be fetched.
145
+
146
+ **One response is never trusted for long.** A new key verifies from its first sighting, so an
147
+ emergency key works at once. But the verifier only *remembers* a key carrying `proof_purpose` or
148
+ `proof_legacy` — keeps it after it disappears from the JWKS, refuses new key material under its
149
+ `kid`, refuses whole a document that widens its window — once two fetches at least 5 minutes apart
150
+ served it unchanged; until then the next document replaces or drops it. A narrower window and a
151
+ tombstone wait for the same confirmation: a tombstone applies only when a fetch at least 5 minutes
152
+ after its first sighting still carries it (a fetch in between that drops it starts over;
153
+ `refreshJwks` forces the second fetch), and once applied it stays until `revoked_at` + 365 days or
154
+ `clearTombstones()`, whatever later documents say.
155
+
156
+ The key set is refetched every 10 minutes. When a refetch fails, the cache's confirmed keys keep
157
+ answering until it is `maxJwksAgeMs` old, then the answer is `jwks_stale`; an applied tombstone
158
+ answers `key_revoked` at any age. A failed refetch of a cached set is retried at most once per 30s.
159
+ The cache lives in the process, keyed by `jwksUri`.
160
+
161
+ | Option | Default | Meaning |
162
+ | --- | --- | --- |
163
+ | `maxJwksAgeMs` | 24 h | How old the cache may be when a refetch fails. Above 25 days, non-integer or ≤ 0 is `invalid_options`. |
164
+ | `keySetStore` | none | `{ load(jwksUri), save(jwksUri, state) }`, sync or async, opaque string state. Persists the key set — tombstones included — across restarts and cold starts. A failing store never fails a verification. |
165
+ | `onJwksEvent` | ignored | Receives `{ type, jwksUri, kid?, message }`: `tombstone_pending`, `tombstone_applied`, `tombstone_for_live_key`, `window_narrowing_pending`, `window_narrowing_applied`, `window_widening_rejected`, `key_material_changed`, `kid_vanished`, `store_error`. |
166
+ | `timeoutMs` | 5000 | Bounds each network request and each `keySetStore` call. |
167
+
168
+ Exported alongside `verifyDelegation`:
169
+
170
+ - `refreshJwks({ jwksUri, fetch?, timeoutMs?, keySetStore?, onJwksEvent? })` — refetch now,
171
+ ignoring the TTL and cooldowns; resolves `{ ok: true }` or `{ ok: false, message }`. The method a
172
+ `jwks_stale` result's `refreshHint` names.
173
+ - `clearTombstones({ jwksUri?, keySetStore? })` — the manual reset: forgets applied and pending
174
+ tombstones for one key set (and its persisted copy), or for every set in memory.
175
+ - `getJwksDiagnostics(jwksUri)` — cache age, whether the registry is live, vanished keys, pending and
176
+ applied tombstones; `null` for a key set this process has never used.
177
+ - `DEFAULT_MAX_JWKS_AGE_MS`, `MAX_JWKS_AGE_CAP_MS` — to validate your own configuration against the
178
+ same bounds.
179
+
124
180
  ## Failure reasons
125
181
 
126
182
  Every failure carries an `outcome: 'invalid' | 'unconfirmed'` beside `reason` —
@@ -132,18 +188,22 @@ because they occur in practice).
132
188
 
133
189
  | `reason` | `outcome` | Meaning |
134
190
  | --- | --- | --- |
135
- | `invalid_options` | `invalid` | The call is wrong — usually a missing or uncanonicalizable `delegate`. |
191
+ | `invalid_options` | `invalid` | The call is wrong — usually a missing or uncanonicalizable `delegate`, or the `requiredScopes` option, removed in 0.2.0 with the `scope` claim (refused rather than ignored, so no caller reads a valid answer as "my scopes were granted"). |
136
192
  | `no_trust_policy` | `invalid` | `trustedIssuers` was empty. There is no default. |
137
193
  | `malformed_token` | `invalid` | Not a decodable JWT. |
138
194
  | `untrusted_issuer` | `invalid` | Signed by someone you did not name. |
139
195
  | `unsupported_algorithm` | `invalid` | The token asked for an algorithm this verifier does not accept. |
140
196
  | `unknown_key` / `bad_signature` | `invalid` | No matching key, or the signature does not hold. |
141
- | `jwks_unavailable` | `unconfirmed` | Could not read the key set — "I could not check", not "it is bad". |
142
- | `not_a_delegation` / `unsupported_schema` | `invalid` | Wrong token type or a schema major this version does not understand. |
197
+ | `jwks_unavailable` | `unconfirmed` | Could not read the key set, or could not refetch it for an unknown `kid` — "I could not check", not "it is bad". |
198
+ | `jwks_stale` | `unconfirmed` | A refetch failed and the cached key set is older than `maxJwksAgeMs`. Carries `cacheAgeSeconds` (null when unknown) and `refreshHint: { jwksUri, method: 'refreshJwks' }`. |
199
+ | `key_revoked` | `invalid` | The issuer revoked the signing key (an applied JWKS tombstone). Carries `pullHint: { issuerOrigin, handle }` — where to ask for a re-signed token. The handle is read from a token whose signature was not checked: an address to ask, not a verified identity. |
200
+ | `wrong_key_purpose` | `invalid` | The signing key belongs to another key family than a delegation. |
201
+ | `outside_key_window` | `invalid` | The token's `iat` lies outside the signing key's window (5 minutes of skew allowed). |
202
+ | `token_lifetime_exceeded` | `invalid` | `exp` lies beyond `iat` plus the signing key's `max_token_lifetime`. |
203
+ | `not_a_delegation` / `unsupported_version` | `invalid` | Wrong token type, or a `delegation_schema_version` major other than 2 (a 1.x token, which still carries `scope`, is refused before any network call). |
143
204
  | `expired` / `not_yet_valid` / `malformed_claims` | `invalid` | Claim-level rejections. |
144
205
  | `principal_mismatch` | `invalid` | Issued by a different domain than you expected. |
145
206
  | `delegate_mismatch` | `invalid` | **Attests a different artifact than the one you resolved.** |
146
- | `scope_not_granted` | `invalid` | Does not grant a scope you required. |
147
207
  | `status_uri_untrusted` | `unconfirmed` | The status-list uri in the token is not the issuer's. Not fetched. |
148
208
  | `status_unavailable` | `unconfirmed` | Revocation could not be checked. Fail closed. |
149
209
  | `revoked` / `suspended` | `invalid` | Withdrawn, or reversibly paused. |
@@ -160,17 +220,19 @@ that token's validity in it. An issuer that keeps a side-channel revocation reas
160
220
  published list is not honoring the channel it chose to publish — a fact about that issuer, not
161
221
  something a generic verifier can special-case.
162
222
 
163
- The endpoint still fires for the cases the bit structurally cannot answer: a token with no
164
- status-list claim at all, `unknown_delegation` ("no record of this handle" is distinguishable
165
- from "a record that is dead" only by a live call), and — for issuers whose validity cascades
166
- through something else, like a domain control proof underneath a delegation — an EXPIRY driven by
167
- that something else, which a status-list bit never encodes (only revoked/suspended are, by the
168
- same draft's own vocabulary). For proof.holdings specifically, that cascade-expiry gap is bounded,
169
- not eliminated: a delegation can no longer be minted longer than its underlying control proof, and
170
- a control cannot be re-issued shorter than an outstanding delegation without capping the
171
- reissue to cover it — but a control shortened before that guarantee existed, or a reissue that hit
172
- its own rare fail-open path, can still leave a delegation whose list bit and own `exp` claim both
173
- read fine while its issuer would answer `expired`. This is a known, accepted trade-off — not
223
+ The endpoint is asked only when the list gives no answer: the token carries no status-list claim,
224
+ or the list cannot be read, is not the trusted issuer's, or holds no valid/revoked/suspended value
225
+ for the token's slot. It is the only channel that can report two things the bit structurally
226
+ cannot — `unknown_delegation` ("no record of this handle" is distinguishable from "a record that
227
+ is dead" only by a live call) and, for issuers whose validity cascades through something else, like
228
+ a domain control proof underneath a delegation, an EXPIRY driven by that something else, which a
229
+ status-list bit never encodes (only revoked/suspended are, by the same draft's own vocabulary) —
230
+ so when the list does answer, neither is checked. For proof.holdings specifically, that
231
+ cascade-expiry gap is bounded, not eliminated: a delegation can no longer be minted longer than its
232
+ underlying control proof, and a control cannot be re-issued shorter than an outstanding delegation
233
+ without capping the reissue to cover it — but a control shortened before that guarantee existed, or a
234
+ reissue that hit its own rare fail-open path, can still leave a delegation whose list bit and own
235
+ `exp` claim both read fine while its issuer would answer `expired`. This is a known, accepted trade-off — not
174
236
  silently assumed away.
175
237
 
176
238
  ### One thing a failure reason is not
@@ -15,8 +15,6 @@ import type { DelegateInput, DelegateType } from './types.js';
15
15
  export declare const DELEGATE_TYPES: readonly DelegateType[];
16
16
  /** Cap on the CANONICAL delegate string. */
17
17
  export declare const DELEGATE_MAX_LENGTH = 512;
18
- export declare const DELEGATION_SCOPE_MAX_ENTRIES = 32;
19
- export declare const DELEGATION_SCOPE_TOKEN_MAX_LENGTH = 64;
20
18
  export declare class DelegateIdentifierError extends Error {
21
19
  constructor(message: string);
22
20
  }
@@ -26,9 +24,4 @@ export declare class DelegateIdentifierError extends Error {
26
24
  * cannot be canonicalized is a bug in the CALL, not evidence about the token.
27
25
  */
28
26
  export declare function canonicalizeDelegate(input: DelegateInput): string;
29
- /**
30
- * Validate and canonicalize a scope list: every entry a lowercase kebab-case token,
31
- * deduplicated, sorted. Throws `DelegateIdentifierError` on any rejection.
32
- */
33
- export declare function canonicalizeScopes(scopes: string[]): string[];
34
27
  //# sourceMappingURL=delegate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA6B,CAAC;AAEhF,4CAA4C;AAC5C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAEvC,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAQpD,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAIjE;AAqGD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAyB7D"}
1
+ {"version":3,"file":"delegate.d.ts","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE9D,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAA6B,CAAC;AAEhF,4CAA4C;AAC5C,eAAO,MAAM,mBAAmB,MAAM,CAAC;AAKvC,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAIjE"}
package/dist/delegate.js CHANGED
@@ -14,10 +14,6 @@
14
14
  export const DELEGATE_TYPES = ['url', 'purl'];
15
15
  /** Cap on the CANONICAL delegate string. */
16
16
  export const DELEGATE_MAX_LENGTH = 512;
17
- export const DELEGATION_SCOPE_MAX_ENTRIES = 32;
18
- export const DELEGATION_SCOPE_TOKEN_MAX_LENGTH = 64;
19
- /** Lowercase kebab-case: `send-email`, `read-profile-v2`. */
20
- const SCOPE_TOKEN_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
21
17
  /** `pkg:type/[namespace/]name` — structure only; per-type case rules applied after. */
22
18
  const PURL_RE = /^pkg:([a-z0-9.+-]+)\/(.+)$/i;
23
19
  export class DelegateIdentifierError extends Error {
@@ -116,10 +112,11 @@ function canonicalizeUrl(value) {
116
112
  throw new DelegateIdentifierError('delegate URL must not carry a fragment');
117
113
  }
118
114
  // URL() already lowercases the host and strips an explicit default port; pathname keeps its
119
- // case. Trailing slash is canonicalized away except at the root, where it is always '/'.
115
+ // case. Trailing slash is canonicalized away except at the root, where it is always '/' — a
116
+ // path of slashes only collapses to that root, so the result is idempotent.
120
117
  let pathname = parsed.pathname;
121
118
  if (pathname.length > 1 && pathname.endsWith('/')) {
122
- pathname = pathname.replace(/\/+$/, '');
119
+ pathname = pathname.replace(/\/+$/, '') || '/';
123
120
  }
124
121
  return enforceLength(`https://${parsed.host}${pathname}`);
125
122
  }
@@ -129,28 +126,4 @@ function enforceLength(canonical) {
129
126
  }
130
127
  return canonical;
131
128
  }
132
- /**
133
- * Validate and canonicalize a scope list: every entry a lowercase kebab-case token,
134
- * deduplicated, sorted. Throws `DelegateIdentifierError` on any rejection.
135
- */
136
- export function canonicalizeScopes(scopes) {
137
- if (!Array.isArray(scopes) || scopes.length === 0) {
138
- throw new DelegateIdentifierError('scope must contain at least one entry');
139
- }
140
- if (scopes.length > DELEGATION_SCOPE_MAX_ENTRIES) {
141
- throw new DelegateIdentifierError(`too many scope entries (max ${DELEGATION_SCOPE_MAX_ENTRIES})`);
142
- }
143
- for (const token of scopes) {
144
- if (typeof token !== 'string' || token.length === 0) {
145
- throw new DelegateIdentifierError('scope entries must be non-empty strings');
146
- }
147
- if (token.length > DELEGATION_SCOPE_TOKEN_MAX_LENGTH) {
148
- throw new DelegateIdentifierError(`scope entry is too long (max ${DELEGATION_SCOPE_TOKEN_MAX_LENGTH} characters)`);
149
- }
150
- if (!SCOPE_TOKEN_RE.test(token)) {
151
- throw new DelegateIdentifierError(`invalid scope entry "${token}" — lowercase kebab-case tokens only (e.g. send-email)`);
152
- }
153
- }
154
- return [...new Set(scopes)].sort();
155
- }
156
129
  //# sourceMappingURL=delegate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"delegate.js","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"delegate.js","sourceRoot":"","sources":["../src/delegate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,CAAC,MAAM,cAAc,GAA4B,CAAC,KAAK,EAAE,MAAM,CAAU,CAAC;AAEhF,4CAA4C;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEvC,uFAAuF;AACvF,MAAM,OAAO,GAAG,6BAA6B,CAAC;AAE9C,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IACxC,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAoB;IACvD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChE,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;QAAE,OAAO,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,IAAI,uBAAuB,CAAC,0BAA0B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,uBAAuB,CAAC,4BAA4B,CAAC,CAAC;IAC1E,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,uBAAuB,CAAC,sCAAsC,CAAC,CAAC;IAC9F,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,uBAAuB,CAAC,+CAA+C,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,uBAAuB,CAAC,2CAA2C,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,uBAAuB,CAAC,mDAAmD,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACpC,IAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAEpB,0FAA0F;IAC1F,IAAI,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,uBAAuB,CAC/B,kFAAkF,CACnF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,uBAAuB,CAAC,2CAA2C,CAAC,CAAC;IACjF,CAAC;IAED,2FAA2F;IAC3F,4FAA4F;IAC5F,wDAAwD;IACxD,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;QACnB,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,CAAC;SAAM,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvF,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,aAAa,CAAC,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,IAAY;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,SAAS;QAC9B,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG;YAAE,SAAS,CAAC,eAAe;QAC7D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,uBAAuB,CAAC,4BAA4B,CAAC,CAAC;IAE1E,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,uBAAuB,CAAC,uCAAuC,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,MAAM,IAAI,uBAAuB,CAAC,6BAA6B,CAAC,CAAC;IACnE,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,uBAAuB,CAAC,yCAAyC,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClB,MAAM,IAAI,uBAAuB,CAAC,4CAA4C,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,uBAAuB,CAAC,wCAAwC,CAAC,CAAC;IAC9E,CAAC;IAED,4FAA4F;IAC5F,4FAA4F;IAC5F,4EAA4E;IAC5E,IAAI,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC/B,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAClD,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;IACjD,CAAC;IAED,OAAO,aAAa,CAAC,WAAW,MAAM,CAAC,IAAI,GAAG,QAAQ,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,aAAa,CAAC,SAAiB;IACtC,IAAI,SAAS,CAAC,MAAM,GAAG,mBAAmB,EAAE,CAAC;QAC3C,MAAM,IAAI,uBAAuB,CAAC,6BAA6B,mBAAmB,cAAc,CAAC,CAAC;IACpG,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @proof-holdings/delegation-verifier — issuer-agnostic verification of Proof of Delegation.
3
3
  *
4
- * A delegation says: the domain in `principal` authorized the artifact in `delegate` for these
5
- * capability scopes, and the issuer checked that domain's control at issuance. It says nothing
4
+ * A delegation says: the domain in `principal` authorized the artifact in `delegate`, and the
5
+ * issuer checked that domain's control at issuance. It says nothing
6
6
  * about whether the artifact is safe, audited, or any good.
7
7
  *
8
8
  * ```ts
@@ -28,10 +28,10 @@ export type { FoundPublication } from './publication.js';
28
28
  */
29
29
  export { parseDelegationPointer, pointerRecordName, POINTER_VERSION, POINTER_RECORD_PREFIX, } from './publication.js';
30
30
  export type { PointerParseResult, PointerRejection } from './publication.js';
31
- export { canonicalizeDelegate, canonicalizeScopes, DelegateIdentifierError, DELEGATE_TYPES, DELEGATE_MAX_LENGTH, } from './delegate.js';
32
- export { resetJwksCache, SUPPORTED_ALGORITHMS } from './jwks.js';
33
- export type { SupportedAlgorithm } from './jwks.js';
31
+ export { canonicalizeDelegate, DelegateIdentifierError, DELEGATE_TYPES, DELEGATE_MAX_LENGTH, } from './delegate.js';
32
+ export { clearTombstones, DEFAULT_MAX_JWKS_AGE_MS, getJwksDiagnostics, MAX_JWKS_AGE_CAP_MS, refreshJwks, resetJwksCache, SUPPORTED_ALGORITHMS, } from './jwks.js';
33
+ export type { JwksDiagnostics, SupportedAlgorithm } from './jwks.js';
34
34
  export { readStatusSlot } from './status.js';
35
35
  export { classifyFailureReason } from './types.js';
36
- export type { DelegateInput, DelegateType, FailureOutcome, FailureReason, FetchLike, TrustedIssuer, VerificationFailure, VerificationResult, VerificationSuccess, VerifiedDelegation, VerifyOptions, } from './types.js';
36
+ export type { DelegateInput, DelegateType, FailureOutcome, FailureReason, FetchLike, JwksEvent, JwksEventType, KeySetStore, TrustedIssuer, VerificationFailure, VerificationResult, VerificationSuccess, VerifiedDelegation, VerifyOptions, } from './types.js';
37
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjE,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,SAAS,EACT,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,GACd,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,oBAAoB,GACrB,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,aAAa,EACb,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,GACd,MAAM,YAAY,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @proof-holdings/delegation-verifier — issuer-agnostic verification of Proof of Delegation.
3
3
  *
4
- * A delegation says: the domain in `principal` authorized the artifact in `delegate` for these
5
- * capability scopes, and the issuer checked that domain's control at issuance. It says nothing
4
+ * A delegation says: the domain in `principal` authorized the artifact in `delegate`, and the
5
+ * issuer checked that domain's control at issuance. It says nothing
6
6
  * about whether the artifact is safe, audited, or any good.
7
7
  *
8
8
  * ```ts
@@ -26,8 +26,8 @@ export { verifyPublishedDelegation, extractDelegationPublications, DELEGATION_ME
26
26
  * and hands the TXT values in.
27
27
  */
28
28
  export { parseDelegationPointer, pointerRecordName, POINTER_VERSION, POINTER_RECORD_PREFIX, } from './publication.js';
29
- export { canonicalizeDelegate, canonicalizeScopes, DelegateIdentifierError, DELEGATE_TYPES, DELEGATE_MAX_LENGTH, } from './delegate.js';
30
- export { resetJwksCache, SUPPORTED_ALGORITHMS } from './jwks.js';
29
+ export { canonicalizeDelegate, DelegateIdentifierError, DELEGATE_TYPES, DELEGATE_MAX_LENGTH, } from './delegate.js';
30
+ export { clearTombstones, DEFAULT_MAX_JWKS_AGE_MS, getJwksDiagnostics, MAX_JWKS_AGE_CAP_MS, refreshJwks, resetJwksCache, SUPPORTED_ALGORITHMS, } from './jwks.js';
31
31
  export { readStatusSlot } from './status.js';
32
32
  export { classifyFailureReason } from './types.js';
33
33
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EACL,yBAAyB,EACzB,6BAA6B,EAC7B,mBAAmB,EACnB,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,kBAAkB,CAAC;AAE1B;;;;;GAKG;AACH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,cAAc,EACd,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,eAAe,EACf,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,oBAAoB,GACrB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC"}
package/dist/jwks.d.ts CHANGED
@@ -1,47 +1,148 @@
1
1
  /**
2
- * JWKS retrieval and key cache.
2
+ * JWKS retrieval and key cache — the I/O half. The rules a fetched key set is held to (merge,
3
+ * tombstones, purpose, window) live in `keyset.ts`; this file decides WHEN to fetch and what to
4
+ * answer when a fetch fails.
3
5
  *
4
- * Three behaviours here are load-bearing rather than tidy, all learned from the hand-rolled Go
5
- * verifier that shipped before this package:
6
+ * Behaviours that are load-bearing rather than tidy, the first three learned from the hand-rolled
7
+ * Go verifier that shipped before this package:
6
8
  *
7
9
  * - The algorithm is chosen from THIS list, never from the token's own header. A token that
8
10
  * asks for `none` or for an HMAC is refused before any key is looked up.
9
- * - An unknown `kid` is indistinguishable from a rotated key, so a miss triggers at most one
10
- * refetch per cooldown window. Without it every junk-kid token costs one outbound request
11
- * and a busy verifier trips the issuer's rate limit — failing its GOOD tokens too.
11
+ * - An unknown `kid` is indistinguishable from a rotated (or emergency) key, so a miss triggers
12
+ * at most one refetch per cooldown window. Without it every junk-kid token costs one outbound
13
+ * request and a busy verifier trips the issuer's rate limit — failing its GOOD tokens too.
12
14
  * - Concurrent misses share ONE in-flight fetch per key set, with the cache re-checked after
13
15
  * it resolves. Serialising is what makes the cooldown hold under concurrency.
16
+ * - A failed refetch does not discard a cached key set. Under `maxJwksAgeMs` its CONFIRMED keys
17
+ * keep answering (a kid they do not cover is `jwks_unavailable`: it may be new); over it the answer
18
+ * is `jwks_stale` — except for an applied tombstone, which is `key_revoked` at any age. A set
19
+ * that was never fetched has no cache to fall back on, so its failure stays `jwks_unavailable`
20
+ * and is retried on every call.
21
+ * - Age is measured on a monotonic clock in-process. Only what is persisted uses wall clock.
14
22
  */
15
- import type { FetchLike } from './types.js';
23
+ import type { KeyFamily, KeyPolicyViolation, KeyRecord } from './keyset.js';
24
+ import type { FetchLike, JwksEvent, KeySetStore } from './types.js';
16
25
  export declare const SUPPORTED_ALGORITHMS: readonly ["RS256", "ES256"];
17
26
  export type SupportedAlgorithm = (typeof SUPPORTED_ALGORITHMS)[number];
18
- /** How long a `kid` miss suppresses further refetches of the same key set. */
27
+ /** How long a `kid` miss — or a failed refetch of a cached set — suppresses further fetches. */
19
28
  export declare const KEY_MISS_COOLDOWN_MS = 30000;
20
29
  /** How long a successfully fetched key set is reused without refetching. */
21
30
  export declare const KEY_SET_TTL_MS: number;
31
+ /** Default `maxJwksAgeMs`. */
32
+ export declare const DEFAULT_MAX_JWKS_AGE_MS: number;
33
+ /** Largest accepted `maxJwksAgeMs` — below the issuer's ~30-day lead on its next key. */
34
+ export declare const MAX_JWKS_AGE_CAP_MS: number;
22
35
  /**
23
36
  * A key set is kilobytes. Anything larger is not a JWKS, and the request is driven by input the
24
37
  * verifier does not control, so it is capped rather than streamed to completion.
25
38
  */
26
39
  export declare const MAX_REMOTE_BODY_BYTES: number;
27
- /** Test seam — drops every cached key set. */
40
+ export interface JwksClock {
41
+ monotonicMs(): number;
42
+ wallMs(): number;
43
+ }
44
+ /** Test seam — replaces both clocks; `null` restores the system clocks. Not exported publicly. */
45
+ export declare function setJwksClockForTests(replacement: JwksClock | null): void;
46
+ /** The per-call knobs that shape key-set handling. */
47
+ export interface KeySetOptions {
48
+ maxJwksAgeMs: number;
49
+ keySetStore?: KeySetStore;
50
+ onJwksEvent?: (event: JwksEvent) => void;
51
+ }
52
+ /** Test seam — drops every cached key set, tombstones included. */
28
53
  export declare function resetJwksCache(): void;
29
54
  export declare class JwksUnavailableError extends Error {
30
55
  constructor(message: string);
31
56
  }
32
57
  export declare function isSupportedAlgorithm(alg: unknown): alg is SupportedAlgorithm;
58
+ export interface KeyCandidate {
59
+ key: CryptoKey;
60
+ record: KeyRecord;
61
+ }
62
+ export type KeyResolution = {
63
+ kind: 'keys';
64
+ candidates: KeyCandidate[];
65
+ registryLive: boolean;
66
+ } | {
67
+ kind: 'revoked';
68
+ kid: string;
69
+ } | {
70
+ kind: 'wrong_purpose';
71
+ kid: string;
72
+ } | {
73
+ kind: 'unknown';
74
+ } | {
75
+ kind: 'stale';
76
+ cacheAgeSeconds: number | null;
77
+ } | {
78
+ kind: 'unavailable';
79
+ message: string;
80
+ };
33
81
  /**
34
- * Candidate verification keys for `kid`, fetching the key set at most once per cooldown.
35
- * Returns an empty array when the key is genuinely unknown; throws `JwksUnavailableError`
36
- * when the key set could not be read at all ("I could not check" is not "the token is bad").
82
+ * The keys that may verify a token of `family` carrying `kid` — or why none may. Fetches the key
83
+ * set when it is older than the TTL, or once per cooldown when `kid` is unknown.
37
84
  */
38
- export declare function getVerificationKeys(params: {
85
+ export declare function resolveVerificationKeys(params: {
39
86
  jwksUri: string;
40
87
  kid?: string;
41
88
  alg: SupportedAlgorithm;
89
+ family: KeyFamily;
42
90
  fetch: FetchLike;
43
91
  timeoutMs: number;
44
- }): Promise<CryptoKey[]>;
92
+ } & KeySetOptions): Promise<KeyResolution>;
45
93
  /** Verify a JWS signature against one candidate key. */
46
94
  export declare function verifySignature(key: CryptoKey, alg: SupportedAlgorithm, signingInput: string, signature: Uint8Array): Promise<boolean>;
95
+ /**
96
+ * Signature first, then the window and lifetime of the key that verified it — so a forged token
97
+ * is reported as `bad_signature`, not as a policy violation of a key it never came from.
98
+ */
99
+ export declare function verifyWithCandidates(resolution: Extract<KeyResolution, {
100
+ kind: 'keys';
101
+ }>, alg: SupportedAlgorithm, signingInput: string, signature: Uint8Array, token: {
102
+ iat: unknown;
103
+ exp: unknown;
104
+ }): Promise<'ok' | 'bad_signature' | KeyPolicyViolation>;
105
+ /**
106
+ * Refetch a key set now, ignoring the TTL and cooldowns — the method a `jwks_stale` answer names.
107
+ * Resolves with the outcome rather than throwing.
108
+ */
109
+ export declare function refreshJwks(params: {
110
+ jwksUri: string;
111
+ fetch?: FetchLike;
112
+ timeoutMs?: number;
113
+ keySetStore?: KeySetStore;
114
+ onJwksEvent?: (event: JwksEvent) => void;
115
+ }): Promise<{
116
+ ok: true;
117
+ } | {
118
+ ok: false;
119
+ message: string;
120
+ }>;
121
+ /**
122
+ * Forget every applied and pending tombstone — the documented manual reset. With `jwksUri`, only
123
+ * that key set (and, given a `keySetStore`, its persisted copy); without, every set in memory.
124
+ */
125
+ export declare function clearTombstones(params?: {
126
+ jwksUri?: string;
127
+ keySetStore?: KeySetStore;
128
+ }): Promise<void>;
129
+ export interface JwksDiagnostics {
130
+ jwksUri: string;
131
+ /** Age of the cached set in seconds; null when never fetched or the persisted age is unknown. */
132
+ cacheAgeSeconds: number | null;
133
+ registryLive: boolean;
134
+ /** Confirmed registry kids no longer served before their retention ended — kept with last-known state. */
135
+ vanishedKids: string[];
136
+ pendingTombstones: Array<{
137
+ kid: string;
138
+ revokedAt: number;
139
+ pendingForSeconds: number;
140
+ }>;
141
+ appliedTombstones: Array<{
142
+ kid: string;
143
+ revokedAt: number;
144
+ }>;
145
+ }
146
+ /** What the verifier currently holds for one key set, or null if it has never been used. */
147
+ export declare function getJwksDiagnostics(jwksUri: string): JwksDiagnostics | null;
47
148
  //# sourceMappingURL=jwks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwks.d.ts","sourceRoot":"","sources":["../src/jwks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,eAAO,MAAM,oBAAoB,6BAA8B,CAAC;AAChE,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAC3C,4EAA4E;AAC5E,eAAO,MAAM,cAAc,QAAc,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAa,CAAC;AAchD,8CAA8C;AAC9C,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,qBAAa,oBAAqB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI5B;AAkBD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,kBAAkB,CAE5E;AA8ED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,kBAAkB,CAAC;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAmDvB;AAED,wDAAwD;AACxD,wBAAsB,eAAe,CACnC,GAAG,EAAE,SAAS,EACd,GAAG,EAAE,kBAAkB,EACvB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,OAAO,CAAC,CAYlB"}
1
+ {"version":3,"file":"jwks.d.ts","sourceRoot":"","sources":["../src/jwks.ts"],"names":[],"mappings":"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"}