@web-ts-toolkit/express-request-attestation 0.51.0 → 0.53.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/index.d.ts CHANGED
@@ -1,27 +1,28 @@
1
- import { P as ProofTiming, R as ResolvedTimePolicy } from './time-policy-DlER-7rf.js';
2
- export { A as ATTESTATION_ERROR_RESPONSE_HEADER, a as AttestationEnvelope, b as AttestationMacInput, c as AttestationProtocolError, D as DEFAULT_ATTESTATION_HEADER, d as DEFAULT_CLOCK_SKEW_MS, e as DEFAULT_CLUSTER_CLOCK_GUARD_MS, f as DEFAULT_MAX_AGE_MS, g as DEFAULT_MAX_BODY_BYTES, E as EMPTY_BODY_SHA256_HEX, G as GLOBAL_MAX_RETENTION_MS, K as KEY_BYTES_LENGTH, M as MAC_BYTES_LENGTH, h as MAC_INPUT_LABEL, i as MAX_BODY_BYTES_HARD_CAP, j as MAX_CLOCK_SKEW_MS, k as MAX_CLUSTER_CLOCK_GUARD_MS, l as MAX_CONTENT_TYPE_BYTES, m as MAX_ENCODED_TRANSACTION_ID_BYTES, n as MAX_HEADER_NAME_BYTES, o as MAX_KEY_ID_LENGTH, p as MAX_MAX_AGE_MS, q as MAX_PUBLIC_ORIGIN_BYTES, r as MAX_REPLAY_KEY_BYTES, s as MAX_REPLAY_NAMESPACE_LENGTH, t as MAX_REQUEST_TARGET_BYTES, u as MIN_CLOCK_SKEW_MS, v as MIN_CLUSTER_CLOCK_GUARD_MS, w as MIN_MAX_AGE_MS, x as MIN_REPLAY_KEY_BYTES, y as MacInputFields, N as NONCE_BYTES_LENGTH, z as PROTOCOL_VERSION, B as ProofTimingDecision, C as REPLAY_KEY_PREFIX, F as RequestSigner, H as RequestSignerOptions, I as RetentionAdmission, S as SignInput, T as TimePolicyOptions, J as TransactionIdFields, L as assertTimestampMs, O as base64urlDecode, Q as base64urlEncode, U as buildMacInputBytes, V as buildReplayKeyPreimageBytes, W as canonicalJsonBytes, X as canonicalizeRequestTarget, Y as checkRetentionAdmissible, Z as decodeTransactionId, _ as encodeTransactionId, $ as evaluateProofTiming, a0 as extractSingleHeaderValue, a1 as maxStoreAdmissionMs, a2 as maxVerifierRemainingMs, a3 as normalizeContentType, a4 as normalizeMethod, a5 as parseCanonicalJsonArray, a6 as resolveTimePolicy, a7 as utf8ByteLength, a8 as utf8Decode, a9 as utf8Encode, aa as validateBodyHashHex, ab as validateHeaderName, ac as validateKeyBytes, ad as validateKeyId, ae as validateMac, af as validateNonceHex, ag as validatePublicOrigin, ah as validateReplayKey, ai as validateReplayNamespace } from './time-policy-DlER-7rf.js';
1
+ import { P as ProofTiming, R as ResolvedTimePolicy } from './time-policy-CquS0Zgm.js';
2
+ export { A as ATTESTATION_ERROR_RESPONSE_HEADER, a as AttestationEnvelope, b as AttestationMacInput, c as AttestationProtocolError, D as DEFAULT_ATTESTATION_HEADER, d as DEFAULT_CLOCK_SKEW_MS, e as DEFAULT_CLUSTER_CLOCK_GUARD_MS, f as DEFAULT_MAX_AGE_MS, g as DEFAULT_MAX_BODY_BYTES, E as EMPTY_BODY_SHA256_HEX, G as GLOBAL_MAX_RETENTION_MS, K as KEY_BYTES_LENGTH, M as MAC_BYTES_LENGTH, h as MAC_INPUT_LABEL, i as MAX_BODY_BYTES_HARD_CAP, j as MAX_CLOCK_SKEW_MS, k as MAX_CLUSTER_CLOCK_GUARD_MS, l as MAX_CONTENT_TYPE_BYTES, m as MAX_ENCODED_TRANSACTION_ID_BYTES, n as MAX_HEADER_NAME_BYTES, o as MAX_KEY_ID_LENGTH, p as MAX_MAX_AGE_MS, q as MAX_PUBLIC_ORIGIN_BYTES, r as MAX_REPLAY_KEY_BYTES, s as MAX_REPLAY_NAMESPACE_LENGTH, t as MAX_REQUEST_TARGET_BYTES, u as MIN_CLOCK_SKEW_MS, v as MIN_CLUSTER_CLOCK_GUARD_MS, w as MIN_MAX_AGE_MS, x as MIN_REPLAY_KEY_BYTES, y as MacInputFields, N as NONCE_BYTES_LENGTH, z as PROTOCOL_VERSION, B as ProofTimingDecision, C as REPLAY_KEY_PREFIX, F as RequestSigner, H as RequestSignerOptions, I as RetentionAdmission, S as SignInput, T as TimePolicyOptions, J as TransactionIdFields, L as assertTimestampMs, O as base64urlDecode, Q as base64urlEncode, U as buildMacInputBytes, V as buildReplayKeyPreimageBytes, W as canonicalJsonBytes, X as canonicalizeRequestTarget, Y as checkRetentionAdmissible, Z as decodeTransactionId, _ as encodeTransactionId, $ as evaluateProofTiming, a0 as extractSingleHeaderValue, a1 as maxStoreAdmissionMs, a2 as maxVerifierRemainingMs, a3 as normalizeContentType, a4 as normalizeMethod, a5 as parseCanonicalJsonArray, a6 as resolveTimePolicy, a7 as utf8ByteLength, a8 as utf8Decode, a9 as utf8Encode, aa as validateBodyHashHex, ab as validateHeaderName, ac as validateKeyBytes, ad as validateKeyId, ae as validateMac, af as validateNonceHex, ag as validatePublicOrigin, ah as validateReplayKey, ai as validateReplayNamespace } from './time-policy-CquS0Zgm.js';
3
3
  import { Router } from 'express';
4
4
 
5
5
  /**
6
- * ATT-01 server-side interfaces and fixed HTTP errors (task section 4.5).
7
- *
8
- * Server-specific ONLY: this module may reference Express/Redis types but is
9
- * imported exclusively by the `.` entry. The `./signer` entry never imports
10
- * it, so browser declarations stay free of server types.
6
+ * Server-side key, replay-store, and Express integration contracts, exported
7
+ * from `@web-ts-toolkit/express-request-attestation`. Browser consumers use
8
+ * the separate `/signer` entry, which needs no server dependencies or types.
11
9
  */
12
10
  /** One accepted key: exactly 32 bytes with an explicit acceptance window. */
13
11
  interface AttestationKeyEntry {
12
+ /** Unique provisioning ID matching `[A-Za-z0-9_-]{1,64}`; never remap a live ID. */
14
13
  readonly keyId: string;
15
14
  /** Exactly 32 bytes; snapshots are owned/copy-isolated. */
16
15
  readonly key: Uint8Array;
17
- /** Nonnegative safe-integer epoch ms; `acceptFrom < acceptUntil`. */
16
+ /** Inclusive activation, nonnegative safe-integer epoch ms; less than `acceptUntil`. */
18
17
  readonly acceptFrom: number;
18
+ /** Exclusive retirement in epoch ms. Identical for this ID on every verifier in the protection space. */
19
19
  readonly acceptUntil: number;
20
20
  }
21
21
  /** Keyring snapshot returned by `AttestationKeyProvider.getSnapshot()`. */
22
22
  interface AttestationKeySnapshot {
23
- /** Must name a present active entry. At most 16 retained keys. */
23
+ /** Names a present entry; discovery additionally requires it active at its post-provider clock. */
24
24
  readonly currentKeyId: string;
25
+ /** 1–16 retained entries, with identical material/windows across the protection space. */
25
26
  readonly keys: readonly AttestationKeyEntry[];
26
27
  }
27
28
  /** Input snapshot for static/rotating providers (owned/copied on entry). */
@@ -36,61 +37,87 @@ interface AttestationKeySnapshotInput {
36
37
  }
37
38
  /**
38
39
  * Typed key provider. Returns an owned, copy-isolated snapshot (or a promise
39
- * of one). Provider/read failure is operational `503`, never a stale-key
40
- * client error. External cron/KMS/config code owns provisioning and refresh;
41
- * there is no default polling timer.
40
+ * of one). Provider throws, rejected reads, malformed snapshots, and timeouts
41
+ * become fixed `503 ATTESTATION_KEYS_UNAVAILABLE`, without a stale-key marker.
42
+ * External cron/KMS/config code owns provisioning, refresh, and dependency
43
+ * deadlines; there is no polling timer or cancellation of underlying work.
42
44
  */
43
45
  interface AttestationKeyProvider {
44
46
  getSnapshot(): AttestationKeySnapshot | Promise<AttestationKeySnapshot>;
45
47
  }
46
- /** Rotating provider with explicit atomic replacement (ATT-05). */
48
+ /** Rotating provider with explicit atomic replacement and copy-isolated reads. */
47
49
  interface AttestationRotatingKeyProvider extends AttestationKeyProvider {
50
+ /** Rejects invalid/remapped retained IDs atomically; does not cancel requests using an earlier snapshot. */
48
51
  replace(snapshot: AttestationKeySnapshotInput): void;
49
52
  }
50
53
  /** Atomic bounded replay-store reservation input. */
51
54
  interface AttestationStoreReserveInput {
52
55
  /** Opaque key: `att:v1:` + SHA-256 hex of `[namespace, origin, nonceHex]`. */
53
56
  readonly replayKey: string;
54
- /** Absolute retention deadline (safe-integer epoch ms, section 4.4). */
57
+ /**
58
+ * Absolute safe-integer epoch-ms deadline:
59
+ * `min(timestampMs + maxAgeMs + clockSkewMs, key.acceptUntil) + clusterClockGuardMs`.
60
+ * Retain until this deadline; never clamp it or extend it on a duplicate.
61
+ */
55
62
  readonly retainUntilMs: number;
56
63
  }
64
+ /** One atomic admission: inserted, already live (no extension), or deadline already elapsed. */
57
65
  type AttestationStoreReserveResult = 'reserved' | 'duplicate' | 'expired';
58
66
  /**
59
67
  * Bounded replay store. `reserve` returns `duplicate` for an admitted nonce,
60
68
  * `expired` when the retention deadline has elapsed, and throws typed errors
61
- * on capacity exhaustion or unavailability. The adapter never interprets
62
- * caller-supplied nonce strings as already authenticated.
69
+ * on capacity exhaustion or unavailability. Verify proofs before reserving;
70
+ * the store does not verify signatures. Successful or possibly committed
71
+ * reservations are never released on handler failure, timeout, or lost response.
72
+ * Shared verifiers must use identical time policies and key acceptance windows.
73
+ * Direct calls have no package-level wait timeout; callers own driver deadlines.
63
74
  */
64
75
  interface AttestationStore {
65
76
  reserve(input: AttestationStoreReserveInput): Promise<AttestationStoreReserveResult>;
66
77
  }
67
78
  /**
68
- * Narrow structural Redis client contract (ATT-04). The library never creates,
69
- * connects, or closes clients and never imports a Redis driver; callers
70
- * inject any compatible client (e.g. `node-redis`). Redis stays a dev
71
- * dependency for integration tests, not a runtime dependency.
79
+ * Narrow structural Redis client contract. Inject an already-connected compatible
80
+ * client (for example `node-redis`). The caller owns connection, error listeners,
81
+ * command/queue deadlines, and shutdown; this package never imports a driver.
72
82
  */
73
83
  interface AttestationRedisClient {
74
84
  sendCommand(args: string[]): Promise<unknown>;
75
85
  }
76
- /** Options for `createRequestAttestationMiddleware` (implemented in ATT-02). */
86
+ /**
87
+ * Express guard configuration. All guards in one `(publicOrigin, replayNamespace)`
88
+ * share replay state, identical time policies, and identical per-key acceptance
89
+ * windows. Migrate live policy changes to a new namespace rather than mixing
90
+ * policies or extending old key deadlines over already-admitted proofs.
91
+ */
77
92
  interface CreateRequestAttestationMiddlewareOptions {
78
- /** Static canonical HTTPS origin (loopback http allowed in development). */
93
+ /** Static canonical HTTPS origin (loopback HTTP allowed); never derive from request headers. */
79
94
  readonly publicOrigin: string;
95
+ /** Protection-space identifier matching `[A-Za-z0-9_-]{1,128}`; must match signers. */
80
96
  readonly replayNamespace: string;
81
- /** Upstream-stripped prefix to re-prepend after `req.originalUrl`. */
97
+ /** Upstream-stripped prefix to prepend to `req.originalUrl`; default empty, no trailing slash. */
82
98
  readonly publicPathPrefix?: string;
99
+ /** Caller-owned live provider; each request validates and copies its bounded snapshot. */
83
100
  readonly keyProvider: AttestationKeyProvider;
101
+ /** Atomic shared nonce admission after successful verification; no fallback on store failure. */
84
102
  readonly store: AttestationStore;
103
+ /** Maximum proof age, default 30000 ms; safe integer in 1000–120000. */
85
104
  readonly maxAgeMs?: number;
105
+ /** Client clock allowance, default 5000 ms; safe integer in 0–30000. */
86
106
  readonly clockSkewMs?: number;
107
+ /** Maximum pairwise verifier/store clock difference, default 5000 ms (0–30000); retention only. */
87
108
  readonly clusterClockGuardMs?: number;
109
+ /** Single proof header; default `x-client-transaction-id`. Must pass shared HTTP name validation. */
88
110
  readonly headerName?: string;
111
+ /** Default `Date.now`; every call must return nonnegative safe-integer epoch ms or the guard fails with 500. */
89
112
  readonly now?: () => number;
90
- /** Bounds async provider/store waits; default 5000 ms. */
113
+ /**
114
+ * Each async provider/store wait, default 5000 ms; integer range 1–60000.
115
+ * Does not cancel underlying work. Configure driver deadlines separately;
116
+ * a timed-out reservation may commit but can never resume the handler.
117
+ */
91
118
  readonly operationTimeoutMs?: number;
92
119
  }
93
- /** Options for `createAttestationBodyCapture` (implemented in ATT-02). */
120
+ /** Exact-byte capture for Express parsers; use the same limit and `inflate: false` on the parser. */
94
121
  interface CreateAttestationBodyCaptureOptions {
95
122
  /** Parser/body budget, default 1 MiB, hard cap 16 MiB. */
96
123
  readonly maxBodyBytes?: number;
@@ -98,35 +125,51 @@ interface CreateAttestationBodyCaptureOptions {
98
125
  /**
99
126
  * Package-owned body-capture integration: an Express parser `verify`
100
127
  * callback storing digest/count in per-request state plus a narrow error
101
- * handler mapping parser size/encoding failures to section 4.5 errors.
128
+ * handler mapping parser size/encoding failures to fixed 413/415 errors.
129
+ * These callbacks assign directly to Express parser/error-handler types.
102
130
  */
103
131
  interface AttestationBodyCapture {
132
+ /** Attach as the parser's `verify` callback; observes bytes without changing `req.body`. */
104
133
  readonly verify: (req: unknown, res: unknown, body: unknown, encoding: string) => void;
134
+ /** Mount after parsers and before the guard; other parser errors pass to `next(err)`. */
105
135
  readonly errorHandler: (err: unknown, req: unknown, res: unknown, next: (err?: unknown) => void) => void;
106
136
  }
107
- /** Options for `createSignerBundleRouter` (implemented in ATT-05). */
137
+ /** Trusted asset-distribution configuration; the caller owns mounting, CORS, and CSP. */
108
138
  interface CreateSignerBundleRouterOptions {
139
+ /** Static canonical HTTPS origin (loopback HTTP allowed); never derived from Host/Forwarded headers. */
109
140
  readonly publicOrigin: string;
141
+ /** Protection-space namespace matching the verifier and browser client. */
110
142
  readonly replayNamespace: string;
143
+ /** Caller-owned provider; discovery publishes only the current key active after the provider read. */
111
144
  readonly keyProvider: AttestationKeyProvider;
112
- /** Relative mount base path used only to publish external URLs. */
145
+ /**
146
+ * Default `/attestation`. Mount the relative router at this path yourself.
147
+ * `/` publishes `/signer-meta`, `/signer.<hash>.mjs`, and `/runtime.<hash>.mjs`.
148
+ */
113
149
  readonly basePath?: string;
150
+ /** Prefix stripped by the upstream proxy, default empty; prepend once to published URLs, not the app mount. */
114
151
  readonly publicPathPrefix?: string;
152
+ /** Default `Date.now`; nonnegative safe-integer epoch ms, checked before and after discovery's provider read. */
115
153
  readonly now?: () => number;
154
+ /** Per-provider wait bound, default 5000 ms (1–60000); does not cancel provider work. Timeout returns fixed 503. */
116
155
  readonly operationTimeoutMs?: number;
117
156
  }
118
- /** Options for `createMemoryAttestationStore` (implemented in ATT-03). */
157
+ /** Process-local replay storage, shared only by callers reusing the same store object. */
119
158
  interface CreateMemoryAttestationStoreOptions {
159
+ /** Positive safe-integer capacity; default 50000. Live entries are never evicted. */
120
160
  readonly maxEntries?: number;
161
+ /** Default `Date.now`; nonnegative safe-integer epoch ms, synchronized with verifier clocks. */
121
162
  readonly now?: () => number;
122
163
  }
123
- /** Options for `createRedisAttestationStore` (implemented in ATT-04). */
164
+ /** Shared Redis replay ledger; the caller owns the connected client and dependency deadlines. */
124
165
  interface CreateRedisAttestationStoreOptions {
125
166
  readonly client: AttestationRedisClient;
167
+ /** Default `wtt-attestation`; 1–256 characters. All instances in a protection space share this ledger prefix. */
126
168
  readonly keyPrefix?: string;
169
+ /** Positive safe-integer capacity; default 50000, identical for every client sharing the prefix. */
127
170
  readonly maxEntries?: number;
128
171
  }
129
- /** Fixed section 4.5 error codes with their HTTP status and message. */
172
+ /** Fixed verifier error codes; `AttestationError` supplies the stable HTTP status and message. */
130
173
  type AttestationErrorCode = 'ATTESTATION_MISSING' | 'ATTESTATION_MALFORMED' | 'ATTESTATION_STALE_KEY' | 'ATTESTATION_EXPIRED' | 'ATTESTATION_FUTURE' | 'ATTESTATION_INVALID_SIGNATURE' | 'ATTESTATION_REPLAY' | 'ATTESTATION_BODY_TOO_LARGE' | 'ATTESTATION_UNSUPPORTED_ENCODING' | 'ATTESTATION_BODY_CAPTURE_REQUIRED' | 'ATTESTATION_INTERNAL_ERROR' | 'ATTESTATION_KEYS_UNAVAILABLE' | 'ATTESTATION_REPLAY_UNAVAILABLE';
131
174
  /**
132
175
  * Typed attestation failure carrying a fixed code/status/message. Responses
@@ -146,32 +189,51 @@ declare function toAttestationErrorPayload(error: AttestationError): {
146
189
  readonly message: string;
147
190
  };
148
191
 
192
+ /** Sanitized failure observation; excludes request/key/body/provider diagnostics. */
149
193
  interface AttestationDiagnosticEvent {
150
194
  readonly code: AttestationErrorCode;
151
195
  readonly status: number;
152
196
  }
153
197
  type AttestationDiagnosticHook = (event: AttestationDiagnosticEvent) => void;
154
198
  type CreateRequestAttestationMiddlewareOptionsWithDiagnostic = CreateRequestAttestationMiddlewareOptions & {
199
+ /** Optional synchronous observation of fixed code/status only; hook failure never changes admission. */
155
200
  readonly onDiagnostic?: AttestationDiagnosticHook;
156
201
  };
157
202
  /**
158
203
  * Create the verify-before-reserve Express guard.
159
204
  *
205
+ * Mount after exact-byte parser capture and its error handler on selected API
206
+ * routes. The returned function assigns directly to `express.RequestHandler`.
207
+ * Missing/empty proofs fail with 403 ATTESTATION_MISSING; duplicate, comma-joined,
208
+ * oversized, or noncanonical proofs and raw duplicate Content-Type fields fail
209
+ * with 403 ATTESTATION_MALFORMED before provider/replay work. Only a stale-key failure
210
+ * carries `X-Attestation-Error: stale-key`; all provider read/snapshot failures
211
+ * use fixed 503 ATTESTATION_KEYS_UNAVAILABLE without that marker.
212
+ *
160
213
  * Required `publicOrigin`/`replayNamespace`/services are validated once and
161
214
  * frozen; key snapshots are owned per request while service references stay
162
215
  * live. Async provider/store waits are bounded by `operationTimeoutMs`
163
216
  * (default 5000 ms, allowed 1-60000 ms); a late settlement after the bound
164
217
  * can never resume the rejected request, call `next`, or release a possibly
165
- * committed reservation.
218
+ * committed reservation. Driver/provider cancellation and deadlines stay caller-owned.
219
+ *
220
+ * Public HMAC material does not authenticate a user/session. Auth before this
221
+ * guard can avoid replay allocation for unauthorized requests; guard-first can
222
+ * reject invalid proofs before auth work but reserves even if later auth fails.
223
+ * Reservations remain committed after downstream failure. Durable business
224
+ * idempotency and authorization are application responsibilities.
166
225
  */
167
226
  declare function createRequestAttestationMiddleware(options: CreateRequestAttestationMiddlewareOptionsWithDiagnostic): (req: unknown, res: unknown, next: (err?: unknown) => void) => void;
168
227
 
228
+ /** Frozen observation of the exact parser bytes, never reconstructed from `req.body`. */
169
229
  interface AttestationBodyRecord {
170
230
  readonly digestHex: string;
171
231
  readonly byteLength: number;
172
232
  }
173
233
  interface AttestationBodyCaptureWithRecord extends AttestationBodyCapture {
234
+ /** Returns a frozen copy, or undefined if no parser capture ran for this request. */
174
235
  readonly getRecord: (req: unknown) => AttestationBodyRecord | undefined;
236
+ /** Validated byte budget; use the same value as the parser and browser wrapper limits. */
175
237
  readonly maxBodyBytes: number;
176
238
  }
177
239
  /**
@@ -182,16 +244,22 @@ interface AttestationBodyCaptureWithRecord extends AttestationBodyCapture {
182
244
  */
183
245
  declare function getAttestationBodyRecord(req: unknown): AttestationBodyRecord | undefined;
184
246
  /**
185
- * Create the parser `verify` callback + narrow error mapper.
247
+ * Create the Express parser `verify` callback and narrow error handler.
186
248
  *
187
249
  * `maxBodyBytes` defaults to 1 MiB and must be a positive safe integer up to
188
250
  * the v1 hard cap of 16 MiB. Configure the SAME value as the Express parser
189
251
  * `limit` option and always use `inflate: false`. Applications may choose a
190
252
  * smaller limit. Over-limit bodies fail in the parser (mapped to 413 before
191
- * the guard) before any replay allocation.
253
+ * the guard) before any replay allocation. Pass `capture.verify` directly to
254
+ * `express.json`/`urlencoded`/`text`/`raw`; mount `capture.errorHandler` after
255
+ * parsers and before the guard. The callbacks are assignable to Express types.
256
+ * Size/encoding failures map to fixed 413/415; other parser errors go to
257
+ * `next(err)`. Capture observes the bytes without mutating `req.body`.
258
+ * Uncaptured nonempty bodies fail at the guard with BODY_CAPTURE_REQUIRED.
192
259
  */
193
260
  declare function createAttestationBodyCapture(options?: CreateAttestationBodyCaptureOptions): AttestationBodyCaptureWithRecord;
194
261
 
262
+ /** Low-level verifier input. Supply trusted origin/policy/key state and a digest of captured exact bytes. */
195
263
  interface AttestationVerifyInput {
196
264
  readonly headerValue: string | readonly string[] | undefined;
197
265
  readonly method: unknown;
@@ -206,6 +274,7 @@ interface AttestationVerifyInput {
206
274
  readonly replayNamespace: string;
207
275
  readonly publicOrigin: string;
208
276
  }
277
+ /** Signature/timing verification result; no nonce has been reserved yet. */
209
278
  interface AttestationVerifiedProof {
210
279
  readonly keyId: string;
211
280
  readonly timestampMs: number;
@@ -213,6 +282,7 @@ interface AttestationVerifiedProof {
213
282
  readonly keyEntry: AttestationKeyEntry;
214
283
  readonly timing: ProofTiming;
215
284
  readonly replayKey: string;
285
+ /** Absolute epoch-ms replay retention deadline, including the cluster clock guard. */
216
286
  readonly retainUntilMs: number;
217
287
  readonly method: string;
218
288
  readonly requestTarget: string;
@@ -230,7 +300,7 @@ declare function checkProofTiming(input: {
230
300
  }): ProofTiming;
231
301
  /** Concatenate the trusted prefix with `originalUrl` and canonicalize. */
232
302
  declare function resolveRequestTarget(originalUrl: unknown, publicPathPrefix: string): string;
233
- /** Single final Content-Type value. Duplicate/controls => MALFORMED. */
303
+ /** Single final Content-Type value. Duplicate fields/controls => MALFORMED; commas within one field are preserved. */
234
304
  declare function resolveContentType(header: string | readonly string[] | undefined | null): string;
235
305
  /** V1 accepts identity / absent Content-Encoding only. */
236
306
  declare function assertAllowedContentEncoding(header: string | readonly string[] | undefined | null): void;
@@ -244,18 +314,21 @@ declare function deriveReplayKey(input: {
244
314
  * Pure pre-reservation verification. Performs header/codec, snapshot lookup,
245
315
  * time, target, content-type/encoding, body digest, and HMAC stages in order
246
316
  * and returns the owned timing + replay key for the middleware to reserve.
317
+ * Always validates the raw proof first; malformed framing cannot be bypassed
318
+ * with a decoded envelope. Throws fixed `AttestationError` failures. This helper
319
+ * neither authenticates the caller nor reserves the nonce: prefer the Express
320
+ * guard for atomic admission and its final timing checks around asynchronous work.
247
321
  */
248
322
  declare function verifyAttestationProof(input: AttestationVerifyInput): AttestationVerifiedProof;
249
323
 
250
324
  /**
251
- * ATT-03 bounded atomic memory replay store (task section 4.4).
325
+ * Bounded atomic memory replay store.
252
326
  *
253
- * Admission contract (shared with the future ATT-04 Redis adapter):
327
+ * Admission contract shared by the memory, Redis, and MongoDB stores:
254
328
  *
255
329
  * - `reserve({ replayKey, retainUntilMs })` returns `'reserved' | 'duplicate' | 'expired'`.
256
- * - The canonical deadline field is `retainUntilMs` (see `AttestationStoreReserveInput`).
257
- * A `retainUntil` alias is accepted at runtime for callers written against the
258
- * task-text shorthand; when both are present the canonical field wins.
330
+ * - `retainUntilMs` is the absolute epoch-ms deadline from verified proof timing,
331
+ * including the cluster clock guard, never a relative TTL.
259
332
  * - Validation order per admission: input shape, store clock, opaque key bounds
260
333
  * (1-256 printable ASCII), deadline shape (nonnegative safe integer), input
261
334
  * expiry (`retainUntilMs <= now` returns `'expired'` with no allocation and no
@@ -283,8 +356,7 @@ declare function verifyAttestationProof(input: AttestationVerifyInput): Attestat
283
356
  /**
284
357
  * Shared capacity exhaustion: no live reservation is evicted and no proof is
285
358
  * admitted without replay state. Handle like any replay-store failure: fail
286
- * closed. Also thrown by the future Redis adapter so callers handle one error
287
- * type across backends.
359
+ * closed. All three built-in backends use this same error type.
288
360
  */
289
361
  declare class AttestationCapacityError extends Error {
290
362
  readonly name = "AttestationCapacityError";
@@ -299,27 +371,29 @@ declare class AttestationCapacityError extends Error {
299
371
  * store when replay state must survive restarts or be shared by multiple
300
372
  * application instances. Replay is shared only by callers reusing this same
301
373
  * object; the store does not verify request signatures. Capacity defaults to
302
- * 50000 entries and never evicts live entries.
374
+ * 50000 entries and never evicts live entries. Admission uses absolute
375
+ * `retainUntilMs`, rejects retention over 240000 ms rather than truncating it,
376
+ * and never extends a duplicate's deadline. Capacity throws
377
+ * `AttestationCapacityError`; malformed inputs/clock values throw
378
+ * `AttestationProtocolError`. Keep its clock synchronized with the verifier.
303
379
  */
304
380
  declare function createMemoryAttestationStore(options?: CreateMemoryAttestationStoreOptions): AttestationStore;
305
381
 
306
382
  /**
307
- * ATT-04 shared bounded Redis replay admission (task sections 4.1/4.4).
383
+ * Shared bounded Redis replay admission.
308
384
  *
309
- * Admission contract (shared with ATT-03 memory; see `test/store-conformance.ts`):
385
+ * Admission contract shared with memory and MongoDB:
310
386
  *
311
387
  * - `reserve({ replayKey, retainUntilMs })` returns
312
388
  * `'reserved' | 'duplicate' | 'expired'`.
313
- * - The canonical deadline field is `retainUntilMs`
314
- * (see `AttestationStoreReserveInput`). A `retainUntil` alias is accepted at
315
- * runtime for callers written against the task-text shorthand; when both are
316
- * present the canonical field wins.
389
+ * - `retainUntilMs` is the absolute epoch-ms deadline from verified proof timing,
390
+ * including the cluster clock guard, never a relative TTL.
317
391
  * - Malformed inputs (non-object, bad opaque key bounds 1–256 printable
318
392
  * ASCII, non-safe-integer deadlines) reject with `AttestationProtocolError`
319
393
  * WITHOUT touching Redis (no allocation, `sendCommand` never called).
320
394
  * - Live/overlong enforcement uses Redis server `TIME` inside one atomic Lua
321
- * admission (`src/stores/redis-script.ts`): `retainUntil <= now` returns
322
- * `'expired'`; `retainUntil - now > 240000` throws `AttestationProtocolError`
395
+ * admission: `retainUntilMs <= nowMs` returns
396
+ * `'expired'`; `retainUntilMs - nowMs > 240000` throws `AttestationProtocolError`
323
397
  * instead of truncating; duplicates return `'duplicate'` even when full and
324
398
  * never change their score; at most 64 expired entries plus the requested
325
399
  * expired one are reclaimed per admission; shared `ZCARD` capacity is checked
@@ -337,20 +411,18 @@ declare function createMemoryAttestationStore(options?: CreateMemoryAttestationS
337
411
  * maxEntries = 50000 })`. The public client type is the narrow structural
338
412
  * `sendCommand(args: string[]): Promise<unknown>` contract compatible with
339
413
  * `node-redis`; the library never creates, connects, or closes clients and
340
- * never imports a Redis driver (driver is a dev dependency for tests only).
414
+ * never imports a Redis driver. Callers own command/queue deadlines and retries.
341
415
  * - One same-slot sorted set per shared prefix
342
416
  * (`<keyPrefix>:attestation-proofs`) holds opaque keys + absolute scores.
343
417
  * All clients sharing a prefix MUST configure identical `maxEntries`;
344
- * mixed capacities admit under the smallest view and reject under the
345
- * largest, breaking the shared bound.
418
+ * mixed capacities break the shared bound.
346
419
  * - Steady-state `EVALSHA`; on cold start / definite `NOSCRIPT`
347
420
  * nonexecution the runner `SCRIPT LOAD`s and retries exactly once. Uncertain
348
421
  * network timeouts are never retried as though nothing committed: a timeout
349
422
  * may have committed server-side but never executes a handler or falls back
350
423
  * to memory (the middleware maps it to `ATTESTATION_REPLAY_UNAVAILABLE`
351
- * without calling `next`). No non-atomic `GET`/`SET`, no separate capacity
352
- * counter, no `nowSkewToleranceMs` hidden clamp — the adapter receives
353
- * section 4.4's exact `retainUntil`.
424
+ * without calling `next`). The adapter preserves the verifier's exact
425
+ * `retainUntilMs` rather than clamping it.
354
426
  *
355
427
  * Capacity / failure / rate notes (no fabricated bytes-per-entry benchmark):
356
428
  *
@@ -361,9 +433,8 @@ declare function createMemoryAttestationStore(options?: CreateMemoryAttestationS
361
433
  * - Redis eviction, restore/state loss, or failover can erase replay
362
434
  * reservations. When relying on window-wide admission history, use a
363
435
  * suitably configured dedicated `noeviction` deployment with persistence /
364
- * replication appropriate to your recovery objectives; after state loss a
365
- * fresh proof is required and old proofs may be admitted once more (they
366
- * still fail verifier age rules once stale).
436
+ * replication appropriate to your recovery objectives; after state loss old
437
+ * proofs may be admitted again while still valid under verifier age rules.
367
438
  * - Sharing is one ledger key per prefix: independent prefixes (and memory
368
439
  * objects) intentionally do not coordinate. Restarts do not reset Redis
369
440
  * state, but `SCRIPT FLUSH` only forces a reload, never a second admission.
@@ -390,23 +461,30 @@ declare class AttestationRedisStoreError extends Error {
390
461
  * sorted-set ledger.
391
462
  *
392
463
  * Pass an already-connected client (or compatible `sendCommand` adapter); the
393
- * store never connects, disconnects, or attaches listeners. All clients of a
394
- * prefix must use identical `maxEntries`. See the module header for capacity,
395
- * failure, and lifecycle notes.
464
+ * caller owns connection, listeners, command deadlines, and shutdown. Defaults:
465
+ * `keyPrefix: 'wtt-attestation'`, `maxEntries: 50000`. All clients sharing the
466
+ * prefix need identical capacity; verifiers in a protection space also need
467
+ * identical time policy/key windows and clocks within `clusterClockGuardMs`
468
+ * of Redis `TIME`.
469
+ *
470
+ * Absolute `retainUntilMs` scores never slide on duplicates; live entries are
471
+ * never evicted by this adapter. Capacity throws `AttestationCapacityError`,
472
+ * malformed inputs throw `AttestationProtocolError`, and dependency failures
473
+ * throw `AttestationRedisStoreError`. There is no direct-call wait timeout or
474
+ * retry after uncertain command execution. Eviction/state loss/failover can
475
+ * erase replay history; configure Redis persistence and noeviction accordingly.
396
476
  */
397
477
  declare function createRedisAttestationStore(options: CreateRedisAttestationStoreOptions): AttestationStore;
398
478
 
399
479
  /**
400
- * MONGO-01 MongoDB replay adapter (task sections 4.1/4.4).
480
+ * Bounded transactional MongoDB replay adapter.
401
481
  *
402
- * Admission contract (shared with memory/Redis; see `test/store-conformance.ts`):
482
+ * Admission contract shared with memory and Redis:
403
483
  *
404
484
  * - `reserve({ replayKey, retainUntilMs })` returns
405
485
  * `'reserved' | 'duplicate' | 'expired'`.
406
- * - The canonical deadline field is `retainUntilMs`
407
- * (see `AttestationStoreReserveInput`). A `retainUntil` alias is accepted at
408
- * runtime for callers written against the task-text shorthand; when both are
409
- * present the canonical field wins.
486
+ * - `retainUntilMs` is the absolute epoch-ms deadline from verified proof timing,
487
+ * including the cluster clock guard, never a relative TTL.
410
488
  * - Validation order per admission: input shape, store clock, opaque key
411
489
  * bounds (1-256 printable ASCII), deadline shape (nonnegative safe
412
490
  * integer), input expiry (`retainUntilMs <= now` returns `'expired'` with
@@ -424,8 +502,7 @@ declare function createRedisAttestationStore(options: CreateRedisAttestationStor
424
502
  * valid; the old proof still fails the verifier's age rules (the store never
425
503
  * sees signatures, only opaque already-derived replay keys).
426
504
  *
427
- * Storage design (follows the vault MongoDB store's DPoP replay
428
- * reservations module):
505
+ * Storage design:
429
506
  *
430
507
  * - One proof collection with unique `_id`s (`{ _id: replayKey,
431
508
  * retainUntilMs }`) plus a separate non-TTL capacity/accounting collection
@@ -453,16 +530,14 @@ declare function createRedisAttestationStore(options: CreateRedisAttestationStor
453
530
  * handles are retained live without cloning, connecting, or closing; the
454
531
  * caller owns the underlying `MongoClient` lifecycle (connect/close).
455
532
  * Production deployments require a replica set (transactions).
456
- * - The `mongodb` driver is a dev dependency for tests only. The library
457
- * uses narrow structural `Db`/collection/session types and never imports a
533
+ * - The library uses narrow structural `Db`/collection/session types and never imports a
458
534
  * driver at runtime, so browser-only `/signer` consumers never need it.
459
535
  * - Capacity throws the shared `AttestationCapacityError` (same class as
460
536
  * memory/Redis). Wrong collection types, transaction failures, and
461
537
  * malformed persisted rows throw the narrow `AttestationMongoStoreError`.
462
538
  * Malformed inputs throw `AttestationProtocolError` without touching the
463
- * database. The middleware already maps unknown store failures to fixed
464
- * `ATTESTATION_REPLAY_UNAVAILABLE` without calling `next`; do not change
465
- * middleware for this adapter.
539
+ * database. The middleware maps store failures to fixed
540
+ * `ATTESTATION_REPLAY_UNAVAILABLE` without calling `next`.
466
541
  *
467
542
  * Sharing boundary: replay state is shared by all clients using the same
468
543
  * database/collections with identical `maxEntries`. Independent databases or
@@ -518,34 +593,50 @@ interface MongoAttestationStoreDb {
518
593
  startSession(): MongoAttestationStoreSession;
519
594
  };
520
595
  }
521
- /** Options for `createMongoAttestationStore` (MONGO-01). */
596
+ /** Caller-owned MongoDB replica-set connection, collection pair, capacity, and clock. */
522
597
  interface CreateMongoAttestationStoreOptions {
523
598
  /** Database handle from an already-connected caller-owned client. */
524
599
  readonly db: MongoAttestationStoreDb;
525
600
  /** Proof collection name; default `attestation_proofs`. */
526
601
  readonly proofsCollectionName?: string;
527
- /** Capacity/accounting collection name; default `attestation_replay_capacity`. */
602
+ /**
603
+ * Default `attestation_replay_capacity`, distinct from the proof collection.
604
+ * Provision a non-TTL `{ kind: 1, retainUntilMs: 1 }` index before traffic.
605
+ * Never TTL-delete accounting rows; the adapter maintains them transactionally.
606
+ */
528
607
  readonly capacityCollectionName?: string;
529
- /** Positive safe-integer shared capacity; default 50000. */
608
+ /** Positive safe-integer shared capacity; default 50000, identical for every client of this collection pair. */
530
609
  readonly maxEntries?: number;
531
- /** App clock; default `Date.now`. Must stay synchronized across instances. */
610
+ /** Default `Date.now`; nonnegative safe-integer epoch ms, synchronized within the verifier's cluster clock guard. */
532
611
  readonly now?: () => number;
533
612
  }
534
613
  /**
535
614
  * Create a shared bounded attestation replay store over two MongoDB
536
615
  * collections.
537
616
  *
538
- * Pass a `Db` from an already-connected caller-owned `MongoClient`; the
539
- * store never connects, disconnects, or attaches listeners. All clients
540
- * sharing one database/collections must configure identical `maxEntries`
541
- * and synchronized clocks. Production deployments require a replica set
542
- * (transactions). Create a non-TTL index on `retainUntilMs` for the bounded
543
- * expiry queries; never add a TTL index to the accounting collection.
617
+ * Pass a `Db` from an already-connected caller-owned `MongoClient`; replica-set
618
+ * transactions are required. Defaults: `attestation_proofs`,
619
+ * `attestation_replay_capacity`, `maxEntries: 50000`, and `Date.now`.
620
+ * All clients of the collection pair need identical capacity and synchronized
621
+ * clocks; verifiers in the protection space need identical time policy/key windows.
622
+ *
623
+ * Before traffic, use the driver's `createIndex({ kind: 1, retainUntilMs: 1 })`
624
+ * on the capacity collection with no `expireAfterSeconds`. The factory performs
625
+ * no database I/O/index creation. Never add TTL cleanup to the accounting ledger.
626
+ * Each admission checks duplicates, reclaims at most 64 expired rows plus the
627
+ * requested expired key, then inserts without live eviction or duplicate extension.
628
+ *
629
+ * The caller owns connection, server-selection/command/transaction deadlines,
630
+ * and shutdown. Per-admission sessions belong to the adapter, but driver retries
631
+ * and cleanup are not bounded by the middleware's waiting timeout. Capacity
632
+ * throws `AttestationCapacityError`; malformed inputs use `AttestationProtocolError`;
633
+ * dependency/row failures use `AttestationMongoStoreError`. State loss can erase
634
+ * replay history; this is not durable business idempotency or a failover guarantee.
544
635
  */
545
636
  declare function createMongoAttestationStore(options: CreateMongoAttestationStoreOptions): AttestationStore;
546
637
 
547
638
  /**
548
- * ATT-05 owned key rotation (task sections 4.1/4.6).
639
+ * Owned key rotation with explicit acceptance windows.
549
640
  *
550
641
  * - `generateAttestationKey(keyId, { acceptFrom, acceptUntil })` creates one
551
642
  * random 32-byte entry for an explicit acceptance window.
@@ -554,7 +645,7 @@ declare function createMongoAttestationStore(options: CreateMongoAttestationStor
554
645
  * `replace(snapshot)`; external cron/KMS/config code owns provisioning and
555
646
  * refresh. No default polling timer, no hardcoded grace policy.
556
647
  *
557
- * Snapshot rules (section 4.1):
648
+ * Snapshot rules:
558
649
  *
559
650
  * - Snapshots have `currentKeyId` plus a bounded list of 1-16 entries
560
651
  * `{ keyId, key: Uint8Array(32), acceptFrom, acceptUntil }`.
@@ -576,7 +667,7 @@ declare function createMongoAttestationStore(options: CreateMongoAttestationStor
576
667
  * snapshot.
577
668
  * - Rejected replacements leave the previous valid snapshot intact
578
669
  * (atomic). Provider/read failure is operational `503` at the verifier,
579
- * never a stale-key client error (see `verify.ts`).
670
+ * never a stale-key client error.
580
671
  *
581
672
  * Runbook (finite acceptance, application-configured):
582
673
  *
@@ -597,7 +688,10 @@ declare function createMongoAttestationStore(options: CreateMongoAttestationStor
597
688
 
598
689
  /**
599
690
  * Create one random 32-byte key entry for an explicit acceptance window.
600
- * Uses `node:crypto` randomness; the returned entry owns its bytes (frozen).
691
+ * Uses `node:crypto` randomness. Times are nonnegative safe-integer epoch ms
692
+ * with inclusive `acceptFrom` and exclusive `acceptUntil`. Distribute the same
693
+ * entry/window to every verifier; do not generate independent per-instance keys.
694
+ * The entry is frozen and owns its bytes; providers copy those bytes on input.
601
695
  */
602
696
  declare function generateAttestationKey(keyId: string, window: {
603
697
  readonly acceptFrom: number;
@@ -607,6 +701,8 @@ declare function generateAttestationKey(keyId: string, window: {
607
701
  * Create an immutable static key provider. The input snapshot is copied on
608
702
  * entry; every `getSnapshot()` returns a fresh owned copy. No timers,
609
703
  * network, or storage work occurs at construction or on reads.
704
+ * Invalid snapshots throw `AttestationProtocolError`. Retain 1–16 keys;
705
+ * `currentKeyId` must name a present entry, which discovery checks for activity.
610
706
  */
611
707
  declare function createStaticKeyProvider(snapshot: AttestationKeySnapshotInput): AttestationKeyProvider;
612
708
  /**
@@ -618,12 +714,14 @@ declare function createStaticKeyProvider(snapshot: AttestationKeySnapshotInput):
618
714
  * fresh IDs are allowed. No historical tombstone ledger is kept: an ID that
619
715
  * is no longer retained may be reintroduced, but provisioners must never
620
716
  * reintroduce a retired ID with different material (generate unique
621
- * version/random identifiers).
717
+ * version/random identifiers). All verifiers need the same material, windows,
718
+ * and time policy. Provision/refresh externally; replacement starts no polling
719
+ * and does not cancel requests already holding a previous snapshot.
622
720
  */
623
721
  declare function createRotatingKeyProvider(snapshot: AttestationKeySnapshotInput): AttestationRotatingKeyProvider;
624
722
 
625
723
  /**
626
- * ATT-05 relative signer-asset router (task sections 4.1/4.6).
724
+ * Relative signer-asset router with content-addressed public modules.
627
725
  *
628
726
  * Server-only: may import Express and Node built-ins. Never imported by the
629
727
  * `./signer` browser entry.
@@ -634,13 +732,12 @@ declare function createRotatingKeyProvider(snapshot: AttestationKeySnapshotInput
634
732
  * `basePath`; the application mounts it (for example
635
733
  * `app.use('/attestation', router)`). `basePath` plus the trusted
636
734
  * `publicPathPrefix` are used solely to publish correct external URLs
637
- * (`publicOrigin + publicPathPrefix + basePath + /signer.<hash>.mjs`).
735
+ * (a root `basePath: '/'` adds no extra slash before the asset name).
638
736
  * Request headers (`Host`, `Forwarded`, `X-Forwarded-*`) are never used
639
737
  * for URL construction.
640
738
  * - Routes: `GET /signer-meta`, `GET /signer.<contenthash>.mjs`,
641
739
  * `GET /runtime.<contenthash>.mjs`. The runtime is the actual built browser
642
- * ESM entry from this installed package (see `signer-assets.ts` for CJS/ESM
643
- * + release-flattening lookup); the 64-hex content hash identifies exact
740
+ * ESM entry from this installed package; the 64-hex content hash identifies exact
644
741
  * immutable bytes. Never serve different content from the same URL.
645
742
  * - Key-specific modules import the runtime via relative
646
743
  * `./runtime.<hash>.mjs` and export `signer: RequestSigner` from
@@ -650,11 +747,12 @@ declare function createRotatingKeyProvider(snapshot: AttestationKeySnapshotInput
650
747
  * - Metadata is bounded JSON
651
748
  * `{ version: 1, keyId, signerUrl, publicOrigin, replayNamespace }` with
652
749
  * `Cache-Control: no-store`, serving the current active key only. The
653
- * current entry must be present and active at `nowMs`; otherwise metadata
654
- * fails with `503 ATTESTATION_KEYS_UNAVAILABLE`.
750
+ * current entry must be present and active at the checked clock after the
751
+ * provider read; otherwise metadata fails with `503 ATTESTATION_KEYS_UNAVAILABLE`.
752
+ * Invalid or throwing clocks fail with `500 ATTESTATION_INTERNAL_ERROR`.
655
753
  * - Immutable assets use `Content-Type: text/javascript; charset=utf-8`,
656
754
  * `X-Content-Type-Options: nosniff`, and
657
- * `Cache-Control: public, max-age=31536000, immutable`. Unknown hashes
755
+ * `Cache-Control: public, max-age=31536000, immutable`. Malformed asset paths/hashes
658
756
  * return controlled `404`; syntactically valid but not currently retained
659
757
  * hashes return `410`; neither redirects to latest. CORS/CSP policy is
660
758
  * application-configured: this router sets no `Access-Control-*` headers
@@ -662,14 +760,14 @@ declare function createRotatingKeyProvider(snapshot: AttestationKeySnapshotInput
662
760
  * serve metadata/modules over the pinned `publicOrigin`, allow
663
761
  * `script-src` for the module URLs, and configure CORS only as the
664
762
  * application requires (same-origin by default; cross-origin deployments
665
- * must explicitly allow `GET` metadata/module fetches which use
666
- * `cache: no-store`, `credentials: omit`, `redirect: error`).
763
+ * must explicitly allow `GET` metadata/module fetches. Discovery uses
764
+ * no-store/omit/error; dynamic imports follow native module-fetch rules).
667
765
  * - Asset generation is bounded by the retained snapshot (max 16 keys) plus
668
766
  * fixed runtime (512 KiB) / module (8 KiB) / metadata (4096 byte) caps.
669
767
  * Key modules are derived deterministically per request from the current
670
768
  * snapshot; no unbounded retired-asset cache is kept. Dropping a retired
671
- * key's asset is safe because the client performs at most one bounded
672
- * discovery refetch on a module-phase retirement race.
769
+ * key's asset must account for the client's one bounded discovery refetch
770
+ * on a module-phase retirement race; repeated retirement failures end the load.
673
771
  *
674
772
  * Runbook: prepare new material on **all** verifiers, publish current
675
773
  * metadata/module, retain old material to its explicit deadline, retire only
@@ -686,7 +784,18 @@ declare function createRotatingKeyProvider(snapshot: AttestationKeySnapshotInput
686
784
  * `basePath` (for example `app.use('/attestation', router)`); this factory
687
785
  * never mounts itself. All external URLs are built from trusted
688
786
  * configuration (`publicOrigin` + `publicPathPrefix` + `basePath`), never
689
- * from request headers.
787
+ * from request headers. With `basePath: '/'`, assets are directly under the
788
+ * origin or configured stripped prefix; their runtime imports remain relative.
789
+ * The default base path is `/attestation`; root URLs are `/signer-meta`,
790
+ * `/signer.<hash>.mjs`, and `/runtime.<hash>.mjs`. With prefix `/prefix`, those
791
+ * external URLs begin `/prefix/` while the app still mounts at `/`.
792
+ * The caller owns actual upstream stripping and CORS/CSP configuration.
793
+ * Discovery checks the clock before provider work and current-key eligibility
794
+ * after it. Inactive keys/provider failures return fixed 503 errors; invalid or
795
+ * throwing clocks return a fixed 500 error. Both use `Cache-Control: no-store`.
796
+ * `operationTimeoutMs` bounds provider waiting (default 5000 ms, range 1–60000),
797
+ * without cancelling underlying provider work. Assets have immutable URLs and
798
+ * no redirect-to-latest behavior; only discovery chooses the current active key.
690
799
  */
691
800
  declare function createSignerBundleRouter(options: CreateSignerBundleRouterOptions): Router;
692
801