@waaskey/sdk 0.2.1 → 0.3.1

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.
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,142 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { createRequire } from 'module';
3
+ import { pathToFileURL } from 'url';
4
+ import { parentPort, workerData } from 'worker_threads';
5
+
6
+ // src/mpc/node/worker.ts
7
+ if (!parentPort) {
8
+ throw new Error("waaskey mpc worker must be spawned as a worker_threads Worker");
9
+ }
10
+ var port = parentPort;
11
+ function post(message) {
12
+ port.postMessage(message);
13
+ }
14
+ var CEREMONY_EXPORTS = ["keygen", "sign", "pregeneratePrimes", "reshareAssemble", "completeReshare", "keygenMember", "signMember", "keygenEddsa", "signEddsa"];
15
+ var nextWsId = 1;
16
+ var sockets = /* @__PURE__ */ new Map();
17
+ var BridgedWebSocket = class _BridgedWebSocket {
18
+ static CONNECTING = 0;
19
+ static OPEN = 1;
20
+ static CLOSING = 2;
21
+ static CLOSED = 3;
22
+ url;
23
+ readyState = _BridgedWebSocket.CONNECTING;
24
+ binaryType = "blob";
25
+ protocol = "";
26
+ onopen = null;
27
+ onmessage = null;
28
+ onclose = null;
29
+ onerror = null;
30
+ wsId;
31
+ listeners = /* @__PURE__ */ new Map();
32
+ constructor(url, protocols) {
33
+ this.url = String(url);
34
+ this.wsId = nextWsId++;
35
+ sockets.set(this.wsId, this);
36
+ const list = protocols === void 0 ? [] : Array.isArray(protocols) ? protocols : [protocols];
37
+ post({ type: "ws-open", wsId: this.wsId, url: this.url, protocols: list });
38
+ }
39
+ send(data) {
40
+ if (typeof data === "string") {
41
+ post({ type: "ws-send", wsId: this.wsId, data });
42
+ return;
43
+ }
44
+ const bytes = ArrayBuffer.isView(data) ? new Uint8Array(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength)) : new Uint8Array(data.slice(0));
45
+ post({ type: "ws-send", wsId: this.wsId, data: bytes });
46
+ }
47
+ close(code, reason) {
48
+ if (this.readyState === _BridgedWebSocket.CLOSING || this.readyState === _BridgedWebSocket.CLOSED) return;
49
+ this.readyState = _BridgedWebSocket.CLOSING;
50
+ post({ type: "ws-close", wsId: this.wsId, code, reason });
51
+ }
52
+ addEventListener(type, listener) {
53
+ let set = this.listeners.get(type);
54
+ if (!set) {
55
+ set = /* @__PURE__ */ new Set();
56
+ this.listeners.set(type, set);
57
+ }
58
+ set.add(listener);
59
+ }
60
+ removeEventListener(type, listener) {
61
+ this.listeners.get(type)?.delete(listener);
62
+ }
63
+ deliver(event) {
64
+ if (event.event === "open") {
65
+ this.readyState = _BridgedWebSocket.OPEN;
66
+ this.protocol = event.protocol;
67
+ this.dispatch("open", { type: "open", target: this });
68
+ return;
69
+ }
70
+ if (event.event === "message") {
71
+ const data = event.data instanceof Uint8Array ? toArrayBuffer(event.data) : event.data;
72
+ this.dispatch("message", { type: "message", data, target: this });
73
+ return;
74
+ }
75
+ if (event.event === "close") {
76
+ this.readyState = _BridgedWebSocket.CLOSED;
77
+ sockets.delete(this.wsId);
78
+ this.dispatch("close", { type: "close", code: event.code, reason: event.reason, wasClean: event.wasClean, target: this });
79
+ return;
80
+ }
81
+ this.dispatch("error", { type: "error", message: event.message, target: this });
82
+ }
83
+ dispatch(type, event) {
84
+ const handler = this[`on${type}`];
85
+ if (typeof handler === "function") handler.call(this, event);
86
+ for (const listener of this.listeners.get(type) ?? []) listener.call(this, event);
87
+ }
88
+ };
89
+ function toArrayBuffer(bytes) {
90
+ if (bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength && bytes.buffer instanceof ArrayBuffer) {
91
+ return bytes.buffer;
92
+ }
93
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
94
+ }
95
+ globalThis.WebSocket = BridgedWebSocket;
96
+ function resolveAsset(spec) {
97
+ for (const base of [import.meta.url, pathToFileURL(`${process.cwd()}/`).href]) {
98
+ try {
99
+ return createRequire(base).resolve(spec);
100
+ } catch {
101
+ }
102
+ }
103
+ return void 0;
104
+ }
105
+ var { wasmPackage } = workerData;
106
+ var moduleExports;
107
+ try {
108
+ const mod = await import(wasmPackage);
109
+ if (typeof mod.default === "function") {
110
+ const wasmPath = resolveAsset(`${wasmPackage}/client_wasm_bg.wasm`);
111
+ if (!wasmPath) {
112
+ throw new Error(`cannot resolve ${wasmPackage}/client_wasm_bg.wasm \u2014 is ${wasmPackage} installed?`);
113
+ }
114
+ await mod.default({ module_or_path: await WebAssembly.compile(await readFile(wasmPath)) });
115
+ }
116
+ moduleExports = mod;
117
+ post({ type: "ready", exports: CEREMONY_EXPORTS.filter((name) => typeof mod[name] === "function") });
118
+ } catch (err) {
119
+ post({ type: "fatal", error: err instanceof Error ? err.message : String(err) });
120
+ throw err;
121
+ }
122
+ port.on("message", (message) => {
123
+ if (message.type === "ws-event") {
124
+ sockets.get(message.wsId)?.deliver(message);
125
+ return;
126
+ }
127
+ const { id, method, paramsJson } = message;
128
+ void (async () => {
129
+ try {
130
+ const fn = moduleExports[method];
131
+ if (typeof fn !== "function") {
132
+ throw new Error(`client-wasm does not expose ${method}`);
133
+ }
134
+ const result = await fn(paramsJson);
135
+ post({ type: "call-result", id, ok: true, resultJson: JSON.stringify(result) });
136
+ } catch (err) {
137
+ post({ type: "call-result", id, ok: false, error: err instanceof Error ? err.message : String(err) });
138
+ }
139
+ })();
140
+ });
141
+ //# sourceMappingURL=node-mpc-worker.js.map
142
+ //# sourceMappingURL=node-mpc-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mpc/node/worker.ts"],"names":[],"mappings":"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","file":"node-mpc-worker.js","sourcesContent":["/**\n * The Node MPC worker (waas-sdk#82): loads the client-wasm module OFF the main thread so\n * the zk-proof computation (minutes of single-threaded WASM during `aux_info_gen`) cannot\n * starve the relay WebSocket. The wasm still believes it owns a `WebSocket` — this file\n * installs a bridged shim whose frames are forwarded over the thread boundary to the real\n * socket on the main thread (see `worker-core.ts`), which stays responsive and pings.\n *\n * Runs as a `worker_threads` ESM worker; `workerData.wasmPackage` names the wasm package\n * (or a file URL to a module with the same export shape, used by tests).\n */\nimport { readFile } from 'node:fs/promises';\nimport { createRequire } from 'node:module';\nimport { pathToFileURL } from 'node:url';\nimport { parentPort, workerData } from 'node:worker_threads';\n\nimport type { MainToWorker, WorkerToMain, WsEvent } from './bridge-protocol';\n\nif (!parentPort) {\n throw new Error('waaskey mpc worker must be spawned as a worker_threads Worker');\n}\nconst port = parentPort;\n\nfunction post(message: WorkerToMain): void {\n port.postMessage(message);\n}\n\n/** The `ClientWasmModule` surface worth reporting to the main thread. */\nconst CEREMONY_EXPORTS = ['keygen', 'sign', 'pregeneratePrimes', 'reshareAssemble', 'completeReshare', 'keygenMember', 'signMember', 'keygenEddsa', 'signEddsa'];\n\nlet nextWsId = 1;\nconst sockets = new Map<number, BridgedWebSocket>();\n\ntype Listener = (event: unknown) => void;\n\n/**\n * The `WebSocket` shim the wasm drives (via `ws_stream_wasm` / web_sys): same surface as\n * the DOM API — `on*` properties + `addEventListener`, `binaryType`, `readyState`,\n * `send`, `close` — but every frame crosses the thread boundary instead of a network\n * stack. Events queue on this worker's loop while the wasm computes and are delivered\n * when it yields, exactly as they would on a blocked single-thread setup; the difference\n * is the REAL socket on the main thread never stops reading or answering pings.\n */\nclass BridgedWebSocket {\n static readonly CONNECTING = 0;\n static readonly OPEN = 1;\n static readonly CLOSING = 2;\n static readonly CLOSED = 3;\n\n readonly url: string;\n readyState = BridgedWebSocket.CONNECTING;\n binaryType = 'blob';\n protocol = '';\n onopen: Listener | null = null;\n onmessage: Listener | null = null;\n onclose: Listener | null = null;\n onerror: Listener | null = null;\n\n private readonly wsId: number;\n private readonly listeners = new Map<string, Set<Listener>>();\n\n constructor(url: string | URL, protocols?: string | string[]) {\n this.url = String(url);\n this.wsId = nextWsId++;\n sockets.set(this.wsId, this);\n const list = protocols === undefined ? [] : Array.isArray(protocols) ? protocols : [protocols];\n post({ type: 'ws-open', wsId: this.wsId, url: this.url, protocols: list });\n }\n\n send(data: string | ArrayBuffer | ArrayBufferView): void {\n if (typeof data === 'string') {\n post({ type: 'ws-send', wsId: this.wsId, data });\n return;\n }\n const bytes = ArrayBuffer.isView(data) ? new Uint8Array(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer) : new Uint8Array(data.slice(0));\n post({ type: 'ws-send', wsId: this.wsId, data: bytes });\n }\n\n close(code?: number, reason?: string): void {\n if (this.readyState === BridgedWebSocket.CLOSING || this.readyState === BridgedWebSocket.CLOSED) return;\n this.readyState = BridgedWebSocket.CLOSING;\n post({ type: 'ws-close', wsId: this.wsId, code, reason });\n }\n\n addEventListener(type: string, listener: Listener): void {\n let set = this.listeners.get(type);\n if (!set) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n }\n\n removeEventListener(type: string, listener: Listener): void {\n this.listeners.get(type)?.delete(listener);\n }\n\n deliver(event: WsEvent): void {\n if (event.event === 'open') {\n this.readyState = BridgedWebSocket.OPEN;\n this.protocol = event.protocol;\n this.dispatch('open', { type: 'open', target: this });\n return;\n }\n if (event.event === 'message') {\n // Binary frames always surface as ArrayBuffer (there is no DOM Blob here; the wasm\n // sets binaryType='arraybuffer' anyway).\n const data: unknown = event.data instanceof Uint8Array ? toArrayBuffer(event.data) : event.data;\n this.dispatch('message', { type: 'message', data, target: this });\n return;\n }\n if (event.event === 'close') {\n this.readyState = BridgedWebSocket.CLOSED;\n sockets.delete(this.wsId);\n this.dispatch('close', { type: 'close', code: event.code, reason: event.reason, wasClean: event.wasClean, target: this });\n return;\n }\n this.dispatch('error', { type: 'error', message: event.message, target: this });\n }\n\n private dispatch(type: 'open' | 'message' | 'close' | 'error', event: unknown): void {\n const handler = this[`on${type}`];\n if (typeof handler === 'function') handler.call(this, event);\n for (const listener of this.listeners.get(type) ?? []) listener.call(this, event);\n }\n}\n\n/** Re-view a (structured-clone-owned) Uint8Array as a standalone ArrayBuffer. */\nfunction toArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n if (bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength && bytes.buffer instanceof ArrayBuffer) {\n return bytes.buffer;\n }\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n}\n\n// The wasm (and any test fixture) resolves `WebSocket` from the global scope — install\n// the shim BEFORE the module loads so every ceremony socket is bridged.\n(globalThis as Record<string, unknown>).WebSocket = BridgedWebSocket;\n\n/** Resolve a package subpath from this worker's location, falling back to the app cwd. */\nfunction resolveAsset(spec: string): string | undefined {\n for (const base of [import.meta.url, pathToFileURL(`${process.cwd()}/`).href]) {\n try {\n return createRequire(base).resolve(spec);\n } catch {\n // try the next base\n }\n }\n return undefined;\n}\n\ntype WasmExport = (paramsJson: string) => unknown;\n\nconst { wasmPackage } = workerData as { wasmPackage: string };\n\nlet moduleExports: Record<string, WasmExport>;\ntry {\n const mod = (await import(wasmPackage)) as Record<string, unknown>;\n // wasm-pack `--target web` modules need explicit init with the engine bytes (Node's\n // fetch cannot read file paths). Test fixtures are plain JS modules without a default init.\n if (typeof mod.default === 'function') {\n const wasmPath = resolveAsset(`${wasmPackage}/client_wasm_bg.wasm`);\n if (!wasmPath) {\n throw new Error(`cannot resolve ${wasmPackage}/client_wasm_bg.wasm — is ${wasmPackage} installed?`);\n }\n await (mod.default as (init: unknown) => Promise<unknown>)({ module_or_path: await WebAssembly.compile(await readFile(wasmPath)) });\n }\n moduleExports = mod as Record<string, WasmExport>;\n post({ type: 'ready', exports: CEREMONY_EXPORTS.filter((name) => typeof mod[name] === 'function') });\n} catch (err) {\n post({ type: 'fatal', error: err instanceof Error ? err.message : String(err) });\n throw err;\n}\n\nport.on('message', (message: MainToWorker) => {\n if (message.type === 'ws-event') {\n sockets.get(message.wsId)?.deliver(message);\n return;\n }\n const { id, method, paramsJson } = message;\n void (async () => {\n try {\n const fn = moduleExports[method];\n if (typeof fn !== 'function') {\n throw new Error(`client-wasm does not expose ${method}`);\n }\n const result = await fn(paramsJson);\n post({ type: 'call-result', id, ok: true, resultJson: JSON.stringify(result) });\n } catch (err) {\n post({ type: 'call-result', id, ok: false, error: err instanceof Error ? err.message : String(err) });\n }\n })();\n});\n"]}
package/dist/node.d.ts ADDED
@@ -0,0 +1,426 @@
1
+ /**
2
+ * Device-party MPC contract.
3
+ *
4
+ * Waaskey wallets are 2-of-3: the user's **device** holds one key share and runs
5
+ * its half of the cggmp24 ceremony locally (in WASM), talking to the Waaskey
6
+ * `signer` (the server party) over the relay. The private key never exists whole
7
+ * anywhere. This is the device side of that ceremony.
8
+ *
9
+ * The wire shapes mirror the waas-core `client-wasm` exports (`keygen` / `sign`),
10
+ * which mirror the server `party-runner`. The backend tells the SDK the per-party
11
+ * routing (relay url, session id, indices, roles) in the create-wallet response.
12
+ */
13
+ /** Curves the device party can run a ceremony on. */
14
+ type MpcCurve = 'secp256k1';
15
+ /** Relay routing + party identity shared by keygen and sign. */
16
+ interface CeremonyParams {
17
+ curve: MpcCurve;
18
+ /** Relay websocket URL both parties connect to. */
19
+ relayUrl: string;
20
+ /** Ceremony id shared by all parties; derives the execution ids. */
21
+ sessionId: string;
22
+ /** This party's relay routing id. Defaults to `"device"`. */
23
+ role?: string;
24
+ /** The peer (server) party's relay routing id. Defaults to `"server"`. */
25
+ peerRole?: string;
26
+ /** This party's keygen index. */
27
+ partyIndex: number;
28
+ /** The peer (server) party's keygen index. */
29
+ peerPartyIndex: number;
30
+ /**
31
+ * Short-lived relay token (JWT) the device presents to the relay to join this session as
32
+ * `role`. Returned by the backend on the ceremony / sign-session response; present only when
33
+ * relay authentication is enabled. Absent ⇒ the relay accepts an unauthenticated join.
34
+ */
35
+ relayToken?: string;
36
+ }
37
+ /** Parameters for the device half of a keygen ceremony. */
38
+ interface DeviceKeygenParams extends CeremonyParams {
39
+ /** Total parties `n`. */
40
+ parties: number;
41
+ /** Signing threshold `t` (`2 <= t <= n`). */
42
+ threshold: number;
43
+ /**
44
+ * Pre-generated Paillier safe-primes (JSON) for this device, produced ahead of time off the
45
+ * hot path (see {@link MpcCore.pregeneratePrimes} / `PrimePool`). When omitted the core
46
+ * generates them inline — correct, but slow in single-threaded browser WASM. The primes are
47
+ * this device's private aux material and are NEVER sent to the server.
48
+ */
49
+ pregeneratedPrimes?: string;
50
+ /**
51
+ * The FULL party roster (`roles[i]` = relay role of protocol index `i`), when the backend
52
+ * provides it (waas-core#131). Required to route a `parties > 2` ceremony — the plain 2-party
53
+ * transport cannot attribute messages from more than one peer.
54
+ */
55
+ roles?: string[];
56
+ }
57
+ /** Result of the device half of keygen — the device's share never leaves the device. */
58
+ interface DeviceKeygenResult {
59
+ /** The device's KeyShare (JSON). Seal + store on the device; never send to the server. */
60
+ keyShare: string;
61
+ /** The ceremony's aux info (JSON). */
62
+ auxInfo: string;
63
+ /** The wallet's shared public key (hex), derived from the share — safe to publish. */
64
+ sharedPublicKey: string;
65
+ }
66
+ /** Parameters for the device half of a sign ceremony. */
67
+ interface DeviceSignParams extends CeremonyParams {
68
+ /**
69
+ * The device's KeyShare as its JSON **string** — the bare crypto material from a prior keygen
70
+ * ({@link DeviceKeygenResult.keyShare}), NOT the on-device storage blob. It rides the sign wire as
71
+ * a parsed JSON OBJECT (the wasm deserializes it with `serde_json::from_value::<KeyShare>`).
72
+ */
73
+ share: string;
74
+ /** Keygen indices signing together (any `t` of `n`). */
75
+ participants: number[];
76
+ /** This party's 0-based position within `participants`. */
77
+ signerPosition: number;
78
+ /** 32-byte hex digest to sign (`0x` prefix optional). */
79
+ digest: string;
80
+ }
81
+ /** Result of the device half of sign. */
82
+ interface DeviceSignResult {
83
+ /** The cggmp24 signature (JSON). */
84
+ signature: string;
85
+ }
86
+ /**
87
+ * Parameters for the device's reshare **assemble** stage ({@link MpcCore.runReshareAssemble}).
88
+ * A structural mirror of the backend `DeviceReshareMaterial` plus the wallet's curve — pure
89
+ * and local, so no relay routing is needed. The fields are opaque JSON produced by the backend
90
+ * reshare; the device only routes them into the core.
91
+ */
92
+ interface DeviceReshareAssembleParams {
93
+ curve: MpcCurve;
94
+ /** This device's 0-based slot within {@link newPreimages}. */
95
+ newPosition: number;
96
+ /** New share preimages `I'` (32-byte big-endian hex scalars), new-holder order. */
97
+ newPreimages: string[];
98
+ /** The new signing threshold `t'`. */
99
+ newThreshold: number;
100
+ /** The unchanged wallet public info (`WalletPublicInfo` JSON, opaque). */
101
+ wallet: unknown;
102
+ /** One Feldman-commitments object per dealer (the broadcast set), opaque JSON. */
103
+ commitments: unknown[];
104
+ /** This device's private sub-share from each dealer, one per dealer, opaque JSON. */
105
+ subShares: unknown[];
106
+ }
107
+ /**
108
+ * Result of the device reshare **assemble** — the bare NEW-epoch core. It cannot sign yet
109
+ * (aux material is generated in {@link MpcCore.runCompleteReshare}), and it is secret: seal it,
110
+ * never log it.
111
+ */
112
+ interface DeviceReshareAssembleResult {
113
+ /** The device's NEW-epoch bare core (`IncompleteKeyShare` JSON). Secret. */
114
+ core: string;
115
+ /** The assembled shared public key (hex) — must equal the wallet's (unchanged across a reshare). */
116
+ sharedPublicKey: string;
117
+ }
118
+ /**
119
+ * Parameters for the device's **complete-reshare** stage ({@link MpcCore.runCompleteReshare}) —
120
+ * the interactive aux-info ceremony over the NEW committee that turns the bare core into a
121
+ * signable share. Relay routing mirrors keygen/sign ({@link CeremonyParams}); the backend uses
122
+ * `<sessionId>/reshare-aux` (kind `reshare-aux`) and drives the server + recovery parties.
123
+ */
124
+ interface DeviceCompleteReshareParams extends CeremonyParams {
125
+ /** The device's bare NEW-epoch core (JSON) from {@link MpcCore.runReshareAssemble}. */
126
+ core: string;
127
+ /** Total parties `n'` in the NEW committee. */
128
+ parties: number;
129
+ /**
130
+ * This device's OWN pre-generated Paillier safe-primes (JSON) for the aux ceremony, off the hot
131
+ * path (see {@link MpcCore.pregeneratePrimes} / `PrimePool`). When omitted the core generates
132
+ * them inline. The primes are the device's private material and are NEVER server-provided — a
133
+ * server-supplied prime pool would collapse the threshold to custodial.
134
+ */
135
+ pregeneratedPrimes?: string;
136
+ }
137
+ /** Result of the device **complete-reshare** — the COMPLETE, signable share. Seal it, never log it. */
138
+ interface DeviceCompleteReshareResult {
139
+ /** The device's complete KeyShare (JSON). Seal + store on the device; never send to the server. */
140
+ keyShare: string;
141
+ /** The wallet's shared public key (hex), preserved across the reshare — verify it is unchanged. */
142
+ sharedPublicKey: string;
143
+ }
144
+ /** Relay connection info shared by an n-party (member-bound) keygen/sign ceremony (#349). */
145
+ interface MemberCeremonyParams {
146
+ curve: MpcCurve;
147
+ /** Relay websocket URL every party connects to. */
148
+ relayUrl: string;
149
+ /** Ceremony id shared by every party; derives the execution ids. */
150
+ sessionId: string;
151
+ /**
152
+ * Short-lived relay token (JWT) this party presents to join the session as its own role in
153
+ * {@link MemberKeygenParams.roles} / {@link MemberSignParams.roles}. Present only when relay
154
+ * authentication is enabled. Absent ⇒ the relay accepts an unauthenticated join.
155
+ */
156
+ relayToken?: string;
157
+ }
158
+ /** Parameters for this party's half of an n-party keygen ceremony (member-bound wallets, #349). */
159
+ interface MemberKeygenParams extends MemberCeremonyParams {
160
+ /** Every party's relay role, in protocol index order — the FULL n-party roster (`roles[i]` is party `i`'s role). */
161
+ roles: string[];
162
+ /** This party's own 0-based index into {@link roles} (its keygen index). */
163
+ partyIndex: number;
164
+ /** Signing threshold `t` (`2 <= t <= roles.length`). */
165
+ threshold: number;
166
+ /**
167
+ * Pre-generated Paillier safe-primes (JSON) for this party, produced ahead of time off the hot
168
+ * path (see {@link MpcCore.pregeneratePrimes}). When omitted the core generates them inline.
169
+ */
170
+ pregeneratedPrimes?: string;
171
+ }
172
+ /** Parameters for this party's half of an n-party sign ceremony (member-bound wallets, #349). */
173
+ interface MemberSignParams extends MemberCeremonyParams {
174
+ /** The FIXED t-of-n quorum's relay roles, in signing order (this ceremony's `PartyRouting`). */
175
+ roles: string[];
176
+ /**
177
+ * This party's KeyShare as its JSON **string** — the bare crypto material from a prior
178
+ * {@link MpcCore.runMemberKeygen} ({@link DeviceKeygenResult.keyShare}), NOT the on-device storage
179
+ * blob. It rides the sign wire as a parsed JSON OBJECT (`serde_json::from_value::<KeyShare>`).
180
+ */
181
+ share: string;
182
+ /** Keygen indices of the parties signing together, parallel to {@link roles}. */
183
+ participants: number[];
184
+ /** This party's 0-based position within {@link roles} / {@link participants}. */
185
+ signerPosition: number;
186
+ /** 32-byte hex digest to sign (`0x` prefix optional). */
187
+ digest: string;
188
+ }
189
+ /** Relay connection info shared by an ed25519 (FROST) keygen/sign ceremony (#110). */
190
+ interface EddsaCeremonyParams {
191
+ /** Relay websocket URL every party connects to. */
192
+ relayUrl: string;
193
+ /** Ceremony id shared by every party; derives the FROST execution ids. */
194
+ sessionId: string;
195
+ /**
196
+ * Short-lived relay token (JWT) this party presents to join the session as its own role in
197
+ * {@link roles}. Present only when relay authentication is enabled; absent ⇒ unauthenticated join.
198
+ */
199
+ relayToken?: string;
200
+ /**
201
+ * Every party's relay role, in protocol-index order (`roles[i]` is party `i`'s role) — for keygen
202
+ * the FULL n-party roster; for sign the FIXED t-of-n quorum in {@link EddsaSignParams.participants}
203
+ * order. MUST match the platform/party-runner order (role ↔ FROST identifier).
204
+ */
205
+ roles: string[];
206
+ /** This party's own 0-based index into {@link roles} (its slot in the ceremony). */
207
+ partyIndex: number;
208
+ }
209
+ /** Parameters for this device's half of an ed25519 (FROST) DKG keygen ceremony (#110). */
210
+ interface EddsaKeygenParams extends EddsaCeremonyParams {
211
+ /** Signing threshold `t` (`2 <= t <= roles.length`, validated by the FROST DKG). */
212
+ threshold: number;
213
+ /**
214
+ * The FROST DKG round-2 encryption roster (#114): one X25519 encryption PUBLIC key (32-byte hex) per
215
+ * party, in `roles`/protocol-index order (`encPubkeys[i]` ↔ FROST identifier `i + 1`). Every party
216
+ * seals its round-2 packages to these keys so no secret share crosses the relay in the clear — the
217
+ * device needs the whole ordered roster (chiefly the server's key). Maps to the wasm `enc_pubkeys`.
218
+ */
219
+ encPubkeys: string[];
220
+ /**
221
+ * This device's OWN X25519 encryption SECRET key (32-byte hex) — opens the round-2 packages sealed to
222
+ * it (the counterpart of this device's entry in {@link encPubkeys}). SECRET: never logged, never sent
223
+ * to the server. Maps to the wasm `enc_secret`.
224
+ */
225
+ encSecret: string;
226
+ }
227
+ /**
228
+ * Result of the device half of an ed25519 keygen — the device's FROST share. The `keyPackage` is
229
+ * **secret** (the device's signing share): seal + store it, never send it to the server. The
230
+ * `publicKeyPackage` is the wallet's group verifying key package (safe to publish). Both are opaque
231
+ * FROST JSON objects (the exact shapes `signEddsa` feeds back), not the cggmp24 KeyShare blob.
232
+ */
233
+ interface EddsaKeygenResult {
234
+ /** The device's FROST `key_package` (JSON object). Secret — seal + store on the device. */
235
+ keyPackage: unknown;
236
+ /** The wallet's shared `public_key_package` (JSON object). Group key — safe to publish. */
237
+ publicKeyPackage: unknown;
238
+ }
239
+ /** Parameters for this device's half of an ed25519 (FROST) two-round sign ceremony (#110). */
240
+ interface EddsaSignParams extends EddsaCeremonyParams {
241
+ /** This device's FROST `key_package` — the secret share from {@link EddsaKeygenResult.keyPackage}, as a JSON object (or its JSON string). */
242
+ keyPackage: unknown;
243
+ /** The wallet's shared `public_key_package` from {@link EddsaKeygenResult.publicKeyPackage}, as a JSON object (or its JSON string). */
244
+ publicKeyPackage: unknown;
245
+ /** The 1-based FROST identifiers of the signing quorum, parallel to {@link EddsaCeremonyParams.roles}. */
246
+ participants: number[];
247
+ /** Hex-encoded raw message bytes to sign (`0x` prefix optional) — the chain adapter's serialized tx message. */
248
+ message: string;
249
+ }
250
+ /** Result of the device half of an ed25519 sign — the RFC 8032 signature. */
251
+ interface EddsaSignResult {
252
+ /** The 64-byte ed25519 signature, hex. */
253
+ signature: string;
254
+ }
255
+ /**
256
+ * The device-party crypto core. Implemented by {@link WasmMpcCore} (web) and, in
257
+ * future, a native core on mobile — the SDK depends on this port, not the engine.
258
+ */
259
+ interface MpcCore {
260
+ runKeygen(params: DeviceKeygenParams): Promise<DeviceKeygenResult>;
261
+ runSign(params: DeviceSignParams): Promise<DeviceSignResult>;
262
+ /**
263
+ * Pre-generate this device's Paillier safe-primes for `curve`, off the keygen hot path
264
+ * (e.g. in a Web Worker during onboarding/idle). Returns opaque serialized primes (JSON) to
265
+ * cache and later pass as {@link DeviceKeygenParams.pregeneratedPrimes}. Optional: a native
266
+ * mobile core generates primes fast inline and need not implement it.
267
+ */
268
+ pregeneratePrimes?(curve: MpcCurve): Promise<string>;
269
+ /**
270
+ * Assemble this device's NEW-epoch bare core from a device-retaining reshare's material (#318).
271
+ * Pure and local — no relay. Optional: only a core built with the reshare capability implements
272
+ * it (the web wasm needs a `--features reshare` build).
273
+ */
274
+ runReshareAssemble?(params: DeviceReshareAssembleParams): Promise<DeviceReshareAssembleResult>;
275
+ /**
276
+ * Complete a reshared bare core into a signable share by running the aux-info ceremony over the
277
+ * NEW committee (#318 / #95). Relay-driven, mirroring keygen's aux phase. Optional: only a core
278
+ * built with the reshare capability implements it (the web wasm needs a `--features reshare` build).
279
+ */
280
+ runCompleteReshare?(params: DeviceCompleteReshareParams): Promise<DeviceCompleteReshareResult>;
281
+ /**
282
+ * Run this party's half of an n-party (member-bound wallet) keygen ceremony (#349) — every
283
+ * member device + the platform join the SAME relay session, addressed by the full
284
+ * {@link MemberKeygenParams.roles} roster rather than a single peer. Optional: only a core built
285
+ * with n-party (member-ceremony) support implements it.
286
+ */
287
+ runMemberKeygen?(params: MemberKeygenParams): Promise<DeviceKeygenResult>;
288
+ /**
289
+ * Run this party's half of an n-party (member-bound wallet) sign ceremony (#349) over the FIXED
290
+ * t-of-n quorum in {@link MemberSignParams.roles}. Optional: only a core built with n-party
291
+ * (member-ceremony) support implements it.
292
+ */
293
+ runMemberSign?(params: MemberSignParams): Promise<DeviceSignResult>;
294
+ /**
295
+ * Run this device's half of an ed25519 (FROST) DKG keygen ceremony (#110) — the device holds one
296
+ * FROST share in a real 2-of-3 wallet, co-generating the group key with the server (+ recovery)
297
+ * parties on the same relay session. Optional: only a core with the ed25519 (FROST) exports
298
+ * (`keygenEddsa`) implements it.
299
+ */
300
+ runEddsaKeygen?(params: EddsaKeygenParams): Promise<EddsaKeygenResult>;
301
+ /**
302
+ * Run this device's half of an ed25519 (FROST) two-round sign ceremony (#110) over the fixed
303
+ * quorum in {@link EddsaSignParams.roles} — co-signing the raw message with the server party.
304
+ * Optional: only a core with the ed25519 (FROST) exports (`signEddsa`) implements it.
305
+ */
306
+ runEddsaSign?(params: EddsaSignParams): Promise<EddsaSignResult>;
307
+ }
308
+
309
+ /** The subset of the client-wasm module the SDK drives. Each export takes one JSON string. */
310
+ interface ClientWasmModule {
311
+ /** Device half of keygen; resolves with `{ aux_info_json, keyshare_json, shared_public_key_json }`. */
312
+ keygen(paramsJson: string): Promise<unknown>;
313
+ /** Device half of sign; resolves with `{ signature_json }`. */
314
+ sign(paramsJson: string): Promise<unknown>;
315
+ /** Pre-generate Paillier safe-primes off the hot path; resolves with `{ primes_json }`. Optional. */
316
+ pregeneratePrimes?(paramsJson: string): Promise<unknown>;
317
+ /**
318
+ * Device reshare **assemble** — compute the NEW-epoch bare core locally; returns
319
+ * `{ core_json, shared_public_key_json }`. Present only in a `--features reshare` wasm build.
320
+ * Synchronous in the core (no relay); the SDK awaits it uniformly.
321
+ */
322
+ reshareAssemble?(paramsJson: string): unknown | Promise<unknown>;
323
+ /**
324
+ * Device **complete-reshare** — run the aux ceremony over the new committee and return the
325
+ * signable share as `{ keyshare_json, shared_public_key_json }`. Present only in a
326
+ * `--features reshare` wasm build.
327
+ */
328
+ completeReshare?(paramsJson: string): Promise<unknown>;
329
+ /**
330
+ * This party's half of an n-party (member-bound wallet) keygen ceremony (#349); resolves with
331
+ * `{ aux_info_json, keyshare_json, shared_public_key_json }`. Present only in a client-wasm
332
+ * build with n-party (member-ceremony) support.
333
+ */
334
+ keygenMember?(paramsJson: string): Promise<unknown>;
335
+ /**
336
+ * This party's half of an n-party (member-bound wallet) sign ceremony over a fixed t-of-n
337
+ * quorum (#349); resolves with `{ signature_json }`. Present only in a client-wasm build with
338
+ * n-party (member-ceremony) support.
339
+ */
340
+ signMember?(paramsJson: string): Promise<unknown>;
341
+ /**
342
+ * This device's half of an ed25519 (FROST) DKG keygen ceremony (#110); resolves with
343
+ * `{ key_package, public_key_package }` (JSON objects). Present only in a client-wasm build with
344
+ * the ed25519 (FROST) exports.
345
+ */
346
+ keygenEddsa?(paramsJson: string): Promise<unknown>;
347
+ /**
348
+ * This device's half of an ed25519 (FROST) two-round sign ceremony (#110); resolves with
349
+ * `{ signature }` (64-byte hex). Present only in a client-wasm build with the ed25519 (FROST) exports.
350
+ */
351
+ signEddsa?(paramsJson: string): Promise<unknown>;
352
+ }
353
+ /** Lazily loads + initializes the client-wasm module (e.g. dynamic `import()` of the wasm-pack pkg). */
354
+ type ClientWasmLoader = () => Promise<ClientWasmModule>;
355
+ /**
356
+ * {@link MpcCore} backed by the waas-core `client-wasm` module (`keygen` / `sign`).
357
+ *
358
+ * The wasm engine is injected via a loader so the SDK stays runtime-agnostic and
359
+ * the core is unit-testable without a real ceremony. The device key share returned
360
+ * by keygen must be sealed and stored on the device — it is never sent to the server.
361
+ */
362
+ declare class WasmMpcCore implements MpcCore {
363
+ private readonly load;
364
+ private modulePromise?;
365
+ constructor(load: ClientWasmLoader);
366
+ private init;
367
+ runKeygen(params: DeviceKeygenParams): Promise<DeviceKeygenResult>;
368
+ runSign(params: DeviceSignParams): Promise<DeviceSignResult>;
369
+ pregeneratePrimes(curve: MpcCurve): Promise<string>;
370
+ runReshareAssemble(params: DeviceReshareAssembleParams): Promise<DeviceReshareAssembleResult>;
371
+ runCompleteReshare(params: DeviceCompleteReshareParams): Promise<DeviceCompleteReshareResult>;
372
+ runMemberKeygen(params: MemberKeygenParams): Promise<DeviceKeygenResult>;
373
+ runMemberSign(params: MemberSignParams): Promise<DeviceSignResult>;
374
+ runEddsaKeygen(params: EddsaKeygenParams): Promise<EddsaKeygenResult>;
375
+ runEddsaSign(params: EddsaSignParams): Promise<EddsaSignResult>;
376
+ /** Build the snake_case params JSON the wasm exports expect from the common routing + extras. */
377
+ private encode;
378
+ /**
379
+ * Build the snake_case params JSON for an n-party (member-bound, #349) member ceremony — the
380
+ * roster-based routing shape (`roles` + `party_index`), not the 2-party `role`/`peer_role` shape
381
+ * {@link encode} builds. The `roles` roster and `party_index` are passed through in `extra` by
382
+ * the caller and land on the wire under those exact keys — what `keygenMember` / `signMember`
383
+ * deserialize.
384
+ */
385
+ private encodeMember;
386
+ /**
387
+ * Build the snake_case params JSON for an ed25519 (FROST, #110) ceremony — the roster-based routing
388
+ * shape (`roles` + `party_index`) the `keygenEddsa` / `signEddsa` exports deserialize. Unlike
389
+ * {@link encodeMember} there is NO `curve` (ed25519-only) and no Paillier primes; the caller passes
390
+ * the ceremony-specific extras (keygen: `threshold`; sign: the FROST packages + participants + message).
391
+ */
392
+ private encodeEddsa;
393
+ }
394
+
395
+ interface NodeWorkerMpcOptions {
396
+ /**
397
+ * The wasm engine package the worker imports (a bare specifier or file URL).
398
+ * Defaults to `@waaskey/client-wasm`.
399
+ */
400
+ wasmPackage?: string;
401
+ /**
402
+ * Protocol-level ping cadence on each open relay socket, keeping the connection alive
403
+ * through proxies while the worker computes. Default 15s; `0` disables.
404
+ */
405
+ pingIntervalMs?: number;
406
+ }
407
+ /**
408
+ * A drop-in {@link WasmMpcCore} for Node ≥ 22 that runs the wasm ceremony in a worker
409
+ * thread and keeps the relay WebSocket responsive on the main thread (waas-sdk#82).
410
+ *
411
+ * ```ts
412
+ * import { NodeWorkerMpcCore } from '@waaskey/sdk/node';
413
+ * const mpc = new NodeWorkerMpcCore();
414
+ * const waaskey = new Waaskey({ apiKey, mpc, ... });
415
+ * // …create / sign…
416
+ * await mpc.terminate(); // the worker keeps the process alive until terminated
417
+ * ```
418
+ */
419
+ declare class NodeWorkerMpcCore extends WasmMpcCore {
420
+ private readonly bridge;
421
+ constructor(options?: NodeWorkerMpcOptions);
422
+ /** Close every bridged socket and terminate the worker; the core is unusable afterwards. */
423
+ terminate(): Promise<void>;
424
+ }
425
+
426
+ export { NodeWorkerMpcCore, type NodeWorkerMpcOptions };