@reclaimprotocol/client 0.1.27 → 0.1.28

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 (73) hide show
  1. package/README.md +634 -42
  2. package/lib/api-client.d.ts +31 -0
  3. package/lib/api-client.js +112 -0
  4. package/lib/api-consts.d.ts +4 -0
  5. package/lib/api-consts.js +4 -0
  6. package/lib/api-errors.d.ts +13 -0
  7. package/lib/api-errors.js +30 -0
  8. package/lib/api.d.ts +7 -0
  9. package/lib/api.js +3 -0
  10. package/lib/attestor-proof.d.ts +6 -0
  11. package/lib/attestor-proof.js +45 -18
  12. package/lib/client.d.ts +1 -6
  13. package/lib/client.js +12 -33
  14. package/lib/consts.d.ts +1 -1
  15. package/lib/consts.js +1 -1
  16. package/lib/eip191.d.ts +4 -2
  17. package/lib/eip191.js +17 -1
  18. package/lib/errors.d.ts +38 -4
  19. package/lib/errors.js +99 -16
  20. package/lib/index.d.ts +9 -7
  21. package/lib/index.js +8 -4
  22. package/lib/jwks.d.ts +25 -1
  23. package/lib/jwks.js +69 -4
  24. package/lib/legacy-proof.d.ts +57 -0
  25. package/lib/legacy-proof.js +211 -0
  26. package/lib/proof-hash.d.ts +35 -0
  27. package/lib/proof-hash.js +216 -0
  28. package/lib/proof-recipe.d.ts +18 -0
  29. package/lib/proof-recipe.js +37 -0
  30. package/lib/proof-trust-constants.d.ts +3 -0
  31. package/lib/proof-trust-constants.js +43 -0
  32. package/lib/proof-types.d.ts +62 -0
  33. package/lib/proof-types.js +1 -0
  34. package/lib/proof-verification-error.d.ts +13 -0
  35. package/lib/proof-verification-error.js +11 -0
  36. package/lib/reclaim.d.ts +27 -5
  37. package/lib/reclaim.js +140 -14
  38. package/lib/result.d.ts +58 -6
  39. package/lib/result.js +252 -61
  40. package/lib/routes.gen.d.ts +54 -6
  41. package/lib/routes.gen.js +15 -3
  42. package/lib/tee-binding.d.ts +82 -0
  43. package/lib/tee-binding.js +112 -0
  44. package/lib/types/openapi.gen.d.ts +1343 -206
  45. package/lib/verification-clients.d.ts +17 -0
  46. package/lib/verification-clients.js +14 -0
  47. package/lib/verify-attestor-tee.d.ts +76 -0
  48. package/lib/verify-attestor-tee.js +243 -0
  49. package/lib/verify-tee.d.ts +32 -0
  50. package/lib/verify-tee.js +347 -0
  51. package/package.json +5 -1
  52. package/src/api.ts +15 -0
  53. package/src/attestor-proof.ts +57 -20
  54. package/src/client.ts +15 -38
  55. package/src/consts.ts +1 -2
  56. package/src/eip191.ts +31 -3
  57. package/src/errors.ts +132 -18
  58. package/src/index.ts +50 -5
  59. package/src/jwks.ts +128 -5
  60. package/src/legacy-proof.ts +367 -0
  61. package/src/proof-hash.ts +316 -0
  62. package/src/proof-recipe.ts +75 -0
  63. package/src/proof-trust-constants.ts +45 -0
  64. package/src/proof-types.ts +57 -0
  65. package/src/proof-verification-error.ts +13 -0
  66. package/src/reclaim.ts +206 -19
  67. package/src/result.ts +403 -68
  68. package/src/routes.gen.ts +15 -3
  69. package/src/tee-binding.ts +192 -0
  70. package/src/types/openapi.gen.ts +1343 -206
  71. package/src/verification-clients.ts +22 -0
  72. package/src/verify-attestor-tee.ts +365 -0
  73. package/src/verify-tee.ts +507 -0
@@ -0,0 +1,216 @@
1
+ import { keccak_256 } from '@noble/hashes/sha3.js';
2
+ import { canonicalJson as canonical } from "./canonical.js";
3
+ import { bytesToHex } from "./encoding.js";
4
+ /** Exact legacy canonical HTTP-provider hashes, including optional matches. */
5
+ export function hashProofClaimParams(params) {
6
+ return canonicalProofParams(params).map((serialized) => ('0x' + bytesToHex(keccak_256(new TextEncoder().encode(serialized)))));
7
+ }
8
+ export function getHashFromProof(proof, hasNoPii = false) {
9
+ if (hasNoPii) {
10
+ const context = JSON.parse(proof.claimData.context);
11
+ if (typeof context.providerHash !== 'string' || !context.providerHash) {
12
+ throw new Error('Provider hash must be present in proof context');
13
+ }
14
+ return [context.providerHash];
15
+ }
16
+ const parsed = JSON.parse(proof.claimData.parameters);
17
+ if (!isHttpProofParams(parsed)) {
18
+ throw new Error('Proof has no HTTP provider params to hash');
19
+ }
20
+ return hashProofClaimParams(parsed);
21
+ }
22
+ export function hashRequestSpec(request) {
23
+ return {
24
+ value: hashProofClaimParams({
25
+ ...request,
26
+ body: request.bodySniff.enabled ? request.bodySniff.template : '',
27
+ }),
28
+ required: request.required,
29
+ multiple: request.multiple,
30
+ };
31
+ }
32
+ export function hashRequirementsFromSpecs(requestData, allowedInjectedRequestData, proofs) {
33
+ const templates = expandTemplates(allowedInjectedRequestData, templateParametersFromProofs(proofs));
34
+ return [...requestData, ...templates].map(hashRequestSpec);
35
+ }
36
+ export function assertValidProofHashes(proofs, requirements, hasNoPii = false) {
37
+ const unmatched = new Map(proofs.map((proof, index) => ([index, getHashFromProof(proof, hasNoPii)])));
38
+ for (const raw of requirements) {
39
+ const requirement = typeof raw === 'string' ? { value: raw } : raw;
40
+ const expected = (Array.isArray(requirement.value)
41
+ ? requirement.value
42
+ : [requirement.value]).map(normalized);
43
+ let found = false;
44
+ for (const [index, hashes] of unmatched) {
45
+ if (!hashes.map(normalized).some((hash) => expected.includes(hash))) {
46
+ continue;
47
+ }
48
+ unmatched.delete(index);
49
+ if (found && requirement.multiple === false) {
50
+ throw new Error(`Proof by hash '${expected.join(',')}' is not allowed `
51
+ + 'to appear more than once');
52
+ }
53
+ found = true;
54
+ }
55
+ if (!found && requirement.required !== false) {
56
+ throw new Error(`Proof by required hash '${expected.join(',')}' was not found`);
57
+ }
58
+ }
59
+ if (unmatched.size) {
60
+ throw new Error(`${unmatched.size} extra proof(s) could not be validated`);
61
+ }
62
+ }
63
+ function canonicalProofParams(params) {
64
+ const count = params.responseMatches.length;
65
+ const values = [];
66
+ for (let mask = 0; mask < 2 ** count; mask++) {
67
+ let valid = true;
68
+ const matches = [];
69
+ const redactions = [];
70
+ for (let index = 0; index < count; index++) {
71
+ const match = params.responseMatches[index];
72
+ const redaction = params.responseRedactions[index];
73
+ if ((mask & (1 << index)) === 0) {
74
+ if (match && !match.isOptional) {
75
+ valid = false;
76
+ }
77
+ continue;
78
+ }
79
+ if (match) {
80
+ matches.push({
81
+ value: match.value ?? '',
82
+ type: match.type ?? 'contains',
83
+ invert: match.invert || undefined,
84
+ });
85
+ }
86
+ if (redaction) {
87
+ redactions.push({
88
+ xPath: redaction.xPath ?? '',
89
+ jsonPath: redaction.jsonPath ?? '',
90
+ regex: redaction.regex ?? '',
91
+ hash: normalizedRedactionHash(redaction.hash),
92
+ });
93
+ }
94
+ }
95
+ if (valid && matches.length) {
96
+ values.push(canonical({
97
+ url: params.url ?? '',
98
+ method: params.method ?? 'GET',
99
+ body: params.body ?? '',
100
+ responseMatches: matches,
101
+ responseRedactions: redactions,
102
+ }));
103
+ }
104
+ }
105
+ return values.length ? values : [canonical({
106
+ url: params.url ?? '',
107
+ method: params.method ?? 'GET',
108
+ body: params.body ?? '',
109
+ responseMatches: [],
110
+ responseRedactions: [],
111
+ })];
112
+ }
113
+ function expandTemplates(templates, parameters) {
114
+ const result = [];
115
+ for (const template of templates) {
116
+ const names = template.templateParams ?? [];
117
+ if (!names.length) {
118
+ result.push(template);
119
+ continue;
120
+ }
121
+ const present = names.filter((name) => parameters[name]?.length);
122
+ if (present.length !== names.length) {
123
+ if (template.required === false && !present.length) {
124
+ continue;
125
+ }
126
+ throw new Error('Not all template variables are present');
127
+ }
128
+ const length = parameters[names[0]].length;
129
+ if (!names.every((name) => parameters[name].length === length)) {
130
+ throw new Error('Not all template variables have same length');
131
+ }
132
+ const expanded = Array.from({ length }, (_, index) => substitute(template, Object.fromEntries(names.map((name) => [name, parameters[name][index]]))));
133
+ if (template.templateParamsMode !== 'merge') {
134
+ result.push(...expanded);
135
+ continue;
136
+ }
137
+ result.push({
138
+ ...template,
139
+ responseMatches: expanded.flatMap((item) => item.responseMatches),
140
+ responseRedactions: expanded.flatMap((item) => item.responseRedactions),
141
+ });
142
+ }
143
+ return result;
144
+ }
145
+ function substitute(template, parameters) {
146
+ const replace = (value) => Object.entries(parameters).reduce((result, [name, replacement]) => (result.split(`\${${name}}`).join(replacement)), value);
147
+ return {
148
+ ...template,
149
+ responseMatches: template.responseMatches.map((match) => ({
150
+ ...match,
151
+ value: replace(match.value ?? ''),
152
+ })),
153
+ responseRedactions: template.responseRedactions.map((redaction) => ({
154
+ ...redaction,
155
+ xPath: replace(redaction.xPath ?? ''),
156
+ jsonPath: replace(redaction.jsonPath ?? ''),
157
+ regex: replace(redaction.regex ?? ''),
158
+ })),
159
+ };
160
+ }
161
+ function templateParametersFromProofs(proofs) {
162
+ const combined = {};
163
+ for (const proof of proofs) {
164
+ for (const raw of [proof.claimData.parameters, proof.claimData.context]) {
165
+ try {
166
+ const parsed = JSON.parse(raw);
167
+ Object.assign(combined, parsed.paramValues, parsed.extractedParameters);
168
+ }
169
+ catch {
170
+ // A proof without template parameters is valid.
171
+ }
172
+ }
173
+ }
174
+ const result = {};
175
+ for (const [name, raw] of Object.entries(combined)) {
176
+ let value = raw;
177
+ if (typeof value === 'string') {
178
+ try {
179
+ value = JSON.parse(value);
180
+ }
181
+ catch { /* plain string */ }
182
+ }
183
+ if (Array.isArray(value) && value.length) {
184
+ result[name] = value.map(String);
185
+ }
186
+ }
187
+ return result;
188
+ }
189
+ function isHttpProofParams(value) {
190
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
191
+ return false;
192
+ }
193
+ const params = value;
194
+ return typeof params.url === 'string'
195
+ && typeof params.method === 'string'
196
+ && ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(params.method)
197
+ && (params.body === null
198
+ || params.body === undefined
199
+ || typeof params.body === 'string')
200
+ && Array.isArray(params.responseMatches)
201
+ && params.responseMatches.length > 0
202
+ && Array.isArray(params.responseRedactions);
203
+ }
204
+ function normalized(value) {
205
+ return value.toLowerCase().trim();
206
+ }
207
+ /**
208
+ * All OPRF transports prove the same provider-level intent: the selected
209
+ * value is hidden behind an OPRF. Verification Clients may use the transport
210
+ * they implement, so transport spelling must not change the provider hash.
211
+ */
212
+ function normalizedRedactionHash(value) {
213
+ return value === 'oprf' || value === 'oprf-mpc' || value === 'oprf-raw'
214
+ ? '_oprf'
215
+ : value || undefined;
216
+ }
@@ -0,0 +1,18 @@
1
+ import type { components } from './types/openapi.gen.ts';
2
+ import type { LegacyProof } from './proof-types.ts';
3
+ type RequestSelection = components['schemas']['RequestSelection'];
4
+ type RequestSelectionTemplate = components['schemas']['RequestSelectionTemplate'];
5
+ /** Resolved Builder provider recipe returned for a verification session. */
6
+ export interface BuilderProofRecipe {
7
+ providerId: string;
8
+ resolvedVersion: string;
9
+ initialUrl: string;
10
+ requests: RequestSelection[];
11
+ allowedJsRequests?: RequestSelectionTemplate[];
12
+ }
13
+ /**
14
+ * Derive the exact hash requirements consumed by legacy `verifyProof` from a
15
+ * resolved Builder recipe. Builder's fixed requests are required once each.
16
+ */
17
+ export declare function proofHashesFromBuilderRecipe(recipe: BuilderProofRecipe, proofs: LegacyProof[]): import("./proof-types.ts").HashRequirement[];
18
+ export {};
@@ -0,0 +1,37 @@
1
+ import { hashRequirementsFromSpecs, } from "./proof-hash.js";
2
+ /**
3
+ * Derive the exact hash requirements consumed by legacy `verifyProof` from a
4
+ * resolved Builder recipe. Builder's fixed requests are required once each.
5
+ */
6
+ export function proofHashesFromBuilderRecipe(recipe, proofs) {
7
+ return hashRequirementsFromSpecs(recipe.requests.map((request) => toRequestSpec(request, { required: true, multiple: false })), (recipe.allowedJsRequests ?? []).map((request) => toRequestSpec(request, {
8
+ required: request.required,
9
+ multiple: request.multiple,
10
+ templateParams: request.templateParams,
11
+ templateParamsMode: request.templateParamsMode,
12
+ })), proofs);
13
+ }
14
+ function toRequestSpec(request, flags) {
15
+ return {
16
+ url: request.url,
17
+ urlType: request.url.includes('{{') ? 'TEMPLATE' : 'CONSTANT',
18
+ method: request.method,
19
+ bodySniff: {
20
+ enabled: request.requestBodyTemplate !== undefined,
21
+ template: request.requestBodyTemplate ?? '',
22
+ },
23
+ responseMatches: (request.responseMatches ?? []).map((match) => ({
24
+ type: match.type,
25
+ value: match.value,
26
+ invert: undefined,
27
+ isOptional: match.isOptional,
28
+ })),
29
+ responseRedactions: (request.responseRedactions ?? []).map((redaction) => ({
30
+ ...(redaction.hash ? { hash: redaction.hash } : {}),
31
+ jsonPath: redaction.jsonPath ?? '',
32
+ regex: redaction.regex ?? '',
33
+ xPath: redaction.xPath ?? '',
34
+ })),
35
+ ...flags,
36
+ };
37
+ }
@@ -0,0 +1,3 @@
1
+ export declare const GCP_CONFIDENTIAL_SPACE_ROOT_CA = "-----BEGIN CERTIFICATE-----\n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\nuyT5CnJulxSC5CT1\n-----END CERTIFICATE-----";
2
+ export declare const ATTESTOR_NONCE_PATTERN: RegExp;
3
+ export declare const GCP_CONFIDENTIAL_SPACE_ISSUER = "https://confidentialcomputing.googleapis.com";
@@ -0,0 +1,43 @@
1
+ /* eslint-disable @stylistic/max-len */
2
+ // GCP Confidential Space Root CA — pinned. Rotates infrequently
3
+ // (current cert valid 2024-2034). Used to verify attestor TEE attestation JWTs.
4
+ export const GCP_CONFIDENTIAL_SPACE_ROOT_CA = `-----BEGIN CERTIFICATE-----
5
+ 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
37
+ uyT5CnJulxSC5CT1
38
+ -----END CERTIFICATE-----`;
39
+ // Matches an `attestor_public_key:0x<40-hex>` entry in a GCP Confidential
40
+ // Space attestation JWT's `eat_nonce` claim.
41
+ export const ATTESTOR_NONCE_PATTERN = /^attestor_public_key:0x([0-9a-fA-F]{40})$/;
42
+ // Expected `iss` claim on GCP Confidential Space attestation JWTs.
43
+ export const GCP_CONFIDENTIAL_SPACE_ISSUER = 'https://confidentialcomputing.googleapis.com';
@@ -0,0 +1,62 @@
1
+ /** Exact legacy proof structures carried inside Builder result envelopes. */
2
+ export interface LegacyProof {
3
+ identifier: string;
4
+ claimData: LegacyClaimData;
5
+ signatures: string[];
6
+ witnesses: LegacyWitness[];
7
+ extractedParameterValues: unknown;
8
+ publicData?: unknown;
9
+ taskId?: number;
10
+ teeAttestation?: TeeAttestation | string;
11
+ }
12
+ export interface LegacyClaimData {
13
+ provider: string;
14
+ parameters: string;
15
+ owner: string;
16
+ timestampS: number;
17
+ context: string;
18
+ identifier: string;
19
+ epoch: number;
20
+ }
21
+ export interface LegacyWitness {
22
+ id: string;
23
+ url: string;
24
+ claimAttestation?: AttestorClaimAttestation;
25
+ }
26
+ export interface AttestorClaimAttestation {
27
+ attestor_address: string;
28
+ claim_signature: string;
29
+ attestation_report: string;
30
+ }
31
+ export interface TeeAttestation {
32
+ proof_version?: 'v2' | 'v3';
33
+ proofVersion?: 'v2' | 'v3';
34
+ tee_provider: string;
35
+ tee_technology: string;
36
+ nonce: string;
37
+ timestamp: string;
38
+ workload: {
39
+ container_name?: string;
40
+ image_digest: string;
41
+ };
42
+ verifier: {
43
+ container_name?: string;
44
+ image_digest: string;
45
+ };
46
+ attestation: {
47
+ token: string;
48
+ };
49
+ error?: {
50
+ code: string;
51
+ message: string;
52
+ };
53
+ }
54
+ export interface TrustedData {
55
+ context: Record<string, unknown>;
56
+ extractedParameters: Record<string, string>;
57
+ }
58
+ export interface HashRequirement {
59
+ value: string | string[];
60
+ required?: boolean;
61
+ multiple?: boolean;
62
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
1
+ export declare function legacyErrorClass(name: string): {
2
+ new (message?: string, innerError?: unknown): {
3
+ readonly innerError?: unknown;
4
+ name: string;
5
+ message: string;
6
+ stack?: string;
7
+ cause?: unknown;
8
+ };
9
+ isError(error: unknown): error is Error;
10
+ captureStackTrace(targetObject: object, constructorOpt?: Function): void;
11
+ prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
12
+ stackTraceLimit: number;
13
+ };
@@ -0,0 +1,11 @@
1
+ export function legacyErrorClass(name) {
2
+ return class extends Error {
3
+ constructor(message, innerError) {
4
+ super(innerError
5
+ ? `${message ?? ''} caused by ${String(innerError)}`
6
+ : message);
7
+ this.name = name;
8
+ this.innerError = innerError;
9
+ }
10
+ };
11
+ }
package/lib/reclaim.d.ts CHANGED
@@ -25,13 +25,14 @@ import type { components } from './types/openapi.gen.ts';
25
25
  import { ReclaimClient } from './client.ts';
26
26
  import { type DecryptionKeyInput } from './enckeys.ts';
27
27
  import { type CallbackBody, type Jws, type VerifyResultFullOptions, type VerifyResultFullOutcome } from './result.ts';
28
+ import { type LegacyTeeNonceMode } from './tee-binding.ts';
28
29
  type CallbackSubscription = components['schemas']['CallbackSubscription'];
29
30
  type VerificationSession = components['schemas']['VerificationSession'];
30
31
  type VerificationEvent = components['schemas']['VerificationEvent'];
31
32
  type VerificationEventRecord = components['schemas']['VerificationEventRecord'];
32
33
  type OrgKeypair = components['schemas']['OrgKeypair'];
33
- /** The three terminal events a subscription can receive. */
34
- export declare const TERMINAL_EVENTS: readonly ["verification_success", "verification_rejected", "verification_error"];
34
+ /** Terminal lifecycle events selected by default for callback subscriptions. */
35
+ export declare const TERMINAL_EVENTS: readonly ["verification_success", "verification_rejected", "verification_error", "verification_cancelled", "session_expired"];
35
36
  export interface RegisterCallbackInput {
36
37
  callbackUrl: string;
37
38
  events: VerificationEvent[];
@@ -44,8 +45,24 @@ export interface CreateSessionInput {
44
45
  version?: string;
45
46
  }[];
46
47
  context: Record<string, unknown>;
47
- /** URL with a `{sessionId}` placeholder the end-user opens. */
48
+ /** Exact registered Verification Client base URL. It may contain a
49
+ * `{sessionId}` placeholder; Builder otherwise adds the session id as a
50
+ * query parameter. Open the returned `verificationUrl`, not this base
51
+ * URL. */
48
52
  verificationClientUrl: string;
53
+ /** Optional legacy-compatible TEE session binding. The app secret is the
54
+ * organization's verification private key and never leaves this process.
55
+ * Session creation returns only after Builder stores the signed binding. */
56
+ teeAttestation?: CreateSessionTeeAttestation;
57
+ }
58
+ /** Options intentionally mirror legacy `verifyProof`'s `teeAttestation`
59
+ * vocabulary. `timestamp` is decimal Unix milliseconds; omit it to use now. */
60
+ export interface CreateSessionTeeAttestation {
61
+ appSecret: string;
62
+ timestamp?: string;
63
+ /** `hash` is the default. Use `signature` only for an older Verification
64
+ * Client that expects the 65-byte EIP-191 nonce. */
65
+ nonceMode?: LegacyTeeNonceMode;
49
66
  }
50
67
  /** Input for `keypair.set`: the org's eth public key as uncompressed hex
51
68
  * (`publicKey`, the form wallets/ethers emit) OR as a secp256k1 JWK
@@ -79,6 +96,11 @@ export interface Reclaim {
79
96
  /** Create a verification session. `verificationClientUrl` defaults to
80
97
  * the Builder's own `/v/{sessionId}` page. */
81
98
  create(input: Omit<CreateSessionInput, 'verificationClientUrl'> & Partial<Pick<CreateSessionInput, 'verificationClientUrl'>>): Promise<VerificationSession>;
99
+ /** Add the immutable legacy-compatible TEE context after session
100
+ * creation. Normally use
101
+ * `sessions.create({ teeAttestation: { appSecret } })`; this method
102
+ * supports retrying or explicitly orchestrating the two-step flow. */
103
+ bindTeeNonce(sessionId: string, input: CreateSessionTeeAttestation): Promise<VerificationSession>;
82
104
  get(sessionId: string): Promise<VerificationSession>;
83
105
  listEvents(sessionId: string): Promise<VerificationEventRecord[]>;
84
106
  };
@@ -86,7 +108,7 @@ export interface Reclaim {
86
108
  /** Register a callback subscription (one-time setup). Delivery is scoped
87
109
  * to the token's org; results are ECIES-encrypted to the org's eth key
88
110
  * when `canEncryptResult` is enabled (see `keypair.set`), else plaintext.
89
- * `events` defaults to all three terminal events. */
111
+ * `events` defaults to all terminal lifecycle events. */
90
112
  register(input: Partial<RegisterCallbackInput> & Pick<RegisterCallbackInput, 'callbackUrl'>): Promise<CallbackSubscription>;
91
113
  list(): Promise<CallbackSubscription[]>;
92
114
  remove(subscriptionId: string): Promise<void>;
@@ -106,7 +128,7 @@ export interface Reclaim {
106
128
  * the delivery is encrypted. */
107
129
  decrypt(body: CallbackBody, credential?: DecryptionKeyInput): Promise<Jws>;
108
130
  /** Verify a decrypted JWS: JWKS discovery, ES256K signature,
109
- * `reclaimSessionId`/`aud`/`exp`, nested attestor proofs. */
131
+ * `reclaimSessionId`/`aud`, any legacy `exp`, and exact legacy proofs. */
110
132
  verify(jws: Jws, opts?: VerifyResultFullOptions): Promise<VerifyResultFullOutcome>;
111
133
  /** Handle any callback delivery in one call — the 90% path. Branches on
112
134
  * the envelope's `event`: a terminal event is decrypted (with