@muhkoo/connect 0.6.0-alpha.11 → 0.7.0-alpha.4

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.
@@ -1,9 +1,9 @@
1
1
  import { prove, verify as verify$1 } from '@zk-kit/groth16';
2
2
  import { p256 } from '@noble/curves/nist.js';
3
3
  import { buildPoseidon } from 'circomlibjs';
4
+ import { scrypt } from '@noble/hashes/scrypt.js';
4
5
  import { entropyToMnemonic, validateMnemonic, mnemonicToEntropy } from '@scure/bip39';
5
6
  import { wordlist } from '@scure/bip39/wordlists/english.js';
6
- import { scrypt } from '@noble/hashes/scrypt.js';
7
7
  import { ristretto255_oprf } from '@noble/curves/ed25519.js';
8
8
 
9
9
  /**
@@ -2380,14 +2380,75 @@ class AuthClient {
2380
2380
  const res = await this.fetchFn(`${this.baseUrl}/api/auth/oprf`, this.json("POST", { username, blinded }));
2381
2381
  return await this.parse("oprfEvaluate", res);
2382
2382
  }
2383
- /** Read a factor record for `username` (returns an indistinguishable decoy if absent). */
2384
- async vaultRead(username, factorType) {
2385
- const res = await this.fetchFn(`${this.baseUrl}/api/auth/vault`, this.json("POST", { username, factorType }));
2383
+ /**
2384
+ * Gated split-key OPRF evaluation (M2 — email/google factors). Requires a
2385
+ * live `verifyToken` from {@link emailOtpVerify}; returns BOTH trust
2386
+ * domains' evaluations. The recover token is consumed by this call.
2387
+ */
2388
+ async oprfEvaluateGated(username, blinded, factorType, purpose, verifyToken) {
2389
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/oprf`, this.json("POST", { username, blinded, factorType, purpose, verifyToken }));
2390
+ return await this.parse("oprfEvaluateGated", res);
2391
+ }
2392
+ /**
2393
+ * Request an email OTP. With a session `token` this is the ENROLL flow
2394
+ * (code sent to `email`); without one it's RECOVER (code sent to the
2395
+ * account's enrolled address — the response is uniform either way).
2396
+ */
2397
+ async emailOtpStart(opts) {
2398
+ const init = this.json("POST", { email: opts.email, username: opts.username });
2399
+ if (opts.token)
2400
+ init.headers["X-Muhkoo-Session"] = opts.token;
2401
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/email/start`, init);
2402
+ await this.parse("emailOtpStart", res);
2403
+ }
2404
+ /** Trade an emailed code for a single-use verifyToken (+ the proven address). */
2405
+ async emailOtpVerify(opts) {
2406
+ const init = this.json("POST", { code: opts.code, username: opts.username });
2407
+ if (opts.token)
2408
+ init.headers["X-Muhkoo-Session"] = opts.token;
2409
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/email/verify`, init);
2410
+ return await this.parse("emailOtpVerify", res);
2411
+ }
2412
+ /**
2413
+ * Verify a Google ID token (M2.3). With a session `token` it's the ENROLL
2414
+ * gate; without, it's RECOVER/LOGIN (the Google account must already be
2415
+ * linked). Returns a single-use verifyToken bound to the account's `sub`.
2416
+ */
2417
+ async googleVerify(opts) {
2418
+ const init = this.json("POST", { idToken: opts.idToken, username: opts.username });
2419
+ if (opts.token)
2420
+ init.headers["X-Muhkoo-Session"] = opts.token;
2421
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/google/verify`, init);
2422
+ return await this.parse("googleVerify", res);
2423
+ }
2424
+ // ---- Hosted-auth authorization-code flow (H0) ---------------------------
2425
+ /** Mint a single-use authorization code for an app (called by the hosted page; session-authed). */
2426
+ async grant(token, body) {
2427
+ const init = this.json("POST", body);
2428
+ init.headers["X-Muhkoo-Session"] = token;
2429
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/grant`, init);
2430
+ return await this.parse("grant", res);
2431
+ }
2432
+ /** Exchange an authorization code + PKCE verifier for the session + sealed key material. */
2433
+ async token(body) {
2434
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/token`, this.json("POST", body));
2435
+ return await this.parse("token", res);
2436
+ }
2437
+ /**
2438
+ * Read a factor record for `username` (returns an indistinguishable decoy if
2439
+ * absent). Gated types (email) require the recover `verifyToken`.
2440
+ */
2441
+ async vaultRead(username, factorType, verifyToken) {
2442
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/vault`, this.json("POST", { username, factorType, verifyToken }));
2386
2443
  return await this.parse("vaultRead", res);
2387
2444
  }
2388
- /** Add/replace a factor (session-authed; the seed is already wrapped client-side). */
2389
- async vaultPutFactor(token, factor) {
2390
- const init = this.json("PUT", { factor });
2445
+ /**
2446
+ * Add/replace a factor (session-authed; the seed is already wrapped
2447
+ * client-side). Email factors additionally require (and consume) the
2448
+ * enroll `verifyToken`; the stored address must be the verified one.
2449
+ */
2450
+ async vaultPutFactor(token, factor, verifyToken) {
2451
+ const init = this.json("PUT", { factor, verifyToken });
2391
2452
  init.headers["Authorization"] = `Bearer ${token}`;
2392
2453
  const res = await this.fetchFn(`${this.baseUrl}/api/auth/vault/factor`, init);
2393
2454
  return await this.parse("vaultPutFactor", res);
@@ -2777,6 +2838,129 @@ async function signMessage(message, privateKey) {
2777
2838
  return bytesToBase64(new Uint8Array(sig));
2778
2839
  }
2779
2840
 
2841
+ /**
2842
+ * Identity vault primitives (M1.0).
2843
+ *
2844
+ * The vault decouples the identity from the password: a random 32-byte **master
2845
+ * seed** yields the (unchanged) ZK identity via {@link deriveIdentityFromSeed},
2846
+ * and that seed is AES-256-GCM-wrapped under one key *per factor* (password,
2847
+ * passkey, recovery phrase). Server stores only ciphertext.
2848
+ *
2849
+ * This module holds the PURE primitives (seed gen, the memory-hard password
2850
+ * pre-hash, AES-GCM wrap/unwrap, the OPRF -> wrap-key HKDF). The networked
2851
+ * orchestration (calling the server's OPRF endpoint, fetching/storing factor
2852
+ * records) lives in the AuthNamespace, which has transport access.
2853
+ *
2854
+ * Memory-hard KDF: we use **scrypt** (vetted, in `@noble/hashes`, runs in
2855
+ * browser + Workers + Node - no wasm dependency) instead of argon2id; it is the
2856
+ * pre-hash fed into the OPRF, so even if the OPRF key ever leaked, guesses still
2857
+ * pay the scrypt cost.
2858
+ */
2859
+ const TE$1 = new TextEncoder();
2860
+ // Interactive-login scrypt params (~100ms on a laptop). N must be a power of 2.
2861
+ const SCRYPT = { N: 1 << 15, r: 8, p: 1, dkLen: 32 };
2862
+ /** 32 random bytes - the master seed for a brand-new identity. */
2863
+ function randomSeed() {
2864
+ return crypto.getRandomValues(new Uint8Array(32));
2865
+ }
2866
+ /** Memory-hard pre-hash of the password - the OPRF input. Salted by username. */
2867
+ function passwordPreHash(username, password) {
2868
+ return scrypt(TE$1.encode(password), TE$1.encode("muhkoo-vault-v1:" + username.toLowerCase()), SCRYPT);
2869
+ }
2870
+ /** HKDF the 64-byte OPRF output into a non-extractable AES-256-GCM wrap key. */
2871
+ async function wrapKeyFromOprf(oprfOutput) {
2872
+ const prk = await crypto.subtle.importKey("raw", oprfOutput, { name: "HKDF" }, false, ["deriveKey"]);
2873
+ return crypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(), info: TE$1.encode("muhkoo-vault-wrap") }, prk, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
2874
+ }
2875
+ /** HKDF arbitrary 32-byte key material (e.g. a passkey PRF output) into an AES-GCM wrap key. */
2876
+ async function wrapKeyFromBytes(keyMaterial, info) {
2877
+ const prk = await crypto.subtle.importKey("raw", keyMaterial, { name: "HKDF" }, false, ["deriveKey"]);
2878
+ return crypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(), info: TE$1.encode(info) }, prk, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
2879
+ }
2880
+ async function wrapSeed(seed, key) {
2881
+ const iv = crypto.getRandomValues(new Uint8Array(12));
2882
+ const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, seed);
2883
+ return { iv: toBase64(iv), ct: toBase64(new Uint8Array(ct)) };
2884
+ }
2885
+ /** Decrypt a wrapped seed. Throws (AES-GCM tag failure) on a wrong key. */
2886
+ async function unwrapSeed(wrapped, key) {
2887
+ const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: fromBase64(wrapped.iv) }, key, fromBase64(wrapped.ct));
2888
+ return new Uint8Array(pt);
2889
+ }
2890
+ function toBase64(bytes) {
2891
+ let s = "";
2892
+ for (let i = 0; i < bytes.length; i++)
2893
+ s += String.fromCharCode(bytes[i]);
2894
+ return btoa(s);
2895
+ }
2896
+ function fromBase64(b64) {
2897
+ const s = atob(b64);
2898
+ const out = new Uint8Array(s.length);
2899
+ for (let i = 0; i < s.length; i++)
2900
+ out[i] = s.charCodeAt(i);
2901
+ return out;
2902
+ }
2903
+
2904
+ /**
2905
+ * Hosted-auth handoff crypto (auth.muhkoo.dev — AUTH_HOSTED_PLAN.md §3).
2906
+ *
2907
+ * The hosted page authenticates the user and ends up holding the master seed.
2908
+ * To pass it to the developer's app without it ever sitting server-readable or
2909
+ * landing in browser history:
2910
+ *
2911
+ * 1. {@link sealSeed} generates a one-time key K_t, AES-256-GCM-seals the seed
2912
+ * under it, and returns the ciphertext envelope (`sealedKeys`, stored in
2913
+ * the grant the server relays but can't read) plus K_t (`fragmentKey`,
2914
+ * placed ONLY in the redirect URL fragment — fragments never hit a server).
2915
+ * 2. The app exchanges the code for `sealedKeys`, reads K_t from the fragment,
2916
+ * and {@link unsealSeed} recovers the seed, then strips the fragment.
2917
+ *
2918
+ * Trust-equivalent to today's embedded SDK (the app still ends up with the
2919
+ * seed) while moving credential entry onto the Muhkoo origin. The envelope is
2920
+ * versioned so a future per-app-key handoff (v2) can swap in without a protocol
2921
+ * change.
2922
+ */
2923
+ const TE = new TextEncoder();
2924
+ const TD = new TextDecoder();
2925
+ /** Seal the master seed under a fresh one-time key for the redirect handoff. */
2926
+ async function sealSeed(seed) {
2927
+ const ktRaw = crypto.getRandomValues(new Uint8Array(32));
2928
+ const key = await crypto.subtle.importKey("raw", ktRaw, { name: "AES-GCM" }, false, ["encrypt"]);
2929
+ const iv = crypto.getRandomValues(new Uint8Array(12));
2930
+ const ct = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv }, key, seed));
2931
+ const envelope = { v: 1, iv: toBase64(iv), ct: toBase64(ct) };
2932
+ return {
2933
+ sealedKeys: toBase64(TE.encode(JSON.stringify(envelope))),
2934
+ fragmentKey: base64UrlEncode(ktRaw),
2935
+ };
2936
+ }
2937
+ /** Recover the master seed from the sealed envelope + the fragment key. */
2938
+ async function unsealSeed(sealedKeys, fragmentKey) {
2939
+ const envelope = JSON.parse(TD.decode(fromBase64(sealedKeys)));
2940
+ if (envelope.v !== 1)
2941
+ throw new Error(`Unsupported handoff envelope version ${envelope.v}`);
2942
+ const ktRaw = base64UrlDecode(fragmentKey);
2943
+ const key = await crypto.subtle.importKey("raw", ktRaw, { name: "AES-GCM" }, false, ["decrypt"]);
2944
+ const seed = await crypto.subtle.decrypt({ name: "AES-GCM", iv: fromBase64(envelope.iv) }, key, fromBase64(envelope.ct));
2945
+ return new Uint8Array(seed);
2946
+ }
2947
+ /** Generate a PKCE verifier + S256 challenge for the authorize→token exchange. */
2948
+ async function generatePkce() {
2949
+ const codeVerifier = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));
2950
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", TE.encode(codeVerifier)));
2951
+ return { codeVerifier, codeChallenge: base64UrlEncode(digest) };
2952
+ }
2953
+ /** A random URL-safe value for the OAuth `state` (CSRF binding on the callback). */
2954
+ function randomState() {
2955
+ return base64UrlEncode(crypto.getRandomValues(new Uint8Array(16)));
2956
+ }
2957
+ function base64UrlEncode(bytes) {
2958
+ return toBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2959
+ }
2960
+ function base64UrlDecode(s) {
2961
+ return fromBase64(s.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(s.length / 4) * 4, "="));
2962
+ }
2963
+
2780
2964
  /**
2781
2965
  * SDK build version — stamped to the console when a {@link Client} is created.
2782
2966
  *
@@ -2785,7 +2969,7 @@ async function signMessage(message, privateKey) {
2785
2969
  * expect (and isn't serving a stale cached bundle). Keep this in sync with
2786
2970
  * `package.json` `version`.
2787
2971
  */
2788
- const VERSION = "0.6.0-alpha.11";
2972
+ const VERSION = "0.7.0-alpha.4";
2789
2973
 
2790
2974
  /**
2791
2975
  * `HttpClient` — the one place the unified {@link Client} talks to the
@@ -3100,69 +3284,6 @@ function mnemonicToSeed(mnemonic) {
3100
3284
  return mnemonicToEntropy(m, wordlist);
3101
3285
  }
3102
3286
 
3103
- /**
3104
- * Identity vault primitives (M1.0).
3105
- *
3106
- * The vault decouples the identity from the password: a random 32-byte **master
3107
- * seed** yields the (unchanged) ZK identity via {@link deriveIdentityFromSeed},
3108
- * and that seed is AES-256-GCM-wrapped under one key *per factor* (password,
3109
- * passkey, recovery phrase). Server stores only ciphertext.
3110
- *
3111
- * This module holds the PURE primitives (seed gen, the memory-hard password
3112
- * pre-hash, AES-GCM wrap/unwrap, the OPRF -> wrap-key HKDF). The networked
3113
- * orchestration (calling the server's OPRF endpoint, fetching/storing factor
3114
- * records) lives in the AuthNamespace, which has transport access.
3115
- *
3116
- * Memory-hard KDF: we use **scrypt** (vetted, in `@noble/hashes`, runs in
3117
- * browser + Workers + Node - no wasm dependency) instead of argon2id; it is the
3118
- * pre-hash fed into the OPRF, so even if the OPRF key ever leaked, guesses still
3119
- * pay the scrypt cost.
3120
- */
3121
- const TE = new TextEncoder();
3122
- // Interactive-login scrypt params (~100ms on a laptop). N must be a power of 2.
3123
- const SCRYPT = { N: 1 << 15, r: 8, p: 1, dkLen: 32 };
3124
- /** 32 random bytes - the master seed for a brand-new identity. */
3125
- function randomSeed() {
3126
- return crypto.getRandomValues(new Uint8Array(32));
3127
- }
3128
- /** Memory-hard pre-hash of the password - the OPRF input. Salted by username. */
3129
- function passwordPreHash(username, password) {
3130
- return scrypt(TE.encode(password), TE.encode("muhkoo-vault-v1:" + username.toLowerCase()), SCRYPT);
3131
- }
3132
- /** HKDF the 64-byte OPRF output into a non-extractable AES-256-GCM wrap key. */
3133
- async function wrapKeyFromOprf(oprfOutput) {
3134
- const prk = await crypto.subtle.importKey("raw", oprfOutput, { name: "HKDF" }, false, ["deriveKey"]);
3135
- return crypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(), info: TE.encode("muhkoo-vault-wrap") }, prk, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
3136
- }
3137
- /** HKDF arbitrary 32-byte key material (e.g. a passkey PRF output) into an AES-GCM wrap key. */
3138
- async function wrapKeyFromBytes(keyMaterial, info) {
3139
- const prk = await crypto.subtle.importKey("raw", keyMaterial, { name: "HKDF" }, false, ["deriveKey"]);
3140
- return crypto.subtle.deriveKey({ name: "HKDF", hash: "SHA-256", salt: new Uint8Array(), info: TE.encode(info) }, prk, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
3141
- }
3142
- async function wrapSeed(seed, key) {
3143
- const iv = crypto.getRandomValues(new Uint8Array(12));
3144
- const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, seed);
3145
- return { iv: toBase64(iv), ct: toBase64(new Uint8Array(ct)) };
3146
- }
3147
- /** Decrypt a wrapped seed. Throws (AES-GCM tag failure) on a wrong key. */
3148
- async function unwrapSeed(wrapped, key) {
3149
- const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: fromBase64(wrapped.iv) }, key, fromBase64(wrapped.ct));
3150
- return new Uint8Array(pt);
3151
- }
3152
- function toBase64(bytes) {
3153
- let s = "";
3154
- for (let i = 0; i < bytes.length; i++)
3155
- s += String.fromCharCode(bytes[i]);
3156
- return btoa(s);
3157
- }
3158
- function fromBase64(b64) {
3159
- const s = atob(b64);
3160
- const out = new Uint8Array(s.length);
3161
- for (let i = 0; i < s.length; i++)
3162
- out[i] = s.charCodeAt(i);
3163
- return out;
3164
- }
3165
-
3166
3287
  /**
3167
3288
  * OPRF (Oblivious PRF) — RFC 9497, ristretto255-SHA512, mode 0x00.
3168
3289
  *
@@ -3196,6 +3317,55 @@ function oprfFinalize(input, blind, evaluated) {
3196
3317
  return OPRF.finalize(input, blind, evaluated);
3197
3318
  }
3198
3319
 
3320
+ /**
3321
+ * Verification-gated vault factors (M2 — email, later Google).
3322
+ *
3323
+ * These factors have NO client-side secret: the user proves *channel control*
3324
+ * (an email OTP) and the server releases a split-key OPRF evaluation —
3325
+ * `wrapKey = HKDF( OPRF(K1, input) ‖ OPRF(K2, input) )` where K1 lives on the
3326
+ * accelerator and K2 in a separate trust domain, so no single server-side
3327
+ * compromise can derive the wrap key offline. See accelerator
3328
+ * `AUTH_M2_PLAN.md` §1; the canonical reference implementation is
3329
+ * accelerator `tests/gated-oprf.test.ts` — keep these derivations in
3330
+ * byte-exact lockstep with it.
3331
+ */
3332
+ /**
3333
+ * Deterministic OPRF input for an email factor. Public-ish by design (the
3334
+ * verification gate, not the input, is what protects the eval) — but it must
3335
+ * be stable forever: username + address are lowercased, the tag is versioned.
3336
+ */
3337
+ async function emailFactorInput(username, email) {
3338
+ const tag = `muhkoo/factor:email:v1:${username.toLowerCase()}:${email.toLowerCase()}`;
3339
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(tag));
3340
+ return new Uint8Array(digest);
3341
+ }
3342
+ /**
3343
+ * Deterministic OPRF input for a google factor — keyed on the stable Google
3344
+ * account id (`sub`), not the email (which can change). Same versioning rules
3345
+ * as {@link emailFactorInput}.
3346
+ */
3347
+ async function googleFactorInput(username, sub) {
3348
+ const tag = `muhkoo/factor:google:v1:${username.toLowerCase()}:${sub}`;
3349
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(tag));
3350
+ return new Uint8Array(digest);
3351
+ }
3352
+ /** Blind a gated input for the split eval (same blind serves both domains). */
3353
+ function gatedBlind(input) {
3354
+ return oprfBlind(input);
3355
+ }
3356
+ /**
3357
+ * Fold the two domains' evaluations into the AES-256-GCM wrap key:
3358
+ * unblind each, concatenate, HKDF with the gated-factor domain separation.
3359
+ */
3360
+ async function gatedWrapKey(input, blind, evaluatedB64, evaluated2B64) {
3361
+ const out1 = oprfFinalize(input, blind, fromBase64(evaluatedB64));
3362
+ const out2 = oprfFinalize(input, blind, fromBase64(evaluated2B64));
3363
+ const ikm = new Uint8Array(out1.length + out2.length);
3364
+ ikm.set(out1);
3365
+ ikm.set(out2, out1.length);
3366
+ return wrapKeyFromBytes(ikm, "muhkoo-gated-wrap-v1");
3367
+ }
3368
+
3199
3369
  /**
3200
3370
  * Passkey factor (M1.1) — WebAuthn with the **PRF extension**.
3201
3371
  *
@@ -3313,6 +3483,160 @@ async function evaluatePasskeyPrf(rpId, credentialId, salt) {
3313
3483
  return new Uint8Array(results.first);
3314
3484
  }
3315
3485
 
3486
+ /**
3487
+ * `client.auth.hosted` — centralized hosted auth (auth.muhkoo.dev).
3488
+ * AUTH_HOSTED_PLAN.md.
3489
+ *
3490
+ * Two sides share this class:
3491
+ * - **Developer apps** call {@link login} (redirect to the hosted page) and
3492
+ * {@link handleCallback} (exchange the returned code → session + seed). The
3493
+ * app carries no snarkjs and never sees the user's credentials.
3494
+ * - **The hosted page** (the muhkoo/auth SPA), once it has authenticated the
3495
+ * user via the normal `client.auth.zk.*` flow, calls
3496
+ * {@link completeAuthorize} to seal the seed, mint the grant, and build the
3497
+ * redirect-back URL.
3498
+ *
3499
+ * Flow (authorization-code + PKCE + sealed-seed handoff):
3500
+ * app → GET auth.muhkoo.dev/authorize?app_id&redirect_uri&state&code_challenge
3501
+ * page → (user authenticates) → POST /api/auth/grant → redirect_uri?code&state#k=<K_t>
3502
+ * app → POST /api/auth/token {code, code_verifier} → {sessionToken, sealedKeys}
3503
+ * → unseal sealedKeys with K_t → master seed → session ready
3504
+ */
3505
+ const PKCE_STORE_KEY = "muhkoo.hosted.pkce";
3506
+ class HostedAuth {
3507
+ deps;
3508
+ constructor(deps) {
3509
+ this.deps = deps;
3510
+ }
3511
+ /**
3512
+ * (App side) Begin hosted login: stash a PKCE verifier + state, then send
3513
+ * the browser to the hosted authorize page. Returns the URL it redirects to
3514
+ * (also useful for popup mode / tests). `redirectUri` must be registered for
3515
+ * the app in the portal.
3516
+ */
3517
+ async login(opts) {
3518
+ const { codeVerifier, codeChallenge } = await generatePkce();
3519
+ const state = randomState();
3520
+ this.store({ codeVerifier, state, appId: opts.appId, redirectUri: opts.redirectUri });
3521
+ const url = `${this.deps.authBaseUrl}/authorize?` + new URLSearchParams({
3522
+ app_id: opts.appId,
3523
+ redirect_uri: opts.redirectUri,
3524
+ state,
3525
+ code_challenge: codeChallenge,
3526
+ code_challenge_method: "S256",
3527
+ }).toString();
3528
+ if (opts.redirect !== false)
3529
+ this.location().assign(url);
3530
+ return url;
3531
+ }
3532
+ /**
3533
+ * (App side) Send the user to centralized account & security management on
3534
+ * the hosted page (auth.muhkoo.dev/security) — manage passkeys, recovery
3535
+ * phrase, email, Google, change password, and remove login methods, all in
3536
+ * one place. `returnUri` (default: the current URL) gives the hosted page a
3537
+ * "Back to app" target. Redirects the browser; resolves to the URL.
3538
+ */
3539
+ async manageAccount(opts = {}) {
3540
+ const ret = opts.returnUri ?? this.location().href;
3541
+ const url = `${this.deps.authBaseUrl}/security?` + new URLSearchParams({ return: ret }).toString();
3542
+ this.location().assign(url);
3543
+ return url;
3544
+ }
3545
+ /** Whether the current URL looks like a hosted-auth callback (has code + state). */
3546
+ isCallback() {
3547
+ try {
3548
+ const q = new URLSearchParams(this.location().search);
3549
+ return q.has("code") && q.has("state");
3550
+ }
3551
+ catch {
3552
+ return false;
3553
+ }
3554
+ }
3555
+ /**
3556
+ * (App side) Complete login on the callback URL: verify state, exchange the
3557
+ * code (+ PKCE verifier) for the session, unseal the seed from the fragment,
3558
+ * establish the session, and scrub the URL. Returns the signed-in user.
3559
+ */
3560
+ async handleCallback() {
3561
+ const loc = this.location();
3562
+ const query = new URLSearchParams(loc.search);
3563
+ const code = query.get("code");
3564
+ const state = query.get("state");
3565
+ const fragmentKey = new URLSearchParams(loc.hash.replace(/^#/, "")).get("k");
3566
+ if (!code || !state)
3567
+ throw new Error("Not a hosted-auth callback.");
3568
+ if (!fragmentKey)
3569
+ throw new Error("Missing handoff key — the login link was altered.");
3570
+ const stash = this.take();
3571
+ if (!stash || stash.state !== state)
3572
+ throw new Error("State mismatch — possible CSRF; start login again.");
3573
+ const res = await this.deps.auth.token({ code, codeVerifier: stash.codeVerifier, appId: stash.appId });
3574
+ const seed = await unsealSeed(res.sealedKeys, fragmentKey);
3575
+ const identity = await deriveIdentityFromSeed(seed);
3576
+ await this.deps.session.setSession({ token: res.sessionToken, username: res.username, commitment: res.commitment });
3577
+ this.deps.session.setIdentity(identity);
3578
+ this.deps.session.setSeed(seed);
3579
+ this.scrubUrl();
3580
+ return { username: res.username, commitment: res.commitment };
3581
+ }
3582
+ /**
3583
+ * (Hosted-page side) Finish an /authorize request after the user has
3584
+ * authenticated here: seal the in-memory seed, mint the grant, and return
3585
+ * the URL to redirect the browser back to. The seed must be held (the user
3586
+ * just signed in via `client.auth.zk.*`).
3587
+ */
3588
+ async completeAuthorize(opts) {
3589
+ const token = this.deps.session.token;
3590
+ const seed = this.deps.session.seed;
3591
+ if (!token || !seed)
3592
+ throw new Error("Authenticate the user before completing authorization.");
3593
+ const { sealedKeys, fragmentKey } = await sealSeed(seed);
3594
+ const { code } = await this.deps.auth.grant(token, {
3595
+ appId: opts.appId, redirectUri: opts.redirectUri, codeChallenge: opts.codeChallenge, sealedKeys,
3596
+ });
3597
+ const url = new URL(opts.redirectUri);
3598
+ url.searchParams.set("code", code);
3599
+ url.searchParams.set("state", opts.state);
3600
+ url.hash = `k=${fragmentKey}`;
3601
+ return url.toString();
3602
+ }
3603
+ // ---- internals ----------------------------------------------------------
3604
+ location() {
3605
+ const loc = globalThis.location;
3606
+ if (!loc)
3607
+ throw new Error("Hosted auth requires a browser environment.");
3608
+ return loc;
3609
+ }
3610
+ store(v) {
3611
+ const ss = globalThis.sessionStorage;
3612
+ if (!ss)
3613
+ throw new Error("Hosted auth requires sessionStorage.");
3614
+ ss.setItem(PKCE_STORE_KEY, JSON.stringify(v));
3615
+ }
3616
+ take() {
3617
+ const ss = globalThis.sessionStorage;
3618
+ if (!ss)
3619
+ return null;
3620
+ const raw = ss.getItem(PKCE_STORE_KEY);
3621
+ if (!raw)
3622
+ return null;
3623
+ ss.removeItem(PKCE_STORE_KEY);
3624
+ try {
3625
+ return JSON.parse(raw);
3626
+ }
3627
+ catch {
3628
+ return null;
3629
+ }
3630
+ }
3631
+ /** Drop `code`, `state`, and the `#k` fragment from the address bar. */
3632
+ scrubUrl() {
3633
+ const h = globalThis.history;
3634
+ const loc = this.location();
3635
+ if (h?.replaceState)
3636
+ h.replaceState(null, "", loc.pathname);
3637
+ }
3638
+ }
3639
+
3316
3640
  /**
3317
3641
  * `client.auth` — user authentication, exposed today through the `zk`
3318
3642
  * sub-namespace (`client.auth.zk.login(...)`). Leaving room under `auth` for
@@ -3561,6 +3885,137 @@ class ZkAuth {
3561
3885
  this.deps.session.setSeed(seed);
3562
3886
  return user;
3563
3887
  }
3888
+ /**
3889
+ * Add an **email** as a recovery + login factor (M2 verification-gated:
3890
+ * the server only releases the split-key OPRF eval after the address is
3891
+ * OTP-verified). Sends the code, then returns a `confirm` continuation:
3892
+ *
3893
+ * const { confirm } = await client.auth.zk.enrollEmailFactor("me@x.com");
3894
+ * await confirm(codeFromInbox);
3895
+ *
3896
+ * Requires being signed in with the seed held (like the other enrolls).
3897
+ * Custody note: gated factors trade some custody for recoverability — the
3898
+ * trade-off should be disclosed in the enroll UI.
3899
+ */
3900
+ async enrollEmailFactor(email) {
3901
+ const seed = this.deps.session.seed;
3902
+ const token = this.deps.session.token;
3903
+ const username = this.deps.session.username;
3904
+ if (!seed || !token || !username) {
3905
+ throw new Error("Sign in first to add a recovery email.");
3906
+ }
3907
+ await this.deps.auth.emailOtpStart({ token, email });
3908
+ return {
3909
+ email,
3910
+ confirm: async (code) => {
3911
+ const { verifyToken, email: verified } = await this.deps.auth.emailOtpVerify({ token, code });
3912
+ const input = await emailFactorInput(username, verified);
3913
+ const { blind, blinded } = gatedBlind(input);
3914
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "email", "enroll", verifyToken);
3915
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
3916
+ const wrapped = await wrapSeed(seed, key);
3917
+ await this.deps.auth.vaultPutFactor(token, { id: "email", type: "email", wrap: wrapped.ct, iv: wrapped.iv, params: { email: verified }, createdAt: Date.now() }, verifyToken);
3918
+ },
3919
+ };
3920
+ }
3921
+ /**
3922
+ * Recover an account via its enrolled **email** factor. Sends a code to the
3923
+ * enrolled address (the response never reveals whether one exists), then
3924
+ * `confirm(code)` completes: gated read + split-key eval → unwrap seed →
3925
+ * normal ZK login. After this, call {@link changePassword} to set a new
3926
+ * password.
3927
+ *
3928
+ * const { confirm } = await client.auth.zk.recoverWithEmail("alice");
3929
+ * const user = await confirm(codeFromInbox);
3930
+ */
3931
+ async recoverWithEmail(username, opts = {}) {
3932
+ await this.deps.auth.emailOtpStart({ username });
3933
+ return {
3934
+ confirm: async (code) => {
3935
+ const { verifyToken, email } = await this.deps.auth.emailOtpVerify({ username, code });
3936
+ const { factor } = await this.deps.auth.vaultRead(username, "email", verifyToken);
3937
+ if (!factor?.wrap || !factor?.iv) {
3938
+ throw new Error("This account has no email recovery set up.");
3939
+ }
3940
+ const input = await emailFactorInput(username, email);
3941
+ const { blind, blinded } = gatedBlind(input);
3942
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "email", "recover", verifyToken);
3943
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
3944
+ const seed = await unwrapSeed({ iv: factor.iv, ct: factor.wrap }, key);
3945
+ const identity = await deriveIdentityFromSeed(seed);
3946
+ const user = await this.proveAndStore(username, identity, opts);
3947
+ this.deps.session.setSeed(seed);
3948
+ return user;
3949
+ },
3950
+ };
3951
+ }
3952
+ // --- Google factor (M2.3) ------------------------------------------------
3953
+ // Google is pure authentication UX: its verified ID token gates the
3954
+ // split-key OPRF eval that releases the master seed. The ZK identity layer
3955
+ // is unchanged. The app obtains `idToken` via Google Identity Services and
3956
+ // passes it in; the SDK never renders Google UI.
3957
+ /**
3958
+ * Link a Google account as a recovery + login factor. Requires being signed
3959
+ * in with the seed held. `idToken` is a fresh Google ID token (GIS).
3960
+ */
3961
+ async enrollGoogleFactor(idToken) {
3962
+ const seed = this.deps.session.seed;
3963
+ const token = this.deps.session.token;
3964
+ const username = this.deps.session.username;
3965
+ if (!seed || !token || !username)
3966
+ throw new Error("Sign in first to link a Google account.");
3967
+ const { verifyToken, sub, emailHint } = await this.deps.auth.googleVerify({ idToken, token });
3968
+ const input = await googleFactorInput(username, sub);
3969
+ const { blind, blinded } = gatedBlind(input);
3970
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "google", "enroll", verifyToken);
3971
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
3972
+ const wrapped = await wrapSeed(seed, key);
3973
+ await this.deps.auth.vaultPutFactor(token, { id: "google", type: "google", wrap: wrapped.ct, iv: wrapped.iv, params: { sub, emailHint: emailHint ?? undefined }, createdAt: Date.now() }, verifyToken);
3974
+ }
3975
+ /**
3976
+ * Sign in with Google — verify the ID token, unlock the seed via the gated
3977
+ * split-key eval, and establish a session. Same machinery serves "Sign in
3978
+ * with Google" and "recover with Google"; the account must have linked the
3979
+ * factor first.
3980
+ */
3981
+ async loginWithGoogle(username, idToken, opts = {}) {
3982
+ const { verifyToken, sub } = await this.deps.auth.googleVerify({ idToken, username });
3983
+ const { factor } = await this.deps.auth.vaultRead(username, "google", verifyToken);
3984
+ if (!factor?.wrap || !factor?.iv)
3985
+ throw new Error("No Google account is linked to this username.");
3986
+ const input = await googleFactorInput(username, sub);
3987
+ const { blind, blinded } = gatedBlind(input);
3988
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "google", "recover", verifyToken);
3989
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
3990
+ const seed = await unwrapSeed({ iv: factor.iv, ct: factor.wrap }, key);
3991
+ const identity = await deriveIdentityFromSeed(seed);
3992
+ const user = await this.proveAndStore(username, identity, opts);
3993
+ this.deps.session.setSeed(seed);
3994
+ return user;
3995
+ }
3996
+ /**
3997
+ * Register a brand-new, **passwordless** account whose only factor is Google.
3998
+ * A random master seed is generated and wrapped under the Google-gated key;
3999
+ * the user can add a password / passkey / phrase later. The ZK registration
4000
+ * is identical to a password signup.
4001
+ */
4002
+ async registerWithGoogle(username, idToken) {
4003
+ const seed = randomSeed();
4004
+ const identity = await deriveIdentityFromSeed(seed);
4005
+ const commitment = await this.commitmentFor(identity);
4006
+ await this.deps.auth.register({
4007
+ username,
4008
+ commitment,
4009
+ ecdhPublicKey: await exportPublicKeyBase64(identity.ecdhKeyPair.publicKey),
4010
+ ecdsaPublicKey: await exportPublicKeyBase64(identity.ecdsaKeyPair.publicKey),
4011
+ email: null,
4012
+ });
4013
+ const user = await this.proveAndStore(username, identity, {});
4014
+ this.deps.session.setSeed(seed);
4015
+ // Enroll Google as the sole factor (session + seed are now in hand).
4016
+ await this.enrollGoogleFactor(idToken);
4017
+ return user;
4018
+ }
3564
4019
  /**
3565
4020
  * Change the password — re-wraps the (unchanged) master seed under the new
3566
4021
  * password's OPRF-gated key. The identity/commitment never moves. Requires
@@ -3638,7 +4093,11 @@ class ZkAuth {
3638
4093
  this.deps.session.setSeed(seed);
3639
4094
  return user;
3640
4095
  }
3641
- /** List the user's enrolled login methods (metadata only). Session-authed. */
4096
+ /**
4097
+ * List the user's enrolled login methods (metadata only). Session-authed.
4098
+ * Gated factors (email/google) carry a `masked` display hint
4099
+ * ("m•••@gmail.com"); the full value is never returned.
4100
+ */
3642
4101
  async listFactors() {
3643
4102
  const token = this.deps.session.token;
3644
4103
  if (!token)
@@ -3765,8 +4224,11 @@ class ZkAuth {
3765
4224
  /** `client.auth` — the auth namespace. Strategies hang off here. */
3766
4225
  class AuthNamespace {
3767
4226
  zk;
4227
+ /** Centralized hosted auth (auth.muhkoo.dev) — `client.auth.hosted.login(...)`. */
4228
+ hosted;
3768
4229
  constructor(deps) {
3769
4230
  this.zk = new ZkAuth(deps);
4231
+ this.hosted = new HostedAuth({ auth: deps.auth, session: deps.session, authBaseUrl: deps.authBaseUrl });
3770
4232
  }
3771
4233
  /** Convenience pass-through: the currently signed-in user, or `null`. */
3772
4234
  get user() {
@@ -7324,6 +7786,7 @@ class AgentsNamespace {
7324
7786
  * });
7325
7787
  * // HTTP-reachable at hello--<slug>.fns.muhkoo.dev
7326
7788
  * await client.functions.enable(appId, config.functionId, spaceId); // Space-bound
7789
+ * const out = await client.functions.invoke({ name: 'hello', slug }, { body: { q: 1 } });
7327
7790
  *
7328
7791
  * These are MANAGEMENT calls — the function itself runs on the accelerator
7329
7792
  * (uploaded just-in-time, decrypted only at invocation). Only an app's owner or
@@ -7337,6 +7800,18 @@ class AgentsNamespace {
7337
7800
  * triggers: `http` (its own subdomain) and `space` (invoked on Space messages,
7338
7801
  * like a Programmable Agent).
7339
7802
  */
7803
+ /** The hosted platform's HTTP-function zone. */
7804
+ const DEFAULT_FN_HOST_SUFFIX = "fns.muhkoo.dev";
7805
+ /** Bodies `fetch` accepts natively — everything else is JSON-encoded. */
7806
+ function isRawBody(body) {
7807
+ return (typeof body === "string" ||
7808
+ body instanceof FormData ||
7809
+ body instanceof Blob ||
7810
+ body instanceof ArrayBuffer ||
7811
+ ArrayBuffer.isView(body) ||
7812
+ body instanceof URLSearchParams ||
7813
+ (typeof ReadableStream !== "undefined" && body instanceof ReadableStream));
7814
+ }
7340
7815
  class FunctionsNamespace {
7341
7816
  deps;
7342
7817
  constructor(deps) {
@@ -7375,6 +7850,71 @@ class FunctionsNamespace {
7375
7850
  async disable(appId, functionId, spaceId, opts = {}) {
7376
7851
  return this.deps.http.post(this.path(appId, `/${encodeURIComponent(functionId)}/disable`, {}), { targetSpaceId: spaceId, space: opts.space });
7377
7852
  }
7853
+ /**
7854
+ * Invoke an HTTP-triggered function and parse its JSON response.
7855
+ *
7856
+ * const out = await client.functions.invoke({ name: 'hello', slug: 'myapp' });
7857
+ * const out = await client.functions.invoke('hello--myapp', { body: { q: 1 } });
7858
+ * const out = await client.functions.invoke('https://hello--myapp.fns.muhkoo.dev/x');
7859
+ *
7860
+ * The request carries the SDK's credentials (`X-Muhkoo-Key` and, when a
7861
+ * user is signed in, `X-Muhkoo-Session`) — the platform passes the session
7862
+ * token through to the function, which can verify it server-side via
7863
+ * `POST /api/auth/verify` to establish the caller's identity.
7864
+ *
7865
+ * Non-2xx responses throw {@link HttpError}. For functions that return
7866
+ * something other than JSON, use {@link invokeRaw}.
7867
+ */
7868
+ async invoke(target, opts = {}) {
7869
+ const res = await this.invokeRaw(target, opts);
7870
+ let parsed = null;
7871
+ try {
7872
+ parsed = await res.json();
7873
+ }
7874
+ catch {
7875
+ // Non-JSON body — fall through to the status-only error below.
7876
+ }
7877
+ if (!res.ok) {
7878
+ const msg = parsed && typeof parsed === "object" && "error" in parsed
7879
+ ? String(parsed.error)
7880
+ : `${res.status} ${res.statusText}`;
7881
+ throw new HttpError(msg, res.status, parsed);
7882
+ }
7883
+ return parsed;
7884
+ }
7885
+ /** Invoke an HTTP-triggered function and return the raw `Response`. */
7886
+ async invokeRaw(target, opts = {}) {
7887
+ const init = {
7888
+ method: opts.method ?? (opts.body !== undefined ? "POST" : "GET"),
7889
+ };
7890
+ const headers = new Headers(opts.headers);
7891
+ if (opts.body !== undefined) {
7892
+ if (isRawBody(opts.body)) {
7893
+ init.body = opts.body;
7894
+ }
7895
+ else {
7896
+ init.body = JSON.stringify(opts.body);
7897
+ if (!headers.has("Content-Type"))
7898
+ headers.set("Content-Type", "application/json");
7899
+ }
7900
+ }
7901
+ init.headers = headers;
7902
+ return this.deps.http.fetch(this.invokeUrl(target, opts.path), init);
7903
+ }
7904
+ /** Resolve an invoke target to the function's absolute URL. */
7905
+ invokeUrl(target, path = "") {
7906
+ const suffix = this.deps.fnHostSuffix ?? DEFAULT_FN_HOST_SUFFIX;
7907
+ let base;
7908
+ if (typeof target === "string") {
7909
+ base = /^https?:\/\//.test(target) ? target : `https://${target}.${suffix}`;
7910
+ }
7911
+ else {
7912
+ base = `https://${target.name}--${target.slug}.${suffix}`;
7913
+ }
7914
+ if (!path)
7915
+ return base;
7916
+ return `${base.replace(/\/+$/, "")}${path.startsWith("/") ? "" : "/"}${path}`;
7917
+ }
7378
7918
  path(appId, suffix, opts) {
7379
7919
  const q = opts.space ? `?space=${encodeURIComponent(opts.space)}` : "";
7380
7920
  return `/api/apps/${encodeURIComponent(appId)}/functions${suffix}${q}`;
@@ -7406,6 +7946,8 @@ class FunctionsNamespace {
7406
7946
  */
7407
7947
  /** The hosted Muhkoo Accelerator — the default {@link ClientOptions.baseUrl}. */
7408
7948
  const DEFAULT_BASE_URL = "https://api.muhkoo.dev";
7949
+ /** The hosted auth SPA — the default {@link ClientOptions.authBaseUrl}. */
7950
+ const DEFAULT_AUTH_BASE_URL = "https://auth.muhkoo.dev";
7409
7951
  /**
7410
7952
  * Turn the HTTP base URL into a websocket base (`http(s)://` → `ws(s)://`).
7411
7953
  * The accelerator serves spaces over both on the same origin.
@@ -7463,7 +8005,8 @@ class Client {
7463
8005
  const circuits = options.circuits ?? defaultCircuitUrls(this.baseUrl);
7464
8006
  const authClient = new AuthClient({ baseUrl: this.baseUrl, fetch: this.http.fetch });
7465
8007
  const wsBaseUrl = toWsBase(this.baseUrl);
7466
- this.auth = new AuthNamespace({ auth: authClient, circuits, session: this.session });
8008
+ const authBaseUrl = (options.authBaseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/+$/, "");
8009
+ this.auth = new AuthNamespace({ auth: authClient, circuits, session: this.session, authBaseUrl });
7467
8010
  this.kv = new KvNamespace({ http: this.http, session: this.session, wsBaseUrl });
7468
8011
  this.db = new DbNamespace({ http: this.http });
7469
8012
  this.storage = new StorageNamespace({ http: this.http, baseUrl: this.baseUrl, kv: this.kv });
@@ -7482,7 +8025,7 @@ class Client {
7482
8025
  cache: spaceKeyCache,
7483
8026
  });
7484
8027
  this.agents = new AgentsNamespace({ http: this.http });
7485
- this.functions = new FunctionsNamespace({ http: this.http });
8028
+ this.functions = new FunctionsNamespace({ http: this.http, fnHostSuffix: options.functionsHostSuffix });
7486
8029
  }
7487
8030
  /** The currently signed-in user, or `null`. */
7488
8031
  get user() {
@@ -8243,5 +8786,5 @@ var bundledLoader = /*#__PURE__*/Object.freeze({
8243
8786
  default: loadRsWasm
8244
8787
  });
8245
8788
 
8246
- export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, DEFAULT_BASE_URL, DbNamespace, DbTable, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, Logger, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, Ready, ReedSolomonCodec, Retry, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, VERSION, VaultUnavailableError, WSTransport, ZkAuth, _accountId, _formatBytes, _messageId, _objectId, _packetId, _socketId, _userId, appLogger$1 as appLogger, assertType, base58Decode, base58Encode, buildCommitment, compilePrograms, concatBytes, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, deserialize, ejectAgentPrompt, ejectAgentTools, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, fromBase64$1 as fromBase64, fromBase64Url, fromHex, generateAuthProof, generateChecksum, generateId, getIPAddress, getId, getMuhkooAppDescriptor, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, isNumber, log, manifestToStat, poseidonHash, quickVerify, randomBytes, serialize, shardHash, signMessage, toBase64$1 as toBase64, toBase64Url, toField, toHex, unwrapWithPassphrase, utf8Decode, utf8Encode, verify, verifyChecksum, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
8789
+ export { AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, DEFAULT_BASE_URL, DEFAULT_FN_HOST_SUFFIX, DbNamespace, DbTable, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, Logger, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, Ready, ReedSolomonCodec, Retry, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, VERSION, VaultUnavailableError, WSTransport, ZkAuth, _accountId, _formatBytes, _messageId, _objectId, _packetId, _socketId, _userId, appLogger$1 as appLogger, assertType, base58Decode, base58Encode, buildCommitment, compilePrograms, concatBytes, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, deserialize, ejectAgentPrompt, ejectAgentTools, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, fromBase64$1 as fromBase64, fromBase64Url, fromHex, generateAuthProof, generateChecksum, generateId, generatePkce, getIPAddress, getId, getMuhkooAppDescriptor, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, isNumber, log, manifestToStat, poseidonHash, quickVerify, randomBytes, randomState, sealSeed, serialize, shardHash, signMessage, toBase64$1 as toBase64, toBase64Url, toField, toHex, unsealSeed, unwrapWithPassphrase, utf8Decode, utf8Encode, verify, verifyChecksum, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
8247
8790
  //# sourceMappingURL=index.js.map