@parity/product-sdk-terminal 0.0.0-dev.312.0 → 0.0.0-dev.454.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/dist/index.d.ts CHANGED
@@ -1,13 +1,50 @@
1
- import { T as TerminalAdapter } from './host-C3KVvc1j.js';
2
- export { A as AllocatableResource, a as ApAllocationOutcome, O as OnExistingAllowancePolicy, R as RequestResourceAllocationOptions, b as TerminalAdapterOptions, c as createTerminalAdapter, r as requestResourceAllocation, s as sendResourceAllocation } from './host-C3KVvc1j.js';
1
+ import { T as TerminalAdapter } from './host-_G9M0isV.js';
2
+ export { A as AllocatableResource, a as ApAllocationOutcome, O as OnExistingAllowancePolicy, R as RequestResourceAllocationOptions, b as TerminalAdapterOptions, c as createTerminalAdapter, i as isBenignTeardownError, r as requestResourceAllocation, s as sendResourceAllocation } from './host-_G9M0isV.js';
3
3
  import { UserSession } from '@novasamatech/host-papp';
4
- export { AllowanceError, AllowanceErrorReason, AllowanceService, HostMetadata, PairingStatus, PappAdapter, SS_PASEO_STABLE_STAGE_ENDPOINTS, SS_STABLE_STAGE_ENDPOINTS, SigningPayloadRequest, SigningPayloadResponse, SigningRawRequest, StoredUserSession, UserSession } from '@novasamatech/host-papp';
4
+ export { AllowanceError, AllowanceErrorReason, AllowanceService, HostMetadata, PairingStatus, PappAdapter, SS_STABLE_STAGE_ENDPOINTS, SigningPayloadRequest, SigningPayloadResponse, SigningRawRequest, StoredUserSession, UserSession } from '@novasamatech/host-papp';
5
5
  import { PolkadotSigner } from 'polkadot-api';
6
6
  export { AllowanceExpiredError, SignerError } from '@parity/product-sdk-signer/errors';
7
7
  import { StatementProver } from '@novasamatech/statement-store';
8
8
  import { StorageAdapter } from '@novasamatech/storage-adapter';
9
9
  import 'neverthrow';
10
10
 
11
+ /**
12
+ * Statement store networks terminal can pair against, keyed as `BULLETIN_RPCS`
13
+ * in `@parity/product-sdk-host`, where `paseo` is Paseo Next v2.
14
+ *
15
+ * Both sides of a pairing must sit on the same people chain. The Polkadot app's
16
+ * nightly build uses `paseo`, which is why it is the default.
17
+ */
18
+
19
+ declare const StatementStoreNetworks: {
20
+ paseo: string[];
21
+ previewnet: string[];
22
+ };
23
+
24
+ /** Network keys with built-in endpoints in {@link StatementStoreNetworks}. */
25
+ type StatementStoreEnvironment = keyof typeof StatementStoreNetworks;
26
+
27
+ /**
28
+ * The product-subtree public key of `//product//{productId}` (RFC-0022).
29
+ *
30
+ * The request is consent-free and cacheable. The disk layer is what makes that
31
+ * worth having: without it every CLI command would need the phone reachable
32
+ * just to render a product address.
33
+ */
34
+
35
+ interface ProductSubtreeOptions {
36
+ /** Names the cache file. Defaults to the productId. */
37
+ appId?: string;
38
+ storageDir?: string;
39
+ }
40
+ /**
41
+ * Fetch the subtree public key for `productId`, reaching the wallet only on a
42
+ * cold cache.
43
+ *
44
+ * @throws Error when the wallet rejects the request or returns a malformed key.
45
+ */
46
+ declare function getProductSubtreePublicKey(session: UserSession, productId: string, options?: ProductSubtreeOptions): Promise<Uint8Array>;
47
+
11
48
  /**
12
49
  * Create a PolkadotSigner from a QR-paired session.
13
50
  *
@@ -37,10 +74,10 @@ import 'neverthrow';
37
74
  * const [session] = adapter.sessions.sessions.read();
38
75
  *
39
76
  * // Default account — uses [adapter.appId, 0]:
40
- * const signer = createSessionSigner(session, adapter);
77
+ * const signer = await createSessionSigner(session, adapter);
41
78
  *
42
79
  * // Non-default derivation index, or a different productId:
43
- * const subSigner = createSessionSignerForAccount(session, {
80
+ * const subSigner = await createSessionSignerForAccount(session, {
44
81
  * productId: "my-product",
45
82
  * derivationIndex: 3,
46
83
  * });
@@ -52,7 +89,7 @@ import 'neverthrow';
52
89
  /**
53
90
  * Identifies which sub-account of a paired session should sign.
54
91
  *
55
- * Mirrors the `host-papp` wire format `productAccountId: [productId, derivationIndex]`:
92
+ * Mirrors the `host-papp` wire format `productAccountId: [productId, index]`:
56
93
  * `productId` is the dotNS-style identifier for the requesting product (matches
57
94
  * the adapter's `appId` in normal usage); `derivationIndex` is the BIP32-style
58
95
  * child-key index, where `0` is the session's default account.
@@ -66,46 +103,41 @@ interface ProductAccountRef {
66
103
  * The **product account's** sr25519 public key (32 bytes), as derived by the
67
104
  * host for `[productId, derivationIndex]`.
68
105
  *
69
- * PAPI stamps this into the extrinsic's signer address and verifies the
70
- * signature against it, so it must be the *product* account's key — not the
71
- * wallet's selected/root account (`session.remoteAccount.accountId`). A
72
- * mismatch produces an invalid signature.
106
+ * PAPI stamps this into the extrinsic's signer address and verifies against
107
+ * it, so a mismatch produces an invalid signature.
73
108
  *
74
- * When omitted, the signer soft-derives it from the session's root account
75
- * (`mnemonic + "/product/{productId}/{derivationIndex}"`), which is correct
76
- * for every product account. Supply it explicitly only to avoid the
77
- * re-derivation or when you've already derived it elsewhere.
109
+ * When omitted, the signer fetches the subtree key and derives it. Supply it
110
+ * to skip that round trip.
78
111
  */
79
112
  publicKey?: Uint8Array;
80
113
  }
114
+ interface SessionSignerOptions extends ProductSubtreeOptions {
115
+ /**
116
+ * Extension version of the supplied signTx bytes. Does not change encoding.
117
+ *
118
+ * Defaults to the lowest pipeline version the chain metadata declares that
119
+ * holds every supplied extension, which is 0 on a chain with one pipeline.
120
+ */
121
+ txExtVersion?: number;
122
+ }
81
123
  declare const INCOMPLETE_SESSION_MESSAGE = "Stored login session is missing the root account public key. Run \"logout\" and then \"login\" to pair again.";
82
124
  /**
83
- * The session's handshake-time root account public key (`rootUserAccountId` =
84
- * the user's bare-mnemonic keypair on current mobile builds). This is the
85
- * parent key product accounts soft-derive from. host-papp's live `UserSession`
86
- * doesn't surface it on the public type, so we read it structurally.
125
+ * The wallet's own root account key. Product accounts do **not** derive from it:
126
+ * RFC-0022 puts two hard junctions in between.
87
127
  *
88
128
  * @throws {@link INCOMPLETE_SESSION_MESSAGE} if the session predates the
89
- * `rootAccountId` field (a stale login that must re-pair).
129
+ * `rootAccountId` field.
90
130
  */
91
131
  declare function sessionRootPublicKey(session: UserSession): Uint8Array;
92
132
  /**
93
- * Soft-derive a product account's sr25519 public key from a paired session's
94
- * root account.
95
- *
96
- * This is the single source of truth for product-account math. The session
97
- * signer uses it to stamp the correct signer address; consumers that need the
98
- * product address *without* building a signer (e.g. a login-status display
99
- * triple) call it directly so the displayed address can't desync from what the
100
- * signer actually signs with.
133
+ * The product account's sr25519 public key for `ref`.
101
134
  *
102
- * sr25519 soft derivation is composable on public keys alone, so deriving from
103
- * `session.rootAccountId` locally produces the SAME key the host derives
104
- * privately via `mnemonic + "/product/{productId}/{derivationIndex}"`.
135
+ * The one place product-account math happens, so a displayed address cannot
136
+ * desync from what the signer signs with.
105
137
  *
106
- * @throws {@link INCOMPLETE_SESSION_MESSAGE} on a stale session with no root key.
138
+ * @throws Error when the wallet is unreachable on a cold cache.
107
139
  */
108
- declare function deriveProductPublicKey(session: UserSession, ref: ProductAccountRef): Uint8Array;
140
+ declare function deriveProductPublicKey(session: UserSession, ref: ProductAccountRef, options?: ProductSubtreeOptions): Promise<Uint8Array>;
109
141
  /**
110
142
  * Create a `PolkadotSigner` backed by a QR-paired mobile wallet session,
111
143
  * using the session's **default account** (`derivationIndex: 0`).
@@ -116,10 +148,10 @@ declare function deriveProductPublicKey(session: UserSession, ref: ProductAccoun
116
148
  * @param adapter The {@link TerminalAdapter} that loaded the session. Its `appId`
117
149
  * is used as the `productId` in the wire request.
118
150
  * @param publicKey The product account's sr25519 public key for
119
- * `[adapter.appId, 0]`. Optional — when omitted it's soft-derived from the
120
- * session root. See {@link ProductAccountRef.publicKey}.
151
+ * `[adapter.appId, 0]`. Optional — when omitted it's fetched and derived.
152
+ * See {@link ProductAccountRef.publicKey}.
121
153
  */
122
- declare function createSessionSigner(session: UserSession, adapter: TerminalAdapter, publicKey?: Uint8Array): PolkadotSigner;
154
+ declare function createSessionSigner(session: UserSession, adapter: TerminalAdapter, publicKey?: Uint8Array, options?: SessionSignerOptions): Promise<PolkadotSigner>;
123
155
  /**
124
156
  * Create a `PolkadotSigner` for a specific sub-account of a paired session.
125
157
  *
@@ -130,7 +162,7 @@ declare function createSessionSigner(session: UserSession, adapter: TerminalAdap
130
162
  * @param session The paired user session.
131
163
  * @param ref The product account to sign as: `{ productId, derivationIndex }`.
132
164
  */
133
- declare function createSessionSignerForAccount(session: UserSession, ref: ProductAccountRef): PolkadotSigner;
165
+ declare function createSessionSignerForAccount(session: UserSession, ref: ProductAccountRef, options?: SessionSignerOptions): Promise<PolkadotSigner>;
134
166
 
135
167
  /**
136
168
  * Get a `PolkadotSigner` for a Bulletin allowance slot.
@@ -223,6 +255,99 @@ declare function hasStatementStoreAllowance(adapter: TerminalAdapter, productId:
223
255
  */
224
256
  declare function waitForSessions(adapter: TerminalAdapter, timeoutMs?: number): Promise<UserSession[]>;
225
257
 
258
+ /**
259
+ * Client-side RFC-0007 product-entropy derivation for terminal (QR/SSO) sessions.
260
+ *
261
+ * Specified normatively by host-spec §C.8 "RFC-0007 product entropy"
262
+ * ({@link https://github.com/paritytech/host-spec/blob/adb3989208ae1c2107dbf0159611353e6989422c/spec/C-account-derivation.md?plain=1#L129-L147 | spec/C-account-derivation.md}):
263
+ *
264
+ * layer1 = blake2b256_keyed(key = utf8("product-entropy-derivation"), msg = rootAccountSecret)
265
+ * layer2 = blake2b256_keyed(key = blake2b256(utf8(productId)), msg = layer1)
266
+ * result = blake2b256_keyed(key = callerKey, msg = layer2)
267
+ *
268
+ * The paired {@link UserSession} already carries layer 1 as `rootEntropySource`,
269
+ * so this module computes only layers 2 and 3 — no host round-trip. The output
270
+ * matches what an in-container app gets from `@parity/product-sdk-host`'s
271
+ * `deriveEntropy` for the same wallet + product + key, so entropy-derived keys
272
+ * interoperate across web (in-container) and terminal (QR/SSO) clients.
273
+ *
274
+ * Layer 1's input is the raw BIP-39 entropy of the root account — 16 or 32
275
+ * bytes, *not* the 64-byte PBKDF2 seed. Terminal never sees it: the host applies
276
+ * layer 1 during the SSO handshake and sends only the 32-byte result.
277
+ *
278
+ * The scheme has parallel implementations in five languages — host-container
279
+ * (TS), host-encoding (Rust, canonical), sdk-swift, sdk-android and
280
+ * brevity-flutter (Dart) — so the shared artifact is the spec rather than a
281
+ * package: Swift, Kotlin and Dart cannot import a TS one. The safety net is the
282
+ * cross-language conformance suite, which the tests below pin against verbatim.
283
+ * Cite the spec, not any one implementation: mirrors drift.
284
+ *
285
+ * A follow-up lifts the two halves into their canonical homes without changing
286
+ * behaviour: `blake2b256Keyed` into `@parity/product-sdk-crypto` (the missing
287
+ * sibling of the `blake2b256` already there) and the pure derivation into
288
+ * `@parity/product-sdk-keys`, beside `product-account.ts`. The session wrapper
289
+ * below stays exactly as it is.
290
+ *
291
+ * @module
292
+ */
293
+
294
+ /**
295
+ * Derive 32 bytes of deterministic entropy for a terminal session, scoped to the
296
+ * calling product and a caller key (RFC-0007 layers 2 + 3).
297
+ *
298
+ * Same wallet + product + key ⇒ same bytes; any difference ⇒ uncorrelated
299
+ * entropy. Because it derives from the wallet (not the device), the entropy is
300
+ * recreatable after device loss as long as the wallet is recoverable.
301
+ *
302
+ * @param session - A QR-paired {@link UserSession}. Must carry
303
+ * `rootEntropySource` (present since host-papp 0.8.6 / RFC-0007).
304
+ * @param productId - The calling product's identifier, used verbatim as the
305
+ * layer-2 scope. Per host-spec §C.7 that is `<label>.dot` for a dotNS product
306
+ * (e.g. `"my-app.dot"`), or `localhost:<port>` — no `.dot` suffix — for local
307
+ * dev. It is case-sensitive and is **not** normalized here: `"My-App.dot"`,
308
+ * `"my-app.dot "` and `"my-app.dot"` derive three uncorrelated entropies.
309
+ *
310
+ * This must be the identifier the *in-container* deployment is served under —
311
+ * the same value you pass as `productId` to `getBulletinSigner` and as
312
+ * `options.productId` to `requestResourceAllocation` — because that is the
313
+ * scope the host derives against. It is **not** `adapter.appId`: the adapter
314
+ * id is a local namespace (cache keys, storage dir) and the two are
315
+ * routinely different. The package's own example pairs
316
+ * `createTerminalAdapter({ appId: "my-cli" })` with
317
+ * `getBulletinSigner(adapter, "my-cli.dot")`. Note that
318
+ * `requestResourceAllocation` falls back to `adapter.appId` when
319
+ * `options.productId` is omitted, so a call relying on that default is *not*
320
+ * a reliable reference point for this argument.
321
+ *
322
+ * The host takes the scope from the iframe's deployment label rather than
323
+ * from the caller, so it differs per deployment: production, each PR preview
324
+ * and local dev all scope to different entropy. A CLI that hardcodes the
325
+ * production id derives valid-but-different bytes against a preview, with no
326
+ * error anywhere.
327
+ *
328
+ * Required and positional **by design** — do not give this a default. Unlike
329
+ * an allocation request, a wrong product scope here fails silently: it yields
330
+ * well-formed entropy that simply cannot decrypt anything.
331
+ * @param key - Caller key, 1..32 bytes (the layer-3 BLAKE2b key). Both
332
+ * bounds are normative: the spec requires zero-length and >32-byte keys to be
333
+ * rejected, because some BLAKE2b implementations silently degrade an empty MAC
334
+ * key to unkeyed mode.
335
+ * @returns 32 bytes of derived entropy.
336
+ * @throws if the session lacks `rootEntropySource` or carries one of the wrong
337
+ * width, if `productId` is not a non-empty string, or if `key` is not 1..32
338
+ * bytes.
339
+ *
340
+ * @remarks
341
+ * This reads nothing account-identifying, and a terminal may hold several paired
342
+ * sessions (see `waitForSessions`). Passing the wrong one yields valid entropy
343
+ * for a *different* wallet: the intended user cannot decrypt, and the other
344
+ * wallet's holder can. Pin the identity with `sessionRootPublicKey` before
345
+ * deriving long-lived keys.
346
+ *
347
+ * The return value is raw key material — do not log it or persist it unwrapped.
348
+ */
349
+ declare function deriveEntropy(session: UserSession, productId: string, key: Uint8Array): Uint8Array;
350
+
226
351
  /**
227
352
  * Terminal QR code rendering using Unicode half-block characters.
228
353
  *
@@ -259,4 +384,4 @@ declare function renderQrCode(data: string, options?: QrRenderOptions): Promise<
259
384
  */
260
385
  declare function createNodeStorageAdapter(appId: string, storageDir?: string): StorageAdapter;
261
386
 
262
- export { INCOMPLETE_SESSION_MESSAGE, type ProductAccountRef, type QrRenderOptions, TerminalAdapter, createNodeStorageAdapter, createSessionSigner, createSessionSignerForAccount, deriveProductPublicKey, getBulletinSigner, getStatementStoreProver, hasBulletinAllowance, hasStatementStoreAllowance, renderQrCode, sessionRootPublicKey, waitForSessions };
387
+ export { INCOMPLETE_SESSION_MESSAGE, type ProductAccountRef, type ProductSubtreeOptions, type QrRenderOptions, type SessionSignerOptions, type StatementStoreEnvironment, StatementStoreNetworks, TerminalAdapter, createNodeStorageAdapter, createSessionSigner, createSessionSignerForAccount, deriveEntropy, deriveProductPublicKey, getBulletinSigner, getProductSubtreePublicKey, getStatementStoreProver, hasBulletinAllowance, hasStatementStoreAllowance, renderQrCode, sessionRootPublicKey, waitForSessions };