@reclaimprotocol/client 0.1.28 → 0.1.31

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 (110) hide show
  1. package/README.md +255 -234
  2. package/lib/api.d.ts +2 -0
  3. package/lib/api.js +2 -0
  4. package/lib/client.js +2 -0
  5. package/lib/enckeys.d.ts +1 -1
  6. package/lib/enckeys.js +2 -2
  7. package/lib/encoding.d.ts +4 -0
  8. package/lib/encoding.js +10 -0
  9. package/lib/feature-flags.d.ts +9 -0
  10. package/lib/feature-flags.js +19 -0
  11. package/lib/index.d.ts +4 -4
  12. package/lib/index.js +4 -4
  13. package/lib/jwks.d.ts +8 -8
  14. package/lib/jwks.js +38 -17
  15. package/lib/launch/destination.d.ts +14 -0
  16. package/lib/launch/destination.js +89 -0
  17. package/lib/launch/device.d.ts +30 -0
  18. package/lib/launch/device.js +168 -0
  19. package/lib/launch/environment.d.ts +224 -0
  20. package/lib/launch/environment.js +98 -0
  21. package/lib/launch/errors.d.ts +20 -0
  22. package/lib/launch/errors.js +25 -0
  23. package/lib/launch/extension.d.ts +42 -0
  24. package/lib/launch/extension.js +72 -0
  25. package/lib/launch/flow.d.ts +82 -0
  26. package/lib/launch/flow.js +222 -0
  27. package/lib/launch/index.d.ts +16 -0
  28. package/lib/launch/index.js +12 -0
  29. package/lib/launch/mobile.d.ts +77 -0
  30. package/lib/launch/mobile.js +124 -0
  31. package/lib/launch/modal.d.ts +50 -0
  32. package/lib/launch/modal.js +258 -0
  33. package/lib/launch/options.d.ts +74 -0
  34. package/lib/launch/options.js +20 -0
  35. package/lib/launch/portal.d.ts +28 -0
  36. package/lib/launch/portal.js +68 -0
  37. package/lib/launch/qr.d.ts +5 -0
  38. package/lib/launch/qr.js +28 -0
  39. package/lib/launch/target.d.ts +46 -0
  40. package/lib/launch/target.js +128 -0
  41. package/lib/launch/vendor/qrcodegen.d.ts +134 -0
  42. package/lib/launch/vendor/qrcodegen.js +822 -0
  43. package/lib/legacy-proof.d.ts +2 -0
  44. package/lib/legacy-proof.js +37 -7
  45. package/lib/proof-hash.d.ts +5 -5
  46. package/lib/proof-hash.js +1 -1
  47. package/lib/proof-recipe.d.ts +3 -3
  48. package/lib/proof-recipe.js +1 -1
  49. package/lib/proof-types.d.ts +35 -8
  50. package/lib/reclaim.d.ts +81 -70
  51. package/lib/reclaim.js +79 -130
  52. package/lib/request-proof.d.ts +60 -0
  53. package/lib/request-proof.js +241 -0
  54. package/lib/result.d.ts +46 -38
  55. package/lib/result.js +160 -100
  56. package/lib/routes.gen.d.ts +76 -28
  57. package/lib/routes.gen.js +24 -11
  58. package/lib/tee-binding.d.ts +1 -1
  59. package/lib/types/openapi.d.ts +1 -0
  60. package/lib/types/openapi.gen.d.ts +1594 -488
  61. package/lib/types/openapi.gen.js +5 -0
  62. package/lib/verification-clients.d.ts +28 -14
  63. package/lib/verification-clients.js +31 -11
  64. package/lib/verify-attestor-tee.d.ts +19 -2
  65. package/lib/verify-attestor-tee.js +40 -4
  66. package/lib/verify-tee.d.ts +10 -1
  67. package/lib/verify-tee.js +34 -10
  68. package/package.json +5 -1
  69. package/src/api.ts +9 -0
  70. package/src/client.ts +2 -0
  71. package/src/enckeys.ts +2 -2
  72. package/src/encoding.ts +12 -0
  73. package/src/feature-flags.ts +39 -0
  74. package/src/index.ts +13 -14
  75. package/src/jwks.ts +52 -30
  76. package/src/launch/destination.ts +111 -0
  77. package/src/launch/device.ts +242 -0
  78. package/src/launch/environment.ts +337 -0
  79. package/src/launch/errors.ts +49 -0
  80. package/src/launch/extension.ts +124 -0
  81. package/src/launch/flow.ts +369 -0
  82. package/src/launch/index.ts +42 -0
  83. package/src/launch/mobile.ts +184 -0
  84. package/src/launch/modal.ts +378 -0
  85. package/src/launch/options.ts +99 -0
  86. package/src/launch/portal.ts +104 -0
  87. package/src/launch/qr.ts +37 -0
  88. package/src/launch/target.ts +150 -0
  89. package/src/launch/vendor/qrcodegen.ts +1034 -0
  90. package/src/proof-hash.ts +6 -6
  91. package/src/proof-recipe.ts +3 -3
  92. package/src/proof-types.ts +36 -8
  93. package/src/reclaim.ts +185 -218
  94. package/src/{legacy-proof.ts → request-proof.ts} +81 -36
  95. package/src/result.ts +238 -172
  96. package/src/routes.gen.ts +24 -11
  97. package/src/tee-binding.ts +1 -1
  98. package/src/types/openapi.gen.ts +1599 -488
  99. package/src/types/openapi.ts +3 -1
  100. package/src/verification-clients.ts +33 -19
  101. package/src/verify-attestor-tee.ts +66 -8
  102. package/src/verify-tee.ts +44 -13
  103. package/lib/api-client.d.ts +0 -31
  104. package/lib/api-client.js +0 -112
  105. package/lib/api-consts.d.ts +0 -4
  106. package/lib/api-consts.js +0 -4
  107. package/lib/api-errors.d.ts +0 -13
  108. package/lib/api-errors.js +0 -30
  109. package/lib/eip712.d.ts +0 -20
  110. package/lib/eip712.js +0 -108
package/README.md CHANGED
@@ -6,17 +6,28 @@ order. Integrate it directly into your app.
6
6
 
7
7
  One package gives you:
8
8
 
9
- - **`createReclaim(...)`** — create verification sessions, register callbacks,
10
- and receive and verify results with a small declarative client.
9
+ - **`ReclaimVerification.create(...)`** — create verification sessions and
10
+ receive and verify results.
11
11
  - **`ReclaimClient`** — call any operation in the spec with the low-level typed
12
12
  HTTP client (`call('OperationId', args)`).
13
13
  - **Crypto helpers** — decrypt ECIES results with the organization's ETH key,
14
- sign and verify result JWSs, discover JWKS keys, and verify exact legacy
15
- proofs.
14
+ verify Builder-signed result JWSs, discover JWKS keys, and verify exact
15
+ proofs. Result signing is Builder-managed in `api=2`.
16
16
 
17
17
  Use the API-only entry point in browsers and edge runtimes. Use the package
18
18
  root in trusted runtimes that need proof verification, signing, or encryption.
19
19
 
20
+ ## Contents
21
+
22
+ - [Choose an entry point](#choose-an-entry-point)
23
+ - [The model in one paragraph](#the-model-in-one-paragraph)
24
+ - [Install](#install)
25
+ - [Quick start](#quick-start-consumer)
26
+ - [API reference](#api-reference)
27
+ - [Crypto and key helpers](#crypto-and-key-helpers)
28
+ - [`ReclaimClient` low-level API](#reclaimclient-low-level)
29
+ - [Security notes](#security-notes)
30
+
20
31
  ## Choose an entry point
21
32
 
22
33
  Use the narrowest entry point that fits the code:
@@ -24,7 +35,7 @@ Use the narrowest entry point that fits the code:
24
35
  | Import | Use it for | Includes |
25
36
  | --- | --- | --- |
26
37
  | `@reclaimprotocol/client/api` | Browser Verification Clients, frontend dashboards, agents, and backend API calls | Browser-safe `fetch` transport plus OpenAPI-generated operation and schema types |
27
- | `@reclaimprotocol/client` | Consumer backends and other trusted runtimes | API client, session workflows, result verification, proof verification, signing, and encryption |
38
+ | `@reclaimprotocol/client` | Consumer backends and other trusted runtimes | API client, session workflows, result verification, proof verification, legacy signing, and encryption |
28
39
  | `@reclaimprotocol/client/openapi` | Type-only integrations | OpenAPI-generated types only |
29
40
 
30
41
  Both runtime entry points export the exact same `ReclaimClient`,
@@ -66,8 +77,8 @@ Boom-compatible: `@hapi/boom`'s `isBoom(error)` returns `true`, and
66
77
  `error.output.statusCode` contains the HTTP status. The API entry point creates
67
78
  this shape without importing the Boom runtime into browser bundles.
68
79
 
69
- A registered Verification Client uses its session-bound UUID header instead
70
- of a consumer bearer token:
80
+ A registered Verification Client uses its session-bound UUID to call the
81
+ bootstrap endpoint:
71
82
 
72
83
  ```ts
73
84
  import { ReclaimClient } from '@reclaimprotocol/client/api'
@@ -77,16 +88,43 @@ const builder = new ReclaimClient({
77
88
  headers: { 'x-reclaim-vc-id': verificationClientId },
78
89
  })
79
90
 
80
- const { data: recipe } = await builder.call('GetVerificationRecipe', {
91
+ const { data: bootstrap } = await builder.call('BootstrapVerificationClient', {
81
92
  params: { sessionId },
82
93
  })
83
94
  ```
84
95
 
96
+ Builder also returns an immutable, session-scoped feature configuration on
97
+ `VerificationSession.runtimeConfig`. Read only keys the Verification Client
98
+ implements, and use a safe fallback because older sessions can omit the
99
+ snapshot:
100
+
101
+ ```ts
102
+ import {
103
+ getBooleanFeatureFlag,
104
+ ReclaimClient,
105
+ } from '@reclaimprotocol/client/api'
106
+
107
+ const { data: bootstrap } = await builder.call('BootstrapVerificationClient', {
108
+ params: { sessionId },
109
+ })
110
+ const useNewPicker = getBooleanFeatureFlag(
111
+ bootstrap.session.runtimeConfig,
112
+ 'portal.newRequestPicker',
113
+ false,
114
+ )
115
+ ```
116
+
117
+ The `/api` and package-root entry points export the same browser-safe flag
118
+ readers. Verification Clients never call the admin feature-flag endpoints or
119
+ fetch live values during a session. See
120
+ [Verification Client feature flags](../../docs/verification-feature-flags.md).
121
+
85
122
  Treat the Verification Client ID as deployment configuration, not as proof of
86
- identity. Builder authorizes it only for the session assigned to that
87
- registered client. Never put an organization secret, result-signing private
88
- key, or proof-owner private key in browser code. Keep result signing in the
89
- Verification Client backend and call `verifyResultFull` from the consumer
123
+ identity. Builder requires it to match the client assigned to the session, but
124
+ the public UUID doesn't authenticate the caller. Never put an organization
125
+ secret, result-signing private key, or proof-owner private key in browser code.
126
+ Builder owns result signing in Builder mode; claimant-facing Verification
127
+ Clients receive no signing key. Call `verifyResultFull` from the consumer
90
128
  backend after receiving a result.
91
129
 
92
130
  ## The model in one paragraph
@@ -97,14 +135,13 @@ secrets. An **Ethereum (secp256k1) public key** identifies the organization.
97
135
  When `canEncryptResult` is enabled, verification **results are encrypted with
98
136
  ECIES** (secp256k1 + AES-256-GCM) to the organization's ETH public key. Keep the
99
137
  matching **ETH private key** (`0x`-hex, the form any wallet or ethers emits) to
100
- decrypt. The Verification Client signs the result JWS with `ES256K`; verify it
101
- against that client's registered trusted JWKS.
138
+ decrypt. Builder signs the result JWS with its managed `ES256K` key; verify it
139
+ against the Builder's trusted issuer and well-known JWKS.
102
140
 
103
141
  ```
104
142
  ┌─ one-time setup ─────────────────────────┐
105
- reclaim.callbacks.register({ callbackUrl }) ← where results go
106
- reclaim.keypair.set(orgId, { publicKey, ← identity + (optional)
107
- canEncryptResult: true }) encrypt to your key
143
+ Configure callback in dashboard or raw API ← where results go
144
+ SetOrgKeypair via dashboard or raw API ← optional encryption
108
145
  └──────────────────────────────────────────┘
109
146
  ┌─ per verification ───────────────────────┐
110
147
  reclaim.sessions.create(...) → open session.verificationUrl
@@ -125,9 +162,9 @@ Configure the client once with your organization secret. In the example,
125
162
  `orgId` identifies the organization that owns the secret.
126
163
 
127
164
  ```ts
128
- import { createReclaim } from '@reclaimprotocol/client'
165
+ import { ReclaimVerification } from '@reclaimprotocol/client'
129
166
 
130
- const reclaim = createReclaim({
167
+ const reclaim = ReclaimVerification.create({
131
168
  orgSecret: process.env.RECLAIM_ORG_SECRET!, // rorg_… (issued by an org owner)
132
169
  })
133
170
  const orgId = process.env.RECLAIM_ORG_ID! // your org's id
@@ -139,9 +176,11 @@ Register the endpoint where Builder posts verification results. Complete this
139
176
  one-time setup with the **dashboard** or the **agent** too; see [Three ways to
140
177
  set up](#three-ways-to-set-up).
141
178
 
142
- ```ts
143
- await reclaim.callbacks.register({ callbackUrl: 'https://my.app/reclaim/callback' })
144
- ```
179
+ The high-level verification client intentionally excludes callback management.
180
+ Use the dashboard, the MCP `create_org_callback_subscription` tool, or call the
181
+ generated `CreateOrgCallbackSubscription` operation with a human-authenticated
182
+ `ReclaimClient` when automating organization setup. The consumer `orgSecret`
183
+ does not authenticate this management operation.
145
184
 
146
185
  ### 2. Enable encryption (optional, one-time)
147
186
 
@@ -158,9 +197,12 @@ const { privateKeyHex, publicKeyHex } = generateEthKeypair()
158
197
 
159
198
  ```ts
160
199
  // (also doable from the dashboard, which can mint the keypair in-browser)
161
- await reclaim.keypair.set(orgId, {
162
- publicKey: publicKeyHex, // 0x04+128 hex (or pass publicKeyJwk)
163
- canEncryptResult: true,
200
+ await reclaim.api('SetOrgKeypair', {
201
+ params: { orgId },
202
+ body: {
203
+ publicKey: publicKeyHex,
204
+ canEncryptResult: true,
205
+ },
164
206
  })
165
207
  ```
166
208
 
@@ -173,7 +215,7 @@ const session = await reclaim.sessions.create({
173
215
  // Optional: snapshot this active organization theme onto the session.
174
216
  themeId: '550e8400-e29b-41d4-a716-446655440000',
175
217
  // Optional: derive and bind the legacy TEE nonce locally after creation.
176
- teeAttestation: { appSecret: organizationVerificationPrivateKey },
218
+ orgEthPrivateKey,
177
219
  })
178
220
  console.log('open this:', session.verificationUrl) // hand to the end-user
179
221
  ```
@@ -184,33 +226,15 @@ console.log('open this:', session.verificationUrl) // hand to the end-user
184
226
  Verification Client with its registered base URL:
185
227
 
186
228
  ```ts
187
- import {
188
- KNOWN_VERIFICATION_CLIENTS,
189
- type KnownVerificationClientUrlMap,
190
- } from '@reclaimprotocol/client'
191
-
192
- const verificationClientUrls = {
193
- [KNOWN_VERIFICATION_CLIENTS.portals]:
194
- process.env.RECLAIM_PORTAL_URL,
195
- [KNOWN_VERIFICATION_CLIENTS.verifierApp]:
196
- process.env.RECLAIM_VERIFIER_APP_URL,
197
- [KNOWN_VERIFICATION_CLIENTS.inAppSdk]:
198
- process.env.RECLAIM_INAPP_URL,
199
- [KNOWN_VERIFICATION_CLIENTS.reclaimBrowserExtension]:
200
- process.env.RECLAIM_BROWSER_EXTENSION_URL,
201
- } satisfies KnownVerificationClientUrlMap
202
-
203
- const clientName = KNOWN_VERIFICATION_CLIENTS.portals
204
- const verificationClientUrl = verificationClientUrls[clientName]
205
- if (!verificationClientUrl) throw new Error(`${clientName} URL isn't configured`)
229
+ import { VerificationClient } from '@reclaimprotocol/client'
206
230
 
207
231
  const session = await reclaim.sessions.create({
208
232
  providers: [
209
- // Blank/omitted version → latest; exact and npm semver ranges also work.
233
+ // Blank/omitted → latest; 1.2.2 → exact; ^1.2.2 → >=1.2.2 <2.0.0.
210
234
  { providerId: 'prov_…', version: '^1.2.0' },
211
235
  ],
212
236
  context: { userId: 'abc', orderId: 'order-123' },
213
- verificationClientUrl,
237
+ verificationClientUrl: VerificationClient.portals,
214
238
  })
215
239
 
216
240
  // Builder resolves the URL to a registered client, pins its UUID on the
@@ -218,6 +242,15 @@ const session = await reclaim.sessions.create({
218
242
  console.log(session.verificationUrl)
219
243
  ```
220
244
 
245
+ Builder resolves each provider independently and returns its exact
246
+ `resolvedVersion`. Verification Clients execute that resolved recipe; they
247
+ don't resolve versions themselves.
248
+
249
+ For imported legacy providers, pass consumer values in `context`. Builder-mode
250
+ clients support `{{context.key}}`, then `{{context_key}}`, then `{{key}}` as
251
+ compatibility aliases. Explicit request or provider-script parameters take
252
+ precedence.
253
+
221
254
  Builder snapshots the selected theme at session creation. Omit `themeId` to use
222
255
  the organization's most recently updated active theme. If the organization has
223
256
  no active theme, the Verification Client uses its default presentation. A
@@ -238,12 +271,14 @@ enable logs containing personal data, so don't add it to normal production
238
271
  links. Redirect parameters are also client-owned: append them to the returned
239
272
  URL instead of putting them in the session request.
240
273
 
241
- Use `{{context.key}}` in new Builder recipes for values supplied in
242
- `context`; Builder validates those through the provider's `requiredContext`.
243
- For an old DevTools recipe that still uses bare `{{key}}`, Builder-mode
244
- Verification Clients seed a missing `parameters.key` from scalar
245
- `context.key`. An explicit parameter wins. This compatibility alias applies
246
- only to `api=2`; it doesn't change legacy sessions. Builder-owned
274
+ Use `{{context.key}}` in new Builder recipes for values supplied in `context`;
275
+ Builder validates those through the provider's `requiredContext`. Imported
276
+ recipes can fall back to `{{context_key}}` and then `{{key}}`. A lower-priority
277
+ alias is used only when the recipe doesn't use a higher-priority spelling, and
278
+ an explicit parameter always wins. Clients apply this to URLs, headers, request
279
+ bodies, response matches, response redactions, provider scripts, and
280
+ interceptor-created claims. This applies only to `api=2`; it doesn't change
281
+ legacy sessions. Builder-owned
247
282
  `reclaimSessionId` and `attestationNonce` remain context-only.
248
283
 
249
284
  The value must exactly equal that deployment's registered
@@ -253,90 +288,87 @@ yourself. Builder either substitutes it or adds a `sessionId` query parameter,
253
288
  then adds `api=2`. An unregistered or slightly different URL is rejected with
254
289
  HTTP 400.
255
290
 
256
- The SDK exposes stable registry names for these intended uses. A standard
257
- deployment seeds all six rows, but an administrator must configure each row's
258
- URL, issuer, JWKS URL, and signing key before consumers select it:
291
+ The SDK exposes declarative URLs for these intended uses.
292
+ Builder owns terminal-result signing and encryption; claimant-facing clients
293
+ receive no signing key, issuer, JWKS URL, or callback key:
259
294
 
260
- | Registry name | How the consumer selects it | Builder v2 use |
261
- | --- | --- | --- |
262
- | `builder` | Omit `verificationClientUrl`. | Built-in `/v/{sessionId}` client; available whenever Builder is running. |
263
- | `portals` | Pass the exact registered portal URL. | Interactive web portal. Its deployment must configure the bridge URL and registered Client UUID. |
264
- | `verifier-app` | Pass the exact registered HTTPS app/universal-link URL. | Reclaim Verifier mobile app. Its build must configure the bridge URL and registered Client UUID. |
265
- | `inapp-sdk` | Pass the exact registered URL owned by the host app. | In-app SDK or add-to-app module. The host app must configure Builder transport and the registered Client UUID. |
266
- | `reclaim-browser-extension` | Pass the exact registered extension launch URL. | Browser extension. Use a build that supports Builder mode (`api=2`). Its existing legacy `oprf-raw` support is independent of Builder-mode hardening. |
267
- | `zkfetch` | Don't use as an interactive claimant client unless the deployment explicitly supports it. | Separate programmatic `ZKFETCH` quota pool, not one of the current portal/mobile Builder flows. |
295
+ | Registry name | Builder v2 use |
296
+ | --- | --- |
297
+ | `builder` | Builder's hosted claimant page at `/v/{sessionId}`. |
298
+ | `portals` | Interactive web portal. The default when you omit the URL. |
299
+ | `verifier-app` | Reclaim Verifier mobile app identifier. |
300
+ | `inapp-sdk` | In-app SDK or add-to-app module identifier. |
301
+ | `reclaim-browser-extension` | Browser extension identifier. |
302
+ | `zkfetch` | Programmatic client in the separate `ZKFETCH` quota pool. |
268
303
 
269
- `KNOWN_VERIFICATION_CLIENTS` is the public string map for these stable registry
270
- names:
304
+ Use `VerificationClient` to select a built-in client or supply a registered
305
+ custom URL:
271
306
 
272
307
  ```ts
273
- {
274
- builder: 'builder',
275
- portals: 'portals',
276
- verifierApp: 'verifier-app',
277
- inAppSdk: 'inapp-sdk',
278
- reclaimBrowserExtension: 'reclaim-browser-extension',
279
- zkFetch: 'zkfetch',
280
- }
308
+ VerificationClient.builder
309
+ VerificationClient.portals
310
+ VerificationClient.verifierApp
311
+ VerificationClient.inAppSdk
312
+ VerificationClient.reclaimBrowserExtension
313
+ VerificationClient.zkFetch
314
+ VerificationClient.custom('https://verify.example.com')
281
315
  ```
282
316
 
283
- Use `KnownVerificationClientName` when accepting a client choice in your own
284
- API and `KnownVerificationClientUrlMap` for deployment configuration. Validate
285
- untrusted input against the map's values. The names are stable; the URLs are
286
- not.
287
-
288
- Client URLs are deployment configuration, not package constants. A seeded
289
- registry row can be only an identifier until an administrator replaces its URI
290
- with the real deployed client URL and configures its issuer, JWKS, bridge, and
291
- signing key. Get the exact value from the Builder administrator or the
292
- admin-only `/admin/verification-clients` registry; never accept it from a
293
- claimant or derive it from a signed result. An organization token cannot list
294
- the admin registry. Administrators can also register additional clients with
295
- their own URL, issuer, JWKS URL, and signing key.
317
+ Administrators can also register additional clients with their own URL.
296
318
 
297
319
  Examples for the currently supported interactive clients:
298
320
 
299
321
  ```ts
300
- // Replace these examples with exact values from your Builder registry.
301
- const registeredClients = {
302
- portal: 'https://portal.your-deployment.example/',
303
- verifierApp: 'https://verify.your-deployment.example/app',
304
- inApp: 'https://app.your-deployment.example/reclaim',
305
- browserExtension: 'https://extension.your-deployment.example/launch',
306
- }
322
+ import { VerificationClient } from '@reclaimprotocol/client'
307
323
 
308
- // 1. Built-in Builder client — omit verificationClientUrl.
309
- const builtIn = await reclaim.sessions.create({
324
+ // 1. Default (Portal) — omit verificationClientUrl.
325
+ const byDefault = await reclaim.sessions.create({
310
326
  providers: [{ providerId }],
311
327
  context,
312
328
  })
313
329
 
314
- // 2. Portal — the URL must be the exact registered portals.uri.
330
+ // 2. Portal, selected explicitly.
315
331
  const portal = await reclaim.sessions.create({
316
332
  providers: [{ providerId }],
317
333
  context,
318
- verificationClientUrl: registeredClients.portal,
334
+ verificationClientUrl: VerificationClient.portals,
319
335
  })
320
336
 
321
- // 3. Reclaim Verifier app — use its registered HTTPS app link.
337
+ // 3. Reclaim Verifier app.
322
338
  const verifierApp = await reclaim.sessions.create({
323
339
  providers: [{ providerId }],
324
340
  context,
325
- verificationClientUrl: registeredClients.verifierApp,
341
+ verificationClientUrl: VerificationClient.verifierApp,
326
342
  })
327
343
 
328
- // 4. In-app SDK/add-to-app host — use the host app's registered link.
344
+ // 4. In-app SDK or add-to-app host.
329
345
  const inApp = await reclaim.sessions.create({
330
346
  providers: [{ providerId }],
331
347
  context,
332
- verificationClientUrl: registeredClients.inApp,
348
+ verificationClientUrl: VerificationClient.inAppSdk,
333
349
  })
334
350
 
335
- // 5. Browser extension — use a deployed build with Builder-mode support.
351
+ // 5. Browser extension.
336
352
  const browserExtension = await reclaim.sessions.create({
337
353
  providers: [{ providerId }],
338
354
  context,
339
- verificationClientUrl: registeredClients.browserExtension,
355
+ verificationClientUrl: VerificationClient.reclaimBrowserExtension,
356
+ })
357
+
358
+ // 6. Builder's hosted claimant page.
359
+ const hosted = await reclaim.sessions.create({
360
+ providers: [{ providerId }],
361
+ context,
362
+ verificationClientUrl: VerificationClient.builder,
363
+ })
364
+
365
+ // 7. Separately registered client or another Builder deployment.
366
+ const custom = await reclaim.sessions.create({
367
+ providers: [{ providerId }],
368
+ context,
369
+ verificationClientUrl: VerificationClient.custom(
370
+ 'https://verify.example.com',
371
+ ),
340
372
  })
341
373
  ```
342
374
 
@@ -385,48 +417,26 @@ sequenceDiagram
385
417
  Backend-->>Frontend: Return verified status
386
418
  ```
387
419
 
388
- **1. Create the request on your backend.** Keep `RECLAIM_ORG_SECRET` private,
389
- validate the requested client name against `KNOWN_VERIFICATION_CLIENTS`, map it
390
- to your deployment's registered URL, and save the returned session ID with your
391
- user or order.
420
+ **1. Create the request on your backend.** Keep `RECLAIM_ORG_SECRET` private
421
+ and save the returned session ID with your user or order. The session opens in
422
+ the Portal unless you pass a `VerificationClient` URL.
392
423
 
393
424
  ```ts
394
425
  import {
395
426
  type CallbackBody,
396
- createReclaim,
397
- KNOWN_VERIFICATION_CLIENTS,
398
- type KnownVerificationClientName,
399
- type KnownVerificationClientUrlMap,
427
+ ReclaimVerification,
400
428
  } from '@reclaimprotocol/client'
401
429
 
402
- const reclaim = createReclaim({
430
+ const reclaim = ReclaimVerification.create({
403
431
  orgSecret: process.env.RECLAIM_ORG_SECRET!,
404
432
  })
405
433
 
406
- const clientUrls: KnownVerificationClientUrlMap = {
407
- [KNOWN_VERIFICATION_CLIENTS.portals]: process.env.RECLAIM_PORTAL_URL!,
408
- [KNOWN_VERIFICATION_CLIENTS.verifierApp]:
409
- process.env.RECLAIM_VERIFIER_APP_URL!,
410
- [KNOWN_VERIFICATION_CLIENTS.inAppSdk]: process.env.RECLAIM_INAPP_URL!,
411
- [KNOWN_VERIFICATION_CLIENTS.reclaimBrowserExtension]:
412
- process.env.RECLAIM_BROWSER_EXTENSION_URL!,
413
- }
414
-
415
434
  async function createVerificationRequest(
416
435
  userId: string,
417
- clientName: KnownVerificationClientName,
418
436
  ) {
419
- const verificationClientUrl = clientName === KNOWN_VERIFICATION_CLIENTS.builder
420
- ? undefined
421
- : clientUrls[clientName]
422
- if (clientName !== KNOWN_VERIFICATION_CLIENTS.builder && !verificationClientUrl) {
423
- throw new Error(`${clientName} URL isn't configured`)
424
- }
425
-
426
437
  const session = await reclaim.sessions.create({
427
438
  providers: [{ providerId: 'prov_…', version: '^1.2.0' }],
428
439
  context: { userId },
429
- ...(verificationClientUrl ? { verificationClientUrl } : {}),
430
440
  })
431
441
  await savePendingVerification({
432
442
  sessionId: session.id,
@@ -467,8 +477,21 @@ encoded. Persist the delivery before returning a successful HTTP status so a
467
477
  process crash can't lose it.
468
478
 
469
479
  ```ts
470
- await reclaim.callbacks.register({
471
- callbackUrl: 'https://my.app/reclaim/callback/{{sessionId}}',
480
+ // One-time setup as the signed-in organization member, not with orgSecret.
481
+ import { ReclaimClient } from '@reclaimprotocol/client/api'
482
+ const setupClient = new ReclaimClient({ token: humanAccessToken })
483
+ await setupClient.call('CreateOrgCallbackSubscription', {
484
+ params: { orgId },
485
+ body: {
486
+ callbackUrl: 'https://my.app/reclaim/callback/{{sessionId}}',
487
+ events: [
488
+ 'verification_success',
489
+ 'verification_rejected',
490
+ 'verification_error',
491
+ 'verification_cancelled',
492
+ 'session_expired',
493
+ ],
494
+ },
472
495
  })
473
496
 
474
497
  // HTTP callback handler. `sessionId` comes from the route path.
@@ -482,17 +505,17 @@ async function acceptCallback(sessionId: string, requestBody: CallbackBody) {
482
505
  Verify the durable delivery in a backend worker. Load the expected session and
483
506
  organization from your database; don't take either expected value from the
484
507
  callback payload. `results.receive` decrypts when needed, validates the
485
- registered Verification Client issuer and JWKS, verifies the outer result JWS,
486
- checks the session/audience and any legacy result expiry, and runs the package's legacy-compatible
487
- `verifyProof` checks for every proof.
508
+ Builder issuer and JWKS, verifies the outer result JWS,
509
+ checks the session/audience and any legacy result expiry, and runs the package's
510
+ `verifyRequestProof` checks for every proof.
488
511
 
489
512
  ```ts
490
513
  async function verifyCallbackDelivery(deliveryId: string) {
491
514
  const delivery = await loadCallbackDelivery(deliveryId)
492
515
  const expected = await loadPendingVerification(delivery.sessionId)
493
516
  const outcome = await reclaim.results.receive(delivery.requestBody, {
494
- credential: process.env.RECLAIM_ETH_PRIVATE_KEY,
495
- expectedReclaimSessionId: expected.sessionId,
517
+ orgEthPrivateKey: process.env.RECLAIM_ETH_PRIVATE_KEY,
518
+ reclaimSessionId: expected.sessionId,
496
519
  expectedAud: expected.orgId,
497
520
  })
498
521
 
@@ -517,14 +540,15 @@ Removing `api=2` does not turn this request into a valid legacy request. To
517
540
  create a legacy verification, continue using the legacy JS SDK/backend request
518
541
  format. URLs without `api=2` stay on each client's unchanged legacy parser.
519
542
 
520
- With `teeAttestation`, the SDK creates the session, derives the selected exact
543
+ With `orgEthPrivateKey`, the SDK creates the session, derives the selected exact
521
544
  legacy nonce, signs the Builder binding with
522
545
  EIP-191, and registers it at
523
546
  `PUT /verifications/sessions/{sessionId}/tee-nonce`. The private key is never
524
547
  sent to Builder. The binding is idempotent for identical values and immutable
525
548
  afterward; bind before proof generation starts. The default `nonceMode: 'hash'`
526
549
  uses `RECLAIM_TEE_NONCE_V1`. Set `nonceMode: 'signature'` for the older
527
- 65-byte EIP-191 nonce that legacy `verifyProof` also accepts.
550
+ 65-byte EIP-191 nonce that legacy `verifyProof` also accepts. Use
551
+ `teeNonceMode: 'signature'` on `sessions.create` for this compatibility mode.
528
552
 
529
553
  Because this endpoint is an authenticated POST, you can use `curl` without the
530
554
  SDK. The organization secret is the only credential:
@@ -552,7 +576,7 @@ Never send the private key in either request.
552
576
 
553
577
  When the user finishes, Builder sends a POST request to your `callbackUrl`.
554
578
  Pass the request body to `results.receive`. It decrypts encrypted results,
555
- verifies the outer signature, and verifies every exact legacy proof. It
579
+ verifies the outer signature, and verifies every exact proof. It
556
580
  **throws if the result is invalid**, so reaching the `result` branch means the
557
581
  result is verified.
558
582
 
@@ -560,12 +584,12 @@ result is verified.
560
584
  // Load these expected values from your database, not from the callback body.
561
585
  const expected = await loadPendingVerification(callbackRouteSessionId)
562
586
  const outcome = await reclaim.results.receive(requestBody, {
563
- credential: process.env.RECLAIM_ETH_PRIVATE_KEY, // 0x-hex ETH key; omit for plaintext
564
- expectedReclaimSessionId: expected.sessionId,
587
+ orgEthPrivateKey: process.env.RECLAIM_ETH_PRIVATE_KEY,
588
+ reclaimSessionId: expected.sessionId,
565
589
  expectedAud: expected.orgId,
566
590
  })
567
591
  if (outcome.kind === 'result') {
568
- // These values came from verifyProof. Don't trust diagnostic copies from
592
+ // These values came from verifyRequestProof. Don't trust diagnostic copies from
569
593
  // the outer payload for application decisions.
570
594
  const trustedClaims = outcome.result.proofs?.flatMap((proof) => (
571
595
  proof.data ? [proof.data] : []
@@ -580,7 +604,9 @@ if (outcome.kind === 'result') {
580
604
 
581
605
  Choose any of these one-time setup methods; each uses the same API:
582
606
 
583
- - **This SDK** — `reclaim.callbacks.register(...)` + `reclaim.keypair.set(...)`, as above (good for scripting/CI).
607
+ - **Raw OpenAPI client** — use a human-authenticated `ReclaimClient` for
608
+ `CreateOrgCallbackSubscription`. Use `SetOrgKeypair` through `reclaim.api`
609
+ when configuring result encryption, which is useful for scripting or CI.
584
610
  - **The dashboard** — the organization's web console has a callback-subscription form and a "Set encryption keypair" dialog that can **generate the ETH keypair in your browser** and download the private key for you.
585
611
  - **The agent** (`@reclaimprotocol/agent` — the `reclaim` CLI / MCP server) — handy while building and testing a provider locally.
586
612
 
@@ -592,10 +618,10 @@ authentication and the ETH private key for decryption. The
592
618
 
593
619
  ## API reference
594
620
 
595
- ### `createReclaim(config)`
621
+ ### `ReclaimVerification.create(config)`
596
622
 
597
- Call `createReclaim(config)` to return a `Reclaim` with four namespaces —
598
- `sessions`, `callbacks`, `keypair`, and `results` — plus an `api` escape hatch.
623
+ Call `ReclaimVerification.create(config)` to return a client with `sessions`
624
+ and `results` namespaces, plus an `api` escape hatch.
599
625
 
600
626
  | `ReclaimConfig` field | type | required | notes |
601
627
  |---|---|---|---|
@@ -604,47 +630,51 @@ Call `createReclaim(config)` to return a `Reclaim` with four namespaces —
604
630
  | `fetch` | `typeof fetch` | no | Custom fetch (tests, edge runtimes). |
605
631
  | `headers` | `Record<string,string>` | no | Extra headers merged into every request. |
606
632
 
607
- `orgSecret` is the only required configuration value. Pass each other value to
608
- the method that uses it: **`orgId`** to `keypair.set`/`get`, the optional **TEE
609
- private key** (`0x`-hex) to `sessions.create({ teeAttestation: { appSecret } })`,
610
- and the **ETH private key** to `results.receive`/`decrypt`.
633
+ `orgSecret` is the only required configuration value. Pass the optional ETH
634
+ private key directly to `sessions.create`, `results.receive`, or
635
+ `results.decrypt`. The SDK doesn't cache or serialize that key.
611
636
 
612
- ### Known verification client names
637
+ Use `reclaim.export()` to serialize non-secret configuration. Restore it with
638
+ runtime credentials:
613
639
 
614
640
  ```ts
615
- import {
616
- KNOWN_VERIFICATION_CLIENTS,
617
- type KnownVerificationClientName,
618
- type KnownVerificationClientUrlMap,
619
- } from '@reclaimprotocol/client'
641
+ const saved = reclaim.export()
642
+ const restored = ReclaimVerification.import(saved, { orgSecret })
643
+ ```
644
+
645
+ The JSON contains only a format version and Builder base URL. It never contains
646
+ the organization secret, ETH private key, headers, or custom `fetch` function.
647
+
648
+ ### Verification clients
649
+
650
+ ```ts
651
+ import { VerificationClient } from '@reclaimprotocol/client'
620
652
  ```
621
653
 
622
- - `KNOWN_VERIFICATION_CLIENTS` maps TypeScript-friendly keys to the stable
623
- registry names `builder`, `portals`, `verifier-app`, `inapp-sdk`,
624
- `reclaim-browser-extension`, and `zkfetch`.
625
- - `KnownVerificationClientName` is the union of those string values.
626
- - `KnownVerificationClientUrlMap` is a partial map from those names to the
627
- exact URLs registered in one Builder deployment.
654
+ - `VerificationClient.builder`, `.portals`, `.verifierApp`, `.inAppSdk`,
655
+ `.reclaimBrowserExtension`, and `.zkFetch` contain production URLs.
656
+ - `VerificationClient.custom(uri)` returns a separately registered client URL
657
+ unchanged.
628
658
 
629
- The map doesn't make a URL trusted and doesn't select a client by name in the
630
- Builder API. Your backend chooses a configured URL, and
659
+ The helper doesn't make a URL trusted or register it in Builder. Your backend
660
+ chooses an already registered URL, and
631
661
  `sessions.create({ verificationClientUrl })` asks Builder to resolve that URL
632
- to its registered client UUID. Omit the URL to select `builder`.
662
+ to its registered client UUID. Omit the URL to select `portals`.
633
663
 
634
664
  ### `reclaim.sessions`
635
665
 
636
666
  ```ts
637
- reclaim.sessions.create({ providers, context, verificationClientUrl?, teeAttestation? })
667
+ reclaim.sessions.create({ providers, context, verificationClientUrl?, orgEthPrivateKey? })
638
668
  // providers: { providerId, version? }[] — proved in order, one combined result; all required.
639
669
  // → VerificationSession (has `id`, `verificationUrl`, `status`, `mode`, …)
640
- // verificationClientUrl selects a registered Verification Client by URL.
641
- // teeAttestation: { appSecret } — optional org verification private key;
642
- // kept local while the SDK derives and registers the post-create TEE binding.
670
+ // verificationClientUrl selects a registered Verification Client by URL;
671
+ // omitted, it defaults to the Portal.
672
+ // orgEthPrivateKey optionally creates the post-create TEE binding locally.
643
673
  // The returned verificationUrl contains sessionId and api=2.
644
- reclaim.sessions.bindTeeNonce(sessionId, { appSecret, timestamp? })
674
+ reclaim.sessions.bindTeeNonce(reclaimSessionId, { orgEthPrivateKey, timestamp? })
645
675
  // Explicit two-step/retry form. Reads the session, derives locally, and binds.
646
- reclaim.sessions.get(sessionId) // → VerificationSession
647
- reclaim.sessions.listEvents(sessionId) // → VerificationEventRecord[]
676
+ reclaim.sessions.get(reclaimSessionId) // → VerificationSession
677
+ reclaim.sessions.listEvents(reclaimSessionId) // → VerificationEventRecord[]
648
678
  ```
649
679
 
650
680
  The Builder resolves `verificationClientUrl` to the session's registered
@@ -654,45 +684,21 @@ the selected Verification Client understands; the Builder does not interpret
654
684
  or execute them. A URL without `api=2` is handled by the selected client's
655
685
  legacy path.
656
686
 
657
- ### `reclaim.callbacks`
658
-
659
- ```ts
660
- reclaim.callbacks.register({ callbackUrl, events? }) // the dashboard preselects 5 terminal events
661
- // → CallbackSubscription. Org-scoped; no address/publicKey/secret.
662
- reclaim.callbacks.list() // → CallbackSubscription[]
663
- reclaim.callbacks.remove(subscriptionId) // → void
664
- ```
665
-
666
- `events` is any subset of `VerificationEvent`. Builder terminal events are
667
- `verification_success`, `verification_rejected`, `verification_error`,
668
- `verification_cancelled`, and `session_expired`. The dashboard preselects all
669
- five. Terminal result deliveries contain the signed result when one is
670
- available. Treat cancellation and expiry as terminal outcomes.
671
- Non-terminal events deliver plaintext progress notifications.
672
-
673
- ### `reclaim.keypair` (the organization's single ETH encryption key)
674
-
675
- ```ts
676
- await reclaim.keypair.set(orgId, { publicKey, canEncryptResult?: true, label?: 'prod' }) // → OrgKeypair
677
- await reclaim.keypair.get(orgId) // → OrgKeypair | undefined (undefined ⇒ keyless / plaintext delivery)
678
- ```
679
-
680
- `publicKey` is the organization's ETH **uncompressed public key** (`0x04` +
681
- 128 hex — the form `generateEthKeypair()` and ethers produce). Alternatively,
682
- pass `publicKeyJwk` (a secp256k1 JWK). `set` stores the JWK and derived ETH
683
- address. There is exactly **one** keypair per organization; `set` replaces it
684
- (rotation). `canEncryptResult` toggles whether results are encrypted to it
685
- (default `false` ⇒ plaintext). Keep the matching **ETH private key** (`0x`-hex)
686
- and pass it as the decryption key to `results.receive`/`decrypt`.
687
-
688
687
  ### `reclaim.results`
689
688
 
690
689
  ```ts
691
690
  reclaim.results.receive(body, opts?) // → ReceivedDelivery ← the 90% path
692
- reclaim.results.decrypt(body, credential?) // → Jws (ECIES or plaintext → signed JWS)
691
+ reclaim.results.decrypt(body, orgEthPrivateKey?) // → Jws
693
692
  reclaim.results.verify(jws, opts?) // → VerifyResultFullOutcome
694
693
  ```
695
694
 
695
+ Each verified `result.proofs` entry includes the exact `proof` received
696
+ from the Verification Client, its provider and request identity, all attestor
697
+ addresses, derived proof hashes, trusted `data`, unverified `publicData`, and
698
+ the claimant/session and attestor TEE verification outcomes. This preserves the
699
+ complete proof for storage or on-chain use while keeping trusted
700
+ extracted parameters separate from unverified convenience data.
701
+
696
702
  `receive` branches on the delivery:
697
703
 
698
704
  ```ts
@@ -701,23 +707,21 @@ type ReceivedDelivery =
701
707
  | { kind: 'event'; event: VerificationEvent; eventData: Record<string, unknown> } // plaintext lifecycle event
702
708
  ```
703
709
 
704
- `opts` (`ReceiveOptions`) is `VerifyResultFullOptions` (`expectedReclaimSessionId`
705
- nonce check, `expectedAud`, trusted issuer configuration, attestor allowlists,
706
- `fetchImpl`, `now`, `ignoreExpiration`) **plus** an optional `credential` — the ETH decryption key
707
- as a **`0x`-hex private key string** (the key content, not a path or environment
708
- variable name). All three methods are async; pass `credential` only when the
709
- delivery is encrypted.
710
+ `ReceiveOptions` includes the `reclaimSessionId` check, `expectedAud`, trusted
711
+ issuer configuration, attestor allowlists, and an optional
712
+ `orgEthPrivateKey`. The private key decrypts encrypted results and verifies TEE
713
+ proof binding. All three methods are async.
710
714
 
711
- `verifyResultFull` is the recommended server-side API. Configure either an
712
- explicit `issuer`/`trustedIssuers` list, or resolve the registered issuer for
713
- the signed session's `verificationClientId`:
715
+ `verifyResultFull` is the recommended server-side API. Configure the canonical
716
+ Builder issuer and well-known JWKS URL (or keep those values in your own
717
+ server-side trust configuration):
714
718
 
715
719
  ```ts
716
720
  await reclaim.results.verify(jws, {
717
- expectedReclaimSessionId: session.id,
721
+ reclaimSessionId: session.id,
718
722
  expectedAud: orgId,
719
- resolveIssuers: (verificationClientId) => registry.issuersFor(verificationClientId),
720
- resolveJwksUrl: (verificationClientId) => registry.jwksUrlFor(verificationClientId),
723
+ trustedIssuers: ['https://build.reclaimprotocol.org'],
724
+ jwksUrl: 'https://build.reclaimprotocol.org/.well-known/jwks.json',
721
725
  })
722
726
  ```
723
727
 
@@ -726,21 +730,36 @@ fetches JWKS. It never uses an untrusted `iss` as a network destination. Keep
726
730
  issuer resolution server-side and treat a missing or changed issuer as a
727
731
  verification failure.
728
732
 
729
- Issuer trust and key discovery are separate inputs: resolve the registered
730
- `verificationClientIssuer` and `verificationClientJwksUrl` independently (or
731
- pass `trustedIssuers` and `jwksUrl`). Do not construct the JWKS URL from the
732
- JWS payload.
733
+ Issuer trust and key discovery are separate Builder deployment inputs. Do not
734
+ construct the JWKS URL from the JWS payload.
733
735
 
734
- `verifyResultFull` calls this package's legacy-compatible `verifyProof`
736
+ `verifyResultFull` calls this package's `verifyRequestProof`
735
737
  implementation for every exact `Proof` object in the signed result. It does
736
738
  not base64-decode, wrap, reconstruct, or otherwise transform those proofs.
739
+ When you provide `reclaimSessionId`, every proof must
740
+ also contain that session ID. This prevents a valid proof from an older session
741
+ from being placed in a newly signed result envelope. For an older deployment
742
+ that produced unbound proofs, set `proofValidation.requireSessionBinding` to
743
+ `false` explicitly during its migration. A different embedded session ID is
744
+ always rejected.
737
745
  By default, it uses each result's
738
- `provider_id` and exact `resolved_version` to resolve the legacy provider hash
746
+ `provider_id` and exact `resolved_version` to resolve the provider hash
739
747
  requirements. You can instead pass `hashes`, `proofValidation`, or
740
- `proofValidationByProviderId`. These options mirror the legacy verifier,
748
+ `proofValidationByProviderId`. These options mirror `verifyRequestProof`,
741
749
  including `hasNoPii`, verifier TEE (`teeAttestation.appSecret`), and
742
- attestor-TEE checks. The same registered Builder JWKS URL supplies the
743
- purpose-bound result keys and its `x-reclaim-attestors` trust list. TEE settings preserve the legacy configuration objects;
750
+ attestor-TEE checks. The configured Builder JWKS URL supplies the
751
+ purpose-bound result keys, its `x-reclaim-attestors` signer list, and its
752
+ `x-reclaim-attestor-image-digests` Confidential Space image allowlist. When
753
+ the JWKS publishes image digests, `verifyResultFull` automatically verifies
754
+ every witness's attestor TEE `claimAttestation` — its attestor address and
755
+ claim signature always, and its `attestation_report` when the witness carries
756
+ one. The report is optional: production attestors sign claims without
757
+ returning one, so a witness with only an address and a signature verifies, and
758
+ `isAttestorTeeAttestationVerified` comes back `false` to say nothing was
759
+ attested. If
760
+ `proofValidation.attestorTeeAttestation.expectedImageDigests` is also set, the
761
+ SDK uses the intersection: caller policy can narrow the published trust list,
762
+ but can't broaden it. TEE settings preserve the same configuration objects;
744
763
  `verifyResultFull` does not accept injected verification hooks. The surrounding
745
764
  issuer, session, and context checks are stateless; persist accepted session IDs
746
765
  in your application to prevent replay.
@@ -772,7 +791,10 @@ is therefore always verified; wrap the call to inspect failures:
772
791
  ```ts
773
792
  import { ResultVerificationError } from '@reclaimprotocol/client'
774
793
  try {
775
- const { result } = await reclaim.results.receive(body, { credential })
794
+ const { result } = await reclaim.results.receive(body, {
795
+ orgEthPrivateKey,
796
+ reclaimSessionId,
797
+ })
776
798
  // result.payload is verified
777
799
  } catch (err) {
778
800
  if (err instanceof ResultVerificationError) {
@@ -802,18 +824,17 @@ loadDecryptionKey(hex): DecryptionKey // 0x-hex eth private key → r
802
824
  uncompressedPubkeyToJwk(hex): Jwk // 0x04+128 hex → secp256k1 JWK
803
825
  pubkeyJwkToEthAddress(jwk): string // secp256k1 JWK → 0x eth address
804
826
 
805
- // ECIES (encryptForEthPublicKey is used by the first-party Verification Client; consumers only decrypt)
827
+ // ECIES (Builder encrypts terminal results; consumers only decrypt)
806
828
  encryptForEthPublicKey(publicJwk, bytes): Promise<string> // → ECIES ciphertext (base64url)
807
829
  decryptCallback(decryptionKey, body): Promise<Jws> // ECIES/plaintext → JWS
808
830
 
809
- // Result JWS (eth ES256K)
810
- signResultJws(ethPrivHex, payload, issuer): Jws // VC-side
831
+ // Result JWS (Builder-managed ES256K)
811
832
  verifyResult(jws, opts): VerifyResultOutcome // offline signature/nonce check (throws if invalid)
812
- verifyResultFull(jws, opts): Promise<VerifyResultFullOutcome> // + JWKS discovery + exact legacy-proof verification (throws if invalid)
833
+ verifyResultFull(jws, opts): Promise<VerifyResultFullOutcome> // + JWKS discovery + exact proof verification (throws if invalid)
813
834
  verifyJwsSignature(jws, signerPublicKey), decodeJwsPayload(jws)
814
835
 
815
- // JWKS (signature-key discovery / publishing)
816
- fetchJwks(url), jwksFromPrivateKey(ethPrivHex), selectKey(...), jwkToUncompressedPublicKey(jwk)
836
+ // JWKS (signature-key discovery)
837
+ fetchJwks(url), selectKey(...), jwkToUncompressedPublicKey(jwk)
817
838
 
818
839
  // Legacy compatibility helper. verifyResultFull does not use an inner proof JWS.
819
840
  verifyNestedProof(...): NestedProofOutcome