@spfn/auth 0.2.0-beta.86 → 0.2.0-beta.88

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.
Files changed (50) hide show
  1. package/README.md +347 -27
  2. package/dist/{authenticate-DTA7W4v2.d.ts → authenticate-ZssfqJ7d.d.ts} +201 -5
  3. package/dist/client-proof.d.ts +185 -46
  4. package/dist/client-proof.js +253 -34
  5. package/dist/client-proof.js.map +1 -1
  6. package/dist/config.d.ts +36 -0
  7. package/dist/config.js +18 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/errors.d.ts +93 -2
  10. package/dist/errors.js +57 -0
  11. package/dist/errors.js.map +1 -1
  12. package/dist/index.d.ts +33 -3
  13. package/dist/index.js +60 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/nextjs/server.d.ts +2 -2
  16. package/dist/server.d.ts +148 -12
  17. package/dist/server.js +1410 -192
  18. package/dist/server.js.map +1 -1
  19. package/dist/{session-CGxgH3C9.d.ts → session-CFK4BT25.d.ts} +1 -1
  20. package/dist/{types-1BMx0OX1.d.ts → types-CD95yudz.d.ts} +15 -1
  21. package/migrations/20251125021229_premium_famine/snapshot.json +2641 -0
  22. package/migrations/20260225130050_smooth_the_fury/snapshot.json +2686 -0
  23. package/migrations/20260308141417_deep_iceman/snapshot.json +2686 -0
  24. package/migrations/20260308151309_perfect_deathbird/snapshot.json +2731 -0
  25. package/migrations/20260308201135_concerned_rawhide_kid/snapshot.json +2786 -0
  26. package/migrations/20260629103209_lethal_lifeguard/snapshot.json +2786 -0
  27. package/migrations/20260709073531_easy_hardball/snapshot.json +3119 -0
  28. package/migrations/20260714081434_glossy_major_mapleleaf/snapshot.json +3112 -0
  29. package/migrations/20260804105939_amazing_bushwacker/migration.sql +3 -0
  30. package/migrations/20260804105939_amazing_bushwacker/snapshot.json +3112 -0
  31. package/migrations/20260804110033_fat_piledriver/migration.sql +2 -0
  32. package/migrations/20260804110033_fat_piledriver/snapshot.json +3138 -0
  33. package/package.json +6 -9
  34. package/migrations/meta/0000_snapshot.json +0 -1632
  35. package/migrations/meta/0001_snapshot.json +0 -1660
  36. package/migrations/meta/0002_snapshot.json +0 -1660
  37. package/migrations/meta/0003_snapshot.json +0 -1689
  38. package/migrations/meta/0004_snapshot.json +0 -1721
  39. package/migrations/meta/0005_snapshot.json +0 -1721
  40. package/migrations/meta/0006_snapshot.json +0 -1921
  41. package/migrations/meta/0007_snapshot.json +0 -1916
  42. package/migrations/meta/_journal.json +0 -62
  43. /package/migrations/{0000_premium_famine.sql → 20251125021229_premium_famine/migration.sql} +0 -0
  44. /package/migrations/{0001_smooth_the_fury.sql → 20260225130050_smooth_the_fury/migration.sql} +0 -0
  45. /package/migrations/{0002_deep_iceman.sql → 20260308141417_deep_iceman/migration.sql} +0 -0
  46. /package/migrations/{0003_perfect_deathbird.sql → 20260308151309_perfect_deathbird/migration.sql} +0 -0
  47. /package/migrations/{0004_concerned_rawhide_kid.sql → 20260308201135_concerned_rawhide_kid/migration.sql} +0 -0
  48. /package/migrations/{0005_lethal_lifeguard.sql → 20260629103209_lethal_lifeguard/migration.sql} +0 -0
  49. /package/migrations/{0006_easy_hardball.sql → 20260709073531_easy_hardball/migration.sql} +0 -0
  50. /package/migrations/{0007_glossy_major_mapleleaf.sql → 20260714081434_glossy_major_mapleleaf/migration.sql} +0 -0
@@ -1,9 +1,10 @@
1
+ import { KeyObject } from 'node:crypto';
1
2
  import { MiddlewareHandler } from 'hono';
2
3
 
3
4
  /**
4
5
  * SPFN-CANON-JSON-1 — the canonical JSON form the mobile contract pins.
5
6
  *
6
- * The rules (Contracts/spfn-mobile-contract.v1.json `canonicalJson`):
7
+ * The rules (contracts/mobile/spfn-mobile-contract.json `canonicalJson`):
7
8
  * - object keys sorted ascending by UTF-8 byte sequence
8
9
  * - no insignificant whitespace
9
10
  * - numbers are signed 64-bit integers only
@@ -43,12 +44,32 @@ declare function isCanonicalBytes(bytes: Uint8Array, value: CanonicalValue): boo
43
44
  /** Encode a value as SPFN-CANON-JSON-1 bytes. */
44
45
  declare function encodeCanonicalJson(value: CanonicalValue): Uint8Array;
45
46
 
47
+ /**
48
+ * SPFN-PROOF-INPUT-1 — proof-input assembly and verification for clientProofV1.
49
+ *
50
+ * The proof input is 8 fields joined by `\n` in fixed order: profile, method,
51
+ * path, clientId, keyId, nonce, issuedAtMillis, bodySha256. Any C0 control
52
+ * character in any field is a hard refusal (the separator would otherwise be
53
+ * ambiguous), never something to escape. The proof is an ECDSA P-256 signature
54
+ * with SHA-256 over the canonical input's UTF-8 bytes, wire-encoded as the raw
55
+ * `r ‖ s` 64 bytes in base16-lower (128 hex characters). DER is never accepted
56
+ * on the wire: a platform signer that emits DER (Java `Signature`) converts to
57
+ * raw before sending. Low-S normalization is not required — uniqueness is owned
58
+ * by the nonce and replay window, so signature malleability cannot replay.
59
+ *
60
+ * @module server/client-proof/proof
61
+ */
62
+
46
63
  /** The only auth profile this module implements. */
47
64
  declare const CLIENT_PROOF_PROFILE = "clientProofV1";
48
65
  /** `bodySha256` when an operation carries no body: 64 zero characters. */
49
66
  declare const ABSENT_BODY_SHA256: string;
50
67
  /** The contract's `clientProofV1.replayWindowMillis`. */
51
68
  declare const DEFAULT_REPLAY_WINDOW_MILLIS = 300000;
69
+ /** Raw `r ‖ s`: two 32-byte big-endian integers, always exactly this long. */
70
+ declare const PROOF_SIGNATURE_BYTES = 64;
71
+ /** The wire form is base16-lower of the raw signature: 128 hex characters. */
72
+ declare const PROOF_SIGNATURE_HEX_LENGTH: number;
52
73
  interface ClientProofInput {
53
74
  method: string;
54
75
  path: string;
@@ -63,22 +84,42 @@ declare class ProofInputError extends Error {
63
84
  constructor();
64
85
  }
65
86
  /**
66
- * The canonical proof-input string the MAC is taken over.
87
+ * The canonical proof-input string the signature is taken over.
67
88
  *
68
89
  * @throws ProofInputError when any field contains a C0 control character.
69
90
  */
70
91
  declare function canonicalProofInput(input: ClientProofInput): string;
71
- /** The base16-lower HMAC-SHA-256 proof for `input` under `key`. */
72
- declare function computeClientProof(input: ClientProofInput, key: Uint8Array): string;
73
- /** Lowercase base16 SHA-256 of `bytes`. */
74
- declare function sha256Hex(bytes: Uint8Array): string;
75
92
  /**
76
- * Constant-time comparison of two proof strings.
93
+ * The contract's public-key representation SPKI DER, base64 (the same
94
+ * representation `user_public_keys` and the web ES256 path store) — as a key
95
+ * object. Anything that is not a P-256 EC key is refused at parse time, so a
96
+ * key that could never verify a proof is never registered.
77
97
  *
78
- * Length is checked first (its leak reveals nothing the expected length is
79
- * public), then the bytes are compared with `timingSafeEqual`.
98
+ * @throws when the input is not base64 SPKI DER naming a P-256 key.
80
99
  */
81
- declare function constantTimeEqualsProof(expected: string, presented: string): boolean;
100
+ declare function parseClientProofPublicKey(spkiDerBase64: string): KeyObject;
101
+ /**
102
+ * Verifies a presented proof against `input` and a registered public key.
103
+ *
104
+ * The input is assembled first, so a C0 control character throws no matter
105
+ * what was presented — an unassemblable input is a contract violation, never
106
+ * a proof answer. Then the wire-format gate: a value that is not exactly 128
107
+ * lowercase hex characters — a DER signature, a truncated one, uppercase hex —
108
+ * is invalid before any cryptography happens.
109
+ *
110
+ * @throws ProofInputError when an input field contains a C0 control character.
111
+ */
112
+ declare function verifyClientProof(input: ClientProofInput, presentedProof: string, publicKey: KeyObject): boolean;
113
+ /**
114
+ * Signs `input` with a PKCS#8 DER base64 private key, producing the wire form
115
+ * (raw `r ‖ s`, base16-lower).
116
+ *
117
+ * The verifying half's counterpart, here for tests and dev clients — a
118
+ * production signer lives in the mobile SDKs against hardware-held keys.
119
+ */
120
+ declare function signClientProof(input: ClientProofInput, privateKeyPkcs8DerBase64: string): string;
121
+ /** Lowercase base16 SHA-256 of `bytes`. */
122
+ declare function sha256Hex(bytes: Uint8Array): string;
82
123
 
83
124
  /** The six wire codes. The SDKs classify by code, never HTTP status. */
84
125
  type ClientProofErrorCode = 'PROOF_INVALID' | 'PROOF_REPLAYED' | 'PROOF_EXPIRED' | 'SESSION_REVOKED' | 'PROFILE_REJECTED' | 'CONTRACT_UNSUPPORTED';
@@ -115,31 +156,6 @@ declare class ClientProofRefusal {
115
156
  static proofInvalid(): ClientProofRefusal;
116
157
  }
117
158
 
118
- /**
119
- * Everything a clientProofV1 server remembers between requests: issued
120
- * sessions, the replay ledger, revoked keys and the key directory.
121
- *
122
- * The admission order is the contract's, not this file's invention
123
- * (`clientProofV1.revocationRule` + the replay fixtures):
124
- *
125
- * 1. revoked keyId / invalid session → SESSION_REVOKED — before proof
126
- * verification, so revocation stays distinguishable from a bad proof;
127
- * 2. issuedAtMillis outside the replay window (0 <= age <= window) → PROOF_EXPIRED;
128
- * 3. a repeated (clientId, nonce) pair inside the window → PROOF_REPLAYED;
129
- * 4. only then HMAC verification → PROOF_INVALID on mismatch.
130
- *
131
- * A nonce is recorded as spent only on admission: a request refused for any
132
- * earlier reason has not spent anything, so a client that fixes the reason and
133
- * retries with the same nonce is not punished twice for one mistake. This is
134
- * why core's `NonceStore.checkAndSet` (which records on check) is not reused
135
- * here — its semantics would spend a nonce on a refused request.
136
- *
137
- * `admit` is synchronous, so on Node's single thread the whole sequence is
138
- * atomic: two requests presenting the same nonce cannot interleave inside it.
139
- *
140
- * @module server/client-proof/state
141
- */
142
-
143
159
  /** Millisecond clock. Injectable so expiry paths are testable without waiting. */
144
160
  interface ClientProofClock {
145
161
  nowMillis(): number;
@@ -164,11 +180,13 @@ interface ClientProofStats {
164
180
  }
165
181
  interface ClientProofStateOptions {
166
182
  /**
167
- * keyId → HMAC key. A string is taken as UTF-8 bytes. Dev provisioning is
168
- * injection at construction; any issuance flow works as long as
169
- * clientId/keyId/key triples exist on both ends.
183
+ * keyId → registered public key, as SPKI DER base64. The private half
184
+ * never reaches the server: a client generates its keypair (hardware-held
185
+ * on mobile) and only the public key is registered at construction here,
186
+ * or later through `registerPublicKey` (the dev `/control/register-key`
187
+ * route).
170
188
  */
171
- keys: Record<string, string | Uint8Array>;
189
+ publicKeys: Record<string, string>;
172
190
  clock?: ClientProofClock;
173
191
  /** @default 600000 */
174
192
  sessionTtlMillis?: number;
@@ -179,9 +197,10 @@ declare const DEFAULT_SESSION_TTL_MILLIS = 600000;
179
197
  declare class ClientProofState {
180
198
  readonly replayWindowMillis: number;
181
199
  private readonly clock;
182
- private readonly keys;
200
+ private readonly initialPublicKeys;
201
+ private readonly publicKeys;
183
202
  private readonly sessions;
184
- /** replayKeyOf(...) the issuedAtMillis it was spent at. */
203
+ /** The replay ledger — the shared memory implementation, used dev-only here. */
185
204
  private readonly spentNonces;
186
205
  private readonly revokedKeyIds;
187
206
  private readonly holds;
@@ -193,6 +212,12 @@ declare class ClientProofState {
193
212
  private itemsListCount;
194
213
  private refusalCount;
195
214
  constructor(options: ClientProofStateOptions);
215
+ /**
216
+ * Registers (or replaces) the public key `keyId` presents proofs under.
217
+ *
218
+ * @throws when the key is not base64 SPKI DER naming a P-256 key.
219
+ */
220
+ registerPublicKey(keyId: string, publicKeySpkiDerBase64: string): void;
196
221
  /**
197
222
  * Runs the contract's checks in the contract's order and returns the
198
223
  * refusal, or null when the request is admitted (spending its nonce).
@@ -217,7 +242,7 @@ declare class ClientProofState {
217
242
  /** Revokes a key and drops the sessions it opened. */
218
243
  revokeKey(keyId: string): void;
219
244
  setSessionTtlMillis(millis: number): void;
220
- /** Returns the state to how it started, counters included. */
245
+ /** Returns the state to how it started, counters and registered keys included. */
221
246
  reset(): void;
222
247
  /** Makes the next `count` requests to `path` wait `millis` before processing. */
223
248
  holdPath(path: string, millis: number, count: number): void;
@@ -295,6 +320,101 @@ declare function admitClientProofRequest(args: {
295
320
  requiresSession: boolean;
296
321
  body: Uint8Array;
297
322
  }): Admission;
323
+ /**
324
+ * The contract header fields, or null when any is absent or malformed.
325
+ *
326
+ * Fetch `Headers` folds a repeated field into one comma-joined value, so
327
+ * "sent more than once" is not directly observable here; a folded value fails
328
+ * either the issuedAt grammar or proof verification instead.
329
+ *
330
+ * Exported for the authenticate middleware's profile path, which runs the
331
+ * same shape checks over arbitrary routes.
332
+ */
333
+ declare function readCredentials(headers: Headers): ClientProofCredentials | null;
334
+ /** Exported for the authenticate middleware's profile path. */
335
+ declare function isRequestContentType(value: string | null): boolean;
336
+
337
+ /**
338
+ * The ledger key. `JSON.stringify` of the pair, so no crafted clientId/nonce
339
+ * concatenation can collide with another pair — the fields are checked for C0
340
+ * controls only later, at proof verification, so the key must be unambiguous
341
+ * for arbitrary strings.
342
+ */
343
+ declare function replayLedgerKey(clientId: string, nonce: string): string;
344
+ /**
345
+ * What the middleware's replay ledger must answer. Both methods may reject;
346
+ * the caller refuses the request when they do (fail-closed).
347
+ */
348
+ interface ClientProofReplayStore {
349
+ /** True when (clientId, nonce) was already spent inside the window. */
350
+ isSpent(clientId: string, nonce: string): Promise<boolean>;
351
+ /**
352
+ * Records the pair as spent. False when it was already spent — the caller
353
+ * lost a race and must answer PROOF_REPLAYED, not accept twice.
354
+ */
355
+ spend(clientId: string, nonce: string): Promise<boolean>;
356
+ }
357
+ /**
358
+ * The in-memory ledger — the single implementation of the window semantics.
359
+ *
360
+ * Entries carry the millisecond they were recorded at; `prune` drops an entry
361
+ * only once a proof carrying that timestamp would be refused as expired
362
+ * anyway (the exact negation of the admission window check). All methods are
363
+ * synchronous so `ClientProofState.admit` can stay atomic on Node's single
364
+ * thread.
365
+ */
366
+ declare class MemoryReplayLedger {
367
+ /** replayLedgerKey(...) → the millis it was spent at. */
368
+ private readonly spent;
369
+ isSpent(clientId: string, nonce: string): boolean;
370
+ /** Records the pair at `atMillis`; false when it was already spent. */
371
+ spend(clientId: string, nonce: string, atMillis: number): boolean;
372
+ /** Drops entries older than the window, judged against `nowMillis`. */
373
+ prune(nowMillis: number, windowMillis: number): void;
374
+ get size(): number;
375
+ clear(): void;
376
+ }
377
+ /**
378
+ * The default store: a process-local `MemoryReplayLedger` on the wall clock.
379
+ *
380
+ * Correct for a single process. Behind a multi-instance deployment each
381
+ * instance keeps its own ledger, so a replay against a *different* instance
382
+ * is not seen — that deployment opts into `RedisReplayStore`.
383
+ */
384
+ declare class MemoryReplayStore implements ClientProofReplayStore {
385
+ private readonly windowMillis;
386
+ private readonly ledger;
387
+ constructor(windowMillis?: number);
388
+ isSpent(clientId: string, nonce: string): Promise<boolean>;
389
+ spend(clientId: string, nonce: string): Promise<boolean>;
390
+ }
391
+ /**
392
+ * The opt-in shared ledger over `getCache()` (ioredis): `SET NX PX <window>`.
393
+ *
394
+ * The key hashes the pair, so arbitrary clientId/nonce strings become short,
395
+ * safe Redis keys with no ambiguity. `PX` makes Redis expire the entry itself
396
+ * exactly when a proof reusing the nonce would pass the window check again.
397
+ *
398
+ * Fail-closed by construction: when the cache is not configured or a command
399
+ * rejects, the error propagates and the caller refuses the request. Nothing
400
+ * here answers "not spent" on a store it could not reach.
401
+ */
402
+ declare class RedisReplayStore implements ClientProofReplayStore {
403
+ private readonly windowMillis;
404
+ constructor(windowMillis?: number);
405
+ isSpent(clientId: string, nonce: string): Promise<boolean>;
406
+ spend(clientId: string, nonce: string): Promise<boolean>;
407
+ private cache;
408
+ private key;
409
+ }
410
+ /**
411
+ * Installs the replay store the authenticate middleware uses. Pass
412
+ * `new RedisReplayStore()` to opt into the shared ledger; pass null to return
413
+ * to the in-memory default.
414
+ */
415
+ declare function configureClientProofReplayStore(store: ClientProofReplayStore | null): void;
416
+ /** The configured store, or a lazily created in-memory default. */
417
+ declare function getClientProofReplayStore(): ClientProofReplayStore;
298
418
 
299
419
  /**
300
420
  * The mobile dev-contract types and operations, decoded from / encoded to
@@ -302,7 +422,7 @@ declare function admitClientProofRequest(args: {
302
422
  * or an unknown field is "not the request type this operation declares".
303
423
  *
304
424
  * This module is the source of truth for `operations`. The exported contract
305
- * bundle (`contracts/mobile/spfn-mobile-contract.v1.json`) is generated from it
425
+ * bundle (`contracts/mobile/spfn-mobile-contract.json`) is generated from it
306
426
  * by `contract-bundle.ts`; spfn-mobile consumes that export rather than the
307
427
  * other way round.
308
428
  *
@@ -310,16 +430,35 @@ declare function admitClientProofRequest(args: {
310
430
  */
311
431
 
312
432
  interface ContractOperation {
313
- id: 'auth.clientProof.handshake' | 'echo.send' | 'items.list';
433
+ id: 'auth.clientProof.handshake' | 'echo.send' | 'items.list' | 'auth.enroll.register' | 'auth.enroll.login' | 'auth.enroll.oauthNative' | 'auth.keys.rotate' | 'auth.keys.list' | 'auth.keys.revoke' | 'auth.keys.revokeAll';
314
434
  method: 'POST';
315
435
  path: string;
316
- authProfile: 'clientProofV1';
436
+ /**
437
+ * How a call is admitted. `clientProofV1` operations run the proof
438
+ * admission order; `none` operations are the unproven class — accepted
439
+ * with neither proof headers nor a session header, because enrollment is
440
+ * called before any key exists to sign with.
441
+ */
442
+ authProfile: 'clientProofV1' | 'none';
317
443
  requiresSession: boolean;
318
444
  requestType: string;
319
445
  responseType: string;
320
446
  summary: string;
321
447
  }
322
448
  declare const CONTRACT_OPERATIONS: readonly ContractOperation[];
449
+ /**
450
+ * The `/_auth` surface exported into the mobile contract: enrollment, login
451
+ * and key rotation. These are ordinary SPFN REST routes, not canonical-JSON
452
+ * operations — the dev handler never serves them, and their wire rules are
453
+ * the `restOperations` section of the bundle, not `canonicalJson`.
454
+ *
455
+ * The three `authProfile: 'none'` operations are the unproven class: they are
456
+ * accepted with neither proof headers nor a session header, because they are
457
+ * how a client obtains a key in the first place. `auth.keys.rotate` requires
458
+ * an authenticated caller (a clientProofV1 proof on this surface); an
459
+ * unproven call to it is refused like any failed admission.
460
+ */
461
+ declare const AUTH_SURFACE_OPERATIONS: readonly ContractOperation[];
323
462
  /** The body is canonical JSON but not the declared request type. */
324
463
  declare class ContractTypeError extends Error {
325
464
  constructor();
@@ -415,4 +554,4 @@ interface ClientProofGuardOptions {
415
554
  */
416
555
  declare function createClientProofGuard(state: ClientProofState, options?: ClientProofGuardOptions): MiddlewareHandler;
417
556
 
418
- export { ABSENT_BODY_SHA256, type Admission, CLIENT_PROOF_CONTENT_TYPE, CLIENT_PROOF_HEADERS, CLIENT_PROOF_PROFILE, CONTRACT_OPERATIONS, CONTROL_PREFIX, CONTROL_TOKEN_HEADER, CanonicalJsonError, type CanonicalJsonErrorCode, type CanonicalObject, type CanonicalValue, type ClientProofClock, type ClientProofContext, type ClientProofCredentials, type ClientProofDevHandler, type ClientProofDevHandlerOptions, type ClientProofErrorCode, type ClientProofGuardOptions, type ClientProofInput, ClientProofRefusal, ClientProofState, type ClientProofStateOptions, type ClientProofStats, type ContractItem, type ContractOperation, ContractTypeError, DEFAULT_REPLAY_WINDOW_MILLIS, DEFAULT_SESSION_TTL_MILLIS, DEV_CATALOGUE, DEV_MAX_LIMIT, type EchoRequest, type HandshakeRequest, type ListItemsRequest, ProofInputError, TestClock, admitClientProofRequest, canonicalProofInput, computeClientProof, constantTimeEqualsProof, createClientProofDevHandler, createClientProofGuard, decodeEchoRequest, decodeHandshakeRequest, decodeListItemsRequest, encodeCanonicalJson, encodeEchoResponse, encodeHandshakeResponse, encodeListItemsResponse, isCanonicalBytes, newHexId, parseCanonicalJson, sha256Hex, systemClock };
557
+ export { ABSENT_BODY_SHA256, AUTH_SURFACE_OPERATIONS, type Admission, CLIENT_PROOF_CONTENT_TYPE, CLIENT_PROOF_HEADERS, CLIENT_PROOF_PROFILE, CONTRACT_OPERATIONS, CONTROL_PREFIX, CONTROL_TOKEN_HEADER, CanonicalJsonError, type CanonicalJsonErrorCode, type CanonicalObject, type CanonicalValue, type ClientProofClock, type ClientProofContext, type ClientProofCredentials, type ClientProofDevHandler, type ClientProofDevHandlerOptions, type ClientProofErrorCode, type ClientProofGuardOptions, type ClientProofInput, ClientProofRefusal, type ClientProofReplayStore, ClientProofState, type ClientProofStateOptions, type ClientProofStats, type ContractItem, type ContractOperation, ContractTypeError, DEFAULT_REPLAY_WINDOW_MILLIS, DEFAULT_SESSION_TTL_MILLIS, DEV_CATALOGUE, DEV_MAX_LIMIT, type EchoRequest, type HandshakeRequest, type ListItemsRequest, MemoryReplayLedger, MemoryReplayStore, PROOF_SIGNATURE_BYTES, PROOF_SIGNATURE_HEX_LENGTH, ProofInputError, RedisReplayStore, TestClock, admitClientProofRequest, canonicalProofInput, configureClientProofReplayStore, createClientProofDevHandler, createClientProofGuard, decodeEchoRequest, decodeHandshakeRequest, decodeListItemsRequest, encodeCanonicalJson, encodeEchoResponse, encodeHandshakeResponse, encodeListItemsResponse, getClientProofReplayStore, isCanonicalBytes, isRequestContentType, newHexId, parseCanonicalJson, parseClientProofPublicKey, readCredentials, replayLedgerKey, sha256Hex, signClientProof, systemClock, verifyClientProof };