@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
  const _isTty = (() => {
@@ -4303,14 +4303,75 @@ class AuthClient {
4303
4303
  const res = await this.fetchFn(`${this.baseUrl}/api/auth/oprf`, this.json("POST", { username, blinded }));
4304
4304
  return await this.parse("oprfEvaluate", res);
4305
4305
  }
4306
- /** Read a factor record for `username` (returns an indistinguishable decoy if absent). */
4307
- async vaultRead(username, factorType) {
4308
- const res = await this.fetchFn(`${this.baseUrl}/api/auth/vault`, this.json("POST", { username, factorType }));
4306
+ /**
4307
+ * Gated split-key OPRF evaluation (M2 — email/google factors). Requires a
4308
+ * live `verifyToken` from {@link emailOtpVerify}; returns BOTH trust
4309
+ * domains' evaluations. The recover token is consumed by this call.
4310
+ */
4311
+ async oprfEvaluateGated(username, blinded, factorType, purpose, verifyToken) {
4312
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/oprf`, this.json("POST", { username, blinded, factorType, purpose, verifyToken }));
4313
+ return await this.parse("oprfEvaluateGated", res);
4314
+ }
4315
+ /**
4316
+ * Request an email OTP. With a session `token` this is the ENROLL flow
4317
+ * (code sent to `email`); without one it's RECOVER (code sent to the
4318
+ * account's enrolled address — the response is uniform either way).
4319
+ */
4320
+ async emailOtpStart(opts) {
4321
+ const init = this.json("POST", { email: opts.email, username: opts.username });
4322
+ if (opts.token)
4323
+ init.headers["X-Muhkoo-Session"] = opts.token;
4324
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/email/start`, init);
4325
+ await this.parse("emailOtpStart", res);
4326
+ }
4327
+ /** Trade an emailed code for a single-use verifyToken (+ the proven address). */
4328
+ async emailOtpVerify(opts) {
4329
+ const init = this.json("POST", { code: opts.code, username: opts.username });
4330
+ if (opts.token)
4331
+ init.headers["X-Muhkoo-Session"] = opts.token;
4332
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/email/verify`, init);
4333
+ return await this.parse("emailOtpVerify", res);
4334
+ }
4335
+ /**
4336
+ * Verify a Google ID token (M2.3). With a session `token` it's the ENROLL
4337
+ * gate; without, it's RECOVER/LOGIN (the Google account must already be
4338
+ * linked). Returns a single-use verifyToken bound to the account's `sub`.
4339
+ */
4340
+ async googleVerify(opts) {
4341
+ const init = this.json("POST", { idToken: opts.idToken, username: opts.username });
4342
+ if (opts.token)
4343
+ init.headers["X-Muhkoo-Session"] = opts.token;
4344
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/google/verify`, init);
4345
+ return await this.parse("googleVerify", res);
4346
+ }
4347
+ // ---- Hosted-auth authorization-code flow (H0) ---------------------------
4348
+ /** Mint a single-use authorization code for an app (called by the hosted page; session-authed). */
4349
+ async grant(token, body) {
4350
+ const init = this.json("POST", body);
4351
+ init.headers["X-Muhkoo-Session"] = token;
4352
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/grant`, init);
4353
+ return await this.parse("grant", res);
4354
+ }
4355
+ /** Exchange an authorization code + PKCE verifier for the session + sealed key material. */
4356
+ async token(body) {
4357
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/token`, this.json("POST", body));
4358
+ return await this.parse("token", res);
4359
+ }
4360
+ /**
4361
+ * Read a factor record for `username` (returns an indistinguishable decoy if
4362
+ * absent). Gated types (email) require the recover `verifyToken`.
4363
+ */
4364
+ async vaultRead(username, factorType, verifyToken) {
4365
+ const res = await this.fetchFn(`${this.baseUrl}/api/auth/vault`, this.json("POST", { username, factorType, verifyToken }));
4309
4366
  return await this.parse("vaultRead", res);
4310
4367
  }
4311
- /** Add/replace a factor (session-authed; the seed is already wrapped client-side). */
4312
- async vaultPutFactor(token, factor) {
4313
- const init = this.json("PUT", { factor });
4368
+ /**
4369
+ * Add/replace a factor (session-authed; the seed is already wrapped
4370
+ * client-side). Email factors additionally require (and consume) the
4371
+ * enroll `verifyToken`; the stored address must be the verified one.
4372
+ */
4373
+ async vaultPutFactor(token, factor, verifyToken) {
4374
+ const init = this.json("PUT", { factor, verifyToken });
4314
4375
  init.headers["Authorization"] = `Bearer ${token}`;
4315
4376
  const res = await this.fetchFn(`${this.baseUrl}/api/auth/vault/factor`, init);
4316
4377
  return await this.parse("vaultPutFactor", res);
@@ -4700,6 +4761,129 @@ async function signMessage(message, privateKey) {
4700
4761
  return bytesToBase64(new Uint8Array(sig));
4701
4762
  }
4702
4763
 
4764
+ /**
4765
+ * Identity vault primitives (M1.0).
4766
+ *
4767
+ * The vault decouples the identity from the password: a random 32-byte **master
4768
+ * seed** yields the (unchanged) ZK identity via {@link deriveIdentityFromSeed},
4769
+ * and that seed is AES-256-GCM-wrapped under one key *per factor* (password,
4770
+ * passkey, recovery phrase). Server stores only ciphertext.
4771
+ *
4772
+ * This module holds the PURE primitives (seed gen, the memory-hard password
4773
+ * pre-hash, AES-GCM wrap/unwrap, the OPRF -> wrap-key HKDF). The networked
4774
+ * orchestration (calling the server's OPRF endpoint, fetching/storing factor
4775
+ * records) lives in the AuthNamespace, which has transport access.
4776
+ *
4777
+ * Memory-hard KDF: we use **scrypt** (vetted, in `@noble/hashes`, runs in
4778
+ * browser + Workers + Node - no wasm dependency) instead of argon2id; it is the
4779
+ * pre-hash fed into the OPRF, so even if the OPRF key ever leaked, guesses still
4780
+ * pay the scrypt cost.
4781
+ */
4782
+ const TE$1 = new TextEncoder();
4783
+ // Interactive-login scrypt params (~100ms on a laptop). N must be a power of 2.
4784
+ const SCRYPT = { N: 1 << 15, r: 8, p: 1, dkLen: 32 };
4785
+ /** 32 random bytes - the master seed for a brand-new identity. */
4786
+ function randomSeed() {
4787
+ return crypto.getRandomValues(new Uint8Array(32));
4788
+ }
4789
+ /** Memory-hard pre-hash of the password - the OPRF input. Salted by username. */
4790
+ function passwordPreHash(username, password) {
4791
+ return scrypt(TE$1.encode(password), TE$1.encode("muhkoo-vault-v1:" + username.toLowerCase()), SCRYPT);
4792
+ }
4793
+ /** HKDF the 64-byte OPRF output into a non-extractable AES-256-GCM wrap key. */
4794
+ async function wrapKeyFromOprf(oprfOutput) {
4795
+ const prk = await crypto.subtle.importKey("raw", oprfOutput, { name: "HKDF" }, false, ["deriveKey"]);
4796
+ 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"]);
4797
+ }
4798
+ /** HKDF arbitrary 32-byte key material (e.g. a passkey PRF output) into an AES-GCM wrap key. */
4799
+ async function wrapKeyFromBytes(keyMaterial, info) {
4800
+ const prk = await crypto.subtle.importKey("raw", keyMaterial, { name: "HKDF" }, false, ["deriveKey"]);
4801
+ 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"]);
4802
+ }
4803
+ async function wrapSeed(seed, key) {
4804
+ const iv = crypto.getRandomValues(new Uint8Array(12));
4805
+ const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, seed);
4806
+ return { iv: toBase64(iv), ct: toBase64(new Uint8Array(ct)) };
4807
+ }
4808
+ /** Decrypt a wrapped seed. Throws (AES-GCM tag failure) on a wrong key. */
4809
+ async function unwrapSeed(wrapped, key) {
4810
+ const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: fromBase64(wrapped.iv) }, key, fromBase64(wrapped.ct));
4811
+ return new Uint8Array(pt);
4812
+ }
4813
+ function toBase64(bytes) {
4814
+ let s = "";
4815
+ for (let i = 0; i < bytes.length; i++)
4816
+ s += String.fromCharCode(bytes[i]);
4817
+ return btoa(s);
4818
+ }
4819
+ function fromBase64(b64) {
4820
+ const s = atob(b64);
4821
+ const out = new Uint8Array(s.length);
4822
+ for (let i = 0; i < s.length; i++)
4823
+ out[i] = s.charCodeAt(i);
4824
+ return out;
4825
+ }
4826
+
4827
+ /**
4828
+ * Hosted-auth handoff crypto (auth.muhkoo.dev — AUTH_HOSTED_PLAN.md §3).
4829
+ *
4830
+ * The hosted page authenticates the user and ends up holding the master seed.
4831
+ * To pass it to the developer's app without it ever sitting server-readable or
4832
+ * landing in browser history:
4833
+ *
4834
+ * 1. {@link sealSeed} generates a one-time key K_t, AES-256-GCM-seals the seed
4835
+ * under it, and returns the ciphertext envelope (`sealedKeys`, stored in
4836
+ * the grant the server relays but can't read) plus K_t (`fragmentKey`,
4837
+ * placed ONLY in the redirect URL fragment — fragments never hit a server).
4838
+ * 2. The app exchanges the code for `sealedKeys`, reads K_t from the fragment,
4839
+ * and {@link unsealSeed} recovers the seed, then strips the fragment.
4840
+ *
4841
+ * Trust-equivalent to today's embedded SDK (the app still ends up with the
4842
+ * seed) while moving credential entry onto the Muhkoo origin. The envelope is
4843
+ * versioned so a future per-app-key handoff (v2) can swap in without a protocol
4844
+ * change.
4845
+ */
4846
+ const TE = new TextEncoder();
4847
+ const TD = new TextDecoder();
4848
+ /** Seal the master seed under a fresh one-time key for the redirect handoff. */
4849
+ async function sealSeed(seed) {
4850
+ const ktRaw = crypto.getRandomValues(new Uint8Array(32));
4851
+ const key = await crypto.subtle.importKey("raw", ktRaw, { name: "AES-GCM" }, false, ["encrypt"]);
4852
+ const iv = crypto.getRandomValues(new Uint8Array(12));
4853
+ const ct = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: iv }, key, seed));
4854
+ const envelope = { v: 1, iv: toBase64(iv), ct: toBase64(ct) };
4855
+ return {
4856
+ sealedKeys: toBase64(TE.encode(JSON.stringify(envelope))),
4857
+ fragmentKey: base64UrlEncode(ktRaw),
4858
+ };
4859
+ }
4860
+ /** Recover the master seed from the sealed envelope + the fragment key. */
4861
+ async function unsealSeed(sealedKeys, fragmentKey) {
4862
+ const envelope = JSON.parse(TD.decode(fromBase64(sealedKeys)));
4863
+ if (envelope.v !== 1)
4864
+ throw new Error(`Unsupported handoff envelope version ${envelope.v}`);
4865
+ const ktRaw = base64UrlDecode(fragmentKey);
4866
+ const key = await crypto.subtle.importKey("raw", ktRaw, { name: "AES-GCM" }, false, ["decrypt"]);
4867
+ const seed = await crypto.subtle.decrypt({ name: "AES-GCM", iv: fromBase64(envelope.iv) }, key, fromBase64(envelope.ct));
4868
+ return new Uint8Array(seed);
4869
+ }
4870
+ /** Generate a PKCE verifier + S256 challenge for the authorize→token exchange. */
4871
+ async function generatePkce() {
4872
+ const codeVerifier = base64UrlEncode(crypto.getRandomValues(new Uint8Array(32)));
4873
+ const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", TE.encode(codeVerifier)));
4874
+ return { codeVerifier, codeChallenge: base64UrlEncode(digest) };
4875
+ }
4876
+ /** A random URL-safe value for the OAuth `state` (CSRF binding on the callback). */
4877
+ function randomState() {
4878
+ return base64UrlEncode(crypto.getRandomValues(new Uint8Array(16)));
4879
+ }
4880
+ function base64UrlEncode(bytes) {
4881
+ return toBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4882
+ }
4883
+ function base64UrlDecode(s) {
4884
+ return fromBase64(s.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(s.length / 4) * 4, "="));
4885
+ }
4886
+
4703
4887
  /**
4704
4888
  * SDK build version — stamped to the console when a {@link Client} is created.
4705
4889
  *
@@ -4708,7 +4892,7 @@ async function signMessage(message, privateKey) {
4708
4892
  * expect (and isn't serving a stale cached bundle). Keep this in sync with
4709
4893
  * `package.json` `version`.
4710
4894
  */
4711
- const VERSION = "0.6.0-alpha.11";
4895
+ const VERSION = "0.7.0-alpha.4";
4712
4896
 
4713
4897
  /**
4714
4898
  * `HttpClient` — the one place the unified {@link Client} talks to the
@@ -5023,69 +5207,6 @@ function mnemonicToSeed(mnemonic) {
5023
5207
  return mnemonicToEntropy(m, wordlist);
5024
5208
  }
5025
5209
 
5026
- /**
5027
- * Identity vault primitives (M1.0).
5028
- *
5029
- * The vault decouples the identity from the password: a random 32-byte **master
5030
- * seed** yields the (unchanged) ZK identity via {@link deriveIdentityFromSeed},
5031
- * and that seed is AES-256-GCM-wrapped under one key *per factor* (password,
5032
- * passkey, recovery phrase). Server stores only ciphertext.
5033
- *
5034
- * This module holds the PURE primitives (seed gen, the memory-hard password
5035
- * pre-hash, AES-GCM wrap/unwrap, the OPRF -> wrap-key HKDF). The networked
5036
- * orchestration (calling the server's OPRF endpoint, fetching/storing factor
5037
- * records) lives in the AuthNamespace, which has transport access.
5038
- *
5039
- * Memory-hard KDF: we use **scrypt** (vetted, in `@noble/hashes`, runs in
5040
- * browser + Workers + Node - no wasm dependency) instead of argon2id; it is the
5041
- * pre-hash fed into the OPRF, so even if the OPRF key ever leaked, guesses still
5042
- * pay the scrypt cost.
5043
- */
5044
- const TE = new TextEncoder();
5045
- // Interactive-login scrypt params (~100ms on a laptop). N must be a power of 2.
5046
- const SCRYPT = { N: 1 << 15, r: 8, p: 1, dkLen: 32 };
5047
- /** 32 random bytes - the master seed for a brand-new identity. */
5048
- function randomSeed() {
5049
- return crypto.getRandomValues(new Uint8Array(32));
5050
- }
5051
- /** Memory-hard pre-hash of the password - the OPRF input. Salted by username. */
5052
- function passwordPreHash(username, password) {
5053
- return scrypt(TE.encode(password), TE.encode("muhkoo-vault-v1:" + username.toLowerCase()), SCRYPT);
5054
- }
5055
- /** HKDF the 64-byte OPRF output into a non-extractable AES-256-GCM wrap key. */
5056
- async function wrapKeyFromOprf(oprfOutput) {
5057
- const prk = await crypto.subtle.importKey("raw", oprfOutput, { name: "HKDF" }, false, ["deriveKey"]);
5058
- 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"]);
5059
- }
5060
- /** HKDF arbitrary 32-byte key material (e.g. a passkey PRF output) into an AES-GCM wrap key. */
5061
- async function wrapKeyFromBytes(keyMaterial, info) {
5062
- const prk = await crypto.subtle.importKey("raw", keyMaterial, { name: "HKDF" }, false, ["deriveKey"]);
5063
- 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"]);
5064
- }
5065
- async function wrapSeed(seed, key) {
5066
- const iv = crypto.getRandomValues(new Uint8Array(12));
5067
- const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, seed);
5068
- return { iv: toBase64(iv), ct: toBase64(new Uint8Array(ct)) };
5069
- }
5070
- /** Decrypt a wrapped seed. Throws (AES-GCM tag failure) on a wrong key. */
5071
- async function unwrapSeed(wrapped, key) {
5072
- const pt = await crypto.subtle.decrypt({ name: "AES-GCM", iv: fromBase64(wrapped.iv) }, key, fromBase64(wrapped.ct));
5073
- return new Uint8Array(pt);
5074
- }
5075
- function toBase64(bytes) {
5076
- let s = "";
5077
- for (let i = 0; i < bytes.length; i++)
5078
- s += String.fromCharCode(bytes[i]);
5079
- return btoa(s);
5080
- }
5081
- function fromBase64(b64) {
5082
- const s = atob(b64);
5083
- const out = new Uint8Array(s.length);
5084
- for (let i = 0; i < s.length; i++)
5085
- out[i] = s.charCodeAt(i);
5086
- return out;
5087
- }
5088
-
5089
5210
  /**
5090
5211
  * OPRF (Oblivious PRF) — RFC 9497, ristretto255-SHA512, mode 0x00.
5091
5212
  *
@@ -5119,6 +5240,55 @@ function oprfFinalize(input, blind, evaluated) {
5119
5240
  return OPRF.finalize(input, blind, evaluated);
5120
5241
  }
5121
5242
 
5243
+ /**
5244
+ * Verification-gated vault factors (M2 — email, later Google).
5245
+ *
5246
+ * These factors have NO client-side secret: the user proves *channel control*
5247
+ * (an email OTP) and the server releases a split-key OPRF evaluation —
5248
+ * `wrapKey = HKDF( OPRF(K1, input) ‖ OPRF(K2, input) )` where K1 lives on the
5249
+ * accelerator and K2 in a separate trust domain, so no single server-side
5250
+ * compromise can derive the wrap key offline. See accelerator
5251
+ * `AUTH_M2_PLAN.md` §1; the canonical reference implementation is
5252
+ * accelerator `tests/gated-oprf.test.ts` — keep these derivations in
5253
+ * byte-exact lockstep with it.
5254
+ */
5255
+ /**
5256
+ * Deterministic OPRF input for an email factor. Public-ish by design (the
5257
+ * verification gate, not the input, is what protects the eval) — but it must
5258
+ * be stable forever: username + address are lowercased, the tag is versioned.
5259
+ */
5260
+ async function emailFactorInput(username, email) {
5261
+ const tag = `muhkoo/factor:email:v1:${username.toLowerCase()}:${email.toLowerCase()}`;
5262
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(tag));
5263
+ return new Uint8Array(digest);
5264
+ }
5265
+ /**
5266
+ * Deterministic OPRF input for a google factor — keyed on the stable Google
5267
+ * account id (`sub`), not the email (which can change). Same versioning rules
5268
+ * as {@link emailFactorInput}.
5269
+ */
5270
+ async function googleFactorInput(username, sub) {
5271
+ const tag = `muhkoo/factor:google:v1:${username.toLowerCase()}:${sub}`;
5272
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(tag));
5273
+ return new Uint8Array(digest);
5274
+ }
5275
+ /** Blind a gated input for the split eval (same blind serves both domains). */
5276
+ function gatedBlind(input) {
5277
+ return oprfBlind(input);
5278
+ }
5279
+ /**
5280
+ * Fold the two domains' evaluations into the AES-256-GCM wrap key:
5281
+ * unblind each, concatenate, HKDF with the gated-factor domain separation.
5282
+ */
5283
+ async function gatedWrapKey(input, blind, evaluatedB64, evaluated2B64) {
5284
+ const out1 = oprfFinalize(input, blind, fromBase64(evaluatedB64));
5285
+ const out2 = oprfFinalize(input, blind, fromBase64(evaluated2B64));
5286
+ const ikm = new Uint8Array(out1.length + out2.length);
5287
+ ikm.set(out1);
5288
+ ikm.set(out2, out1.length);
5289
+ return wrapKeyFromBytes(ikm, "muhkoo-gated-wrap-v1");
5290
+ }
5291
+
5122
5292
  /**
5123
5293
  * Passkey factor (M1.1) — WebAuthn with the **PRF extension**.
5124
5294
  *
@@ -5236,6 +5406,160 @@ async function evaluatePasskeyPrf(rpId, credentialId, salt) {
5236
5406
  return new Uint8Array(results.first);
5237
5407
  }
5238
5408
 
5409
+ /**
5410
+ * `client.auth.hosted` — centralized hosted auth (auth.muhkoo.dev).
5411
+ * AUTH_HOSTED_PLAN.md.
5412
+ *
5413
+ * Two sides share this class:
5414
+ * - **Developer apps** call {@link login} (redirect to the hosted page) and
5415
+ * {@link handleCallback} (exchange the returned code → session + seed). The
5416
+ * app carries no snarkjs and never sees the user's credentials.
5417
+ * - **The hosted page** (the muhkoo/auth SPA), once it has authenticated the
5418
+ * user via the normal `client.auth.zk.*` flow, calls
5419
+ * {@link completeAuthorize} to seal the seed, mint the grant, and build the
5420
+ * redirect-back URL.
5421
+ *
5422
+ * Flow (authorization-code + PKCE + sealed-seed handoff):
5423
+ * app → GET auth.muhkoo.dev/authorize?app_id&redirect_uri&state&code_challenge
5424
+ * page → (user authenticates) → POST /api/auth/grant → redirect_uri?code&state#k=<K_t>
5425
+ * app → POST /api/auth/token {code, code_verifier} → {sessionToken, sealedKeys}
5426
+ * → unseal sealedKeys with K_t → master seed → session ready
5427
+ */
5428
+ const PKCE_STORE_KEY = "muhkoo.hosted.pkce";
5429
+ class HostedAuth {
5430
+ deps;
5431
+ constructor(deps) {
5432
+ this.deps = deps;
5433
+ }
5434
+ /**
5435
+ * (App side) Begin hosted login: stash a PKCE verifier + state, then send
5436
+ * the browser to the hosted authorize page. Returns the URL it redirects to
5437
+ * (also useful for popup mode / tests). `redirectUri` must be registered for
5438
+ * the app in the portal.
5439
+ */
5440
+ async login(opts) {
5441
+ const { codeVerifier, codeChallenge } = await generatePkce();
5442
+ const state = randomState();
5443
+ this.store({ codeVerifier, state, appId: opts.appId, redirectUri: opts.redirectUri });
5444
+ const url = `${this.deps.authBaseUrl}/authorize?` + new URLSearchParams({
5445
+ app_id: opts.appId,
5446
+ redirect_uri: opts.redirectUri,
5447
+ state,
5448
+ code_challenge: codeChallenge,
5449
+ code_challenge_method: "S256",
5450
+ }).toString();
5451
+ if (opts.redirect !== false)
5452
+ this.location().assign(url);
5453
+ return url;
5454
+ }
5455
+ /**
5456
+ * (App side) Send the user to centralized account & security management on
5457
+ * the hosted page (auth.muhkoo.dev/security) — manage passkeys, recovery
5458
+ * phrase, email, Google, change password, and remove login methods, all in
5459
+ * one place. `returnUri` (default: the current URL) gives the hosted page a
5460
+ * "Back to app" target. Redirects the browser; resolves to the URL.
5461
+ */
5462
+ async manageAccount(opts = {}) {
5463
+ const ret = opts.returnUri ?? this.location().href;
5464
+ const url = `${this.deps.authBaseUrl}/security?` + new URLSearchParams({ return: ret }).toString();
5465
+ this.location().assign(url);
5466
+ return url;
5467
+ }
5468
+ /** Whether the current URL looks like a hosted-auth callback (has code + state). */
5469
+ isCallback() {
5470
+ try {
5471
+ const q = new URLSearchParams(this.location().search);
5472
+ return q.has("code") && q.has("state");
5473
+ }
5474
+ catch {
5475
+ return false;
5476
+ }
5477
+ }
5478
+ /**
5479
+ * (App side) Complete login on the callback URL: verify state, exchange the
5480
+ * code (+ PKCE verifier) for the session, unseal the seed from the fragment,
5481
+ * establish the session, and scrub the URL. Returns the signed-in user.
5482
+ */
5483
+ async handleCallback() {
5484
+ const loc = this.location();
5485
+ const query = new URLSearchParams(loc.search);
5486
+ const code = query.get("code");
5487
+ const state = query.get("state");
5488
+ const fragmentKey = new URLSearchParams(loc.hash.replace(/^#/, "")).get("k");
5489
+ if (!code || !state)
5490
+ throw new Error("Not a hosted-auth callback.");
5491
+ if (!fragmentKey)
5492
+ throw new Error("Missing handoff key — the login link was altered.");
5493
+ const stash = this.take();
5494
+ if (!stash || stash.state !== state)
5495
+ throw new Error("State mismatch — possible CSRF; start login again.");
5496
+ const res = await this.deps.auth.token({ code, codeVerifier: stash.codeVerifier, appId: stash.appId });
5497
+ const seed = await unsealSeed(res.sealedKeys, fragmentKey);
5498
+ const identity = await deriveIdentityFromSeed(seed);
5499
+ await this.deps.session.setSession({ token: res.sessionToken, username: res.username, commitment: res.commitment });
5500
+ this.deps.session.setIdentity(identity);
5501
+ this.deps.session.setSeed(seed);
5502
+ this.scrubUrl();
5503
+ return { username: res.username, commitment: res.commitment };
5504
+ }
5505
+ /**
5506
+ * (Hosted-page side) Finish an /authorize request after the user has
5507
+ * authenticated here: seal the in-memory seed, mint the grant, and return
5508
+ * the URL to redirect the browser back to. The seed must be held (the user
5509
+ * just signed in via `client.auth.zk.*`).
5510
+ */
5511
+ async completeAuthorize(opts) {
5512
+ const token = this.deps.session.token;
5513
+ const seed = this.deps.session.seed;
5514
+ if (!token || !seed)
5515
+ throw new Error("Authenticate the user before completing authorization.");
5516
+ const { sealedKeys, fragmentKey } = await sealSeed(seed);
5517
+ const { code } = await this.deps.auth.grant(token, {
5518
+ appId: opts.appId, redirectUri: opts.redirectUri, codeChallenge: opts.codeChallenge, sealedKeys,
5519
+ });
5520
+ const url = new URL(opts.redirectUri);
5521
+ url.searchParams.set("code", code);
5522
+ url.searchParams.set("state", opts.state);
5523
+ url.hash = `k=${fragmentKey}`;
5524
+ return url.toString();
5525
+ }
5526
+ // ---- internals ----------------------------------------------------------
5527
+ location() {
5528
+ const loc = globalThis.location;
5529
+ if (!loc)
5530
+ throw new Error("Hosted auth requires a browser environment.");
5531
+ return loc;
5532
+ }
5533
+ store(v) {
5534
+ const ss = globalThis.sessionStorage;
5535
+ if (!ss)
5536
+ throw new Error("Hosted auth requires sessionStorage.");
5537
+ ss.setItem(PKCE_STORE_KEY, JSON.stringify(v));
5538
+ }
5539
+ take() {
5540
+ const ss = globalThis.sessionStorage;
5541
+ if (!ss)
5542
+ return null;
5543
+ const raw = ss.getItem(PKCE_STORE_KEY);
5544
+ if (!raw)
5545
+ return null;
5546
+ ss.removeItem(PKCE_STORE_KEY);
5547
+ try {
5548
+ return JSON.parse(raw);
5549
+ }
5550
+ catch {
5551
+ return null;
5552
+ }
5553
+ }
5554
+ /** Drop `code`, `state`, and the `#k` fragment from the address bar. */
5555
+ scrubUrl() {
5556
+ const h = globalThis.history;
5557
+ const loc = this.location();
5558
+ if (h?.replaceState)
5559
+ h.replaceState(null, "", loc.pathname);
5560
+ }
5561
+ }
5562
+
5239
5563
  /**
5240
5564
  * `client.auth` — user authentication, exposed today through the `zk`
5241
5565
  * sub-namespace (`client.auth.zk.login(...)`). Leaving room under `auth` for
@@ -5484,6 +5808,137 @@ class ZkAuth {
5484
5808
  this.deps.session.setSeed(seed);
5485
5809
  return user;
5486
5810
  }
5811
+ /**
5812
+ * Add an **email** as a recovery + login factor (M2 verification-gated:
5813
+ * the server only releases the split-key OPRF eval after the address is
5814
+ * OTP-verified). Sends the code, then returns a `confirm` continuation:
5815
+ *
5816
+ * const { confirm } = await client.auth.zk.enrollEmailFactor("me@x.com");
5817
+ * await confirm(codeFromInbox);
5818
+ *
5819
+ * Requires being signed in with the seed held (like the other enrolls).
5820
+ * Custody note: gated factors trade some custody for recoverability — the
5821
+ * trade-off should be disclosed in the enroll UI.
5822
+ */
5823
+ async enrollEmailFactor(email) {
5824
+ const seed = this.deps.session.seed;
5825
+ const token = this.deps.session.token;
5826
+ const username = this.deps.session.username;
5827
+ if (!seed || !token || !username) {
5828
+ throw new Error("Sign in first to add a recovery email.");
5829
+ }
5830
+ await this.deps.auth.emailOtpStart({ token, email });
5831
+ return {
5832
+ email,
5833
+ confirm: async (code) => {
5834
+ const { verifyToken, email: verified } = await this.deps.auth.emailOtpVerify({ token, code });
5835
+ const input = await emailFactorInput(username, verified);
5836
+ const { blind, blinded } = gatedBlind(input);
5837
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "email", "enroll", verifyToken);
5838
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
5839
+ const wrapped = await wrapSeed(seed, key);
5840
+ await this.deps.auth.vaultPutFactor(token, { id: "email", type: "email", wrap: wrapped.ct, iv: wrapped.iv, params: { email: verified }, createdAt: Date.now() }, verifyToken);
5841
+ },
5842
+ };
5843
+ }
5844
+ /**
5845
+ * Recover an account via its enrolled **email** factor. Sends a code to the
5846
+ * enrolled address (the response never reveals whether one exists), then
5847
+ * `confirm(code)` completes: gated read + split-key eval → unwrap seed →
5848
+ * normal ZK login. After this, call {@link changePassword} to set a new
5849
+ * password.
5850
+ *
5851
+ * const { confirm } = await client.auth.zk.recoverWithEmail("alice");
5852
+ * const user = await confirm(codeFromInbox);
5853
+ */
5854
+ async recoverWithEmail(username, opts = {}) {
5855
+ await this.deps.auth.emailOtpStart({ username });
5856
+ return {
5857
+ confirm: async (code) => {
5858
+ const { verifyToken, email } = await this.deps.auth.emailOtpVerify({ username, code });
5859
+ const { factor } = await this.deps.auth.vaultRead(username, "email", verifyToken);
5860
+ if (!factor?.wrap || !factor?.iv) {
5861
+ throw new Error("This account has no email recovery set up.");
5862
+ }
5863
+ const input = await emailFactorInput(username, email);
5864
+ const { blind, blinded } = gatedBlind(input);
5865
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "email", "recover", verifyToken);
5866
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
5867
+ const seed = await unwrapSeed({ iv: factor.iv, ct: factor.wrap }, key);
5868
+ const identity = await deriveIdentityFromSeed(seed);
5869
+ const user = await this.proveAndStore(username, identity, opts);
5870
+ this.deps.session.setSeed(seed);
5871
+ return user;
5872
+ },
5873
+ };
5874
+ }
5875
+ // --- Google factor (M2.3) ------------------------------------------------
5876
+ // Google is pure authentication UX: its verified ID token gates the
5877
+ // split-key OPRF eval that releases the master seed. The ZK identity layer
5878
+ // is unchanged. The app obtains `idToken` via Google Identity Services and
5879
+ // passes it in; the SDK never renders Google UI.
5880
+ /**
5881
+ * Link a Google account as a recovery + login factor. Requires being signed
5882
+ * in with the seed held. `idToken` is a fresh Google ID token (GIS).
5883
+ */
5884
+ async enrollGoogleFactor(idToken) {
5885
+ const seed = this.deps.session.seed;
5886
+ const token = this.deps.session.token;
5887
+ const username = this.deps.session.username;
5888
+ if (!seed || !token || !username)
5889
+ throw new Error("Sign in first to link a Google account.");
5890
+ const { verifyToken, sub, emailHint } = await this.deps.auth.googleVerify({ idToken, token });
5891
+ const input = await googleFactorInput(username, sub);
5892
+ const { blind, blinded } = gatedBlind(input);
5893
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "google", "enroll", verifyToken);
5894
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
5895
+ const wrapped = await wrapSeed(seed, key);
5896
+ 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);
5897
+ }
5898
+ /**
5899
+ * Sign in with Google — verify the ID token, unlock the seed via the gated
5900
+ * split-key eval, and establish a session. Same machinery serves "Sign in
5901
+ * with Google" and "recover with Google"; the account must have linked the
5902
+ * factor first.
5903
+ */
5904
+ async loginWithGoogle(username, idToken, opts = {}) {
5905
+ const { verifyToken, sub } = await this.deps.auth.googleVerify({ idToken, username });
5906
+ const { factor } = await this.deps.auth.vaultRead(username, "google", verifyToken);
5907
+ if (!factor?.wrap || !factor?.iv)
5908
+ throw new Error("No Google account is linked to this username.");
5909
+ const input = await googleFactorInput(username, sub);
5910
+ const { blind, blinded } = gatedBlind(input);
5911
+ const evals = await this.deps.auth.oprfEvaluateGated(username, toBase64(blinded), "google", "recover", verifyToken);
5912
+ const key = await gatedWrapKey(input, blind, evals.evaluated, evals.evaluated2);
5913
+ const seed = await unwrapSeed({ iv: factor.iv, ct: factor.wrap }, key);
5914
+ const identity = await deriveIdentityFromSeed(seed);
5915
+ const user = await this.proveAndStore(username, identity, opts);
5916
+ this.deps.session.setSeed(seed);
5917
+ return user;
5918
+ }
5919
+ /**
5920
+ * Register a brand-new, **passwordless** account whose only factor is Google.
5921
+ * A random master seed is generated and wrapped under the Google-gated key;
5922
+ * the user can add a password / passkey / phrase later. The ZK registration
5923
+ * is identical to a password signup.
5924
+ */
5925
+ async registerWithGoogle(username, idToken) {
5926
+ const seed = randomSeed();
5927
+ const identity = await deriveIdentityFromSeed(seed);
5928
+ const commitment = await this.commitmentFor(identity);
5929
+ await this.deps.auth.register({
5930
+ username,
5931
+ commitment,
5932
+ ecdhPublicKey: await exportPublicKeyBase64(identity.ecdhKeyPair.publicKey),
5933
+ ecdsaPublicKey: await exportPublicKeyBase64(identity.ecdsaKeyPair.publicKey),
5934
+ email: null,
5935
+ });
5936
+ const user = await this.proveAndStore(username, identity, {});
5937
+ this.deps.session.setSeed(seed);
5938
+ // Enroll Google as the sole factor (session + seed are now in hand).
5939
+ await this.enrollGoogleFactor(idToken);
5940
+ return user;
5941
+ }
5487
5942
  /**
5488
5943
  * Change the password — re-wraps the (unchanged) master seed under the new
5489
5944
  * password's OPRF-gated key. The identity/commitment never moves. Requires
@@ -5561,7 +6016,11 @@ class ZkAuth {
5561
6016
  this.deps.session.setSeed(seed);
5562
6017
  return user;
5563
6018
  }
5564
- /** List the user's enrolled login methods (metadata only). Session-authed. */
6019
+ /**
6020
+ * List the user's enrolled login methods (metadata only). Session-authed.
6021
+ * Gated factors (email/google) carry a `masked` display hint
6022
+ * ("m•••@gmail.com"); the full value is never returned.
6023
+ */
5565
6024
  async listFactors() {
5566
6025
  const token = this.deps.session.token;
5567
6026
  if (!token)
@@ -5688,8 +6147,11 @@ class ZkAuth {
5688
6147
  /** `client.auth` — the auth namespace. Strategies hang off here. */
5689
6148
  class AuthNamespace {
5690
6149
  zk;
6150
+ /** Centralized hosted auth (auth.muhkoo.dev) — `client.auth.hosted.login(...)`. */
6151
+ hosted;
5691
6152
  constructor(deps) {
5692
6153
  this.zk = new ZkAuth(deps);
6154
+ this.hosted = new HostedAuth({ auth: deps.auth, session: deps.session, authBaseUrl: deps.authBaseUrl });
5693
6155
  }
5694
6156
  /** Convenience pass-through: the currently signed-in user, or `null`. */
5695
6157
  get user() {
@@ -7593,6 +8055,7 @@ class AgentsNamespace {
7593
8055
  * });
7594
8056
  * // HTTP-reachable at hello--<slug>.fns.muhkoo.dev
7595
8057
  * await client.functions.enable(appId, config.functionId, spaceId); // Space-bound
8058
+ * const out = await client.functions.invoke({ name: 'hello', slug }, { body: { q: 1 } });
7596
8059
  *
7597
8060
  * These are MANAGEMENT calls — the function itself runs on the accelerator
7598
8061
  * (uploaded just-in-time, decrypted only at invocation). Only an app's owner or
@@ -7606,6 +8069,18 @@ class AgentsNamespace {
7606
8069
  * triggers: `http` (its own subdomain) and `space` (invoked on Space messages,
7607
8070
  * like a Programmable Agent).
7608
8071
  */
8072
+ /** The hosted platform's HTTP-function zone. */
8073
+ const DEFAULT_FN_HOST_SUFFIX = "fns.muhkoo.dev";
8074
+ /** Bodies `fetch` accepts natively — everything else is JSON-encoded. */
8075
+ function isRawBody(body) {
8076
+ return (typeof body === "string" ||
8077
+ body instanceof FormData ||
8078
+ body instanceof Blob ||
8079
+ body instanceof ArrayBuffer ||
8080
+ ArrayBuffer.isView(body) ||
8081
+ body instanceof URLSearchParams ||
8082
+ (typeof ReadableStream !== "undefined" && body instanceof ReadableStream));
8083
+ }
7609
8084
  class FunctionsNamespace {
7610
8085
  deps;
7611
8086
  constructor(deps) {
@@ -7644,6 +8119,71 @@ class FunctionsNamespace {
7644
8119
  async disable(appId, functionId, spaceId, opts = {}) {
7645
8120
  return this.deps.http.post(this.path(appId, `/${encodeURIComponent(functionId)}/disable`, {}), { targetSpaceId: spaceId, space: opts.space });
7646
8121
  }
8122
+ /**
8123
+ * Invoke an HTTP-triggered function and parse its JSON response.
8124
+ *
8125
+ * const out = await client.functions.invoke({ name: 'hello', slug: 'myapp' });
8126
+ * const out = await client.functions.invoke('hello--myapp', { body: { q: 1 } });
8127
+ * const out = await client.functions.invoke('https://hello--myapp.fns.muhkoo.dev/x');
8128
+ *
8129
+ * The request carries the SDK's credentials (`X-Muhkoo-Key` and, when a
8130
+ * user is signed in, `X-Muhkoo-Session`) — the platform passes the session
8131
+ * token through to the function, which can verify it server-side via
8132
+ * `POST /api/auth/verify` to establish the caller's identity.
8133
+ *
8134
+ * Non-2xx responses throw {@link HttpError}. For functions that return
8135
+ * something other than JSON, use {@link invokeRaw}.
8136
+ */
8137
+ async invoke(target, opts = {}) {
8138
+ const res = await this.invokeRaw(target, opts);
8139
+ let parsed = null;
8140
+ try {
8141
+ parsed = await res.json();
8142
+ }
8143
+ catch {
8144
+ // Non-JSON body — fall through to the status-only error below.
8145
+ }
8146
+ if (!res.ok) {
8147
+ const msg = parsed && typeof parsed === "object" && "error" in parsed
8148
+ ? String(parsed.error)
8149
+ : `${res.status} ${res.statusText}`;
8150
+ throw new HttpError(msg, res.status, parsed);
8151
+ }
8152
+ return parsed;
8153
+ }
8154
+ /** Invoke an HTTP-triggered function and return the raw `Response`. */
8155
+ async invokeRaw(target, opts = {}) {
8156
+ const init = {
8157
+ method: opts.method ?? (opts.body !== undefined ? "POST" : "GET"),
8158
+ };
8159
+ const headers = new Headers(opts.headers);
8160
+ if (opts.body !== undefined) {
8161
+ if (isRawBody(opts.body)) {
8162
+ init.body = opts.body;
8163
+ }
8164
+ else {
8165
+ init.body = JSON.stringify(opts.body);
8166
+ if (!headers.has("Content-Type"))
8167
+ headers.set("Content-Type", "application/json");
8168
+ }
8169
+ }
8170
+ init.headers = headers;
8171
+ return this.deps.http.fetch(this.invokeUrl(target, opts.path), init);
8172
+ }
8173
+ /** Resolve an invoke target to the function's absolute URL. */
8174
+ invokeUrl(target, path = "") {
8175
+ const suffix = this.deps.fnHostSuffix ?? DEFAULT_FN_HOST_SUFFIX;
8176
+ let base;
8177
+ if (typeof target === "string") {
8178
+ base = /^https?:\/\//.test(target) ? target : `https://${target}.${suffix}`;
8179
+ }
8180
+ else {
8181
+ base = `https://${target.name}--${target.slug}.${suffix}`;
8182
+ }
8183
+ if (!path)
8184
+ return base;
8185
+ return `${base.replace(/\/+$/, "")}${path.startsWith("/") ? "" : "/"}${path}`;
8186
+ }
7647
8187
  path(appId, suffix, opts) {
7648
8188
  const q = opts.space ? `?space=${encodeURIComponent(opts.space)}` : "";
7649
8189
  return `/api/apps/${encodeURIComponent(appId)}/functions${suffix}${q}`;
@@ -7675,6 +8215,8 @@ class FunctionsNamespace {
7675
8215
  */
7676
8216
  /** The hosted Muhkoo Accelerator — the default {@link ClientOptions.baseUrl}. */
7677
8217
  const DEFAULT_BASE_URL = "https://api.muhkoo.dev";
8218
+ /** The hosted auth SPA — the default {@link ClientOptions.authBaseUrl}. */
8219
+ const DEFAULT_AUTH_BASE_URL = "https://auth.muhkoo.dev";
7678
8220
  /**
7679
8221
  * Turn the HTTP base URL into a websocket base (`http(s)://` → `ws(s)://`).
7680
8222
  * The accelerator serves spaces over both on the same origin.
@@ -7732,7 +8274,8 @@ class Client {
7732
8274
  const circuits = options.circuits ?? defaultCircuitUrls(this.baseUrl);
7733
8275
  const authClient = new AuthClient({ baseUrl: this.baseUrl, fetch: this.http.fetch });
7734
8276
  const wsBaseUrl = toWsBase(this.baseUrl);
7735
- this.auth = new AuthNamespace({ auth: authClient, circuits, session: this.session });
8277
+ const authBaseUrl = (options.authBaseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/+$/, "");
8278
+ this.auth = new AuthNamespace({ auth: authClient, circuits, session: this.session, authBaseUrl });
7736
8279
  this.kv = new KvNamespace({ http: this.http, session: this.session, wsBaseUrl });
7737
8280
  this.db = new DbNamespace({ http: this.http });
7738
8281
  this.storage = new StorageNamespace({ http: this.http, baseUrl: this.baseUrl, kv: this.kv });
@@ -7751,7 +8294,7 @@ class Client {
7751
8294
  cache: spaceKeyCache,
7752
8295
  });
7753
8296
  this.agents = new AgentsNamespace({ http: this.http });
7754
- this.functions = new FunctionsNamespace({ http: this.http });
8297
+ this.functions = new FunctionsNamespace({ http: this.http, fnHostSuffix: options.functionsHostSuffix });
7755
8298
  }
7756
8299
  /** The currently signed-in user, or `null`. */
7757
8300
  get user() {
@@ -8042,5 +8585,5 @@ var bundledLoader = /*#__PURE__*/Object.freeze({
8042
8585
  default: loadRsWasm
8043
8586
  });
8044
8587
 
8045
- 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, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, VaultUnavailableError, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, ejectAgentPrompt, ejectAgentTools, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, getMuhkooAppDescriptor, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, shardHash, signMessage, toField, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
8588
+ 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_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_FN_HOST_SUFFIX, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HashKnowledge, HttpClient, HttpError, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvNamespace, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardClient, SharedSpaceClient, Space, SpaceKeyring, SpaceNamespace, SpacePacketCipher, StorageNamespace, VaultUnavailableError, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, ejectAgentPrompt, ejectAgentTools, encodeToHex, encryptAesGcm, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, generatePkce, getMuhkooAppDescriptor, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, manifestToStat, poseidonHash, quickVerify, randomBytes, randomState, sealSeed, shardHash, signMessage, toField, unsealSeed, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase };
8046
8589
  //# sourceMappingURL=index.js.map