@provablehq/veil-aleo-sdk 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,27 +31,37 @@ const aleo = await loadNetwork('testnet')
31
31
  // A record scanner so the wallet client can find the private records that
32
32
  // program calls spend. The first requestRecords registers the view key with the
33
33
  // service (one network round-trip); later calls reuse it.
34
- const scanner = aleo.createRemoteScanner({
35
- url: 'https://api.provable.com/scanner',
36
- consumerId: CONSUMER_ID,
37
- apiKey: DPS_API_KEY, // authenticates + registers the view key for scanning
38
- })
34
+ // `url` defaults to Provable's hosted scanner, so this needs no arguments.
35
+ const scanner = aleo.createRemoteScanner()
39
36
 
40
37
  // A fully-wired client pair: an account from the private key, a public client
41
- // for reads, and a wallet client with proving + the scanner attached.
38
+ // for reads, and a wallet client with proving + the scanner attached. The
39
+ // credential store holds one Provable API session shared by proving and
40
+ // scanning — it registers a consumer on the first run and reuses it after.
41
+ import { fileCredentialStore } from '@provablehq/veil-aleo-sdk/node'
42
+
42
43
  const { publicClient, walletClient, account } = aleo.createAleoClient({
43
44
  privateKey: PRIVATE_KEY,
44
45
  networkUrl: 'https://api.provable.com/v2',
45
- provingMode: 'delegated',
46
- proverUrl: 'https://api.provable.com/prove/testnet',
47
- apiKey: DPS_API_KEY,
48
- consumerId: CONSUMER_ID,
46
+ credentialStore: fileCredentialStore('./.provable-credentials.json'),
49
47
  records: scanner,
50
48
  })
51
49
 
52
50
  account.address // 'aleo1...'
53
51
  ```
54
52
 
53
+ No API key appears above: the store registers a Provable API consumer the first
54
+ time something needs one and reuses it from then on. Already hold credentials?
55
+ Pass `consumerId` and `apiKey` instead and drop the store — see
56
+ [Provable API credentials](#provable-api-credentials).
57
+
58
+ `proverUrl` is a base URL — the active network is appended, the same way the
59
+ record scanner's `url` works — so `switchChain` re-targets proving instead of
60
+ leaving it on the network the client started from. Do not include the network
61
+ segment yourself. It defaults to Provable's hosted prover
62
+ (`DEFAULT_PROVER_URL`) under delegated proving, so the option only needs setting
63
+ for a self-hosted one.
64
+
55
65
  Pass `provingMode: 'local'` to prove in-process instead of delegating to a prover
56
66
  service (drop `proverUrl`/`apiKey`/`consumerId`). The `walletClient` composes with
57
67
  action packages the same way a wallet-backed client does:
@@ -60,10 +70,137 @@ action packages the same way a wallet-backed client does:
60
70
  import { shieldSwapActions } from '@provablehq/shield-swap-sdk'
61
71
 
62
72
  const client = walletClient.extend(
63
- shieldSwapActions({ api: { baseUrl: 'https://amm-api.dev.provable.com' } }),
73
+ shieldSwapActions({ api: {} }),
64
74
  )
65
75
  ```
66
76
 
77
+ ## Provable API credentials
78
+
79
+ Delegated proving and the hosted record scanner both authenticate with a consumer
80
+ id and API key, which the SDK exchanges for short-lived JWTs. A client builds a
81
+ single session covering both services, so one credential mints one token instead
82
+ of each service minting its own.
83
+
84
+ Where that credential comes from is the only decision. Three options:
85
+
86
+ | | Use when | Registers? |
87
+ | --- | --- | --- |
88
+ | `fileCredentialStore(path)` from `/node` | Bots, scripts, servers, CI that can write to disk | On first run, then reuses |
89
+ | `consumerId` + `apiKey` | You already hold credentials — from a secret manager or env | Never |
90
+ | `memoryCredentialStore()` (the default) | Tests, ephemeral workers | Every process, key discarded at exit |
91
+
92
+ `memoryCredentialStore()` is what a client falls back to when given neither, so
93
+ delegated proving works with no configuration at all. It is only appropriate for
94
+ a single short run: the API issues each key exactly once, so a process that
95
+ registers into memory and runs again registers a second consumer nobody can
96
+ reclaim. Anything long-lived wants a persistent store.
97
+
98
+ The session resolves on the first prove or scan. `authenticateProvableApi()` does
99
+ it eagerly, which is worth doing at startup so a bad key fails before you have
100
+ built a transaction:
101
+
102
+ ```ts
103
+ const { credentials, expiration, registered, applied } =
104
+ await walletClient.authenticateProvableApi()
105
+
106
+ applied // { proving: true, recordScanning: true }
107
+ ```
108
+
109
+ `applied` reports which paths the session actually reaches. `recordScanning` is
110
+ `false` when the client was given a record provider it cannot share a session
111
+ with — any implementation other than the ones this package builds — in which case
112
+ that provider keeps using whatever credentials it was constructed with.
113
+
114
+ If you do not have credentials yet, register a consumer once. The API key is
115
+ issued exactly once and cannot be recovered, so persist it immediately:
116
+
117
+ ```ts
118
+ import { registerProvableApi } from '@provablehq/veil-aleo-sdk'
119
+
120
+ const credentials = await registerProvableApi({ username: 'my-bot-42' })
121
+ await writeFile('creds.json', JSON.stringify(credentials), { mode: 0o600 })
122
+ ```
123
+
124
+ A username is spent once. It is globally unique, the API exposes no endpoint that
125
+ reads a consumer back, and a duplicate registration answers 409 with nothing
126
+ usable in it — so a taken name cannot be traded for the credentials it belongs to.
127
+ The stored key is the only copy, which is the real reason to give a client a
128
+ persistent store rather than the in-memory default.
129
+
130
+ When a client registers for you, `username` chooses the name:
131
+
132
+ ```ts
133
+ const { walletClient } = aleo.createAleoClient({
134
+ privateKey: PRIVATE_KEY,
135
+ networkUrl: 'https://api.provable.com/v2',
136
+ proverUrl: 'https://api.provable.com/prove',
137
+ credentialStore: fileCredentialStore('./.provable-credentials.json'),
138
+ username: 'my-bot-42', // or () => `bot-${shard}`, resolved at registration
139
+ })
140
+ ```
141
+
142
+ Supplied names are used verbatim, so the consumer is identifiable in your account
143
+ — and a collision fails with an error saying the name is taken rather than quietly
144
+ registering something else. Omit it and the name is derived from the account
145
+ address with a random suffix, which keeps the zero-configuration path working:
146
+ since a username cannot be reused, an account that lost its stored key still needs
147
+ to be able to register.
148
+
149
+ On Node, `fileCredentialStore` covers this. It writes with mode `0600`, treats a
150
+ missing file as "not registered yet", and reports a corrupt one rather than
151
+ registering over credentials that might still be recoverable by hand. It lives on
152
+ the `/node` subpath so the `node:fs` import never reaches a browser bundle:
153
+
154
+ ```ts
155
+ import { fileCredentialStore } from '@provablehq/veil-aleo-sdk/node'
156
+
157
+ const { walletClient } = aleo.createAleoClient({
158
+ privateKey: PRIVATE_KEY,
159
+ networkUrl: 'https://api.provable.com/v2',
160
+ proverUrl: 'https://api.provable.com/prove',
161
+ credentialStore: fileCredentialStore('./.provable-credentials.json'),
162
+ records: scanner,
163
+ })
164
+
165
+ const { registered } = await walletClient.authenticateProvableApi()
166
+ registered // true on the first run, false afterward
167
+ ```
168
+
169
+ Anywhere else, implement the two-method interface yourself — a keychain,
170
+ `localStorage`, IndexedDB, or a secret manager all satisfy it, and the SDK assumes
171
+ nothing about which:
172
+
173
+ ```ts
174
+ import type { ProvableCredentialStore } from '@provablehq/veil-aleo-sdk'
175
+
176
+ const credentialStore: ProvableCredentialStore = {
177
+ load: () => {
178
+ const raw = localStorage.getItem('provable-credentials')
179
+ return raw ? JSON.parse(raw) : undefined // undefined → register
180
+ },
181
+ save: (c) => localStorage.setItem('provable-credentials', JSON.stringify(c)),
182
+ }
183
+ ```
184
+
185
+ Two rules for a hand-written store. `load` MUST return `undefined` rather than
186
+ throw when nothing is stored, or resolution fails instead of registering. And
187
+ `save` must actually persist: it runs before the credentials are handed back
188
+ precisely so a failed write fails the call, since a swallowed one orphans a
189
+ consumer whose key cannot be reissued. That also means a genuinely read-only
190
+ environment should supply `consumerId`/`apiKey` directly rather than rely on
191
+ registration.
192
+
193
+ An explicit `consumerId`/`apiKey` pair takes precedence over the store, so an
194
+ operator can inject a rotated key or CI credentials without clearing persisted
195
+ state first.
196
+
197
+ One caveat on the mint itself: the API does not validate the consumer id against
198
+ the API key. A mismatched id still yields a working token, because the token's
199
+ issuer comes from the key. The mismatch surfaces later as a rejection from the
200
+ service you call — the record scanner reports it as `No credentials found for
201
+ given 'iss'`. Nothing in the mint path can catch that for you, so keep the pair
202
+ together.
203
+
67
204
  The handle also exposes the pieces individually when the caller does not want the
68
205
  full pair:
69
206
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { loadNetwork as loadNetwork$1 } from '@provablehq/sdk/dynamic.js';
2
2
  export { DEVNODE_ADDR, DEVNODE_PRIVATE_KEY } from '@provablehq/veil-aleo-devnode';
3
- import { LocalAccount, ProvingConfig, RecordProvider, StandaloneRecordScanner, PublicClient, WalletClient } from '@provablehq/veil-core';
3
+ import { LocalAccount, RecordProvider, StandaloneRecordScanner, PublicClient, WalletClient } from '@provablehq/veil-core';
4
+ import { P as ProvableSession, a as ProvingConfigWithSession, b as ProvableCredentialStore, c as ProvableWalletClient } from './provableApi-DsStWMOJ.js';
5
+ export { A as AuthenticateProvableApiParameters, d as AuthenticateProvableApiReturnType, C as CreateProvableSessionOptions, e as ProvableApiActions, f as ProvableApiCredentials, g as ProvableJwt, h as ProvableSessionConsumers, R as RegisterProvableApiParameters, i as authenticateProvableApi, j as createProvableSession, m as memoryCredentialStore, p as provableApiActions, r as registerProvableApi } from './provableApi-DsStWMOJ.js';
4
6
 
5
7
  /**
6
8
  * Names the derivation-path convention used to turn a seed into Aleo keys.
@@ -162,6 +164,20 @@ declare function mnemonicToHDKey(mnemonic: string, options?: {
162
164
 
163
165
  /** Networks supported by `@provablehq/sdk/dynamic.js`. */
164
166
  type SupportedNetwork = 'mainnet' | 'testnet';
167
+ /**
168
+ * Base URL of Provable's hosted delegated proving service.
169
+ *
170
+ * The default `proverUrl` for `mode: 'delegated'`. A base, so the active network
171
+ * is appended — which is what lets `switchChain` re-target proving.
172
+ */
173
+ declare const DEFAULT_PROVER_URL = "https://api.provable.com/prove";
174
+ /**
175
+ * Base URL of Provable's hosted Record Scanner Service.
176
+ *
177
+ * The default `url` for both scanner factories. A base — the SDK appends the
178
+ * network segment, which is what lets a scanner follow `switchChain`.
179
+ */
180
+ declare const DEFAULT_SCANNER_URL = "https://api.provable.com/scanner";
165
181
  type SdkModule = Awaited<ReturnType<typeof loadNetwork$1<'testnet'>>>;
166
182
  /**
167
183
  * A network-bound SDK handle. All functions on this handle use the binary
@@ -222,7 +238,22 @@ interface AleoSdk {
222
238
  verifySignature(address: string, message: Uint8Array, signature: string): boolean;
223
239
  /** Creates an `AleoNetworkClient` for direct SDK access. */
224
240
  createNetworkClient(url: string): InstanceType<SdkModule['AleoNetworkClient']>;
225
- /** Creates a `ProvingConfig` for `createWalletClient({ proving })`. */
241
+ /**
242
+ * Creates a `ProvingConfig` for `createWalletClient({ proving })`.
243
+ *
244
+ * @param options.proverUrl Base URL of the delegated proving service — the
245
+ * network segment is appended, so do not include it. That is what lets
246
+ * `switchChain` re-target proving instead of leaving it on the network the
247
+ * client started from. A base that already ends in `/mainnet` or `/testnet`
248
+ * is re-targeted rather than doubled. Defaults to
249
+ * {@link DEFAULT_PROVER_URL} under `mode: 'delegated'`; unused under
250
+ * `mode: 'local'`, which reaches no prover.
251
+ * @param options.session Optional Provable API session. When present the
252
+ * configuration authenticates from it and withholds `apiKey`/`consumerId`
253
+ * from the prover client, so one party mints JWTs. The session is attached
254
+ * to the returned configuration, which is what lets
255
+ * `authenticateProvableApi` find it on a client.
256
+ */
226
257
  createProvingConfig(options: {
227
258
  mode: 'delegated' | 'local';
228
259
  networkUrl: string;
@@ -232,7 +263,8 @@ interface AleoSdk {
232
263
  account?: LocalAccount<'privateKey'>;
233
264
  confirmationTimeout?: number;
234
265
  useFeeMaster?: boolean;
235
- }): ProvingConfig;
266
+ session?: ProvableSession;
267
+ }): ProvingConfigWithSession;
236
268
  /**
237
269
  * Creates a record scanner backed by Provable's Record Scanner Service.
238
270
  *
@@ -245,41 +277,112 @@ interface AleoSdk {
245
277
  * against the new network and re-registers lazily on the next scan.
246
278
  *
247
279
  * @param options.url Base URL of the service (the SDK appends the network
248
- * segment — do not include it).
249
- * @param options.consumerId Consumer id used for JWT refresh.
280
+ * segment — do not include it). Defaults to {@link DEFAULT_SCANNER_URL}.
281
+ * @param options.consumerId Optional consumer id used for JWT refresh.
282
+ * Unnecessary when a `session` supplies the token. Required alongside
283
+ * `apiKey` otherwise — a JWT is minted from the pair, so half of it
284
+ * authenticates nothing and construction throws rather than 401ing later.
250
285
  * @param options.apiKey Optional API key for the authenticated service
251
286
  * (e.g. the hosted Provable RSS). Omit for an open/unauthenticated service.
287
+ * @param options.session Optional Provable API session to authenticate from,
288
+ * shared with delegated proving. `createAleoClient` supplies its own
289
+ * session through `setSession` on the returned provider, so a caller who
290
+ * passes the scanner to that factory does not need this.
252
291
  * @param options.startBlock Optional block height to begin scanning from at
253
292
  * registration. Defaults to 0 (full history).
293
+ * @returns The provider, plus `setSession` for a factory to share one session
294
+ * across proving and scanning after construction.
254
295
  */
255
- createRemoteScanner(options: {
256
- url: string;
257
- consumerId: string;
296
+ createRemoteScanner(options?: {
297
+ url?: string;
298
+ consumerId?: string;
258
299
  apiKey?: string;
300
+ session?: ProvableSession;
259
301
  startBlock?: number;
260
- }): RecordProvider;
302
+ }): RecordProvider & {
303
+ setSession: (session: ProvableSession) => void;
304
+ };
261
305
  /**
262
306
  * Creates a standalone record scanner with an explicit view key.
263
307
  *
264
308
  * Like {@link createRemoteScanner}, the first `requestRecords` registers the
265
309
  * view key with the service (a network round-trip) to obtain the scanning UUID.
266
310
  *
267
- * @param options.url Base URL of the service (the SDK appends the network segment).
268
- * @param options.consumerId Consumer id used for JWT refresh.
311
+ * @param options.url Base URL of the service (the SDK appends the network
312
+ * segment). Defaults to {@link DEFAULT_SCANNER_URL}.
313
+ * @param options.consumerId Optional consumer id used for JWT refresh.
314
+ * Unnecessary when a `session` supplies the token. Required alongside
315
+ * `apiKey` otherwise — a JWT is minted from the pair, so half of it
316
+ * authenticates nothing and construction throws rather than 401ing later.
269
317
  * @param options.viewKey The view key (`AViewKey1…`) to scan and decrypt with.
270
318
  * @param options.apiKey Optional API key for the authenticated service. Omit
271
319
  * for an open/unauthenticated service.
320
+ * @param options.session Optional Provable API session to authenticate from.
321
+ * Supplied at construction only — a standalone scanner is not pluggable
322
+ * into a wallet client, so nothing shares a session with it later.
272
323
  * @param options.startBlock Optional block height to begin scanning from at
273
324
  * registration. Defaults to 0 (full history).
274
325
  */
275
326
  createStandaloneScanner(options: {
276
- url: string;
277
- consumerId: string;
327
+ url?: string;
328
+ consumerId?: string;
278
329
  viewKey: string;
279
330
  apiKey?: string;
331
+ session?: ProvableSession;
280
332
  startBlock?: number;
281
333
  }): StandaloneRecordScanner;
282
- /** Creates a fully-wired Aleo client from a private key and network URL. */
334
+ /**
335
+ * Creates a fully-wired Aleo client from a private key and network URL.
336
+ *
337
+ * Builds one Provable API session from the credential options and shares it
338
+ * across delegated proving and record scanning, so a single
339
+ * `walletClient.authenticateProvableApi()` covers both. Omit the credential
340
+ * options for a client that authenticates neither.
341
+ *
342
+ * @param options.apiKey Optional Provable API key. Paired with `consumerId`,
343
+ * it seeds the session directly.
344
+ * @param options.consumerId Optional Provable API consumer id.
345
+ * @param options.proverUrl Base URL of the delegated proving service — the
346
+ * network segment is appended, so do not include it. That is what lets
347
+ * `switchChain` re-target proving. Defaults to {@link DEFAULT_PROVER_URL},
348
+ * since `provingMode` itself defaults to `'delegated'`; pass an override for
349
+ * a self-hosted prover.
350
+ * @param options.confirmationTimeout Milliseconds to wait for a submitted
351
+ * transaction to confirm. Defaults to 60_000 (one minute), which covers a
352
+ * healthy confirmation with room to spare; a transaction still absent after
353
+ * that is more often one the node never included than one about to land.
354
+ * Raise it for a congested network or a multi-transition call that takes
355
+ * longer to include, rather than treating a slow confirmation as a failure.
356
+ * @param options.username Optional handle to register a Provable API consumer
357
+ * under, used only when no credentials and no stored pair are available.
358
+ * A function is called lazily, at the moment registration happens. Defaults
359
+ * to a name derived from the account address plus a random suffix — the
360
+ * suffix matters because a username is spent once, so an account that lost
361
+ * its stored key must still be able to register. Supplying a fixed name
362
+ * makes the consumer identifiable but fails if that name is taken, since
363
+ * credentials cannot be recovered from a username.
364
+ * @param options.credentialStore Optional persistence for Provable API
365
+ * credentials. When neither `consumerId`/`apiKey` nor a stored pair is
366
+ * available, a consumer is registered under a name derived from the account
367
+ * address and saved here. Defaults to `memoryCredentialStore()`, which holds
368
+ * a registered consumer only for the life of the process — pass
369
+ * `fileCredentialStore` from `@provablehq/veil-aleo-sdk/node`, or any
370
+ * {@link ProvableCredentialStore}, for anything longer-lived. A client left
371
+ * fully unconfigured does not share its session with `records`, so a scanner
372
+ * aimed at an open service keeps needing no credential.
373
+ * @param options.session Optional pre-built session, for a caller that owns
374
+ * one already. Takes precedence over the credential options.
375
+ * @returns A public client, a wallet client carrying
376
+ * `authenticateProvableApi`, and the account.
377
+ *
378
+ * @example
379
+ * const scanner = aleo.createRemoteScanner({ url: SCANNER_URL })
380
+ * const { walletClient } = aleo.createAleoClient({
381
+ * privateKey, networkUrl, proverUrl, records: scanner, credentialStore: store,
382
+ * })
383
+ * const { credentials, registered } = await walletClient.authenticateProvableApi()
384
+ * if (registered) console.log('registered consumer', credentials.consumerId)
385
+ */
283
386
  createAleoClient(options: {
284
387
  privateKey: string;
285
388
  networkUrl: string;
@@ -288,16 +391,22 @@ interface AleoSdk {
288
391
  apiKey?: string;
289
392
  consumerId?: string;
290
393
  useFeeMaster?: boolean;
394
+ confirmationTimeout?: number;
395
+ username?: string | (() => string);
396
+ credentialStore?: ProvableCredentialStore;
397
+ session?: ProvableSession;
291
398
  /**
292
399
  * Record provider for `requestRecords`. Not wired by default — pass
293
400
  * `aleo.createRemoteScanner(...)` or any
294
401
  * custom `RecordProvider`. `requestRecords` throws with a setup hint
295
402
  * when no provider is configured.
296
403
  */
297
- records?: RecordProvider;
404
+ records?: RecordProvider & {
405
+ setSession?: (session: ProvableSession) => void;
406
+ };
298
407
  }): {
299
408
  publicClient: PublicClient;
300
- walletClient: WalletClient;
409
+ walletClient: ProvableWalletClient;
301
410
  account: LocalAccount<'privateKey'>;
302
411
  };
303
412
  }
@@ -345,4 +454,4 @@ declare function createDevnodeClient(options?: {
345
454
  account: LocalAccount<'privateKey'>;
346
455
  };
347
456
 
348
- export { type AleoDerivationId, type AleoSdk, BLS12377HDKey, LEGACY_PATH, STANDARD_PATH, type SupportedNetwork, createDevnodeClient, generateAccount, generateMnemonic, loadNetwork, mnemonicToHDKey, mnemonicToSeed, validateMnemonic, validateWord };
457
+ export { type AleoDerivationId, type AleoSdk, BLS12377HDKey, DEFAULT_PROVER_URL, DEFAULT_SCANNER_URL, LEGACY_PATH, ProvableCredentialStore, ProvableSession, ProvableWalletClient, ProvingConfigWithSession, STANDARD_PATH, type SupportedNetwork, createDevnodeClient, generateAccount, generateMnemonic, loadNetwork, mnemonicToHDKey, mnemonicToSeed, validateMnemonic, validateWord };