@terminal3/t3n-sdk 0.10.0 → 0.12.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.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/dist/demo.d.ts +25 -0
  3. package/dist/index.d.ts +347 -172
  4. package/dist/index.esm.js +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/src/client/actions.d.ts +22 -0
  7. package/dist/src/client/config.d.ts +10 -30
  8. package/dist/src/client/encryption.d.ts +30 -0
  9. package/dist/src/client/handlers.d.ts +73 -0
  10. package/dist/src/client/index.d.ts +4 -0
  11. package/dist/src/client/request-parser.d.ts +48 -0
  12. package/dist/src/client/t3n-client.d.ts +113 -26
  13. package/dist/src/index.d.ts +6 -7
  14. package/dist/src/types/auth.d.ts +5 -6
  15. package/dist/src/types/index.d.ts +37 -13
  16. package/dist/src/utils/index.d.ts +0 -1
  17. package/dist/src/wasm/interface.d.ts +95 -54
  18. package/dist/src/wasm/loader.d.ts +25 -55
  19. package/dist/wasm/generated/interfaces/component-session-client-auth.d.ts +12 -0
  20. package/dist/wasm/generated/interfaces/component-session-client-handshake.d.ts +12 -0
  21. package/dist/wasm/generated/interfaces/component-session-cookie.d.ts +8 -0
  22. package/dist/wasm/generated/interfaces/component-session-session.d.ts +7 -0
  23. package/dist/wasm/generated/session.core.wasm +0 -0
  24. package/dist/wasm/generated/session.d.ts +10 -87
  25. package/dist/wasm/generated/session.js +3254 -6713
  26. package/package.json +1 -1
  27. package/dist/src/utils/hkdf.d.ts +0 -36
  28. package/dist/wasm/generated/interfaces/host-session-interfaces-contract-dispatch.d.ts +0 -2
  29. package/dist/wasm/generated/interfaces/host-session-interfaces-entropy.d.ts +0 -2
  30. package/dist/wasm/generated/interfaces/host-session-interfaces-eth-signer.d.ts +0 -2
  31. package/dist/wasm/generated/interfaces/host-session-interfaces-kem.d.ts +0 -3
  32. package/dist/wasm/generated/interfaces/host-session-interfaces-oidc-client.d.ts +0 -2
  33. package/dist/wasm/generated/interfaces/host-session-interfaces-oidc.d.ts +0 -3
  34. package/dist/wasm/generated/interfaces/host-session-interfaces-session-ops.d.ts +0 -9
  35. package/dist/wasm/generated/interfaces/host-session-interfaces-transport.d.ts +0 -2
  36. package/dist/wasm/generated/interfaces/tee-session-client-auth.d.ts +0 -7
  37. package/dist/wasm/generated/interfaces/tee-session-client-handshake.d.ts +0 -12
  38. package/dist/wasm/generated/interfaces/tee-session-cookie.d.ts +0 -7
  39. package/dist/wasm/generated/interfaces/tee-session-server-admin.d.ts +0 -2
  40. package/dist/wasm/generated/interfaces/tee-session-server-auth.d.ts +0 -10
  41. package/dist/wasm/generated/interfaces/tee-session-server-handshake.d.ts +0 -15
  42. package/dist/wasm/generated/interfaces/tee-session-server-webhook.d.ts +0 -6
  43. package/dist/wasm/generated/interfaces/tee-session-session-crypto.d.ts +0 -3
  44. package/dist/wasm/generated/session.core2.wasm +0 -0
  45. package/dist/wasm/generated/session.core3.wasm +0 -0
package/README.md CHANGED
@@ -586,7 +586,7 @@ To use the real WASM component:
586
586
 
587
587
  ```bash
588
588
  cd node
589
- cargo build -p session --target wasm32-wasip2 --release
589
+ cargo build -p session-client --target wasm32-wasip2 --release
590
590
  ```
591
591
 
592
592
  2. **Copy the WASM file** to the SDK directory:
package/dist/demo.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * T3n SDK Real WASM Demo Script
3
+ *
4
+ * This script demonstrates the T3n SDK using the actual WASM component
5
+ * built from the node session module.
6
+ */
7
+ import { SessionStatus, Did } from "./src/index";
8
+ import { type ChildProcess } from "child_process";
9
+ declare global {
10
+ var __oidcServerProcess: ChildProcess | undefined;
11
+ }
12
+ interface DemoResult {
13
+ sessionId: {
14
+ value: string;
15
+ };
16
+ status: SessionStatus;
17
+ wasmLoaded: boolean;
18
+ authenticated: boolean;
19
+ did: Did | null;
20
+ }
21
+ /**
22
+ * Main Demo Function
23
+ */
24
+ declare function runRealWasmDemo(): Promise<DemoResult>;
25
+ export { runRealWasmDemo, type DemoResult };
package/dist/index.d.ts CHANGED
@@ -1,71 +1,112 @@
1
1
  /**
2
- * WASM Component Interface async direct-call.
2
+ * WASM Component Interface - Mirrors the WIT specification exactly
3
3
  *
4
- * Mirrors `tee:session@1.0.0` WIT world. The SDK talks to the WASM
5
- * only through these exports (plus host imports supplied at
6
- * instantiation); all protocol glue lives inside the contract.
4
+ * This interface works with completely opaque byte arrays, just like the WIT interface.
5
+ * The TypeScript SDK doesn't know about internal state machine phases or details.
7
6
  */
8
- interface ClientSessionKeys {
9
- /** `encrypt_key || decrypt_key`, 64 bytes. */
10
- blob: Uint8Array;
11
- sid: Uint8Array;
12
- }
13
- interface HandshakeOutcome {
14
- keys: ClientSessionKeys;
15
- authenticated: boolean;
16
- did?: string;
17
- expirySec: bigint;
7
+ /**
8
+ * Result type for WASM next() operations
9
+ */
10
+ interface WasmNextResult {
11
+ state: Uint8Array;
12
+ request: Uint8Array;
18
13
  }
14
+ /**
15
+ * Client handshake operations - completely opaque byte arrays only
16
+ */
19
17
  interface ClientHandshake {
20
- run(sid: Uint8Array, cookie: string | undefined): HandshakeOutcome;
21
- }
22
- interface AuthOutcome {
23
- did: string;
24
- cookie?: string;
18
+ /**
19
+ * Process next step in handshake
20
+ * @param state - Current handshake state (null for initial call)
21
+ * @param action - Action to process (opaque bytes)
22
+ * @returns Promise with new state and request to send
23
+ */
24
+ next(state: Uint8Array | null, action: Uint8Array): Promise<WasmNextResult>;
25
+ /**
26
+ * Attempt to finalize handshake
27
+ * @param state - Current handshake state
28
+ * @returns Promise with session bytes if successful
29
+ * @throws Error containing "not yet finalized" if the state machine
30
+ * has not reached its terminal phase. The SDK runtime treats
31
+ * this as the loop's "keep going" signal, not a real error.
32
+ */
33
+ finish(state: Uint8Array): Promise<Uint8Array>;
25
34
  }
35
+ /**
36
+ * Client authentication operations - completely opaque byte arrays only
37
+ */
26
38
  interface ClientAuth {
27
- runEth(sessionKeys: Uint8Array, ethAddress: string, siweDomain: string | undefined, siweUrl: string | undefined, siweChainId: bigint | undefined): AuthOutcome;
28
39
  /**
29
- * Run the full OIDC flow in one call. The contract drives both
30
- * server round-trips and invokes the SDK's `getIdToken(provider,
31
- * nonce)` host import in between to obtain the IdP-signed token.
40
+ * Process next step in authentication
41
+ * @param state - Current auth state (null for initial call)
42
+ * @param action - Action to process (opaque bytes)
43
+ * @returns Promise with new state and request to send
32
44
  */
33
- runOidc(sessionKeys: Uint8Array, provider: string): AuthOutcome;
34
- }
35
- interface ServerSessionKeys {
36
- c2s: Uint8Array;
37
- s2c: Uint8Array;
38
- sid: Uint8Array;
39
- }
40
- interface ServerOutcome {
41
- keys: ServerSessionKeys;
42
- authenticated: boolean;
43
- did?: string;
44
- expirySec: bigint;
45
- refreshedCookie?: string;
45
+ next(state: Uint8Array | null, action: Uint8Array): Promise<WasmNextResult>;
46
+ /**
47
+ * Attempt to finalize authentication
48
+ * @param state - Current auth state
49
+ * @returns Promise with DID bytes if successful
50
+ * @throws Error containing "not yet finalized" if the state machine
51
+ * has not reached its terminal phase. The SDK runtime treats
52
+ * this as the loop's "keep going" signal, not a real error.
53
+ */
54
+ finish(state: Uint8Array): Promise<Uint8Array>;
46
55
  }
47
- interface ServerHandshake {
48
- run(sid: Uint8Array, ciphertext: Uint8Array, cookieValue: string | undefined): ServerOutcome;
56
+ /**
57
+ * Client authentication operations - completely opaque byte arrays only
58
+ */
59
+ interface ClientExecute {
60
+ /**
61
+ * Process next step in authentication
62
+ * @param state - Current auth state (null for initial call)
63
+ * @param action - Action to process (opaque bytes)
64
+ * @returns Promise with new state and request to send
65
+ */
66
+ next(state: Uint8Array | null, action: Uint8Array): Promise<WasmNextResult>;
67
+ /**
68
+ * Attempt to finalize authentication
69
+ * @param state - Current auth state
70
+ * @returns Promise with DID bytes if successful
71
+ * @throws Error containing "not yet finalized" if the state machine
72
+ * has not reached its terminal phase. The SDK runtime treats
73
+ * this as the loop's "keep going" signal, not a real error.
74
+ */
75
+ finish(state: Uint8Array): Promise<Uint8Array>;
49
76
  }
77
+ /**
78
+ * Session encryption/decryption operations - completely opaque byte arrays only
79
+ */
50
80
  interface SessionCrypto {
51
- encrypt(keys: Uint8Array, plaintext: Uint8Array): Uint8Array;
52
- decrypt(keys: Uint8Array, ciphertext: Uint8Array): Uint8Array;
53
- }
54
- interface Validation {
55
- authenticated: boolean;
56
- did?: string;
57
- exp: bigint;
58
- }
59
- interface CookieIface {
60
- validate(cookieValue: string, teeAddress: Uint8Array, nowSec: bigint): Validation;
81
+ /**
82
+ * Encrypt plaintext using session
83
+ * @param session - Session state (opaque bytes)
84
+ * @param plaintext - Data to encrypt
85
+ * @returns Promise with encrypted bytes
86
+ */
87
+ encrypt(session: Uint8Array, plaintext: Uint8Array): Promise<Uint8Array>;
88
+ /**
89
+ * Decrypt ciphertext using session
90
+ * @param session - Session state (opaque bytes)
91
+ * @param ciphertext - Data to decrypt
92
+ * @returns Promise with decrypted bytes
93
+ */
94
+ decrypt(session: Uint8Array, ciphertext: Uint8Array): Promise<Uint8Array>;
61
95
  }
62
- /** Fully instantiated session component. */
96
+ /**
97
+ * Main WASM Component interface - mirrors the WIT interface exactly
98
+ *
99
+ * This is completely opaque to the TypeScript layer. All state machine logic,
100
+ * authentication flows, and cryptographic operations are handled in WASM.
101
+ */
63
102
  interface WasmComponent {
64
- clientHandshake: ClientHandshake;
65
- clientAuth: ClientAuth;
66
- serverHandshake: ServerHandshake;
67
- sessionCrypto: SessionCrypto;
68
- cookie: CookieIface;
103
+ /** Client-side state machines exported by the WASM component. */
104
+ flow: {
105
+ handshake: ClientHandshake;
106
+ auth: ClientAuth;
107
+ execute: ClientExecute;
108
+ };
109
+ session: SessionCrypto;
69
110
  }
70
111
 
71
112
  /**
@@ -172,75 +213,38 @@ declare function createLogger(level?: LogLevel): Logger;
172
213
  declare function getLogger(): Logger;
173
214
 
174
215
  /**
175
- * WASM Component Loader — async direct-call shape.
176
- *
177
- * Loads the `tee:session@1.0.0` jco-transpiled component. Host
178
- * imports (`host:session-interfaces/*`) are supplied at
179
- * instantiation; the caller can override any of them via
180
- * `WasmLoadConfig.hostImports` to plug a custom KEM public-key
181
- * source, ETH signer, etc. Defaults cover the common case for the
182
- * browser SDK:
216
+ * WASM Component Loader
183
217
  *
184
- * - `entropy.random`: WebCrypto `getRandomValues`
185
- * - `kem.mlKemPublicKey`: HTTP fetch of /pubkey (caller sets URL)
186
- * - `kem.decapsulate`: server-only; client-side stub returns error
187
- * - `eth-signer.eth-sign`: injected wallet (window.ethereum) if present
188
- * - `session-ops.now-ms`: Date.now()
189
- * - `session-ops.tee-address`, `set-cookie`: caller-supplied or stubs
218
+ * This module provides utilities for loading and initializing the WASM component.
219
+ * The actual WASM loading implementation will depend on the build system and
220
+ * deployment environment.
190
221
  */
191
222
 
192
- /**
193
- * Host imports supplied at WASM instantiation. Any subset may be
194
- * overridden by the caller; unset ones fall back to a safe default
195
- * (or an error stub for server-only methods like `decapsulate`).
196
- */
197
- interface SessionHostImports {
198
- /** Fetch the server's ML-KEM public key (client-side). */
199
- mlKemPublicKey?: () => Uint8Array | Promise<Uint8Array>;
200
- /** CSPRNG. Defaults to WebCrypto `getRandomValues`. */
201
- random?: (len: number) => Uint8Array;
202
- /** EIP-191 / SIWE signer. Defaults to error (caller must supply). */
203
- ethSign?: (message: Uint8Array) => Uint8Array | Promise<Uint8Array>;
204
- /**
205
- * OIDC user-interaction callback. The contract supplies the
206
- * server-bound `nonce`; the SDK consumer runs whatever popup /
207
- * redirect flow is appropriate for `provider` and returns the
208
- * IdP-signed `id_token`. Defaults to error (caller must supply
209
- * when using OIDC auth).
210
- */
211
- getIdToken?: (provider: string, nonce: string) => string | Promise<string>;
212
- /** Current time in ms. Defaults to Date.now() (as bigint). */
213
- nowMs?: () => bigint;
214
- /** TEE address — server-only; client stub returns 20 zero bytes. */
215
- teeAddress?: () => Uint8Array;
216
- /** Called when the guest emits a refreshed cookie. */
217
- setCookie?: (value: string) => void;
218
- /**
219
- * HTTP transport the contract uses to POST JSON-RPC requests. The
220
- * SDK wires this to its `Transport` under the hood — the contract
221
- * supplies the method name and params bytes; the SDK layers on the
222
- * Session-Id header, JSON-RPC envelope, and returns the raw result
223
- * bytes.
224
- */
225
- postRpc?: (method: string, sessionId: string, params: string) => string | Promise<string>;
226
- }
227
223
  /**
228
224
  * Configuration for WASM component loading
229
225
  */
230
226
  interface WasmLoadConfig {
231
227
  /** Path or URL to the WASM module */
232
228
  wasmPath?: string;
229
+ /** Custom fetch function for loading WASM */
230
+ fetchFn?: typeof fetch;
231
+ /** Additional initialization options */
232
+ initOptions?: Record<string, unknown>;
233
233
  /** Optional logger instance - if not provided, uses global default */
234
234
  logger?: Logger;
235
- /** Overrides for the host-import functions. */
236
- hostImports?: SessionHostImports;
237
235
  }
238
236
  /**
239
- * Load and initialize the T3n WASM component (async direct-call).
237
+ * Load and initialize the T3n WASM component
240
238
  *
241
- * The caller normally only needs to override `mlKemPublicKey` (to
242
- * point at the node URL) and `ethSign` (to bridge to the browser
243
- * wallet). All other imports have sensible defaults.
239
+ * @param config - Optional configuration for loading the WASM component
240
+ * @returns Promise that resolves to the initialized WASM component
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * const wasmComponent = await loadWasmComponent({
245
+ * wasmPath: '/path/to/t3n.wasm'
246
+ * });
247
+ * ```
244
248
  */
245
249
  declare function loadWasmComponent(config?: WasmLoadConfig): Promise<WasmComponent>;
246
250
 
@@ -282,15 +286,14 @@ declare enum AuthMethod {
282
286
  /**
283
287
  * OIDC credentials interface.
284
288
  *
285
- * The TEE generates a session-binding nonce; the user-interaction
286
- * step is wired at WASM load time via `hostImports.getIdToken`
287
- * (see `loadWasmComponent`), mirroring how `hostImports.ethSign`
288
- * supplies wallet access. The contract calls `getIdToken(provider,
289
- * nonce)` from inside `runOidc` and feeds the returned `id_token`
290
- * to the server.
289
+ * The TEE generates a session-binding nonce that must be included in
290
+ * the Google authorization URL (`&nonce=…`). The `getIdToken` callback
291
+ * receives this nonce and must return the `id_token` JWT obtained
292
+ * from the OIDC provider with the nonce baked into its claims.
291
293
  */
292
294
  interface OidcCredentials {
293
295
  provider: string;
296
+ getIdToken: (nonce: string) => Promise<string>;
294
297
  }
295
298
  /**
296
299
  * Base authentication input with method discriminator
@@ -322,6 +325,47 @@ type AuthInput = EthAuthInput | OidcAuthInput;
322
325
  declare function createEthAuthInput(address: string): EthAuthInput;
323
326
  declare function createOidcAuthInput(credentials: OidcCredentials): OidcAuthInput;
324
327
 
328
+ /**
329
+ * Public types export for T3n SDK
330
+ */
331
+ /**
332
+ * Guest-to-Host request handler function type
333
+ *
334
+ * Handles requests from WASM guest that need host (SDK) to perform side
335
+ * effects. The exact shape of `requestData` depends on the specific
336
+ * handler — see `GuestToHostHandlers` below for the per-handler shapes.
337
+ * The wrapper layer in `T3nClient.handleGuestToHost` parses the JSON
338
+ * envelope and calls the matching handler with the parsed data, so
339
+ * each handler's implementation should narrow `requestData` to its
340
+ * own expected shape.
341
+ */
342
+ type GuestToHostHandler = (requestData: Record<string, unknown>) => Promise<Uint8Array>;
343
+ /**
344
+ * Map of guest-to-host request handlers
345
+ * Keys match the guest_to_host tag values from the WASM
346
+ */
347
+ interface GuestToHostHandlers {
348
+ /**
349
+ * Handle Ethereum signature requests
350
+ * requestData: { guest_to_host: "EthSign", challenge: string (base64) }
351
+ * Returns: JSON bytes of { host_to_guest: "EthSign", challenge: string, signature: string }
352
+ */
353
+ EthSign?: GuestToHostHandler;
354
+ /**
355
+ * Handle MlKem public key requests
356
+ * requestData: { guest_to_host: "MlKemPublicKey" }
357
+ * Returns: JSON bytes of { host_to_guest: "MlKemPublicKey", key: string }
358
+ */
359
+ MlKemPublicKey?: GuestToHostHandler;
360
+ /**
361
+ * Handle random bytes requests
362
+ * requestData: { guest_to_host: "Random", len?: number }
363
+ * Returns: JSON bytes of { host_to_guest: "Random", bytes: string (base64) }
364
+ */
365
+ Random?: GuestToHostHandler;
366
+ [key: string]: GuestToHostHandler | undefined;
367
+ }
368
+
325
369
  /**
326
370
  * Transport layer for T3n SDK
327
371
  *
@@ -440,7 +484,7 @@ declare class MockTransport implements Transport {
440
484
  interface T3nClientConfig {
441
485
  /** Base URL of the T3n node (used if transport not provided) */
442
486
  baseUrl?: string;
443
- /** WASM component instance (async direct-call `tee:session@1.0.0`). */
487
+ /** WASM component instance for cryptographic operations */
444
488
  wasmComponent: WasmComponent;
445
489
  /** Optional transport layer - if not provided, uses HttpTransport with baseUrl */
446
490
  transport?: Transport;
@@ -450,90 +494,221 @@ interface T3nClientConfig {
450
494
  timeout?: number;
451
495
  /** Optional custom headers to include in requests */
452
496
  headers?: Record<string, string>;
453
- /** Log level for this client instance. */
497
+ /**
498
+ * Log level for this client instance.
499
+ * Defaults to global log level (LogLevel.ERROR) if not specified.
500
+ * Use LogLevel.DEBUG for verbose logging, LogLevel.INFO for informational messages,
501
+ * LogLevel.WARN for warnings, or LogLevel.ERROR for errors only.
502
+ */
454
503
  logLevel?: LogLevel;
455
504
  /** Optional custom logger - if provided, overrides logLevel */
456
505
  logger?: Logger;
457
- /**
458
- * Optional signer bridge used by `authenticate()` for the ETH
459
- * (SIWE) flow. Given the SIWE message bytes, the callback must
460
- * produce a 65-byte `(r || s || v)` signature over the EIP-191
461
- * personal-sign digest — matching `cryptography::ecdsa::eth`
462
- * recovery on the node. A convenience wrapper for raw-private-key
463
- * signing is planned for the follow-up commit that lands full
464
- * ETH auth support.
465
- */
466
- ethSign?: (message: Uint8Array) => Promise<Uint8Array>;
467
- /**
468
- * Ethereum address (0x-prefixed, 20 bytes hex) of the user
469
- * authenticating. Required by `authenticate()` for the ETH flow —
470
- * the server recovers the signer from the SIWE message and
471
- * compares to this address.
472
- */
473
- ethAddress?: string;
474
- /**
475
- * SIWE domain / URI / chain-id used in the message the SDK builds.
476
- * Matches `SiwePolicy` on the server — the server's allowlist
477
- * policy (if configured in NodeConfig) only accepts matching
478
- * values. Defaults: domain `"localhost"`, URL `"https://trinity.io"`,
479
- * chain ID `1` (Ethereum mainnet).
480
- */
481
- siweDomain?: string;
482
- siweUrl?: string;
483
- siweChainId?: number;
506
+ /** Optional guest-to-host request handlers - provides custom behavior for WASM requests */
507
+ handlers?: GuestToHostHandlers;
484
508
  }
485
509
 
486
510
  /**
487
- * T3n Client — thin host-function provider + WASM sequencer.
488
- *
489
- * The SDK communicates with the session contract strictly through
490
- * WIT:
491
- * - Calls `clientHandshake.run(sid)` — contract handles ML-KEM
492
- * encapsulation, HKDF, POSTs via `host.transport.postRpc`,
493
- * derives session keys, returns them.
494
- * - Calls `clientAuth.runEth(keys, address, ...)` — contract
495
- * builds the SIWE message, signs via `host.eth-signer.ethSign`,
496
- * POSTs + parses the Finish response, returns the DID.
497
- * - For `execute()`, the SDK just encrypts the JSON-RPC payload
498
- * via `sessionCrypto.encrypt` and POSTs it through its transport.
511
+ * T3n Client - Main SDK class
499
512
  *
500
- * No SIWE building, no HKDF, no hex/base64 shuffling, no wire
501
- * envelopes outside the contract. The contract is the single source
502
- * of protocol truth.
513
+ * Provides a simple interface for establishing secure sessions with T3n nodes.
514
+ * All cryptographic complexity is handled in WASM components.
503
515
  */
504
516
 
517
+ /**
518
+ * Main T3n SDK Client
519
+ */
505
520
  declare class T3nClient {
506
521
  private readonly config;
507
522
  private readonly transport;
508
523
  private readonly sessionId;
509
524
  private readonly logger;
525
+ private readonly encryption;
510
526
  private status;
511
- private sessionKeys;
527
+ /**
528
+ * In-flight WASM state-machine bytes. Holds the opaque state
529
+ * returned by `flow[method].next()` between iterations of
530
+ * `runFlow`. Always cleared at the top of `runFlow` and again
531
+ * once `tryFinalize` has extracted the terminal payload — so
532
+ * outside of an active loop these slots are always `null`.
533
+ */
534
+ private wasmState;
535
+ /**
536
+ * Terminal payloads produced by `flow[method].finish()`:
537
+ * - `handshake` → serialized session blob, used by
538
+ * `getSessionState()` for subsequent `session.encrypt` calls.
539
+ * - `auth` → serialized DID; the public `authenticate()` decodes
540
+ * it into `this.did` and the slot is otherwise unused.
541
+ * - `execute` → unused (executes return immediately to the caller).
542
+ *
543
+ * Stored in a dedicated field instead of reusing `wasmState`
544
+ * because the two meanings — "in-flight state machine" vs
545
+ * "finalized payload" — are semantically different and merging
546
+ * them invites the bug-class Devin flagged in PR #1140.
547
+ */
548
+ private finalizedPayload;
512
549
  private did;
550
+ private handshakeResult;
513
551
  constructor(config: T3nClientConfig);
552
+ /**
553
+ * Start the handshake process with the T3n node
554
+ */
514
555
  handshake(): Promise<HandshakeResult>;
556
+ /**
557
+ * Authenticate with the T3n node.
558
+ *
559
+ * For OIDC, this runs a two-step nonce-bound flow:
560
+ * 1. Sends `InitOidcAuth` to server → receives session-binding nonce.
561
+ * 2. Calls `getIdToken(nonce)` callback so the app can include the
562
+ * nonce in the Google authorization URL.
563
+ * 3. Sends `SubmitIdToken` with the nonce-bearing token → receives DID.
564
+ */
515
565
  authenticate(authInput: AuthInput): Promise<Did>;
566
+ /**
567
+ * OIDC two-step authentication with session-binding nonce.
568
+ *
569
+ * Bypasses the WASM client state machine and makes two encrypted
570
+ * RPC calls directly:
571
+ * 1. `InitOidcAuth { provider }` → server generates nonce → returns
572
+ * `ProvideNonce { nonce }`.
573
+ * 2. App calls `getIdToken(nonce)` to obtain a nonce-bound `id_token`.
574
+ * 3. `SubmitIdToken { id_token }` → server verifies token + nonce →
575
+ * returns `Finish { did }`.
576
+ */
577
+ private authenticateOidc;
578
+ /**
579
+ * Execute an action on the T3n node
580
+ */
516
581
  execute(payload: unknown): Promise<string>;
517
582
  getSessionId(): SessionId;
518
583
  getStatus(): SessionStatus;
519
584
  getDid(): Did | null;
520
- isAuthenticated(): boolean;
521
585
  getLastSetCookie(): string | null;
522
586
  getLastResponseHeaders(): Record<string, string>;
587
+ isAuthenticated(): boolean;
523
588
  /**
524
- * Build the `host.transport.postRpc` callback the contract uses for
525
- * all its HTTP round-trips. Must be passed into `loadWasmComponent`
526
- * at instantiation time so the contract can POST during handshake
527
- * and auth.
589
+ * Run a WASM state machine flow to completion.
590
+ *
591
+ * Clears both `wasmState[method]` and `finalizedPayload[method]`
592
+ * at entry so a flow that previously threw partway (e.g. an RPC
593
+ * error) starts from a clean slate on retry. Without the reset,
594
+ * stale state from the failed attempt leaks into the new flow
595
+ * and `tryFinalize` may either spuriously succeed or run `next()`
596
+ * against a state that no longer matches the action we're sending.
528
597
  *
529
- * `params` from the contract is the opaque JSON-RPC params (already
530
- * encrypted where encryption is needed). The SDK wraps in the
531
- * JSON-RPC envelope and injects the Session-Id header.
598
+ * The `tryFinalize`-then-`next` order is load-bearing: the loop's
599
+ * exit condition fires *after* the previous iteration's
600
+ * `handleWasmRequest` has flushed the outbound peer reply, so
601
+ * every state-machine emission reaches the wire before we extract
602
+ * the final payload.
532
603
  */
533
- buildPostRpcHostImport(): (method: string, _sessionIdFromGuest: string, params: string) => Promise<string>;
534
- private sendRpcRaw;
604
+ private runFlow;
605
+ /**
606
+ * Try to finalize the current flow. Returns the finish() payload
607
+ * (a serialized Session for handshake, a serialized DID for auth)
608
+ * or `null` if the state machine has not reached its terminal phase
609
+ * yet.
610
+ *
611
+ * The "not yet finalized" case is the loop's signal to keep
612
+ * iterating, not a real error. Any *other* failure must propagate
613
+ * so callers see real WASM errors instead of silent retries that
614
+ * spin forever.
615
+ *
616
+ * The terminal payload is stored in `finalizedPayload[method]`
617
+ * (a separate field from `wasmState[method]`) so the in-flight
618
+ * state-machine bytes and the finalized session/DID bytes never
619
+ * occupy the same slot. `getSessionState()` reads from
620
+ * `finalizedPayload.handshake`.
621
+ */
622
+ private tryFinalize;
623
+ /**
624
+ * Handle a WASM request based on its type
625
+ */
626
+ private handleWasmRequest;
627
+ /**
628
+ * Handle a send-remote request by calling the RPC endpoint
629
+ */
630
+ private handleSendRemote;
631
+ private captureHandshakeResult;
632
+ /**
633
+ * Handle a guest-to-host request using configured handlers
634
+ */
635
+ private handleGuestToHost;
636
+ /**
637
+ * Send an RPC request with automatic encryption/decryption
638
+ */
639
+ private sendRpcRequest;
640
+ /**
641
+ * Get the finalized session blob (for `session.encrypt` calls).
642
+ * Populated by `tryFinalize` once the handshake state machine
643
+ * reaches its terminal phase.
644
+ */
645
+ private getSessionState;
535
646
  }
536
647
 
648
+ /**
649
+ * Guest-to-Host Request Handlers
650
+ *
651
+ * These handle requests from WASM that need the host environment to perform side effects.
652
+ * Examples: signing challenges, providing public keys, generating random bytes.
653
+ */
654
+
655
+ /**
656
+ * Account — MetaMask handler accepts either a plain address string or an
657
+ * object with an `address` field (for compatibility with various wallet
658
+ * libraries).
659
+ */
660
+ type EthAccount = string | {
661
+ address: string;
662
+ };
663
+ /**
664
+ * Create an EthSign handler using MetaMask (window.ethereum)
665
+ * @param account - MetaMask account (string address or object with address property)
666
+ * @param logger - Optional logger instance. Defaults to a logger using the global log level (LogLevel.ERROR).
667
+ * Pass a custom logger to override logging behavior for this handler.
668
+ * @param privateKey - Optional private key for signing (if provided, MetaMask is not used)
669
+ */
670
+ declare function metamask_sign(account: EthAccount, logger?: Logger, privateKey?: string | undefined): GuestToHostHandler;
671
+ /**
672
+ * Get the current MetaMask address
673
+ * @returns Ethereum address (lowercase, 0x prefixed)
674
+ */
675
+ declare function metamask_get_address(): Promise<string>;
676
+ /**
677
+ * Get the address for a given private key
678
+ * @param privateKey - Ethereum private key (0x prefixed hex string)
679
+ * @returns Ethereum address (lowercase, 0x prefixed)
680
+ */
681
+ declare function eth_get_address(privateKey: string): string;
682
+ /**
683
+ * Create an MlKemPublicKey handler that lazily fetches the root public key
684
+ * from `${baseUrl}/status` on first invocation and caches the encoded
685
+ * response for subsequent calls.
686
+ *
687
+ * @param baseUrl - **Required**. The node URL whose `/status` endpoint should
688
+ * serve the ML-KEM public key. Must be the same URL the
689
+ * T3nClient is constructed with — otherwise the handshake
690
+ * encrypts to one node and sends ciphertext to another.
691
+ *
692
+ * Was optional in 0.3.x, where omitting it caused the lazy
693
+ * fetch to silently fall back to `NODE_URLS[currentEnv]` and
694
+ * hit the wrong node. Three downstream consumers (demo.ts,
695
+ * t3-apps dev wallet hooks, t3n-mcp session manager) all
696
+ * hit this trap before we tightened the type.
697
+ */
698
+ declare function createMlKemPublicKeyHandler(baseUrl: string): GuestToHostHandler;
699
+ /**
700
+ * Create Random handler backed by crypto.getRandomValues
701
+ * Note: The Rust Vec<u8> type serializes as an array of bytes, not a base64 string
702
+ */
703
+ declare function createRandomHandler(): GuestToHostHandler;
704
+ /**
705
+ * Create the default handler set required by the T3n handshake.
706
+ *
707
+ * @param baseUrl - **Required**. Forwarded to `createMlKemPublicKeyHandler`
708
+ * so the lazy /status fetch hits the right node.
709
+ */
710
+ declare function createDefaultHandlers(baseUrl: string): GuestToHostHandlers;
711
+
537
712
  /**
538
713
  * Cryptographic utilities for T3n SDK
539
714
  *
@@ -817,5 +992,5 @@ declare function clearKeyCache(): void;
817
992
  */
818
993
  declare function loadConfig(baseUrl?: string): SdkConfig;
819
994
 
820
- export { AuthMethod, AuthenticationError, HandshakeError, HttpTransport, LogLevel, MockTransport, NODE_URLS, RpcError, SessionStateError, SessionStatus, T3nClient, T3nError, WasmError, bytesToString, clearKeyCache, createEthAuthInput, createLogger, createOidcAuthInput, decodeWasmErrorMessage, extractWasmError, fetchDkgAttestation, fetchMlKemPublicKey, generateRandomString, generateUUID, getEnvironment, getEnvironmentName, getGlobalLogLevel, getLogger, getNodeUrl, getScriptVersion, loadConfig, loadWasmComponent, redactSecrets, redactSecretsFromJson, setEnvironment, setGlobalLogLevel, setNodeUrl, stringToBytes, validateConfig, verifyDkgAttestation, verifyTdxQuote };
821
- export type { AuthInput, AuthOutcome, ClientAuth, ClientHandshake, ClientSessionKeys, ConfigValidationResult, CookieIface, Did, DkgAttestation, DkgVerifyResult, Environment, EthAuthInput, HandshakeOutcome, HandshakeResult, JsonRpcRequest, JsonRpcResponse, Logger, OidcAuthInput, OidcCredentials, PeerQuoteResult, QuoteVerifyResult, SdkConfig, ServerHandshake, ServerOutcome, ServerSessionKeys, SessionCrypto, SessionHostImports, SessionId, T3nClientConfig, Transport, Validation, WasmComponent };
995
+ export { AuthMethod, AuthenticationError, HandshakeError, HttpTransport, LogLevel, MockTransport, NODE_URLS, RpcError, SessionStateError, SessionStatus, T3nClient, T3nError, WasmError, bytesToString, clearKeyCache, createDefaultHandlers, createEthAuthInput, createLogger, createMlKemPublicKeyHandler, createOidcAuthInput, createRandomHandler, decodeWasmErrorMessage, eth_get_address, extractWasmError, fetchDkgAttestation, fetchMlKemPublicKey, generateRandomString, generateUUID, getEnvironment, getEnvironmentName, getGlobalLogLevel, getLogger, getNodeUrl, getScriptVersion, loadConfig, loadWasmComponent, metamask_get_address, metamask_sign, redactSecrets, redactSecretsFromJson, setEnvironment, setGlobalLogLevel, setNodeUrl, stringToBytes, validateConfig, verifyDkgAttestation, verifyTdxQuote };
996
+ export type { AuthInput, ClientAuth, ClientHandshake, ConfigValidationResult, Did, DkgAttestation, DkgVerifyResult, Environment, EthAuthInput, GuestToHostHandler, GuestToHostHandlers, HandshakeResult, JsonRpcRequest, JsonRpcResponse, Logger, OidcAuthInput, OidcCredentials, PeerQuoteResult, QuoteVerifyResult, SdkConfig, SessionCrypto, SessionId, T3nClientConfig, Transport, WasmComponent, WasmNextResult };