@muhkoo/connect 0.1.0-alpha.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,2548 @@
1
+ /**
2
+ * @public
3
+ * @remarks
4
+ * The body type is a generic type that can be any value. Internally we handle the serialization and deserialization of the body.
5
+ */
6
+ type MessageBody = any;
7
+ /**
8
+ * @public
9
+ * @remarks
10
+ * The message headers type is a simple record object that contains key value pairs. Think of these as metadata for the message, similar to HTTP headers.
11
+ */
12
+ type MessageHeaders = Map<string, string>;
13
+ /**
14
+ * @public
15
+ * @remarks
16
+ * The message options type is a record object that contains the options for the message.
17
+ */
18
+ interface MessageOptions {
19
+ id?: string;
20
+ body: MessageBody;
21
+ status?: "pending" | "processed" | "failed" | "delivered";
22
+ checksum?: string;
23
+ }
24
+ /**
25
+ * @public
26
+ * @remarks
27
+ * The message class is a simple class that represents a message object.
28
+ * @sealed
29
+ */
30
+ declare class Message {
31
+ id: string;
32
+ timestamp: number;
33
+ status?: "pending" | "processed" | "failed" | "delivered";
34
+ checksum?: string;
35
+ private _body;
36
+ constructor(msg: MessageBody);
37
+ constructor(msg: MessageOptions);
38
+ get body(): MessageBody;
39
+ set body(value: MessageBody);
40
+ verifyChecksum: () => void;
41
+ private validatebodySize;
42
+ serialize: () => string;
43
+ static deserialize: (data: string) => Message;
44
+ }
45
+
46
+ type Address = string;
47
+ interface PacketOptions {
48
+ id?: string;
49
+ subject: string;
50
+ source: Address;
51
+ target: Address;
52
+ headers?: {
53
+ [key: string]: string | number | boolean | undefined;
54
+ };
55
+ message?: Message;
56
+ ttl?: number;
57
+ timestamp?: number;
58
+ signature?: string;
59
+ }
60
+ /**
61
+ * @public
62
+ * @remarks
63
+ * This class represents a message object that can be sent over the network
64
+ * @sealed
65
+ */
66
+ declare class Packet {
67
+ id: string;
68
+ subject: string;
69
+ source: Address;
70
+ target: Address;
71
+ headers?: PacketOptions['headers'];
72
+ message?: Message;
73
+ timestamp: number;
74
+ ttl?: number;
75
+ signature?: string;
76
+ constructor({ id, subject, source, target, headers, message, ttl, signature, timestamp }: PacketOptions);
77
+ serialize(): string;
78
+ static deserialize(data: string): Packet;
79
+ isExpired(): boolean;
80
+ }
81
+
82
+ declare function SerializeMessage(target: any, propertyKey: string, descriptor: PropertyDescriptor): PropertyDescriptor;
83
+
84
+ declare const decorators: {
85
+ SerializeMessage: typeof SerializeMessage;
86
+ };
87
+
88
+ /**
89
+ * Shared cryptographic types for Connect and Accelerator
90
+ */
91
+ /**
92
+ * Supported ECDH curves
93
+ */
94
+ type ECDHCurve = 'P-256' | 'P-384';
95
+ /**
96
+ * JSON Web Key format for public keys
97
+ */
98
+ interface PublicKeyJWK {
99
+ kty: 'EC';
100
+ crv: 'P-256' | 'P-384';
101
+ x: string;
102
+ y: string;
103
+ ext?: boolean;
104
+ }
105
+ /**
106
+ * Keypair with dehydrated public key (JWK) and CryptoKey private key
107
+ */
108
+ interface Keypair {
109
+ publicKey: PublicKeyJWK;
110
+ privateKey: CryptoKey;
111
+ }
112
+ /**
113
+ * Dehydrated keypair (both keys as strings for transport/storage)
114
+ */
115
+ interface DehydratedKeypair {
116
+ publicKey: string;
117
+ privateKey: string;
118
+ }
119
+
120
+ /**
121
+ * Shared identity types for Connect and Accelerator
122
+ */
123
+ /**
124
+ * User identity - public key is the primary identifier
125
+ */
126
+ interface UserIdentity {
127
+ publicKey: string;
128
+ accountType: 'self-sovereign' | 'custodial';
129
+ did?: string;
130
+ provider?: string;
131
+ createdAt: number;
132
+ }
133
+ /**
134
+ * Session
135
+ */
136
+ interface Session {
137
+ sessionId: string;
138
+ publicKey: string;
139
+ appPublicKey: string;
140
+ createdAt: number;
141
+ expiresAt: number;
142
+ sharedSecret?: string;
143
+ }
144
+ /**
145
+ * Authentication types
146
+ */
147
+ type AuthType = 'self-sovereign' | 'custodial';
148
+ /**
149
+ * OAuth provider types
150
+ */
151
+ type OAuthProvider = 'google' | 'github' | 'discord' | 'twitter';
152
+
153
+ /**
154
+ * Shared permission types for Connect and Accelerator
155
+ */
156
+ /**
157
+ * Permission types for resources
158
+ */
159
+ type Permission = 'read' | 'write' | 'delete' | 'admin' | 'share' | 'invite';
160
+ /**
161
+ * Role types
162
+ */
163
+ type Role = 'owner' | 'admin' | 'editor' | 'viewer' | 'member';
164
+ /**
165
+ * ACL Entry - grants permissions to a user (identified by public key)
166
+ */
167
+ interface ACLEntry {
168
+ subject: string;
169
+ permissions: Permission[];
170
+ role?: Role;
171
+ expiresAt?: number;
172
+ conditions?: {
173
+ ipAllowlist?: string[];
174
+ ipBlocklist?: string[];
175
+ validAfter?: number;
176
+ validBefore?: number;
177
+ };
178
+ }
179
+ /**
180
+ * Access Control List for a resource
181
+ */
182
+ interface ACL {
183
+ resource: string;
184
+ entries: Record<string, ACLEntry>;
185
+ defaultPermissions?: Permission[];
186
+ publicPermissions?: Permission[];
187
+ inheritFromParent?: boolean;
188
+ createdAt: number;
189
+ updatedAt: number;
190
+ createdBy: string;
191
+ }
192
+ /**
193
+ * Participant in a shared space
194
+ */
195
+ interface Participant {
196
+ publicKey: string;
197
+ role: Role;
198
+ addedAt: number;
199
+ addedBy: string;
200
+ customPermissions?: Permission[];
201
+ }
202
+ /**
203
+ * Permission check request
204
+ */
205
+ interface PermissionCheckRequest {
206
+ publicKey: string;
207
+ resource: string;
208
+ requiredPermissions: Permission | Permission[];
209
+ context?: {
210
+ timestamp?: number;
211
+ ipAddress?: string;
212
+ };
213
+ }
214
+ /**
215
+ * Permission check result
216
+ */
217
+ interface PermissionCheckResult {
218
+ granted: boolean;
219
+ reason?: string;
220
+ grantedBy?: string;
221
+ permissions: Permission[];
222
+ }
223
+
224
+ /**
225
+ * Shared messaging types for Connect and Accelerator
226
+ */
227
+ /**
228
+ * Message event for pub/sub messaging
229
+ */
230
+ interface MessageEvent {
231
+ type: string;
232
+ topic: string;
233
+ data: any;
234
+ senderPublicKey?: string;
235
+ timestamp: number;
236
+ }
237
+ /**
238
+ * Message subscription
239
+ */
240
+ interface Subscription {
241
+ connectionId: string;
242
+ publicKey: string;
243
+ topic: string;
244
+ filters?: any;
245
+ }
246
+ /**
247
+ * WebSocket message types
248
+ */
249
+ type WSMessageType = 'connect' | 'connected' | 'subscribe' | 'subscribed' | 'unsubscribe' | 'publish' | 'event' | 'message' | 'error';
250
+ /**
251
+ * WebSocket message
252
+ */
253
+ interface WSMessage {
254
+ type: WSMessageType;
255
+ topic?: string;
256
+ data?: any;
257
+ publicKey?: string;
258
+ connectionId?: string;
259
+ timestamp?: number;
260
+ }
261
+
262
+ /**
263
+ * Shared zero-knowledge types and circuit constants.
264
+ *
265
+ * Used by both the browser/server builds (where snarkjs handles proof generation
266
+ * and verification) and the workers build (where verification runs through
267
+ * `bn128.wasm` via `src/workers/groth16-verifier.ts`). Keeping these here ensures
268
+ * proof shapes and verification keys stay identical across runtimes.
269
+ */
270
+ /**
271
+ * Groth16 proof structure (BN128).
272
+ *
273
+ * - `pi_a`: G1 point as `[x, y]` or projective `[x, y, z]`.
274
+ * - `pi_b`: G2 point as `[[x0, x1], [y0, y1]]` or projective `[..., [z0, z1]]`.
275
+ * - `pi_c`: G1 point as `[x, y]` or projective `[x, y, z]`.
276
+ *
277
+ * Field elements are decimal-string-encoded BigInts (snarkjs convention).
278
+ */
279
+ interface Groth16Proof {
280
+ pi_a: string[];
281
+ pi_b: string[][];
282
+ pi_c: string[];
283
+ protocol?: string;
284
+ curve?: string;
285
+ }
286
+ /**
287
+ * Groth16 verification key (BN128).
288
+ *
289
+ * Field elements are decimal-string-encoded BigInts (snarkjs convention).
290
+ */
291
+ interface VerificationKey {
292
+ protocol: string;
293
+ curve: string;
294
+ nPublic: number;
295
+ vk_alpha_1: string[];
296
+ vk_beta_2: string[][];
297
+ vk_gamma_2: string[][];
298
+ vk_delta_2: string[][];
299
+ vk_alphabeta_12: string[][][];
300
+ IC: string[][];
301
+ }
302
+ /**
303
+ * Verification key for the `preimagePoK` circuit (Poseidon-commitment
304
+ * proof-of-knowledge with three public signals: commitment, nonce, ecdsaPubHash).
305
+ *
306
+ * Pinned here so worker-side and SDK-side verification agree on the key being
307
+ * checked. If the circuit is recompiled, regenerate this constant and
308
+ * `public/circuits/build/preimagePoK_verification_key.json` together.
309
+ */
310
+ declare const PREIMAGE_POK_VERIFICATION_KEY: VerificationKey;
311
+
312
+ /**
313
+ * WebCrypto subtle-interface resolver.
314
+ *
315
+ * Available natively in every runtime this SDK targets (modern browsers,
316
+ * Node 16+, CF Workers). Centralized here so the runtime check + error
317
+ * message are consistent across every primitive — and so a future polyfill
318
+ * shim has one place to plug in.
319
+ */
320
+ declare function getSubtle(): SubtleCrypto;
321
+
322
+ /**
323
+ * Cryptographically secure random bytes.
324
+ *
325
+ * Wraps `crypto.getRandomValues` with two small affordances every caller
326
+ * needs:
327
+ * - Allocates over a fresh, non-shared `ArrayBuffer` so the returned
328
+ * `Uint8Array` satisfies WebCrypto's `BufferSource` type under strict
329
+ * TS lib settings (where `Uint8Array<ArrayBufferLike>` isn't directly
330
+ * assignable).
331
+ * - Chunks requests larger than 65 KiB, the per-call cap browsers enforce
332
+ * on `getRandomValues`.
333
+ */
334
+ declare function randomBytes(n: number): Uint8Array;
335
+
336
+ /**
337
+ * AES-256-GCM encryption / decryption with raw key bytes.
338
+ *
339
+ * The two functions here are the single canonical AES-GCM call site in the
340
+ * SDK. Earlier copies of this logic existed inline in `DoubleRatchet`, in the
341
+ * storage `ChunkCipher`, and in the passphrase-wrap helpers — they all now
342
+ * route through here so the algorithm, IV size, and tag handling stay in one
343
+ * place.
344
+ *
345
+ * Conventions:
346
+ * - 256-bit keys only. Passing 16- or 24-byte keys throws; callers that
347
+ * need AES-128 should add a dedicated primitive.
348
+ * - 96-bit IVs. WebCrypto's recommended GCM IV length; trades the 4-byte
349
+ * IV economy of 128-bit for compatibility with every spec-compliant
350
+ * implementation.
351
+ * - The 16-byte GCM authentication tag is appended to the ciphertext by
352
+ * WebCrypto's default behavior. Callers don't manage it separately.
353
+ *
354
+ * If a future need arises for AAD (associated data), add an optional `aad`
355
+ * parameter to both functions rather than introducing a parallel primitive.
356
+ */
357
+ /** Length of the AES key in bytes. */
358
+ declare const AES_GCM_KEY_BYTES = 32;
359
+ /** Length of the AES-GCM IV in bytes (WebCrypto-recommended). */
360
+ declare const AES_GCM_IV_BYTES = 12;
361
+ /** Length of the AES-GCM authentication tag in bytes (appended to ciphertext). */
362
+ declare const AES_GCM_TAG_BYTES = 16;
363
+ /**
364
+ * Encrypt `plaintext` under a 32-byte AES-256 key with the supplied 12-byte
365
+ * IV. Returns ciphertext with the 16-byte GCM tag appended (so the output
366
+ * length is `plaintext.length + 16`).
367
+ *
368
+ * The IV must be unique per (key, message). Callers that generate the IV
369
+ * randomly should grab it via `randomBytes(AES_GCM_IV_BYTES)`; callers
370
+ * managing a counter pass their own.
371
+ */
372
+ declare function encryptAesGcm(key: Uint8Array, iv: Uint8Array, plaintext: Uint8Array): Promise<Uint8Array>;
373
+ /**
374
+ * Inverse of {@link encryptAesGcm}. Throws if the GCM auth tag fails — this
375
+ * captures "wrong key", "wrong IV", or "ciphertext was tampered with",
376
+ * indistinguishably. Callers that care about distinguishing those should
377
+ * keep separate provenance bookkeeping, not invent a parallel MAC.
378
+ */
379
+ declare function decryptAesGcm(key: Uint8Array, iv: Uint8Array, ciphertext: Uint8Array): Promise<Uint8Array>;
380
+
381
+ /**
382
+ * Key-derivation primitives — PBKDF2 (for passphrase → key) and HKDF (for
383
+ * symmetric key material → expanded key material).
384
+ *
385
+ * PBKDF2 is the slow, salted KDF the passphrase-wrap helper uses to harden
386
+ * user passwords against offline guessing. HKDF is the fast, deterministic
387
+ * KDF the Double Ratchet uses to derive root + chain + message keys from
388
+ * shared secrets.
389
+ *
390
+ * Both are SHA-256-backed everywhere in this codebase — keep it that way
391
+ * unless there's a concrete reason to diverge.
392
+ */
393
+ /**
394
+ * Iteration count default for PBKDF2 passphrase hardening. Tuned for
395
+ * 2024–2026 hardware: slow enough to deter offline guessing of weak
396
+ * passphrases (~100ms on commodity laptops), fast enough that interactive
397
+ * unlock stays sub-second.
398
+ */
399
+ declare const PBKDF2_DEFAULT_ITERATIONS = 200000;
400
+ /**
401
+ * Derive a 256-bit AES-GCM key from `passphrase + salt` via PBKDF2-SHA256.
402
+ *
403
+ * Returned as raw bytes (not a `CryptoKey`) so callers can hand it straight
404
+ * to {@link encryptAesGcm} — that's the convention everywhere else in the
405
+ * primitive layer. If a future caller needs the non-extractable
406
+ * `CryptoKey` form, add a parallel `deriveCryptoKeyFromPassphrase` variant
407
+ * rather than overloading this one.
408
+ */
409
+ declare function deriveAesKeyFromPassphrase(passphrase: string, salt: Uint8Array, iterations?: number): Promise<Uint8Array>;
410
+ /**
411
+ * HKDF-Expand with SHA-256, returning `lengthBytes` raw bytes of key material.
412
+ *
413
+ * `info` is the per-purpose label (e.g. `"DoubleRatchetMsg"`). Optional
414
+ * `salt` defaults to an empty buffer, matching the way the Double Ratchet
415
+ * code has always used it.
416
+ *
417
+ * Note: this is intentionally a one-shot Extract-then-Expand call rather than
418
+ * exposing Extract and Expand separately — every call site in this codebase
419
+ * wants the same shape, and a richer API can be added later if needed.
420
+ */
421
+ declare function deriveBitsHkdf(inputKeyMaterial: Uint8Array, info: string, lengthBytes: number, salt?: Uint8Array): Promise<Uint8Array>;
422
+
423
+ interface KeyPair {
424
+ privateKey: CryptoKey | null;
425
+ publicKey: CryptoKey;
426
+ }
427
+ interface AuthKeyPair {
428
+ privateKey: CryptoKey | null;
429
+ publicKey: CryptoKey;
430
+ }
431
+ interface DehydratedKeys {
432
+ ecdhPub: string;
433
+ ecdhPriv: string;
434
+ ecdsaPub: string;
435
+ ecdsaPriv: string;
436
+ }
437
+ declare class KeyStore {
438
+ private static instance;
439
+ private _keys;
440
+ private _authKeys;
441
+ private constructor();
442
+ static getInstance(): KeyStore;
443
+ get keys(): Map<string, KeyPair>;
444
+ get authKeys(): Map<string, AuthKeyPair>;
445
+ generateOwnKeyPair(id: string): Promise<KeyPair>;
446
+ storeRemotePublicKeys(id: string, ecdhPublicKey: CryptoKey, ecdsaPublicKey: CryptoKey): Promise<void>;
447
+ hydrateKeyPair(id: string, dehydrated: DehydratedKeys): Promise<void>;
448
+ getKeyPair(id: string): KeyPair | null;
449
+ getAuthKeyPair(id: string): AuthKeyPair | null;
450
+ dehydrateKeyPair(id: string): Promise<DehydratedKeys>;
451
+ getRawEcdsaPublicKey(id: string): Promise<Uint8Array | null>;
452
+ packDehydratedKeys(id: string): Promise<string>;
453
+ hydrateFromPacked(id: string, masterKey: string): Promise<void>;
454
+ }
455
+
456
+ interface CipherMessageHeader {
457
+ dhPub: string;
458
+ prevChainLength: number;
459
+ messageNumber: number;
460
+ sessionId: string;
461
+ sessionType: 'global' | 'specific';
462
+ senderId: string;
463
+ recipientId: string;
464
+ timestamp: number;
465
+ signature: string;
466
+ }
467
+
468
+ interface CipherMessage {
469
+ header: CipherMessageHeader;
470
+ ciphertext: string; // Hex-encoded
471
+ nonce: string; // Hex-encoded
472
+ }
473
+
474
+ // interface RatchetState {
475
+ // clientDhPriv: Uint8Array;
476
+ // clientDhPub: Uint8Array;
477
+ // serverDhPriv: Uint8Array;
478
+ // serverDhPub: Uint8Array;
479
+ // rootKey: Uint8Array | null;
480
+ // sendChainKey: Uint8Array | null;
481
+ // recvChainKey: Uint8Array | null;
482
+ // sendCount: number;
483
+ // recvCount: number;
484
+ // prevChainLength: number;
485
+ // skippedKeys: Map<number, Uint8Array>;
486
+ // }
487
+
488
+ // interface RatchetState {
489
+ // clientDhPriv: CryptoKey | null; // ECDH private key
490
+ // clientDhPub: CryptoKey; //
491
+ // serverDhPriv: CryptoKey | null; // ECDH private key
492
+ // serverDhPub: CryptoKey; // ECDH public key
493
+ // rootKey: CryptoKey | null; // AES-GCM key
494
+ // sendChainKey: CryptoKey | null; // AES-GCM key for sending
495
+ // recvChainKey: CryptoKey | null; // AES-GCM key for receiving
496
+ // sendCount: number; // Number of messages sent
497
+ // recvCount: number; // Number of messages received
498
+ // prevChainLength: number; // Length of the previous chain
499
+ // skippedKeys: Map<number, CryptoKey>; // Skipped keys for messages
500
+ // }
501
+
502
+ interface RatchetState {
503
+ clientDhPriv: CryptoKey | null;
504
+ clientDhPub: CryptoKey ;
505
+ serverDhPriv: CryptoKey | null;
506
+ serverDhPub: CryptoKey ;
507
+ // Symmetric key material (root key + chain keys + skipped message keys).
508
+ // Stored as Uint8Array rather than Node's Buffer so this type works in the
509
+ // browser/Workers builds.
510
+ rootKey: Uint8Array | null;
511
+ sendChainKey: Uint8Array | null;
512
+ recvChainKey: Uint8Array | null;
513
+ sendCount: number;
514
+ recvCount: number;
515
+ prevChainLength: number;
516
+ currentSkippedKeys: Map<number, Uint8Array>;
517
+ oldSkippedMessageKeys: Map<string, { skips: Map<number, Uint8Array>; created: number }>;
518
+ }
519
+
520
+ declare class DoubleRatchet {
521
+ private state;
522
+ private sessionType;
523
+ private maxSkip;
524
+ private overlapPeriod;
525
+ private windowSize;
526
+ constructor(senderId: string, recipientId: string, sessionType: 'global' | 'specific', isClient?: boolean);
527
+ private hkdf;
528
+ initializeSession(isClient?: boolean): Promise<void>;
529
+ private dhRatchet;
530
+ private symmetricRatchet;
531
+ encrypt(plaintext: string, newDhKey: boolean | undefined, senderId: string, recipientId: string, sessionId: string, sessionType: 'global' | 'specific'): Promise<CipherMessage>;
532
+ private cleanOldSkippedKeys;
533
+ decrypt(message: CipherMessage, isClient?: boolean): Promise<string>;
534
+ getState(): RatchetState;
535
+ setState(state: RatchetState): void;
536
+ }
537
+
538
+ type SnarkProof = Groth16Proof;
539
+
540
+ interface ZkCompiled {
541
+ verificationKey: VerificationKey;
542
+ }
543
+ declare class AuthPublicInput {
544
+ commitment: string;
545
+ nonce: string;
546
+ ecdsaPubHash: string;
547
+ constructor(commitment: string, nonce: string, ecdsaPubHash: string);
548
+ toPublicSignals(): string[];
549
+ }
550
+ declare class Field {
551
+ private value;
552
+ static readonly FIELD_SIZE: bigint;
553
+ constructor(value: bigint | string | number);
554
+ toBigInt(): bigint;
555
+ toString(): string;
556
+ toHex(): string;
557
+ equals(other: Field): boolean;
558
+ static fromHex(hex: string): Field;
559
+ }
560
+ declare class Poseidon {
561
+ static hash(inputs: Field[]): Promise<Field>;
562
+ }
563
+ declare function encodeToHex(field: Field): string;
564
+ declare function decodeFromHex(hexStr: string): Field;
565
+ interface CircuitBufferConfig {
566
+ wasm: Uint8Array;
567
+ zkey: Uint8Array;
568
+ verificationKey: VerificationKey;
569
+ }
570
+ declare class HashKnowledge {
571
+ private static wasmBuffer?;
572
+ private static zkeyBuffer?;
573
+ private static verificationKey?;
574
+ static setCircuitBuffers(wasm: Uint8Array, zkey: Uint8Array, verificationKey: VerificationKey): void;
575
+ static compile(): Promise<ZkCompiled>;
576
+ static prove(secret: Field): Promise<{
577
+ proof: SnarkProof;
578
+ publicSignals: string[];
579
+ }>;
580
+ static verify(proof: SnarkProof, publicSignals?: string[]): Promise<boolean>;
581
+ }
582
+ declare class PreimagePoK {
583
+ private static wasmBuffer?;
584
+ private static zkeyBuffer?;
585
+ private static verificationKey?;
586
+ static setCircuitBuffers(wasm: Uint8Array, zkey: Uint8Array, verificationKey: VerificationKey): void;
587
+ static compile(): Promise<ZkCompiled>;
588
+ static prove(publicInput: AuthPublicInput, secret: Field, salt: Field, ecdsaPub: Field): Promise<{
589
+ proof: SnarkProof;
590
+ publicSignals: string[];
591
+ }>;
592
+ static verify(proof: SnarkProof, publicSignals?: string[]): Promise<boolean>;
593
+ }
594
+ declare function verifyHashKnowledge(proof: SnarkProof, expectedOutput?: Field, publicSignals?: string[]): Promise<boolean>;
595
+ declare function verifyPreimagePoK(proof: SnarkProof, publicSignals?: string[]): Promise<boolean>;
596
+ declare function verify(programType: 'HashKnowledge' | 'PreimagePoK', proof: SnarkProof, options?: {
597
+ expectedOutput?: Field;
598
+ publicSignals?: string[];
599
+ }): Promise<boolean>;
600
+ declare function quickVerify(program: typeof HashKnowledge | typeof PreimagePoK, proof: SnarkProof, publicSignals?: string[]): Promise<boolean>;
601
+ declare function initializeCircuits(config: {
602
+ hashKnowledge?: CircuitBufferConfig;
603
+ preimagePoK?: CircuitBufferConfig;
604
+ }): void;
605
+ declare function compilePrograms(): Promise<{
606
+ hashKnowledge: ZkCompiled;
607
+ preimagePoK: ZkCompiled;
608
+ }>;
609
+
610
+ interface AuthToken {
611
+ peerId: string;
612
+ timestamp: number;
613
+ signature: string;
614
+ }
615
+ declare class Authenticator {
616
+ private trustedKeys;
617
+ private zkVerificationKey;
618
+ initializeZK(): Promise<void>;
619
+ getZKVerificationKey(): VerificationKey | null;
620
+ addTrustedServer(serverId: string, publicKey: CryptoKey): void;
621
+ generateAuthToken(peerId: string, privateKey: CryptoKey): Promise<AuthToken>;
622
+ verifyAuthToken(token: AuthToken, clientPublicKey: CryptoKey): Promise<boolean>;
623
+ verifyZKProof(proof: any, publicInput: AuthPublicInput, storedCommitment: Field, ecdsaPub: CryptoKey): Promise<boolean>;
624
+ }
625
+
626
+ declare class DoubleRatchetManager {
627
+ private sessions;
628
+ private sessionStates;
629
+ private keyStore;
630
+ private _authenticator;
631
+ private registeredUsers;
632
+ private id;
633
+ constructor(id: string);
634
+ get authenticator(): Authenticator;
635
+ addTrustedServer(serverId: string, publicKey: CryptoKey): Promise<void>;
636
+ registerZK(clientId: string, secret: Field, salt: Field, ecdsaPub: CryptoKey): Promise<void>;
637
+ performHandshake(senderId: string, recipientId: string, zkProof: any, publicInput: AuthPublicInput, clientPublicKey: CryptoKey, // ECDH pub for session
638
+ clientAuthPublicKey: CryptoKey, // ECDSA pub for auth
639
+ authToken: AuthToken): Promise<void>;
640
+ initializeSession(senderId: string, recipientId: string, isClient?: boolean, sessionType?: 'global' | 'specific', sessionId?: string, zkProof?: any, publicInput?: AuthPublicInput, clientPublicKey?: CryptoKey, clientAuthPublicKey?: CryptoKey, clientEcdhPrivateKey?: CryptoKey, clientAuthPrivateKey?: CryptoKey): Promise<string>;
641
+ encrypt(senderId: string, recipientId: string, sessionId: string, plaintext: string, newDhKey?: boolean, sessionType?: 'global' | 'specific'): Promise<CipherMessage>;
642
+ decrypt(message: CipherMessage, isClient?: boolean): Promise<string>;
643
+ getSessionSharedSecret(sessionId: string): Promise<Uint8Array | null>;
644
+ private saveState;
645
+ private loadState;
646
+ }
647
+
648
+ /**
649
+ * Per-chunk AES-256-GCM encryption with random keys and IVs.
650
+ *
651
+ * Each chunk of a stored file gets its own freshly generated 256-bit AES key
652
+ * and 96-bit IV. The (key, IV) pair lives in the file manifest, which the
653
+ * application is responsible for storing under whatever access-control regime
654
+ * it uses (in this codebase: the `SharedSpaceClient` writes manifests into
655
+ * ZK-gated multi-user spaces; the shard store itself is open).
656
+ *
657
+ * Per-chunk keys (rather than a per-file DEK) lets future versions revoke or
658
+ * re-share individual chunks without re-encrypting the whole file. The
659
+ * additional manifest size is dominated by the shard hash list anyway.
660
+ *
661
+ * The cipher itself delegates to the canonical `encryptAesGcm` / `decryptAesGcm`
662
+ * primitives — this class only wraps them with key-material generation and
663
+ * the JSON-friendly base64 serialization the manifest needs.
664
+ */
665
+ /**
666
+ * Per-chunk key material, base64-encoded for JSON-friendly storage in the
667
+ * manifest. The IV is unique per chunk; the key is unique per chunk too in
668
+ * this codebase, though that's a stricter property than AES-GCM strictly
669
+ * requires (which is "unique IV per key").
670
+ */
671
+ interface ChunkKeyMaterial {
672
+ /** Base64-encoded 32-byte AES-256 key. */
673
+ key: string;
674
+ /** Base64-encoded 12-byte AES-GCM IV. */
675
+ iv: string;
676
+ }
677
+ declare class ChunkCipher {
678
+ /**
679
+ * Generate a fresh random (key, iv) pair encoded for the manifest.
680
+ * Callers should generate one of these per chunk before calling
681
+ * {@link encryptChunk}.
682
+ */
683
+ generateKeyMaterial(): ChunkKeyMaterial;
684
+ /**
685
+ * Encrypt `plaintext` under the supplied key + IV. Returns the AES-GCM
686
+ * ciphertext with the 16-byte auth tag appended (WebCrypto's default
687
+ * GCM behavior).
688
+ */
689
+ encryptChunk(plaintext: Uint8Array, material: ChunkKeyMaterial): Promise<Uint8Array>;
690
+ /**
691
+ * Inverse of {@link encryptChunk}. Throws if the auth tag fails — that
692
+ * means the ciphertext was tampered with, the key/iv was wrong, or the
693
+ * shards were reassembled incorrectly.
694
+ */
695
+ decryptChunk(ciphertext: Uint8Array, material: ChunkKeyMaterial): Promise<Uint8Array>;
696
+ }
697
+
698
+ /**
699
+ * Passphrase-based wrap / unwrap helpers using WebCrypto.
700
+ *
701
+ * Encrypt a payload with a user-supplied passphrase before persisting it
702
+ * somewhere the server can see (e.g. the manifest blob in a ZK-gated
703
+ * SharedSpace, or chat-key material in a PersonalSpace). The server never
704
+ * sees the passphrase or the plaintext — both wrap and unwrap run entirely
705
+ * client-side.
706
+ *
707
+ * Algorithm (the only one this codebase has ever shipped):
708
+ * - PBKDF2-SHA256 with 200_000 iterations derives a 256-bit AES key from
709
+ * `passphrase || salt`.
710
+ * - AES-256-GCM encrypts the plaintext with a fresh random 12-byte IV.
711
+ * - The auth tag is appended to the ciphertext by WebCrypto's default
712
+ * GCM behaviour, so {@link WrappedPayload.ciphertext} carries it implicitly.
713
+ *
714
+ * The returned {@link WrappedPayload} is JSON-friendly (all base64 fields), so
715
+ * it can be `JSON.stringify`-ed straight into a KV layer.
716
+ */
717
+ /**
718
+ * The persisted, JSON-friendly representation of a passphrase-wrapped blob.
719
+ *
720
+ * The `alg` field is a self-describing marker — if we ever rotate KDF/cipher,
721
+ * unwrap can switch on this value to keep older payloads readable.
722
+ */
723
+ interface WrappedPayload {
724
+ /** Random per-wrap salt for PBKDF2, base64. */
725
+ salt: string;
726
+ /** AES-GCM IV, base64. */
727
+ iv: string;
728
+ /** AES-GCM ciphertext (includes 16-byte GCM auth tag), base64. */
729
+ ciphertext: string;
730
+ /** Algorithm marker so future readers know what they're looking at. */
731
+ alg: "PBKDF2-SHA256/AES-256-GCM";
732
+ /** PBKDF2 iteration count used. */
733
+ iter: number;
734
+ }
735
+ /**
736
+ * Encrypt `plaintext` under a key derived from `passphrase`. The returned
737
+ * payload bundles the salt, IV, and ciphertext so unwrap only needs the
738
+ * passphrase to reverse the operation.
739
+ */
740
+ declare function wrapWithPassphrase(passphrase: string, plaintext: Uint8Array): Promise<WrappedPayload>;
741
+ /**
742
+ * Decrypt a {@link WrappedPayload} produced by {@link wrapWithPassphrase}.
743
+ * Throws if the passphrase is wrong, the payload is tampered with, or the
744
+ * algorithm marker is unrecognised.
745
+ */
746
+ declare function unwrapWithPassphrase(passphrase: string, wrapped: WrappedPayload): Promise<Uint8Array>;
747
+
748
+ declare enum EventCoreEvents {
749
+ CONNECTED = "connected",
750
+ DISCONNECTED = "disconnected",
751
+ RECONNECTING = "reconnecting",
752
+ ERROR = "error",
753
+ DATA_RECEIVED = "data_received",
754
+ DATA_SENT = "data_sent",
755
+ MESSAGE = "message",
756
+ GET_HISTORY = "get_history",
757
+ RECEIVED_HISTORY = "received_history"
758
+ }
759
+ /**
760
+ * @public
761
+ * EventCore is a static event emitter with singleton functionality for global event handling.
762
+ */
763
+ declare class EventCore {
764
+ private static eventTarget;
765
+ private static events;
766
+ /**
767
+ * @remarks Listen to an event.
768
+ * @example EventCore.on('event', (e) =\> appLogger.debug(e));
769
+ */
770
+ static on(event: EventCoreEvents, handler: EventListener | CallableFunction): void;
771
+ /**
772
+ * @remarks Stop listening to an event.
773
+ * @example EventCore.off('event', handler);
774
+ */
775
+ static off(event: EventCoreEvents, handler: EventListener | CallableFunction): void;
776
+ /**
777
+ * @remarks Emit an event.
778
+ * @example EventCore.emit('event', data);
779
+ */
780
+ static emit(event: EventCoreEvents, data: any): void;
781
+ }
782
+
783
+ /**
784
+ * An end-to-end-encrypted communication session for one local identity
785
+ * (`myId`) talking to an arbitrary set of peers. Wraps `KeyStore` +
786
+ * per-pair `DoubleRatchet` instances and owns the protocol bookkeeping
787
+ * (handshake reciprocation, dedup, role assignment, broadcast fan-out,
788
+ * recipient filtering) so application code doesn't have to.
789
+ *
790
+ * Transport-agnostic by design: the Session never touches a WebSocket
791
+ * (or HTTP, or anything else). Application code:
792
+ * - calls `session.encrypt(text)` and gets back an array of frames it
793
+ * ships over whatever transport it has;
794
+ * - calls `session.receive(frame)` with each inbound frame and acts on
795
+ * the typed result (plaintext, handshake-done, ignored).
796
+ *
797
+ * Frames are plain JSON objects with the same shape the existing chat
798
+ * protocol uses (`{ keyExchange: {...} }` and `{ cipherMessage: {...} }`),
799
+ * so this can drop into a chat app without changing the wire format.
800
+ */
801
+
802
+ interface SessionOptions {
803
+ /** This client's identifier (e.g. username / DID / public-key string). */
804
+ myId: string;
805
+ }
806
+ /** A handshake frame the application ships to peers (or receives from them). */
807
+ interface KeyExchangeFrame {
808
+ type: "handshake" | "update";
809
+ userId: string;
810
+ /** Base64-encoded JWK of the sender's ECDH public key. */
811
+ ecdhPublicKey: string;
812
+ /** Base64-encoded JWK of the sender's ECDSA public key. */
813
+ ecdsaPublicKey: string;
814
+ }
815
+ /** A ciphertext frame, addressed to exactly one peer. */
816
+ interface CipherFrame {
817
+ cipherMessage: CipherMessage;
818
+ }
819
+ /** Anything inbound the application might hand to `session.receive()`. */
820
+ type IncomingFrame = {
821
+ keyExchange: KeyExchangeFrame;
822
+ } | {
823
+ cipherMessage: CipherMessage;
824
+ } | Record<string, unknown>;
825
+ /** Outcome of feeding one frame through `session.receive()`. */
826
+ type ReceiveResult = {
827
+ kind: "plaintext";
828
+ /** The sender's id (from the cipherMessage header). */
829
+ from: string;
830
+ /** Decrypted message body. */
831
+ text: string;
832
+ /** The original cipherMessage, for callers that want it. */
833
+ cipherMessage: CipherMessage;
834
+ } | {
835
+ kind: "handshake";
836
+ /** The peer whose handshake was just processed. */
837
+ peerId: string;
838
+ /**
839
+ * If non-null, the application MUST send this frame back to the peer
840
+ * (e.g. broadcast on the room WebSocket). It's our reciprocation —
841
+ * giving the peer our public keys so they can verify our messages.
842
+ *
843
+ * The Session dedups internally: we only emit this on the first
844
+ * handshake we process for a given peer in this Session's lifetime.
845
+ */
846
+ outbound: {
847
+ keyExchange: KeyExchangeFrame;
848
+ } | null;
849
+ } | {
850
+ kind: "ignored";
851
+ /** Why we ignored the frame (debugging aid; not for branching on). */
852
+ reason: string;
853
+ };
854
+ declare class EncryptedSession {
855
+ private readonly myId;
856
+ private readonly keyStore;
857
+ /** One DoubleRatchet per peer pair. */
858
+ private readonly ratchets;
859
+ /** Peers we've already sent our keyExchange to in this Session. */
860
+ private readonly sentHandshakeTo;
861
+ /** Resolved when own keys are generated and ready to use. */
862
+ private readonly _ready;
863
+ private _resolveReady;
864
+ private _initialized;
865
+ constructor(opts: SessionOptions);
866
+ /** A promise that resolves once `initialize()` has completed. */
867
+ get ready(): Promise<void>;
868
+ /** Our local identifier. */
869
+ get id(): string;
870
+ /**
871
+ * Generate this client's ECDH + ECDSA keypair (idempotent — safe to call
872
+ * multiple times across reconnects; only the first call generates).
873
+ */
874
+ initialize(): Promise<void>;
875
+ /**
876
+ * Build the keyExchange frame this client should send to peers when it
877
+ * comes online. Application is responsible for actually transmitting it
878
+ * (e.g. ws.send(JSON.stringify(frame))).
879
+ */
880
+ getOwnKeyExchange(): Promise<{
881
+ keyExchange: KeyExchangeFrame;
882
+ }>;
883
+ /**
884
+ * Encrypt a plaintext to every peer we currently have a ratchet for, and
885
+ * return one cipherMessage frame per recipient. The application sends
886
+ * each frame over its transport.
887
+ *
888
+ * If there are no peers yet (or all peers are still mid-handshake), the
889
+ * returned array may be empty — the caller should typically render the
890
+ * outgoing message locally in that case (since there's no one to send
891
+ * it to yet).
892
+ */
893
+ encrypt(plaintext: string): Promise<CipherFrame[]>;
894
+ /**
895
+ * Hand an inbound frame to the session. Returns a typed result the
896
+ * application acts on.
897
+ *
898
+ * - `kind: 'plaintext'` — decrypted; render `result.text`
899
+ * - `kind: 'handshake'` — a peer's keys are now stored, and if
900
+ * `result.outbound` is set, send it
901
+ * - `kind: 'ignored'` — not for us, malformed, duplicate, etc.
902
+ *
903
+ * Calling code should always handle `'ignored'` (no UI side effect).
904
+ */
905
+ receive(frame: IncomingFrame): Promise<ReceiveResult>;
906
+ /** List of peer ids we have an established ratchet for. */
907
+ peers(): string[];
908
+ /** Has the handshake completed for this peer? */
909
+ hasRatchetFor(peerId: string): boolean;
910
+ /**
911
+ * Tear down a peer's ratchet (e.g. when they leave the room). The peer's
912
+ * stored keys remain in `KeyStore` so a fresh handshake can be set up
913
+ * later without re-fetching them, but the ratchet state is dropped.
914
+ */
915
+ forgetPeer(peerId: string): void;
916
+ /** Deterministic per-pair session id — same string on both sides. */
917
+ private _sessionIdFor;
918
+ }
919
+
920
+ /**
921
+ * Pure WebSocket transport — owns the socket lifecycle and nothing else.
922
+ *
923
+ * Hands raw frame strings up (via the `MESSAGE` event) and accepts raw frame
924
+ * strings down (via `send`). Anything frame-shape, encryption, or serialization
925
+ * related belongs in the layer above (e.g. `Network` for client↔server
926
+ * encrypted pipes, `BroadcastChannel` for multi-peer E2E rooms).
927
+ *
928
+ * Responsibilities:
929
+ * - Open / close the WebSocket
930
+ * - Auto-reconnect with linear backoff after an unexpected close
931
+ * - Buffer outbound frames while disconnected (capped queue)
932
+ * - Emit lifecycle events (CONNECTED / DISCONNECTED / RECONNECTING / ERROR)
933
+ * plus raw inbound MESSAGE events
934
+ */
935
+
936
+ interface WSTransportOptions {
937
+ /** WebSocket URL, e.g. `ws://localhost:8787/foo`. */
938
+ url: string;
939
+ /** Reconnect automatically after unexpected disconnect (default `true`). */
940
+ autoReconnect?: boolean;
941
+ /** Delay between reconnect attempts in ms (default `3000`). */
942
+ reconnectDelay?: number;
943
+ /** Cap on reconnect attempts (default `5`; `0` means unlimited). */
944
+ maxReconnectAttempts?: number;
945
+ /**
946
+ * Max outbound frames buffered while disconnected. Sending past this caps
947
+ * throws synchronously. Default `100`.
948
+ */
949
+ maxQueueSize?: number;
950
+ }
951
+ declare class WSTransport extends EventCore {
952
+ emit: typeof EventCore.emit;
953
+ on: typeof EventCore.on;
954
+ off: typeof EventCore.off;
955
+ private url;
956
+ private socket;
957
+ private _isConnected;
958
+ private _isConnecting;
959
+ private autoReconnect;
960
+ private reconnectDelay;
961
+ private maxReconnectAttempts;
962
+ private reconnectAttempts;
963
+ private reconnectTimeout;
964
+ private outboundQueue;
965
+ private maxQueueSize;
966
+ constructor(opts: WSTransportOptions);
967
+ /** Open the socket. Resolves when CONNECTED fires. Rejects on first error. */
968
+ connect(): Promise<void>;
969
+ /** Close the socket and stop reconnecting. */
970
+ disconnect(): void;
971
+ /**
972
+ * Send a raw frame. If currently disconnected, the frame is queued and sent
973
+ * after the next successful reconnect (up to `maxQueueSize`).
974
+ */
975
+ send(frame: string): void;
976
+ isConnected(): boolean;
977
+ isConnecting(): boolean;
978
+ queuedFrames(): number;
979
+ reconnectAttemptCount(): number;
980
+ private scheduleReconnect;
981
+ private flushQueue;
982
+ }
983
+
984
+ /**
985
+ * BroadcastChannel — a turnkey end-to-end encrypted channel for multi-peer
986
+ * "room"-style apps (e.g. a chat). Wires together:
987
+ *
988
+ * - `WSTransport` for socket lifecycle, reconnect, and offline frame queue
989
+ * - `EncryptedSession` for per-peer Double Ratchet state, handshake
990
+ * reciprocation, fan-out encryption, and recipient filtering
991
+ *
992
+ * The host app only deals with high-level events and method calls; it never
993
+ * touches `new WebSocket(...)` or builds cipherMessage frames by hand.
994
+ *
995
+ * Wire protocol (kept compatible with the existing chat protocol so this
996
+ * drops into the deployed Accelerator without server changes):
997
+ *
998
+ * outbound: `{ keyExchange: {...} }` — produced by us on `announce()`
999
+ * `{ cipherMessage: {...} }` — produced by `send(text)`,
1000
+ * one frame per peer
1001
+ * arbitrary JSON — sent via `sendRaw(obj)`
1002
+ *
1003
+ * inbound: `{ keyExchange: {...} }` — routed into EncryptedSession;
1004
+ * emits `peer_handshake`
1005
+ * `{ cipherMessage: {...} }` — decrypted; emits `message`
1006
+ * anything else — passes through as `raw_frame`
1007
+ * (the app decides what to do)
1008
+ *
1009
+ * **Event scoping.** Unlike `Network`, BroadcastChannel uses a per-instance
1010
+ * `EventTarget`, so two channels (e.g. two rooms in the same app) don't
1011
+ * cross-contaminate each other's events.
1012
+ *
1013
+ * Note: this class shadows the `BroadcastChannel` Web API global (cross-tab
1014
+ * messaging) only when imported by name. They're unrelated.
1015
+ */
1016
+
1017
+ /**
1018
+ * Event names BroadcastChannel emits. Lifecycle events reuse the
1019
+ * `EventCoreEvents` string values (`connected`, `disconnected`, etc.) so
1020
+ * apps can use shared constants if they want. Channel-specific events are
1021
+ * prefixed `channel:` to avoid colliding with other event systems in the
1022
+ * same app.
1023
+ */
1024
+ declare const BroadcastChannelEvents: {
1025
+ readonly CONNECTED: EventCoreEvents.CONNECTED;
1026
+ readonly DISCONNECTED: EventCoreEvents.DISCONNECTED;
1027
+ readonly RECONNECTING: EventCoreEvents.RECONNECTING;
1028
+ readonly ERROR: EventCoreEvents.ERROR;
1029
+ /** A peer's ratchet is now ready. `detail.peerId` is the peer's id. */
1030
+ readonly PEER_HANDSHAKE: "channel:peer_handshake";
1031
+ /** Decrypted message. `detail` is `{ from, text, cipherMessage }`. */
1032
+ readonly MESSAGE: "channel:message";
1033
+ /** Inbound frame BroadcastChannel didn't recognize. App handles it. */
1034
+ readonly RAW_FRAME: "channel:raw_frame";
1035
+ };
1036
+ type BroadcastChannelEvent = (typeof BroadcastChannelEvents)[keyof typeof BroadcastChannelEvents];
1037
+ interface BroadcastChannelOptions extends WSTransportOptions {
1038
+ /** This client's identifier (username, DID, public-key string, etc.). */
1039
+ myId: string;
1040
+ /**
1041
+ * If true (default false), automatically send our keyExchange as soon as
1042
+ * the socket is connected. For chat-style apps that send a `{name}` frame
1043
+ * first, leave this false and call `announce()` after that handshake.
1044
+ */
1045
+ autoAnnounce?: boolean;
1046
+ }
1047
+ declare class BroadcastChannel {
1048
+ readonly transport: WSTransport;
1049
+ readonly session: EncryptedSession;
1050
+ readonly myId: string;
1051
+ private autoAnnounce;
1052
+ private announced;
1053
+ /** Per-instance EventTarget — events do NOT leak across BroadcastChannels. */
1054
+ private events;
1055
+ constructor(opts: BroadcastChannelOptions);
1056
+ /** Subscribe to a channel event. Handler receives a CustomEvent. */
1057
+ on(event: BroadcastChannelEvent | string, handler: (e: CustomEvent) => void): void;
1058
+ /** Unsubscribe. */
1059
+ off(event: BroadcastChannelEvent | string, handler: (e: CustomEvent) => void): void;
1060
+ private emit;
1061
+ /** Generate our keys (idempotent) and open the WebSocket. */
1062
+ connect(): Promise<void>;
1063
+ /** Close the WebSocket and stop reconnecting. */
1064
+ disconnect(): void;
1065
+ /**
1066
+ * Send our `keyExchange` frame to the room. Idempotent — only sends on the
1067
+ * first call. Call this once your higher-level handshake (e.g. sending a
1068
+ * `{name}` identification frame) has completed.
1069
+ */
1070
+ announce(): Promise<void>;
1071
+ /**
1072
+ * Encrypt `plaintext` to every peer we have a ratchet with, and send each
1073
+ * cipherMessage as its own WS frame. Returns the number of peers we sent to
1074
+ * (0 if no peers are handshaken yet — in which case the caller should
1075
+ * typically render the message locally for the sender and try again later).
1076
+ */
1077
+ send(plaintext: string): Promise<number>;
1078
+ /**
1079
+ * Send an arbitrary JSON-serializable frame as-is. The app uses this for
1080
+ * any frame that isn't channel-managed (e.g. a chat's `{name}`, `{file}`,
1081
+ * or unencrypted `{message}`).
1082
+ */
1083
+ sendRaw(frame: unknown): void;
1084
+ /** List of peer ids we have an established ratchet for. */
1085
+ peers(): string[];
1086
+ /** Drop a peer's ratchet (e.g. when they leave the room). */
1087
+ forgetPeer(peerId: string): void;
1088
+ isConnected(): boolean;
1089
+ private handleInboundFrame;
1090
+ }
1091
+
1092
+ /**
1093
+ * Cloudflare-Workers-compatible Groth16 ZK proof verifier.
1094
+ *
1095
+ * Drives the BN128 WASM module's exported pairing functions directly. Does NOT
1096
+ * use snarkjs/ffjavascript — those depend on runtime features (most notably
1097
+ * `URL.createObjectURL`) that CF Workers don't expose. See ROADMAP / CLAUDE.md.
1098
+ *
1099
+ * Two ways to initialize:
1100
+ * 1. `await initBn128Wasm()` — uses the bundled bn128.wasm (base64-inlined at
1101
+ * build time via `@rollup/plugin-wasm`). Works in any modern JS runtime
1102
+ * that allows runtime `WebAssembly.compile()` — Node, browsers, most
1103
+ * Workers configurations.
1104
+ * 2. `await initBn128Wasm(myWasmModule)` — accepts a pre-compiled
1105
+ * `WebAssembly.Module`. Lets a CF-Workers consumer keep the native pattern
1106
+ * of importing `.wasm` so wrangler precompiles it at deploy time and the
1107
+ * worker never pays for runtime compilation.
1108
+ */
1109
+
1110
+ interface Bn128WasmInstance {
1111
+ instance: WebAssembly.Instance;
1112
+ memory: WebAssembly.Memory;
1113
+ initialPFree: number;
1114
+ }
1115
+ /**
1116
+ * Initialize the BN128 WASM module.
1117
+ *
1118
+ * @param wasmModule Optional pre-compiled `WebAssembly.Module`. If omitted, the
1119
+ * bundled bn128.wasm (base64-inlined by `@rollup/plugin-wasm` at build time)
1120
+ * is used. Passing your own module is the recommended path inside CF Workers,
1121
+ * where wrangler can precompile a `.wasm` import at deploy time and avoid the
1122
+ * runtime compile cost.
1123
+ */
1124
+ declare function initBn128Wasm(wasmModule?: WebAssembly.Module): Promise<Bn128WasmInstance>;
1125
+ /**
1126
+ * Verify a Groth16 proof.
1127
+ *
1128
+ * Returns `false` (rather than throwing) for any structural problem — malformed
1129
+ * proof, out-of-range field elements, off-curve points, or a failed pairing.
1130
+ * Throws only for runtime/WASM faults.
1131
+ */
1132
+ declare function verifyGroth16(wasmInstance: WebAssembly.Instance, memory: WebAssembly.Memory, initialPFree: number, verificationKey: VerificationKey, proof: Groth16Proof, publicSignals: string[]): Promise<boolean>;
1133
+
1134
+ /**
1135
+ * `PersonalSpaceClient` — the client-side counterpart to the accelerator's
1136
+ * `PersonalSpaceDO`. Wraps the `/api/personal/:commitment/*` HTTP protocol so
1137
+ * callers can `put` / `get` / `delete` / `list` per-user KV blobs without
1138
+ * thinking about challenges, nonces, or Groth16 proof generation.
1139
+ *
1140
+ * Every gated operation:
1141
+ * 1. Requests a one-shot challenge from the DO (`POST /challenge`).
1142
+ * 2. Converts the returned hex nonce into a BN254 field element.
1143
+ * 3. Generates a fresh Groth16 proof for the `preimagePoK` circuit using
1144
+ * snarkjs, with the user's secret/salt/ecdsaPub as private inputs and
1145
+ * `{commitment, nonce, ecdsaPubHash}` as public signals.
1146
+ * 4. POSTs the proof + publicSignals + (optional) value to the gated endpoint.
1147
+ *
1148
+ * snarkjs is loaded lazily (dynamic `import`) the first time a proof is
1149
+ * generated — it's an optional peer dependency that only the ZK-proof path
1150
+ * needs, so importing this module (or constructing a `Client`) never requires
1151
+ * it. Consumers using the proof path install `snarkjs`, or provide it via an
1152
+ * import map in the browser (see the README).
1153
+ *
1154
+ * Encryption of the stored values is the caller's responsibility — the DO
1155
+ * sees only opaque JSON. The companion `wrap.ts` helpers in this folder are
1156
+ * a convenient way to passphrase-encrypt a payload before putting it.
1157
+ */
1158
+ /**
1159
+ * Constructor options for {@link PersonalSpaceClient}.
1160
+ *
1161
+ * All of `secret`, `salt`, `ecdsaPub`, and `ecdsaPubHash` are decimal-encoded
1162
+ * BigInt strings (snarkjs convention). `commitment` is likewise decimal — it's
1163
+ * the Poseidon hash of `(secret, salt, ecdsaPubHash)`.
1164
+ */
1165
+ interface PersonalSpaceClientOptions {
1166
+ /** Base URL of the accelerator, e.g. `http://localhost:8787`. */
1167
+ baseUrl: string;
1168
+ /** The user's Poseidon commitment as a decimal string (~78 digits). */
1169
+ commitment: string;
1170
+ /** ZK identity material — private inputs to the proof. Decimal BigInt string. */
1171
+ secret: string;
1172
+ /** ZK identity salt. Decimal BigInt string. */
1173
+ salt: string;
1174
+ /** Raw ECDSA public-key field representation. Decimal BigInt string. */
1175
+ ecdsaPub: string;
1176
+ /** Poseidon hash of `ecdsaPub`, used as a public signal. Decimal BigInt string. */
1177
+ ecdsaPubHash: string;
1178
+ /** Where the runtime can fetch the compiled circuit assets. */
1179
+ circuits: {
1180
+ /** URL of the `preimagePoK.wasm` witness generator. */
1181
+ wasmUrl: string;
1182
+ /** URL of the `preimagePoK_0001.zkey` proving key. */
1183
+ zkeyUrl: string;
1184
+ };
1185
+ }
1186
+ declare class PersonalSpaceClient {
1187
+ private readonly baseUrl;
1188
+ private readonly commitment;
1189
+ private readonly secret;
1190
+ private readonly salt;
1191
+ private readonly ecdsaPub;
1192
+ private readonly ecdsaPubHash;
1193
+ private readonly wasmUrl;
1194
+ private readonly zkeyUrl;
1195
+ constructor(opts: PersonalSpaceClientOptions);
1196
+ /** Store `value` under `key`, replacing any prior value. */
1197
+ put(key: string, value: unknown): Promise<void>;
1198
+ /** Fetch the value stored under `key`. Returns `null` if absent. */
1199
+ get<T = unknown>(key: string): Promise<T | null>;
1200
+ /** Remove `key` from the user's space. Returns whether the key existed. */
1201
+ delete(key: string): Promise<boolean>;
1202
+ /** List every key currently stored in the user's space. */
1203
+ list(): Promise<string[]>;
1204
+ /**
1205
+ * Run the full pre-flight: ask the DO for a challenge, reduce the nonce to
1206
+ * a field element, and generate a fresh Groth16 proof. Returns everything
1207
+ * the gated endpoints need in their body.
1208
+ */
1209
+ private proveFreshChallenge;
1210
+ /** Hit `POST /challenge` and return the issued nonce + ID. */
1211
+ private requestChallenge;
1212
+ /**
1213
+ * Thin wrapper over `globalThis.fetch` so all methods share a single set of
1214
+ * headers and body-encoding behaviour. `globalThis.fetch` is available in
1215
+ * modern browsers, Node 18+, Workers, and Bun.
1216
+ */
1217
+ private fetch;
1218
+ }
1219
+
1220
+ /**
1221
+ * Wire types for the storage layer.
1222
+ *
1223
+ * The `FileManifest` is the single piece of metadata the system needs to
1224
+ * reassemble a file: per-chunk encryption material, RS parameters, and the
1225
+ * SHA-256 content hashes of every shard that belongs to the chunk. With the
1226
+ * manifest in hand, a reader can pull each shard from the open content-
1227
+ * addressed shard store, decode, decrypt, and concatenate to recover the
1228
+ * original bytes.
1229
+ *
1230
+ * The manifest itself lives inside a `SharedSpaceDO` (Phase B) which gates
1231
+ * access by ZK proof + ACL. Shards are stored in the open content-addressed
1232
+ * `ShardStoreDO` — anyone can put/get any shard, but without the manifest
1233
+ * (which has the keys) the ciphertext is just noise.
1234
+ */
1235
+
1236
+ /**
1237
+ * One chunk's metadata. A file is the concatenation of `chunks` in order.
1238
+ *
1239
+ * - `originalSize` is the chunk's plaintext length, before AES-GCM expanded
1240
+ * it by 16 bytes for the auth tag and before RS padded it for shard
1241
+ * alignment.
1242
+ * - `ciphertextSize` is the AES-GCM output length (plaintext + 16). This is
1243
+ * what the RS decoder trims back to.
1244
+ * - `shardHashes` lists every shard's SHA-256 in encoding order — the first
1245
+ * `dataShards` are data shards, the trailing `parityShards` are parity.
1246
+ */
1247
+ interface ChunkManifest {
1248
+ /** Stable per-chunk ID (uuid-like). Useful for client-side resume / dedup. */
1249
+ id: string;
1250
+ /** Position of this chunk in the file (0-indexed). */
1251
+ chunkIndex: number;
1252
+ /** Plaintext byte length of the chunk before encryption. */
1253
+ originalSize: number;
1254
+ /** AES-GCM ciphertext byte length (originalSize + 16 for the tag). */
1255
+ ciphertextSize: number;
1256
+ /** Number of RS data shards. */
1257
+ dataShards: number;
1258
+ /** Number of RS parity shards. */
1259
+ parityShards: number;
1260
+ /** Length in bytes of every shard (data and parity are equal). */
1261
+ shardSize: number;
1262
+ /** SHA-256 (hex) of each shard's ciphertext bytes, in shard order. */
1263
+ shardHashes: string[];
1264
+ /** Per-chunk AES-256-GCM key + IV. Plain in the manifest — gating happens at the SpaceDO. */
1265
+ cipher: ChunkKeyMaterial;
1266
+ }
1267
+ /**
1268
+ * Top-level manifest for a single stored file. Persisted in the user's
1269
+ * SharedSpace by file id; readable / writable per the space's ACL.
1270
+ */
1271
+ interface FileManifest {
1272
+ /** Stable file ID. Assigned by the SDK at write time; used to read or delete. */
1273
+ id: string;
1274
+ /** Human-readable filename (display only — not used by the shard store). */
1275
+ name: string;
1276
+ /** Total plaintext byte length. Sum of chunk `originalSize`s. */
1277
+ size: number;
1278
+ /** MIME type the application supplied at write time. Opaque to storage. */
1279
+ type: string;
1280
+ /** Optional virtual path / folder hint for the application. */
1281
+ path?: string;
1282
+ /** When the file was last modified per the application (ms epoch). */
1283
+ lastModified: number;
1284
+ /** When this manifest was created on the SDK side (ms epoch). */
1285
+ createdAt: number;
1286
+ /** Ordered list of chunks. Concatenate them in index order to rebuild the file. */
1287
+ chunks: ChunkManifest[];
1288
+ /** SDK schema version. Bump on breaking manifest-shape changes. */
1289
+ version: 1;
1290
+ }
1291
+ /**
1292
+ * Lightweight summary returned by `FileStorage.writeFile` and surfaced in
1293
+ * directory-style listings. The full manifest is only fetched when actually
1294
+ * reassembling the file.
1295
+ */
1296
+ interface FileStat {
1297
+ id: string;
1298
+ name: string;
1299
+ size: number;
1300
+ type: string;
1301
+ path?: string;
1302
+ lastModified: number;
1303
+ createdAt: number;
1304
+ chunkCount: number;
1305
+ shardCount: number;
1306
+ }
1307
+ /** Distil a `FileManifest` into a `FileStat`. */
1308
+ declare function manifestToStat(manifest: FileManifest): FileStat;
1309
+
1310
+ /**
1311
+ * HTTP client for the open content-addressed shard store.
1312
+ *
1313
+ * Each shard is named by the lowercase hex SHA-256 of its ciphertext bytes.
1314
+ * The server verifies the hash on PUT, so the address authenticates the
1315
+ * contents — there's no benefit to ACLing PUT/GET, and making them open
1316
+ * enables cross-user dedup down the line.
1317
+ *
1318
+ * Wire protocol:
1319
+ * PUT /api/shards/:hash body: shard bytes (application/octet-stream)
1320
+ * 200 { ok: true, hash, size, dedup: boolean }
1321
+ * 400 if hash doesn't match body
1322
+ *
1323
+ * GET /api/shards/:hash 200 with shard bytes (application/octet-stream)
1324
+ * 404 if absent
1325
+ *
1326
+ * HEAD /api/shards/:hash 200 if present (no body) — used by callers to
1327
+ * skip a PUT they'd otherwise dedup-rewrite. The
1328
+ * server can ignore the optimization if it wants.
1329
+ */
1330
+ interface ShardClientOptions {
1331
+ /** Base URL of the accelerator (e.g. `https://muhkoo-accelerator.workers.dev`). */
1332
+ baseUrl: string;
1333
+ /**
1334
+ * Path prefix for the shard endpoints, joined to `baseUrl` before the
1335
+ * hash. Defaults to `/api/shards`, which targets a global shard store
1336
+ * (e.g. a dedicated `ShardStoreDO`). Pass a room-scoped value like
1337
+ * `/api/spaces/{roomId}/shards` to keep shards bound to a specific
1338
+ * `SharedSpaceDO` — that's the path the chat application uses today.
1339
+ *
1340
+ * Leading slash optional; trailing slash stripped. The final URL is
1341
+ * always `${baseUrl}${normalizedPrefix}/${encodeURIComponent(hash)}`.
1342
+ */
1343
+ pathPrefix?: string;
1344
+ /**
1345
+ * Optional custom fetch implementation. Defaults to `globalThis.fetch`.
1346
+ * Provided so callers can inject retries / auth / instrumentation.
1347
+ */
1348
+ fetch?: typeof fetch;
1349
+ }
1350
+ /**
1351
+ * Compute the lowercase hex SHA-256 of `bytes`. Available via WebCrypto in
1352
+ * every runtime this SDK targets.
1353
+ */
1354
+ declare function shardHash(bytes: Uint8Array): Promise<string>;
1355
+ declare class ShardClient {
1356
+ private readonly baseUrl;
1357
+ private readonly pathPrefix;
1358
+ private readonly fetchFn;
1359
+ constructor(opts: ShardClientOptions);
1360
+ /** Build the URL for a given shard hash — exposed for callers that want to log or pre-resolve. */
1361
+ urlForHash(hash: string): string;
1362
+ /**
1363
+ * Upload a shard. The server verifies SHA-256(body) matches `:hash` and
1364
+ * rejects with 400 if not, so the caller is the source of truth on
1365
+ * naming. Re-uploading an existing shard is idempotent — the server
1366
+ * returns `dedup: true` in that case.
1367
+ */
1368
+ putShard(hash: string, bytes: Uint8Array): Promise<{
1369
+ dedup: boolean;
1370
+ }>;
1371
+ /**
1372
+ * Fetch a shard by hash. Throws if the server returns a non-200; returns
1373
+ * `null` for 404 so callers can distinguish "missing" from "errored".
1374
+ */
1375
+ getShard(hash: string): Promise<Uint8Array | null>;
1376
+ /**
1377
+ * Check whether a shard exists. Lets the caller skip a PUT they'd otherwise
1378
+ * dedup-rewrite. The server can implement this as a HEAD or return a small
1379
+ * JSON body — we don't depend on either.
1380
+ */
1381
+ hasShard(hash: string): Promise<boolean>;
1382
+ }
1383
+
1384
+ /**
1385
+ * HTTP client for the multi-user `SharedSpaceDO` (Phase B).
1386
+ *
1387
+ * Mirrors the proven shape of `PersonalSpaceClient` — every gated call:
1388
+ * 1. POSTs `/challenge` to get a fresh nonce keyed to the caller's commitment
1389
+ * 2. Generates a Groth16 proof for the `preimagePoK` circuit with the nonce
1390
+ * reduced into a BN254 field element as the public signal
1391
+ * 3. POSTs the operation with `{ challengeId, proof, publicSignals, ... }`
1392
+ *
1393
+ * What's different from `PersonalSpaceClient`:
1394
+ * - Addressed by space ID, not user commitment. Multiple users can be
1395
+ * members of the same space.
1396
+ * - The space carries a participants list + ACL (shared types from
1397
+ * `src/types/permissions.ts`). The server checks that the proving
1398
+ * commitment is a participant with the requested permission before each
1399
+ * mutation.
1400
+ * - Stores file manifests (not generic KV blobs). The shape is constrained
1401
+ * by `FileManifest` from `../types`.
1402
+ *
1403
+ * Wire protocol (this client implements the client side; the server side is
1404
+ * the next slice of work in accelerator):
1405
+ *
1406
+ * POST /api/spaces create a new space (gated by caller proof)
1407
+ * body: { challengeId, proof, publicSignals, initialAcl? }
1408
+ * returns: { spaceId }
1409
+ *
1410
+ * POST /api/spaces/:spaceId/challenge issue a nonce for the caller
1411
+ * body: { commitment }
1412
+ * returns: { challengeId, nonce }
1413
+ *
1414
+ * POST /api/spaces/:spaceId/files write a file manifest
1415
+ * body: { challengeId, proof, publicSignals, manifest }
1416
+ * returns: { ok: true, fileId }
1417
+ *
1418
+ * POST /api/spaces/:spaceId/files/:fileId/get read a file manifest
1419
+ * body: { challengeId, proof, publicSignals }
1420
+ * returns: { manifest }
1421
+ *
1422
+ * DELETE /api/spaces/:spaceId/files/:fileId delete a file manifest
1423
+ * body: { challengeId, proof, publicSignals }
1424
+ * returns: { ok: true, existed: boolean }
1425
+ *
1426
+ * POST /api/spaces/:spaceId/files/list list manifests in space
1427
+ * body: { challengeId, proof, publicSignals }
1428
+ * returns: { files: FileStat[] }
1429
+ *
1430
+ * POST /api/spaces/:spaceId/participants add a participant
1431
+ * body: { challengeId, proof, publicSignals, participant: Participant }
1432
+ * returns: { ok: true }
1433
+ *
1434
+ * DELETE /api/spaces/:spaceId/participants/:commitment remove a participant
1435
+ * body: { challengeId, proof, publicSignals }
1436
+ * returns: { ok: true, existed: boolean }
1437
+ *
1438
+ * POST /api/spaces/:spaceId/acl set / replace an ACL entry
1439
+ * body: { challengeId, proof, publicSignals, entry: ACLEntry }
1440
+ * returns: { ok: true }
1441
+ */
1442
+
1443
+ interface SharedSpaceClientOptions {
1444
+ /** Base URL of the accelerator. */
1445
+ baseUrl: string;
1446
+ /** This caller's Poseidon commitment (decimal string). */
1447
+ commitment: string;
1448
+ /** Private witnesses for the `preimagePoK` circuit. */
1449
+ secret: string;
1450
+ salt: string;
1451
+ ecdsaPub: string;
1452
+ /** Public signal: Poseidon hash of `ecdsaPub`. */
1453
+ ecdsaPubHash: string;
1454
+ /** Compiled circuit assets — same files PersonalSpaceClient uses. */
1455
+ circuits: {
1456
+ wasmUrl: string;
1457
+ zkeyUrl: string;
1458
+ };
1459
+ /** Optional custom fetch (defaults to `globalThis.fetch`). */
1460
+ fetch?: typeof fetch;
1461
+ }
1462
+ declare class SharedSpaceClient {
1463
+ private readonly baseUrl;
1464
+ private readonly commitment;
1465
+ private readonly secret;
1466
+ private readonly salt;
1467
+ private readonly ecdsaPub;
1468
+ private readonly ecdsaPubHash;
1469
+ private readonly wasmUrl;
1470
+ private readonly zkeyUrl;
1471
+ private readonly fetchFn;
1472
+ constructor(opts: SharedSpaceClientOptions);
1473
+ /**
1474
+ * Create a brand new space. The caller becomes the owner; `initialAcl`
1475
+ * adds any additional ACL entries up front (e.g. inviting a co-owner at
1476
+ * creation time).
1477
+ */
1478
+ createSpace(initialAcl?: ACLEntry[]): Promise<{
1479
+ spaceId: string;
1480
+ }>;
1481
+ /** Write a file manifest into the space. Requires `write` permission. */
1482
+ writeFileManifest(spaceId: string, manifest: FileManifest): Promise<void>;
1483
+ /** Read a file manifest by id. Requires `read` permission. */
1484
+ readFileManifest(spaceId: string, fileId: string): Promise<FileManifest>;
1485
+ /** Delete a file manifest by id. Requires `delete` permission. */
1486
+ deleteFileManifest(spaceId: string, fileId: string): Promise<boolean>;
1487
+ /** List file summaries in the space. Requires `read` permission. */
1488
+ listFiles(spaceId: string): Promise<FileStat[]>;
1489
+ /** Add a participant. Requires `invite` or `admin`. */
1490
+ addParticipant(spaceId: string, participant: Participant): Promise<void>;
1491
+ /** Remove a participant by their public-key identity. Requires `admin`. */
1492
+ removeParticipant(spaceId: string, participantCommitment: string): Promise<boolean>;
1493
+ /** Set / replace an ACL entry. Requires `admin`. */
1494
+ setACLEntry(spaceId: string, entry: ACLEntry): Promise<void>;
1495
+ /** Request a challenge for `spaceId` and generate the matching proof. */
1496
+ private proveFreshChallenge;
1497
+ /**
1498
+ * Hit `/challenge`. The body includes the caller's commitment so the
1499
+ * server can scope the issued nonce to this principal.
1500
+ */
1501
+ private requestChallenge;
1502
+ private jsonRequest;
1503
+ }
1504
+
1505
+ /**
1506
+ * `FileStorage` — top-level entry point for chunked, encrypted, erasure-coded
1507
+ * file storage.
1508
+ *
1509
+ * Pipeline for writing a file:
1510
+ * 1. Split the plaintext into fixed-size chunks (default 4 MiB).
1511
+ * 2. For each chunk:
1512
+ * a. Generate a fresh AES-256-GCM key + 12-byte IV (`ChunkCipher`).
1513
+ * b. Encrypt the chunk → ciphertext (plaintext.length + 16 bytes tag).
1514
+ * c. Reed–Solomon encode the ciphertext into `dataShards + parityShards`
1515
+ * equal-length shards (`ReedSolomonCodec`).
1516
+ * d. PUT every shard to the content-addressed shard store, keyed by
1517
+ * SHA-256 of the shard bytes (`ShardClient`).
1518
+ * 3. Build a `FileManifest` listing every chunk's keys, IVs, sizes, and
1519
+ * the SHA-256 of every shard in order.
1520
+ *
1521
+ * Two ways to use the manifest:
1522
+ * - {@link FileStorage.writeFileToShards} returns it to the caller, who is
1523
+ * then responsible for delivering it to readers (e.g. by E2E-encrypting
1524
+ * it as a chat message). The chat application takes this path.
1525
+ * - {@link FileStorage.writeFile} additionally POSTs the manifest to a
1526
+ * gated multi-user space (`SharedSpaceClient`) so any participant of the
1527
+ * space can read the file by manifest id later.
1528
+ *
1529
+ * Reading is the inverse: {@link FileStorage.readFileFromShards} takes a
1530
+ * manifest and reassembles the plaintext; {@link FileStorage.readFile} first
1531
+ * fetches the manifest from the space.
1532
+ */
1533
+
1534
+ interface FileStorageOptions {
1535
+ /** Talks to the content-addressed shard store. Always required. */
1536
+ shards: ShardClient;
1537
+ /**
1538
+ * Talks to the gated multi-user space holding manifests + ACLs.
1539
+ * Optional — only required by {@link FileStorage.writeFile} /
1540
+ * {@link FileStorage.readFile} / {@link FileStorage.deleteFile} /
1541
+ * {@link FileStorage.listFiles} / {@link FileStorage.readManifest}.
1542
+ *
1543
+ * Callers that ship the manifest themselves (e.g. via an encrypted chat
1544
+ * message) can omit `space` and use the `*FromShards` / `*ToShards`
1545
+ * methods directly.
1546
+ */
1547
+ space?: SharedSpaceClient;
1548
+ /**
1549
+ * Plaintext chunk size in bytes. After encryption each chunk gains 16
1550
+ * bytes (GCM tag); after RS encoding the size is rounded up to a multiple
1551
+ * of `dataShards` per chunk. Default 4 MiB — a balance between
1552
+ * round-trip cost and parallelism on the upload path.
1553
+ */
1554
+ chunkSize?: number;
1555
+ /** RS data shards per chunk (default 4). */
1556
+ dataShards?: number;
1557
+ /** RS parity shards per chunk (default 2). */
1558
+ parityShards?: number;
1559
+ /**
1560
+ * Cap on concurrent shard uploads / downloads. Tune for the network. The
1561
+ * default of 8 saturates most home connections without overwhelming the
1562
+ * server's per-IP rate limiter.
1563
+ */
1564
+ concurrency?: number;
1565
+ /**
1566
+ * Optional pre-compiled RS WebAssembly module. Defaults to the bundled
1567
+ * loader. Pass this in environments where the bundled loader path isn't
1568
+ * available (vitest, deploy-time-precompiled CF Workers).
1569
+ */
1570
+ rsWasmModule?: WebAssembly.Module;
1571
+ }
1572
+ /**
1573
+ * Input to {@link FileStorage.writeFile} (which writes the manifest to a
1574
+ * `SharedSpaceClient`). Carries the destination `spaceId`.
1575
+ */
1576
+ interface WriteFileInput extends WriteToShardsInput {
1577
+ /** Space to write the manifest to — must allow the caller `write`. */
1578
+ spaceId: string;
1579
+ }
1580
+ /**
1581
+ * Input to {@link FileStorage.writeFileToShards} (which doesn't write a
1582
+ * manifest anywhere — that's the caller's job).
1583
+ */
1584
+ interface WriteToShardsInput {
1585
+ /** Raw data, a Blob, or a File. */
1586
+ data: Uint8Array | Blob | File;
1587
+ /** Application-supplied display metadata. */
1588
+ metadata: {
1589
+ name: string;
1590
+ type: string;
1591
+ path?: string;
1592
+ lastModified?: number;
1593
+ };
1594
+ /**
1595
+ * Optional pre-allocated file id. Defaults to a fresh one. Useful for
1596
+ * resumable uploads: the same id always maps to the same manifest slot.
1597
+ */
1598
+ fileId?: string;
1599
+ }
1600
+ declare class FileStorage {
1601
+ private readonly space;
1602
+ private readonly shards;
1603
+ private readonly chunkSize;
1604
+ private readonly dataShards;
1605
+ private readonly parityShards;
1606
+ private readonly concurrency;
1607
+ private readonly cipher;
1608
+ private readonly codec;
1609
+ private readonly rsWasmModule?;
1610
+ constructor(opts: FileStorageOptions);
1611
+ /**
1612
+ * Chunk → encrypt → encode → upload-shards. Returns the resulting manifest
1613
+ * along with a `FileStat` summary. The caller is responsible for storing
1614
+ * or transmitting the manifest however it likes.
1615
+ */
1616
+ writeFileToShards(input: WriteToShardsInput): Promise<{
1617
+ manifest: FileManifest;
1618
+ stat: FileStat;
1619
+ }>;
1620
+ /**
1621
+ * Inverse of {@link writeFileToShards}. Takes a manifest the caller
1622
+ * obtained somehow (chat message, share link, etc.) and reassembles the
1623
+ * plaintext. Throws if too many shards are missing or any chunk's auth
1624
+ * tag fails.
1625
+ */
1626
+ readFileFromShards(manifest: FileManifest): Promise<{
1627
+ data: Uint8Array;
1628
+ stat: FileStat;
1629
+ }>;
1630
+ /** As {@link writeFileToShards}, plus POSTs the manifest to `input.spaceId`. */
1631
+ writeFile(input: WriteFileInput): Promise<FileStat>;
1632
+ /** As {@link readFileFromShards}, but fetches the manifest from a space first. */
1633
+ readFile(spaceId: string, fileId: string): Promise<{
1634
+ data: Uint8Array;
1635
+ stat: FileStat;
1636
+ }>;
1637
+ /**
1638
+ * Delete the manifest from a space. **Does not GC the shards** — those
1639
+ * are content-addressed and may be referenced by other manifests.
1640
+ * Garbage collection is a separate server-side concern (Phase D / D-ish).
1641
+ */
1642
+ deleteFile(spaceId: string, fileId: string): Promise<boolean>;
1643
+ /** Convenience pass-through. */
1644
+ listFiles(spaceId: string): Promise<FileStat[]>;
1645
+ /** Read a manifest verbatim — useful for "share file" / export flows. */
1646
+ readManifest(spaceId: string, fileId: string): Promise<FileManifest>;
1647
+ private requireSpace;
1648
+ /** Encrypt + encode + upload one chunk; return its manifest entry. */
1649
+ private writeChunk;
1650
+ /** Inverse of `writeChunk`. */
1651
+ private readChunk;
1652
+ /** Run `tasks` with at most `this.concurrency` in flight. */
1653
+ private runWithConcurrency;
1654
+ }
1655
+
1656
+ /**
1657
+ * Reed–Solomon erasure-coding codec.
1658
+ *
1659
+ * Wraps the WASM-backed `encode` / `reconstruct` primitives in `wasm/rs.ts`
1660
+ * with shard-padding bookkeeping so callers can hand it any-length input and
1661
+ * get back uniform-size shards.
1662
+ *
1663
+ * Usage:
1664
+ * const codec = new ReedSolomonCodec();
1665
+ * await codec.ready();
1666
+ * const { shards, shardSize, originalSize } = codec.encode(plaintext, 4, 2);
1667
+ * // 6 shards (4 data + 2 parity), each `shardSize` bytes
1668
+ * // …ship them, lose up to 2, then …
1669
+ * const recovered = codec.decode(shards, 2, deadIndexes, originalSize);
1670
+ *
1671
+ * Why the explicit `originalSize`: RS pads input to make every data shard the
1672
+ * same length. The decoder must trim that padding back off after recovery.
1673
+ * The caller is responsible for stashing `originalSize` somewhere durable
1674
+ * (the file manifest, in this codebase).
1675
+ */
1676
+ interface EncodeResult {
1677
+ /** All `dataShards + parityShards` shards. All have length `shardSize`. */
1678
+ shards: Uint8Array[];
1679
+ /** Length of every shard (data and parity). */
1680
+ shardSize: number;
1681
+ /** Length of the original input before padding. Needed to trim on decode. */
1682
+ originalSize: number;
1683
+ /** How many of the leading shards are data (the rest are parity). */
1684
+ dataShards: number;
1685
+ /** How many parity shards trail the data shards. */
1686
+ parityShards: number;
1687
+ }
1688
+ declare class ReedSolomonCodec {
1689
+ /**
1690
+ * Resolves once the WASM module is loaded; safe to call repeatedly.
1691
+ *
1692
+ * Pass `wasmModule` to use a pre-compiled module (e.g. in tests where the
1693
+ * rollup-inlined loader path isn't available, or in CF Workers where you
1694
+ * want to load the module at deploy time). Default path uses the bundled
1695
+ * base64-inlined wasm — works in Node, browsers, and Workers.
1696
+ */
1697
+ ready(wasmModule?: WebAssembly.Module): Promise<void>;
1698
+ /**
1699
+ * Split `data` into `dataShards` equal-size chunks (zero-padded to fit),
1700
+ * compute `parityShards` parity shards, and return all of them.
1701
+ */
1702
+ encode(data: Uint8Array, dataShards: number, parityShards: number): EncodeResult;
1703
+ /**
1704
+ * Reconstruct the original `data` from `shards`. Up to `parityShards`
1705
+ * shards may be missing; for each missing shard, pass an empty
1706
+ * `Uint8Array` placeholder at its position and list the position in
1707
+ * `deadShardIndexes`.
1708
+ *
1709
+ * `originalSize` is required so the trailing zero-padding can be trimmed.
1710
+ */
1711
+ decode(shards: Uint8Array[], parityShards: number, deadShardIndexes: number[], originalSize: number): Uint8Array;
1712
+ }
1713
+
1714
+ /**
1715
+ * Universal wasm-bindgen glue for `wasm-reed-solomon-erasure` (v0.2.2 — Rust
1716
+ * Reed–Solomon erasure-coding library compiled with `wasm-pack --target nodejs`).
1717
+ *
1718
+ * The upstream npm package ships `wasm_reed_solomon_erasure.js` which uses
1719
+ * `require('fs').readFileSync()` to load the WASM file at module load time —
1720
+ * that hard-codes Node and breaks browsers / CF Workers. This file is a port
1721
+ * of that glue with the Node-only pieces removed:
1722
+ *
1723
+ * - `TextDecoder` is taken from the global (available in Node 12+, every
1724
+ * modern browser, and Workers — no `require('util')`).
1725
+ * - The WASM module is loaded via the rollup-inlined `.wasm` import, the
1726
+ * same `targetEnv: 'auto-inline'` path the bn128 verifier uses. Works in
1727
+ * Node, browser, and Workers without runtime fs access.
1728
+ *
1729
+ * Only the two public functions consumers actually need (`encode` and
1730
+ * `reconstruct`) are exported. The rest is the wbindgen heap-table dance.
1731
+ *
1732
+ * Encoding:
1733
+ * `encode(dataShards: Uint8Array[], parityShard: number)` → array of
1734
+ * `dataShards.length + parityShard` Uint8Arrays. The first N are the input
1735
+ * shards unchanged; the trailing M are the freshly computed parity shards.
1736
+ * All shards are the same length — caller is responsible for padding the
1737
+ * data into equal-size data shards.
1738
+ *
1739
+ * Reconstruction:
1740
+ * `reconstruct(corruptedShards, parityShard, deadIndexes)` → fully recovered
1741
+ * array. Pass placeholders (empty Uint8Arrays) at the dead indexes; the
1742
+ * reconstructor fills them in. Up to `parityShard` shards may be missing.
1743
+ */
1744
+ /**
1745
+ * Load and instantiate the WASM module. Idempotent — repeated calls await the
1746
+ * same one-shot promise.
1747
+ *
1748
+ * Two ways to initialize, matching the pattern used by `workers/groth16-verifier`:
1749
+ * 1. `await initRsWasm()` — uses the bundled .wasm (base64-inlined at build
1750
+ * time by `@rollup/plugin-wasm`). Works in any runtime that allows
1751
+ * runtime `WebAssembly.compile()`.
1752
+ * 2. `await initRsWasm(myWasmModule)` — accepts a pre-compiled
1753
+ * `WebAssembly.Module`. Useful when the bundled loader path isn't
1754
+ * available (vitest, CF Workers wanting deploy-time precompile, etc.).
1755
+ */
1756
+ declare function initRsWasm(wasmModule?: WebAssembly.Module): Promise<void>;
1757
+
1758
+ /**
1759
+ * HTTP client for the accelerator's `/api/auth/*` endpoints.
1760
+ *
1761
+ * Owns the wire shapes for register / challenge / authenticate / verify so
1762
+ * the React SPA (and any future consumers) don't reimplement them. The ZK
1763
+ * identity derivation + Groth16 proof generation stay on the application
1764
+ * side — this client handles only the round-trips.
1765
+ *
1766
+ * Mirrors `PersonalSpaceClient` / `ShardClient` / `SharedSpaceClient`: takes
1767
+ * a `baseUrl`, optional custom `fetch`, throws typed `Error`s on failure
1768
+ * (with the worker's message inlined where available).
1769
+ */
1770
+
1771
+ interface ZkRegisterRequest {
1772
+ username: string;
1773
+ /** Decimal-string Poseidon commitment. */
1774
+ commitment: string;
1775
+ /** Base64-encoded raw uncompressed ECDH public key (SEC1 0x04‖X‖Y). */
1776
+ ecdhPublicKey: string;
1777
+ /** Base64-encoded raw uncompressed ECDSA public key. */
1778
+ ecdsaPublicKey: string;
1779
+ email?: string | null;
1780
+ }
1781
+ interface ZkRegisterResponse {
1782
+ success: boolean;
1783
+ }
1784
+ interface ZkChallengeResponse {
1785
+ challengeId: string;
1786
+ /** Hex-encoded random nonce — caller reduces into a BN254 field element for the proof. */
1787
+ nonce: string;
1788
+ /** Decimal-string Poseidon commitment the server has on file for this user. */
1789
+ commitment: string;
1790
+ }
1791
+ interface ZkAuthenticateRequest {
1792
+ challengeId: string;
1793
+ proof: {
1794
+ commitment: string;
1795
+ /** Original hex nonce from the challenge (NOT the field-reduced version). */
1796
+ nonce: string;
1797
+ response: {
1798
+ proof: Groth16Proof;
1799
+ publicSignals: string[];
1800
+ };
1801
+ /** Base64 ECDSA-SHA256 signature over `JSON.stringify(response.proof)`. */
1802
+ signature: string;
1803
+ };
1804
+ rememberMe?: boolean;
1805
+ }
1806
+ interface AuthSuccess {
1807
+ token: string;
1808
+ username: string;
1809
+ }
1810
+ interface AuthClientOptions {
1811
+ /** Absolute URL of the accelerator worker (no trailing slash required). */
1812
+ baseUrl: string;
1813
+ /** Optional custom fetch — defaults to `globalThis.fetch`. */
1814
+ fetch?: typeof fetch;
1815
+ }
1816
+ declare class AuthClient {
1817
+ private readonly baseUrl;
1818
+ private readonly fetchFn;
1819
+ constructor(opts: AuthClientOptions);
1820
+ /** Submit ZK registration. Caller has already derived the identity + commitment. */
1821
+ register(req: ZkRegisterRequest): Promise<ZkRegisterResponse>;
1822
+ /** Request an auth challenge for `username` (server issues a fresh nonce). */
1823
+ getChallenge(username: string): Promise<ZkChallengeResponse>;
1824
+ /** Trade a challenge + proof for a session token. */
1825
+ authenticate(req: ZkAuthenticateRequest): Promise<AuthSuccess>;
1826
+ /**
1827
+ * Validate a previously stored session token. Returns `{ username }` on
1828
+ * success; throws on 401/404 — caller uses that to drop a stale token
1829
+ * when restoring a session after a reload.
1830
+ */
1831
+ verify(token: string): Promise<{
1832
+ username: string;
1833
+ }>;
1834
+ private json;
1835
+ private parse;
1836
+ }
1837
+
1838
+ /**
1839
+ * Deterministic ZK identity derivation from `(username, password)`.
1840
+ *
1841
+ * Same inputs on ANY device produce the same secret, salt, and P-256
1842
+ * ECDSA + ECDH keypairs — which is what makes federated login from a
1843
+ * fresh browser possible. There is no localStorage involvement: every
1844
+ * register and login call re-derives the identity on demand.
1845
+ *
1846
+ * Pipeline:
1847
+ * master_seed = PBKDF2-SHA256(password, "muhkoo-zk-v1:" || username, 200k iters)
1848
+ * secret = HKDF-Expand-SHA256(master_seed, "zk-secret", 32 bytes) → hex
1849
+ * salt = HKDF-Expand-SHA256(master_seed, "zk-salt", 32 bytes) → hex
1850
+ * ecdsa_priv = HKDF-Expand-SHA256(master_seed, "ecdsa-priv", 32 bytes)
1851
+ * ecdh_priv = HKDF-Expand-SHA256(master_seed, "ecdh-priv", 32 bytes)
1852
+ *
1853
+ * P-256 public points are computed via `@noble/curves` (WebCrypto has no
1854
+ * derive-public-from-private-scalar API), then both keypairs are imported
1855
+ * into SubtleCrypto in JWK form so the rest of the crypto stack can keep
1856
+ * using `crypto.subtle.sign` / `crypto.subtle.deriveBits`.
1857
+ *
1858
+ * Originally lived in `accelerator/public/js/zk-identity.js` and was ported
1859
+ * to `muhkoo/web/src/zk/identity.ts`; consolidated here so the SDK owns
1860
+ * identity derivation end-to-end.
1861
+ */
1862
+ interface ZkIdentity {
1863
+ /** 32-byte secret, hex-encoded. Private witness for the ZK circuit. */
1864
+ secretHex: string;
1865
+ /** 32-byte salt, hex-encoded. Private witness for the ZK circuit. */
1866
+ saltHex: string;
1867
+ /** P-256 ECDSA keypair, used to sign auth proofs. */
1868
+ ecdsaKeyPair: CryptoKeyPair;
1869
+ /** P-256 ECDH keypair, used by the chat ratchet. */
1870
+ ecdhKeyPair: CryptoKeyPair;
1871
+ }
1872
+ /**
1873
+ * Derive a deterministic ZK identity from `(username, password)`.
1874
+ *
1875
+ * Same inputs always produce the same identity, on any device — that's
1876
+ * what makes federated login work without dragging encrypted state across
1877
+ * devices.
1878
+ */
1879
+ declare function deriveIdentity(username: string, password: string): Promise<ZkIdentity>;
1880
+
1881
+ /**
1882
+ * Poseidon hash wrapper used by the ZK auth flow.
1883
+ *
1884
+ * Backed by `circomlibjs`'s `buildPoseidon`. Builder is lazy and cached
1885
+ * because the JS-side Babyjubjub field reps take ~100ms to construct, and
1886
+ * we'd rather not pay that on every page load.
1887
+ *
1888
+ * Returns hashes as decimal-string BigInts — same format snarkjs and the
1889
+ * `preimagePoK` circuit expect for inputs.
1890
+ *
1891
+ * Note: circomlibjs is `optionalDependencies` in connect's package.json.
1892
+ * Consumers that use the ZK auth flow (browser/server, not workers) need
1893
+ * it installed. The workers build does not import this module.
1894
+ */
1895
+ /**
1896
+ * Poseidon-hash `inputs` and return the result as a decimal-string BigInt.
1897
+ * Inputs are converted to BigInt — pass either bigints, numbers, or numeric
1898
+ * strings.
1899
+ */
1900
+ declare function poseidonHash(inputs: Array<bigint | string | number>): Promise<string>;
1901
+
1902
+ /**
1903
+ * Groth16 proof generation for the `preimagePoK` circuit.
1904
+ *
1905
+ * The circuit proves knowledge of `(secret, salt, ecdsaPub)` such that
1906
+ * commitment = Poseidon(secret, salt, Poseidon(ecdsaPub))
1907
+ * while only revealing `(commitment, nonce, ecdsaPubHash)` as public signals.
1908
+ *
1909
+ * Circuit assets (wasm + zkey) are served by the accelerator worker. Their
1910
+ * URLs are a required argument of {@link generateAuthProof} — there is no
1911
+ * default. The web app constructs them relative to its configured worker
1912
+ * `baseUrl` so the SPA can target either the local wrangler dev or a deployed
1913
+ * worker depending on `VITE_WORKER_URL`.
1914
+ */
1915
+
1916
+ interface CircuitUrls {
1917
+ /** preimagePoK.wasm — the circuit's witness generator. */
1918
+ wasmUrl: string;
1919
+ /** preimagePoK_0001.zkey — the proving key. */
1920
+ zkeyUrl: string;
1921
+ }
1922
+ /**
1923
+ * Canonical {@link CircuitUrls} for the `preimagePoK` login circuit, anchored
1924
+ * at a base URL where the accelerator serves the circuit assets out of
1925
+ * `circuits/build/`. The filenames here are fixed by the circom build output
1926
+ * (the witness generator lands inside a `preimagePoK_js/` directory); callers
1927
+ * shouldn't hard-code them. `Client` uses this to default its `circuits`
1928
+ * option, and `PersonalSpaceClient` consumers can pass `defaultCircuitUrls(baseUrl)`.
1929
+ */
1930
+ declare function defaultCircuitUrls(baseUrl: string): CircuitUrls;
1931
+ /**
1932
+ * Reduce any hex/decimal/byte input to a decimal-string field element in
1933
+ * `[0, FIELD_SIZE)`. snarkjs and the `preimagePoK` circuit both want decimal
1934
+ * strings; everything else gets normalized here.
1935
+ *
1936
+ * **Behavior is intentionally byte-for-byte compatible with the legacy
1937
+ * `accelerator/public/js/zk-snarkjs-client.js#toField`.** Diverging produces
1938
+ * different commitments than accounts registered via the legacy flow,
1939
+ * which locks those users out forever — the worker stores whatever
1940
+ * commitment the client sent at register time and compares it as a string
1941
+ * at login time.
1942
+ *
1943
+ * In particular: a 32+ char string of hex chars is always treated as hex,
1944
+ * even if every character happens to be 0-9 with no a-f letters. An
1945
+ * earlier version of this function gated the hex branch on "has at least
1946
+ * one letter" — that broke the digit-only-hex edge case (rare in practice,
1947
+ * but real). DO NOT add that letter check back.
1948
+ */
1949
+ declare function toField(value: string | bigint | number): string;
1950
+ /**
1951
+ * Build the user's Poseidon commitment binding `(secret, salt, ecdsaPubHash)`.
1952
+ * Same formula the worker verifies against on register and authenticate.
1953
+ */
1954
+ declare function buildCommitment(secretHex: string, saltHex: string, ecdsaPubHex: string): Promise<string>;
1955
+ /**
1956
+ * Generate a Groth16 proof for the `preimagePoK` circuit. Public signals end
1957
+ * up `[commitment, nonce, ecdsaPubHash]` — the worker checks the commitment
1958
+ * matches the stored one and the nonce matches the issued challenge nonce
1959
+ * reduced into the field.
1960
+ *
1961
+ * Throws if circuit URLs are unreachable or snarkjs decides the inputs are
1962
+ * malformed.
1963
+ */
1964
+ declare function generateAuthProof(args: {
1965
+ secretHex: string;
1966
+ saltHex: string;
1967
+ ecdsaPubHex: string;
1968
+ nonceHex: string;
1969
+ /** Required — caller supplies URLs scoped to the worker they want to talk to. */
1970
+ circuits: CircuitUrls;
1971
+ }): Promise<{
1972
+ proof: Groth16Proof;
1973
+ publicSignals: string[];
1974
+ commitment: string;
1975
+ nonceField: string;
1976
+ ecdsaPubHash: string;
1977
+ }>;
1978
+
1979
+ /**
1980
+ * Conversions between SubtleCrypto `CryptoKey`s and the wire formats the
1981
+ * accelerator's auth endpoints + `preimagePoK` circuit expect.
1982
+ *
1983
+ * Two outputs we need from an ECDSA `CryptoKey`:
1984
+ * - **base64 raw** — passed to `/api/auth/zk-register` as
1985
+ * `ecdsaPublicKey` / `ecdhPublicKey`. (Name is historical;
1986
+ * the legacy client called it `exportPublicKeyToBase58`
1987
+ * but actually used base64 — kept as base64.)
1988
+ * - **hex raw** — fed to the proof as `ecdsaPub` input; the circuit
1989
+ * reduces it into a field element.
1990
+ *
1991
+ * Both go through `crypto.subtle.exportKey("raw", ...)` which yields the
1992
+ * uncompressed SEC1 form (`0x04 || X || Y`).
1993
+ */
1994
+ /** Raw uncompressed (`0x04 || X || Y`) public key bytes, as lowercase hex. */
1995
+ declare function exportPublicKeyHex(publicKey: CryptoKey): Promise<string>;
1996
+ /** Raw uncompressed public key as base64 — wire format for the auth endpoints. */
1997
+ declare function exportPublicKeyBase64(publicKey: CryptoKey): Promise<string>;
1998
+ /** Sign a UTF-8 `message` with an ECDSA private key. Returns base64. */
1999
+ declare function signMessage(message: string, privateKey: CryptoKey): Promise<string>;
2000
+
2001
+ /**
2002
+ * `SessionState` — the in-memory source of truth for "who is signed in" on a
2003
+ * {@link Client}.
2004
+ *
2005
+ * It separates two concerns that have different lifetimes and security
2006
+ * properties:
2007
+ *
2008
+ * - **session** (`{ token, username, commitment }`) — proves the user is
2009
+ * authenticated to the accelerator. Cheap to persist (the token is an
2010
+ * opaque server-side handle) and enough to authorize token-gated REST
2011
+ * calls. Restored from a {@link SessionStore} across reloads.
2012
+ *
2013
+ * - **identity** ({@link ZkIdentity}) — the derived secret + keypairs. Lets
2014
+ * the client *prove* ownership (ZK), derive at-rest encryption keys, and
2015
+ * run the messaging ratchet. It is derived from `(username, password)` and
2016
+ * is deliberately **never** persisted by default — a restored session has
2017
+ * a valid token but no identity until the user re-derives it
2018
+ * (`client.auth.zk.unlock(password)`).
2019
+ *
2020
+ * This split is what makes `restore()` (authenticated, can't decrypt) and
2021
+ * `unlock()` (now can decrypt) two distinct, intentional states.
2022
+ */
2023
+
2024
+ /** The persistable part of a session — safe-ish to keep across reloads. */
2025
+ interface StoredSession {
2026
+ token: string;
2027
+ username: string;
2028
+ /** Decimal-string Poseidon commitment — the user's stable id. */
2029
+ commitment: string;
2030
+ }
2031
+ /**
2032
+ * Pluggable persistence for the session token. Browser apps typically back
2033
+ * this with `localStorage`; the default is in-memory (lost on reload).
2034
+ *
2035
+ * Identity material is intentionally not part of this interface — see the
2036
+ * class docs. Apps that want federated session restore without re-prompting
2037
+ * for a password can persist it themselves and call `unlock()` on boot.
2038
+ */
2039
+ interface SessionStore {
2040
+ load(): StoredSession | null | Promise<StoredSession | null>;
2041
+ save(session: StoredSession): void | Promise<void>;
2042
+ clear(): void | Promise<void>;
2043
+ }
2044
+ /** Default in-memory store — holds the session for the process lifetime only. */
2045
+ declare class MemorySessionStore implements SessionStore {
2046
+ private current;
2047
+ load(): StoredSession | null;
2048
+ save(session: StoredSession): void;
2049
+ clear(): void;
2050
+ }
2051
+ /**
2052
+ * A {@link SessionStore} backed by the browser's `localStorage`, so a session
2053
+ * survives page reloads. This is the store most browser apps want — pass it to
2054
+ * `new Client({ sessionStore: new LocalStorageSessionStore() })`.
2055
+ *
2056
+ * Browser-only: construction throws if `localStorage` is unavailable (Node,
2057
+ * Workers, or SSR before hydration) — use {@link MemorySessionStore} or a
2058
+ * custom store there. Keys are namespaced under `keyPrefix` so multiple apps
2059
+ * on the same origin don't collide.
2060
+ */
2061
+ declare class LocalStorageSessionStore implements SessionStore {
2062
+ private readonly tokenKey;
2063
+ private readonly usernameKey;
2064
+ private readonly commitmentKey;
2065
+ constructor(keyPrefix?: string);
2066
+ load(): StoredSession | null;
2067
+ save(session: StoredSession): void;
2068
+ clear(): void;
2069
+ }
2070
+ /**
2071
+ * The {@link SessionStore} a {@link Client} uses when none is supplied: a
2072
+ * {@link LocalStorageSessionStore} in the browser (so sessions survive
2073
+ * reloads), falling back to {@link MemorySessionStore} anywhere `localStorage`
2074
+ * is unavailable or unusable (Node, Workers, SSR, or private-mode browsers
2075
+ * that throw on access). Pass an explicit store to override.
2076
+ */
2077
+ declare function defaultSessionStore(keyPrefix?: string): SessionStore;
2078
+ declare class SessionState {
2079
+ private session;
2080
+ private _identity;
2081
+ private readonly store;
2082
+ constructor(store?: SessionStore);
2083
+ /** True once a token-bearing session exists (regardless of identity). */
2084
+ get isAuthenticated(): boolean;
2085
+ /** True once identity material is available (login or unlock happened). */
2086
+ get isUnlocked(): boolean;
2087
+ get token(): string | null;
2088
+ get username(): string | null;
2089
+ get commitment(): string | null;
2090
+ get identity(): ZkIdentity | null;
2091
+ /** Identity material, throwing a clear error when the client is locked. */
2092
+ requireIdentity(): ZkIdentity;
2093
+ /** Persist + hold a freshly authenticated session. */
2094
+ setSession(session: StoredSession): Promise<void>;
2095
+ /** Attach derived identity material to the current session. */
2096
+ setIdentity(identity: ZkIdentity): void;
2097
+ /** Load a persisted session (used by `restore()`); identity stays null. */
2098
+ loadPersisted(): Promise<StoredSession | null>;
2099
+ /** Drop all session + identity state and clear persistence. */
2100
+ clear(): Promise<void>;
2101
+ }
2102
+
2103
+ /**
2104
+ * `client.auth` — user authentication, exposed today through the `zk`
2105
+ * sub-namespace (`client.auth.zk.login(...)`). Leaving room under `auth` for
2106
+ * other strategies later (e.g. `auth.oauth`) without reshaping the surface.
2107
+ *
2108
+ * `ZkAuth` absorbs the full ZK register/login dance that used to live in the
2109
+ * web app's `AuthContext`:
2110
+ *
2111
+ * register: derive identity → commitment → POST /api/auth/zk-register
2112
+ * login: derive identity → challenge → Groth16 proof → sign →
2113
+ * POST /api/auth/zk-authenticate → session token
2114
+ *
2115
+ * The derived {@link ZkIdentity} (secret + ECDSA/ECDH keypairs) is held in
2116
+ * {@link SessionState} so the storage + message namespaces can reuse it for
2117
+ * at-rest encryption and the messaging ratchet without re-deriving.
2118
+ */
2119
+
2120
+ /** What the auth methods resolve to — the stable, non-secret user facts. */
2121
+ interface AuthUser {
2122
+ username: string;
2123
+ /** Decimal-string Poseidon commitment — the user's stable id. */
2124
+ commitment: string;
2125
+ }
2126
+ interface RegisterParams {
2127
+ username: string;
2128
+ password: string;
2129
+ email?: string | null;
2130
+ /** Sign in immediately after registering (default: true). */
2131
+ login?: boolean;
2132
+ }
2133
+ interface LoginOptions {
2134
+ /** Ask the accelerator for a long-lived (30d) session (default: false). */
2135
+ rememberMe?: boolean;
2136
+ }
2137
+ interface ZkAuthDeps {
2138
+ auth: AuthClient;
2139
+ circuits: CircuitUrls;
2140
+ session: SessionState;
2141
+ }
2142
+ /**
2143
+ * ZK identity auth. One instance per {@link Client}; reads/writes the shared
2144
+ * {@link SessionState}.
2145
+ */
2146
+ declare class ZkAuth {
2147
+ private readonly deps;
2148
+ constructor(deps: ZkAuthDeps);
2149
+ /**
2150
+ * Register a new user, then (by default) sign them in. The identity is
2151
+ * derived deterministically from `(username, password)` — the same inputs
2152
+ * reproduce it on any device, so there's no key material to ship around.
2153
+ */
2154
+ register(params: RegisterParams): Promise<AuthUser>;
2155
+ /**
2156
+ * Sign in: re-derive the identity, prove knowledge of it against a fresh
2157
+ * server challenge, and trade the proof for a session token. On success
2158
+ * both the session and the identity are stored on the client.
2159
+ */
2160
+ login(username: string, password: string, opts?: LoginOptions): Promise<AuthUser>;
2161
+ /**
2162
+ * Restore a persisted session on boot. Validates the stored token with the
2163
+ * accelerator; returns the user when valid, or `null` (and clears the
2164
+ * stale session) otherwise. Identity stays locked — call {@link unlock}
2165
+ * to re-enable encryption/messaging without a full re-login.
2166
+ */
2167
+ restore(): Promise<AuthUser | null>;
2168
+ /**
2169
+ * Re-derive identity material for an already-authenticated (restored)
2170
+ * session, so the client can decrypt storage and run the ratchet. Verifies
2171
+ * the password by checking the derived commitment matches the session's.
2172
+ */
2173
+ unlock(password: string): Promise<void>;
2174
+ /** Sign out: clears the session, identity, and persisted token. */
2175
+ logout(): Promise<void>;
2176
+ /** The signed-in user, or `null`. Synchronous read of current state. */
2177
+ get user(): AuthUser | null;
2178
+ /**
2179
+ * The derived identity material for the current session, or `null` when
2180
+ * locked. Exposed so apps that still drive lower-level primitives (e.g. a
2181
+ * chat ratchet keyed off the same identity) can reuse it without
2182
+ * re-deriving from the password.
2183
+ */
2184
+ get identity(): ZkIdentity | null;
2185
+ /** The current session token, or `null` when signed out. */
2186
+ get token(): string | null;
2187
+ /** Poseidon commitment for an identity — the value the server stores. */
2188
+ private commitmentFor;
2189
+ }
2190
+ /** `client.auth` — the auth namespace. Strategies hang off here. */
2191
+ declare class AuthNamespace {
2192
+ readonly zk: ZkAuth;
2193
+ constructor(deps: ZkAuthDeps);
2194
+ /** Convenience pass-through: the currently signed-in user, or `null`. */
2195
+ get user(): AuthUser | null;
2196
+ }
2197
+
2198
+ /**
2199
+ * `HttpClient` — the one place the unified {@link Client} talks to the
2200
+ * accelerator over HTTP.
2201
+ *
2202
+ * Every request carries two independent credentials:
2203
+ * - the **app key** (`mk_…`) on `X-Muhkoo-Key` — resolves the app /
2204
+ * developer / env context the accelerator uses for routing + billing;
2205
+ * - the **user session token** on `X-Muhkoo-Session` (when a user is
2206
+ * logged in) — the accelerator validates it and injects a trusted
2207
+ * `X-Muhkoo-User-Context` downstream so per-user spaces can authorize
2208
+ * the request without a fresh Groth16 proof.
2209
+ *
2210
+ * `X-Muhkoo-Key` is used (rather than `Authorization: Bearer`) because it is
2211
+ * a non-forbidden header that skips the CORS preflight in browsers.
2212
+ *
2213
+ * The class exposes a header-injecting {@link fetch} that can be handed to
2214
+ * lower-level building blocks (`AuthClient`, `PersonalSpaceClient`) so they
2215
+ * keep owning their own wire shapes while still getting the credentials, plus
2216
+ * convenience JSON helpers (`get`/`post`/`del`) for code that talks to the
2217
+ * accelerator directly.
2218
+ */
2219
+ interface HttpClientOptions {
2220
+ /** Absolute URL of the accelerator worker (trailing slash optional). */
2221
+ baseUrl: string;
2222
+ /**
2223
+ * App / publishable key, e.g. `mk_test_pk_…`. Sent on every request as
2224
+ * `X-Muhkoo-Key` when present. Transitionally optional (auth + personal
2225
+ * storage still function without it), but the product direction is to
2226
+ * REQUIRE it — it attributes traffic for billing/metering and authorizes
2227
+ * shared-space (messaging) websockets.
2228
+ */
2229
+ apiKey?: string;
2230
+ /** Returns the current user session token, or `null` when logged out. */
2231
+ getSessionToken?: () => string | null;
2232
+ /** Custom fetch — defaults to `globalThis.fetch`. */
2233
+ fetch?: typeof fetch;
2234
+ }
2235
+ /** Thrown for any non-2xx accelerator response. Carries the HTTP status. */
2236
+ declare class HttpError extends Error {
2237
+ readonly status: number;
2238
+ readonly body?: unknown | undefined;
2239
+ constructor(message: string, status: number, body?: unknown | undefined);
2240
+ }
2241
+ declare class HttpClient {
2242
+ readonly baseUrl: string;
2243
+ private readonly apiKey;
2244
+ private readonly getSessionToken;
2245
+ private readonly fetchFn;
2246
+ constructor(opts: HttpClientOptions);
2247
+ /**
2248
+ * A `fetch`-compatible function that injects the app key + session token
2249
+ * headers. Hand this to `AuthClient` / `PersonalSpaceClient` so they pick
2250
+ * up credentials without knowing about them.
2251
+ */
2252
+ readonly fetch: typeof fetch;
2253
+ get<T = unknown>(path: string): Promise<T>;
2254
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
2255
+ del<T = unknown>(path: string, body?: unknown): Promise<T>;
2256
+ private request;
2257
+ }
2258
+
2259
+ /**
2260
+ * `client.storage` — per-user persistent storage backed by the accelerator's
2261
+ * personal space.
2262
+ *
2263
+ * await client.storage.set('todos', id, { title, completed })
2264
+ * const todo = await client.storage.get('todos', id)
2265
+ * const ids = await client.storage.list('todos')
2266
+ * await client.storage.delete('todos', id)
2267
+ * const off = client.storage.on('change', e => …) // realtime, cross-device
2268
+ *
2269
+ * A *collection* is a key namespace within the user's space; the stored key is
2270
+ * `${collection}/${id}`. Operations are authorized by the user's session token
2271
+ * (stamped on every request by {@link HttpClient}) — no per-op Groth16 proof.
2272
+ *
2273
+ * Values are encrypted at rest by default ({@link StorageCipher}, an AES-GCM
2274
+ * key derived from the ZK identity); pass `{ encrypt: false }` to store
2275
+ * plaintext. The realtime feed rides the personal space's own websocket, so no
2276
+ * separate transport is involved.
2277
+ *
2278
+ * `query()` is intentionally deferred — see the SDK overhaul plan.
2279
+ */
2280
+
2281
+ interface StorageNamespaceDeps {
2282
+ http: HttpClient;
2283
+ session: SessionState;
2284
+ /** WebSocket base (ws/wss) for the change feed; derived from baseUrl. */
2285
+ wsBaseUrl: string;
2286
+ }
2287
+ interface SetOptions {
2288
+ /** Encrypt the value at rest (default: true). */
2289
+ encrypt?: boolean;
2290
+ }
2291
+ interface StorageChangeEvent<T = unknown> {
2292
+ collection: string;
2293
+ id: string;
2294
+ type: "set" | "delete";
2295
+ /** The decrypted value for `set`; `null` for `delete`. */
2296
+ data: T | null;
2297
+ }
2298
+ declare class StorageNamespace {
2299
+ private readonly deps;
2300
+ private cipher;
2301
+ private cipherIdentity;
2302
+ constructor(deps: StorageNamespaceDeps);
2303
+ /** Store `value` under `collection`/`id` (encrypted at rest by default). */
2304
+ set<T = unknown>(collection: string, id: string, value: T, opts?: SetOptions): Promise<void>;
2305
+ /** Fetch the value at `collection`/`id`, or `null` if absent. */
2306
+ get<T = unknown>(collection: string, id: string): Promise<T | null>;
2307
+ /** Delete `collection`/`id`. Returns whether the key existed. */
2308
+ delete(collection: string, id: string): Promise<boolean>;
2309
+ /** List the ids present in `collection`. */
2310
+ list(collection: string): Promise<string[]>;
2311
+ /** Reserved — server-side query is deferred (see the overhaul plan). */
2312
+ query(_collection: string, _filter?: unknown): Promise<never>;
2313
+ /**
2314
+ * Subscribe to realtime changes to this user's data across devices. Opens
2315
+ * the personal space's websocket and decrypts each change before handing
2316
+ * it to `handler`. Returns an unsubscribe function.
2317
+ *
2318
+ * Requires a `WebSocket` global (browsers; Node 22+ or a `ws` shim).
2319
+ */
2320
+ on<T = unknown>(event: "change", handler: (e: StorageChangeEvent<T>) => void): () => void;
2321
+ private dispatchChange;
2322
+ /** Decode a stored value: decrypt envelopes, pass plaintext through. */
2323
+ private decode;
2324
+ private commitment;
2325
+ private spacePath;
2326
+ private kvPath;
2327
+ /** The at-rest cipher for the current identity (re-derived if it changes). */
2328
+ private getCipher;
2329
+ }
2330
+
2331
+ /**
2332
+ * `Room` — a client handle for one shared space (a chat-style room).
2333
+ *
2334
+ * A shared space carries two things, and `Room` exposes both:
2335
+ * - **realtime E2E messaging** over a {@link BroadcastChannel} (group
2336
+ * handshake, ratchet send/receive, roster frames);
2337
+ * - **room-scoped file storage** via {@link FileStorage} over the room's
2338
+ * shard endpoint.
2339
+ *
2340
+ * Obtained from `client.message.room(name)`. The websocket isn't opened until
2341
+ * `connect()` — and `putFile`/`getFile` don't need it at all (they're plain
2342
+ * shard HTTP), so reading a shared file never opens a socket. The upgrade
2343
+ * ticket + identity are resolved lazily inside `connect()`.
2344
+ *
2345
+ * `Room` intentionally mirrors the `BroadcastChannel` surface the chat hook
2346
+ * already uses (`on`/`off`/`send`/`sendRaw`/`announce`/`peers`/`isConnected`)
2347
+ * so adopting it is a drop-in swap for `new BroadcastChannel(...)`.
2348
+ */
2349
+
2350
+ type Listener = (e: CustomEvent) => void;
2351
+ interface RoomFileMetadata {
2352
+ name: string;
2353
+ type: string;
2354
+ path?: string;
2355
+ lastModified?: number;
2356
+ }
2357
+ interface RoomDeps {
2358
+ name: string;
2359
+ wsBaseUrl: string;
2360
+ httpBaseUrl: string;
2361
+ /** Header-injecting fetch from the client's HttpClient. */
2362
+ fetch: typeof fetch;
2363
+ /** Resolves the user id used for the ratchet (the signed-in username). */
2364
+ myId: () => string;
2365
+ /** Resolves a short-lived WS upgrade ticket (or null when keyless). */
2366
+ fetchTicket: () => Promise<string | null>;
2367
+ }
2368
+ declare class Room {
2369
+ private readonly deps;
2370
+ readonly name: string;
2371
+ private channel;
2372
+ private readonly pending;
2373
+ constructor(deps: RoomDeps);
2374
+ /** Open the room websocket (resolves the ticket + identity lazily). */
2375
+ connect(): Promise<void>;
2376
+ disconnect(): void;
2377
+ /** Subscribe to a channel event. Buffered until `connect()` if early. */
2378
+ on(event: string, handler: Listener): void;
2379
+ off(event: string, handler: Listener): void;
2380
+ /** Advertise our keyExchange to the room (idempotent per connection). */
2381
+ announce(): Promise<void>;
2382
+ /** E2E-encrypt `text` to every peer; returns the recipient count. */
2383
+ send(text: string): Promise<number>;
2384
+ /** Send an arbitrary JSON frame (e.g. the `{name}` handshake). */
2385
+ sendRaw(frame: unknown): void;
2386
+ peers(): string[];
2387
+ isConnected(): boolean;
2388
+ /** The underlying channel, for advanced use. `null` before `connect()`. */
2389
+ get raw(): BroadcastChannel | null;
2390
+ /** Encrypt + erasure-code + upload `file` to this room's shards. */
2391
+ putFile(file: File | Blob | Uint8Array, metadata: RoomFileMetadata): Promise<{
2392
+ manifest: FileManifest;
2393
+ stat: FileStat;
2394
+ }>;
2395
+ /** Fetch + decode a file previously shared in this room. */
2396
+ getFile(manifest: FileManifest): Promise<{
2397
+ data: Uint8Array;
2398
+ stat: FileStat;
2399
+ }>;
2400
+ private fileStorage;
2401
+ }
2402
+
2403
+ /**
2404
+ * `client.message` — realtime messaging over the shared-space primitive.
2405
+ *
2406
+ * const sub = client.message.subscribe('todos', e => …) // pub/sub feed
2407
+ * await client.message.publish('todos', data)
2408
+ * await client.message.send('user:abc', { text: 'Hello!' }) // E2E DM
2409
+ *
2410
+ * Two distinct modes, one transport (a shared-space room websocket via
2411
+ * {@link BroadcastChannel}):
2412
+ *
2413
+ * - **pub/sub** — `publish`/`subscribe(subject)` ride plaintext `pub` frames
2414
+ * the accelerator blind-relays to everyone in the room `pub:<subject>`.
2415
+ * - **direct messages** — `send('user:x', …)` is end-to-end encrypted via the
2416
+ * Double Ratchet, fanned out in the recipient's inbox room `inbox:x`;
2417
+ * the recipient receives them by `subscribe('user:x', …)` (their own id).
2418
+ *
2419
+ * Every room websocket is opened with a short-lived signed ticket fetched from
2420
+ * `POST /api/ws-ticket` (browsers can't set the app-key header on
2421
+ * `new WebSocket()`), then completes the room's `{name}` → `{ready}` handshake
2422
+ * before any frame is sent.
2423
+ */
2424
+
2425
+ /** The subset of {@link BroadcastChannel} this namespace drives (test seam). */
2426
+ interface ChannelLike {
2427
+ on(event: string, handler: (e: CustomEvent) => void): void;
2428
+ off(event: string, handler: (e: CustomEvent) => void): void;
2429
+ connect(): Promise<void>;
2430
+ disconnect(): void;
2431
+ announce(): Promise<void>;
2432
+ send(plaintext: string): Promise<number>;
2433
+ sendRaw(frame: unknown): void;
2434
+ }
2435
+ interface MessageNamespaceDeps {
2436
+ http: HttpClient;
2437
+ session: SessionState;
2438
+ /** WebSocket base (ws/wss); derived from the accelerator baseUrl. */
2439
+ wsBaseUrl: string;
2440
+ /** Test seam — defaults to constructing a real {@link BroadcastChannel}. */
2441
+ createChannel?: (url: string, myId: string) => ChannelLike;
2442
+ }
2443
+ interface MuhkooMessageEvent<T = unknown> {
2444
+ subject: string;
2445
+ from: string;
2446
+ data: T;
2447
+ }
2448
+ /** Handle returned by `subscribe`; call `.unsubscribe()` to detach. */
2449
+ interface MessageSubscription {
2450
+ subject: string;
2451
+ unsubscribe(): void;
2452
+ }
2453
+ declare class MessageNamespace {
2454
+ private readonly deps;
2455
+ /** One connection per room, memoized. */
2456
+ private readonly rooms;
2457
+ constructor(deps: MessageNamespaceDeps);
2458
+ /**
2459
+ * Subscribe to a subject (pub/sub) or to this user's own id (`user:<me>`)
2460
+ * to receive direct messages. Returns an unsubscribe handle.
2461
+ */
2462
+ subscribe<T = unknown>(subject: string, handler: (e: MuhkooMessageEvent<T>) => void): MessageSubscription;
2463
+ /** Publish `data` to every subscriber of `subject` (plaintext fan-out). */
2464
+ publish<T = unknown>(subject: string, data: T): Promise<void>;
2465
+ /**
2466
+ * Send an end-to-end-encrypted direct message to `user:<id>`. Delivered in
2467
+ * the recipient's inbox room; the recipient must be subscribed
2468
+ * (`subscribe('user:<id>')`) and online to receive it (E2E requires a live
2469
+ * ratchet between both parties).
2470
+ */
2471
+ send<T = unknown>(target: string, payload: T): Promise<void>;
2472
+ /**
2473
+ * Mint a new private shared-space id, server-side. The id MUST come from
2474
+ * the accelerator: it's a 64-hex string that embeds the spaces DO class
2475
+ * hash, which the worker validates on every subsequent request — client-
2476
+ * generated ids would be rejected. Pass the returned id to {@link room}
2477
+ * (or share it) to open the space.
2478
+ */
2479
+ createRoom(): Promise<string>;
2480
+ /**
2481
+ * Open a handle to a shared-space room — group E2E messaging plus
2482
+ * room-scoped file storage. The websocket isn't opened until
2483
+ * `room.connect()`; `room.putFile`/`getFile` work without it. This is the
2484
+ * primitive group chat is built on (`publish`/`subscribe`/`send` are the
2485
+ * higher-level conveniences layered over the same space transport).
2486
+ */
2487
+ room(roomName: string): Room;
2488
+ /** Close every open room connection (e.g. on logout). */
2489
+ disconnect(): void;
2490
+ private getRoom;
2491
+ private openRoom;
2492
+ /** Fetch a short-lived WS upgrade ticket scoped to this app key. */
2493
+ private fetchTicket;
2494
+ private myId;
2495
+ }
2496
+
2497
+ /** The hosted Muhkoo Accelerator — the default {@link ClientOptions.baseUrl}. */
2498
+ declare const DEFAULT_BASE_URL = "https://api.muhkoo.dev";
2499
+ interface ClientOptions {
2500
+ /**
2501
+ * App / publishable key issued by the accelerator (e.g. `mk_test_pk_…`).
2502
+ *
2503
+ * TRANSITIONALLY optional: auth + storage currently function without it so
2504
+ * the in-tree web app can migrate before a key is provisioned. The product
2505
+ * direction is that the app key becomes REQUIRED — it's how every app
2506
+ * authenticates to the Accelerator and gets attributed for metering/billing.
2507
+ * New integrations should always pass one.
2508
+ */
2509
+ apiKey?: string;
2510
+ /**
2511
+ * Absolute URL of the accelerator (trailing slash optional). Defaults to
2512
+ * the hosted Muhkoo Accelerator ({@link DEFAULT_BASE_URL}); override to
2513
+ * point at staging, a local `wrangler dev`, or a self-hosted deployment.
2514
+ */
2515
+ baseUrl?: string;
2516
+ /**
2517
+ * URLs of the `preimagePoK` circuit assets used for ZK login proofs.
2518
+ * Defaults to {@link defaultCircuitUrls} anchored at `baseUrl` — i.e.
2519
+ * `${baseUrl}/circuits/build/preimagePoK_js/preimagePoK.wasm` and
2520
+ * `…/preimagePoK_0001.zkey`, where the accelerator serves them.
2521
+ */
2522
+ circuits?: CircuitUrls;
2523
+ /** Pluggable session-token persistence. Defaults to in-memory. */
2524
+ sessionStore?: SessionStore;
2525
+ /** Custom fetch — defaults to `globalThis.fetch`. */
2526
+ fetch?: typeof fetch;
2527
+ /** Optional log level for the SDK logger. */
2528
+ logLevel?: string;
2529
+ }
2530
+ declare class Client {
2531
+ readonly baseUrl: string;
2532
+ /** Authentication — `client.auth.zk.login(...)`, etc. */
2533
+ readonly auth: AuthNamespace;
2534
+ /** Per-user persistent storage — `client.storage.set(...)`, etc. */
2535
+ readonly storage: StorageNamespace;
2536
+ /** Realtime messaging — `client.message.subscribe(...)`, etc. */
2537
+ readonly message: MessageNamespace;
2538
+ private readonly session;
2539
+ private readonly http;
2540
+ constructor(options?: ClientOptions);
2541
+ /** The currently signed-in user, or `null`. */
2542
+ get user(): AuthUser | null;
2543
+ /** Whether a token-bearing session is active. */
2544
+ get isAuthenticated(): boolean;
2545
+ }
2546
+
2547
+ export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChunkCipher, Client, DEFAULT_BASE_URL, DoubleRatchet, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, HashKnowledge, HttpClient, HttpError, KeyStore, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, StorageNamespace, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, shardHash, signMessage, toField, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
2548
+ export type { ACL, ACLEntry, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, DehydratedKeypair, DehydratedKeys, ECDHCurve, EncodeResult, FileManifest, FileStat, FileStorageOptions, Groth16Proof, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, LoginOptions, MessageBody, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOptions, MessageSubscription, MuhkooMessageEvent, OAuthProvider, PacketOptions, Participant, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, PublicKeyJWK, RatchetState, ReceiveResult, RegisterParams, Role, RoomDeps, RoomFileMetadata, Session, SessionOptions, SessionStore, SetOptions, ShardClientOptions, SharedSpaceClientOptions, SnarkProof, StorageChangeEvent, StorageNamespaceDeps, StoredSession, Subscription, UserIdentity, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WrappedPayload, WriteFileInput, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };