@muhkoo/connect 0.6.0-alpha.1 → 0.7.0-alpha.3

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.
package/dist/connect.d.ts CHANGED
@@ -1812,6 +1812,18 @@ interface AuthSuccess {
1812
1812
  token: string;
1813
1813
  username: string;
1814
1814
  }
1815
+ /** A vault factor record (M1.0 + M2 gated types). Wrap/iv are base64 AES-GCM; absent for phrase. */
1816
+ interface VaultFactor {
1817
+ id: string;
1818
+ type: "password" | "passkey" | "phrase-marker" | "email" | "google";
1819
+ wrap?: string;
1820
+ iv?: string;
1821
+ params?: Record<string, unknown>;
1822
+ label?: string;
1823
+ createdAt?: number;
1824
+ /** Display hint for gated factors ("m•••@gmail.com") — list responses only. */
1825
+ masked?: string;
1826
+ }
1815
1827
  interface AuthClientOptions {
1816
1828
  /** Absolute URL of the accelerator worker (no trailing slash required). */
1817
1829
  baseUrl: string;
@@ -1836,6 +1848,98 @@ declare class AuthClient {
1836
1848
  verify(token: string): Promise<{
1837
1849
  username: string;
1838
1850
  }>;
1851
+ /** Blinded OPRF evaluation — the server-gated half of a password/recovery wrap key. */
1852
+ oprfEvaluate(username: string, blinded: string): Promise<{
1853
+ evaluated: string;
1854
+ }>;
1855
+ /**
1856
+ * Gated split-key OPRF evaluation (M2 — email/google factors). Requires a
1857
+ * live `verifyToken` from {@link emailOtpVerify}; returns BOTH trust
1858
+ * domains' evaluations. The recover token is consumed by this call.
1859
+ */
1860
+ oprfEvaluateGated(username: string, blinded: string, factorType: "email" | "google", purpose: "enroll" | "recover", verifyToken: string): Promise<{
1861
+ evaluated: string;
1862
+ evaluated2: string;
1863
+ }>;
1864
+ /**
1865
+ * Request an email OTP. With a session `token` this is the ENROLL flow
1866
+ * (code sent to `email`); without one it's RECOVER (code sent to the
1867
+ * account's enrolled address — the response is uniform either way).
1868
+ */
1869
+ emailOtpStart(opts: {
1870
+ token?: string;
1871
+ email?: string;
1872
+ username?: string;
1873
+ }): Promise<void>;
1874
+ /** Trade an emailed code for a single-use verifyToken (+ the proven address). */
1875
+ emailOtpVerify(opts: {
1876
+ code: string;
1877
+ token?: string;
1878
+ username?: string;
1879
+ }): Promise<{
1880
+ verifyToken: string;
1881
+ email: string;
1882
+ }>;
1883
+ /**
1884
+ * Verify a Google ID token (M2.3). With a session `token` it's the ENROLL
1885
+ * gate; without, it's RECOVER/LOGIN (the Google account must already be
1886
+ * linked). Returns a single-use verifyToken bound to the account's `sub`.
1887
+ */
1888
+ googleVerify(opts: {
1889
+ idToken: string;
1890
+ token?: string;
1891
+ username?: string;
1892
+ }): Promise<{
1893
+ verifyToken: string;
1894
+ sub: string;
1895
+ emailHint: string | null;
1896
+ }>;
1897
+ /** Mint a single-use authorization code for an app (called by the hosted page; session-authed). */
1898
+ grant(token: string, body: {
1899
+ appId: string;
1900
+ redirectUri: string;
1901
+ codeChallenge: string;
1902
+ sealedKeys: string;
1903
+ }): Promise<{
1904
+ code: string;
1905
+ }>;
1906
+ /** Exchange an authorization code + PKCE verifier for the session + sealed key material. */
1907
+ token(body: {
1908
+ code: string;
1909
+ codeVerifier: string;
1910
+ appId?: string;
1911
+ }): Promise<{
1912
+ sessionToken: string;
1913
+ username: string;
1914
+ commitment: string;
1915
+ sealedKeys: string;
1916
+ appId: string;
1917
+ }>;
1918
+ /**
1919
+ * Read a factor record for `username` (returns an indistinguishable decoy if
1920
+ * absent). Gated types (email) require the recover `verifyToken`.
1921
+ */
1922
+ vaultRead(username: string, factorType: VaultFactor["type"], verifyToken?: string): Promise<{
1923
+ factor: VaultFactor | null;
1924
+ }>;
1925
+ /**
1926
+ * Add/replace a factor (session-authed; the seed is already wrapped
1927
+ * client-side). Email factors additionally require (and consume) the
1928
+ * enroll `verifyToken`; the stored address must be the verified one.
1929
+ */
1930
+ vaultPutFactor(token: string, factor: VaultFactor, verifyToken?: string): Promise<{
1931
+ ok: boolean;
1932
+ id: string;
1933
+ }>;
1934
+ /** List the user's factor metadata (no ciphertext). Session-authed. */
1935
+ vaultFactors(token: string): Promise<{
1936
+ factors: VaultFactor[];
1937
+ migrated: boolean;
1938
+ }>;
1939
+ /** Remove a factor by id (can't remove the last one). Session-authed. */
1940
+ vaultDeleteFactor(token: string, id: string): Promise<{
1941
+ deleted: boolean;
1942
+ }>;
1839
1943
  private json;
1840
1944
  private parse;
1841
1945
  }
@@ -1882,6 +1986,23 @@ interface ZkIdentity {
1882
1986
  * devices.
1883
1987
  */
1884
1988
  declare function deriveIdentity(username: string, password: string): Promise<ZkIdentity>;
1989
+ /**
1990
+ * Legacy master-seed derivation: `PBKDF2(password, "muhkoo-zk-v1:"||username)`.
1991
+ *
1992
+ * This was the *only* path pre-vault, where the identity descended directly from
1993
+ * the password. It's kept so existing accounts still re-derive their seed (and
1994
+ * the vault can adopt that exact seed on migration, preserving the commitment).
1995
+ * New accounts use a random seed (`vault.randomSeed`) instead, so their identity
1996
+ * is no longer a function of the password.
1997
+ */
1998
+ declare function deriveMasterSeedFromPassword(username: string, password: string): Promise<Uint8Array>;
1999
+ /**
2000
+ * Derive the (unchanged) ZK identity from a 32-byte **master seed**. The seed is
2001
+ * HKDF-expanded into secret/salt + the P-256 ECDSA & ECDH keypairs exactly as
2002
+ * before — so the resulting commitment is byte-identical for a given seed. The
2003
+ * vault wraps THIS seed under each recovery factor.
2004
+ */
2005
+ declare function deriveIdentityFromSeed(seed: Uint8Array): Promise<ZkIdentity>;
1885
2006
 
1886
2007
  /**
1887
2008
  * Poseidon hash wrapper used by the ZK auth flow.
@@ -2003,6 +2124,44 @@ declare function exportPublicKeyBase64(publicKey: CryptoKey): Promise<string>;
2003
2124
  /** Sign a UTF-8 `message` with an ECDSA private key. Returns base64. */
2004
2125
  declare function signMessage(message: string, privateKey: CryptoKey): Promise<string>;
2005
2126
 
2127
+ /**
2128
+ * Hosted-auth handoff crypto (auth.muhkoo.dev — AUTH_HOSTED_PLAN.md §3).
2129
+ *
2130
+ * The hosted page authenticates the user and ends up holding the master seed.
2131
+ * To pass it to the developer's app without it ever sitting server-readable or
2132
+ * landing in browser history:
2133
+ *
2134
+ * 1. {@link sealSeed} generates a one-time key K_t, AES-256-GCM-seals the seed
2135
+ * under it, and returns the ciphertext envelope (`sealedKeys`, stored in
2136
+ * the grant the server relays but can't read) plus K_t (`fragmentKey`,
2137
+ * placed ONLY in the redirect URL fragment — fragments never hit a server).
2138
+ * 2. The app exchanges the code for `sealedKeys`, reads K_t from the fragment,
2139
+ * and {@link unsealSeed} recovers the seed, then strips the fragment.
2140
+ *
2141
+ * Trust-equivalent to today's embedded SDK (the app still ends up with the
2142
+ * seed) while moving credential entry onto the Muhkoo origin. The envelope is
2143
+ * versioned so a future per-app-key handoff (v2) can swap in without a protocol
2144
+ * change.
2145
+ */
2146
+ interface SealedHandoff {
2147
+ /** base64 JSON envelope — relayed by the server in the grant; server-opaque. */
2148
+ sealedKeys: string;
2149
+ /** base64url one-time key — rides ONLY in the redirect URL fragment. */
2150
+ fragmentKey: string;
2151
+ }
2152
+ /** Seal the master seed under a fresh one-time key for the redirect handoff. */
2153
+ declare function sealSeed(seed: Uint8Array): Promise<SealedHandoff>;
2154
+ /** Recover the master seed from the sealed envelope + the fragment key. */
2155
+ declare function unsealSeed(sealedKeys: string, fragmentKey: string): Promise<Uint8Array>;
2156
+ interface Pkce {
2157
+ codeVerifier: string;
2158
+ codeChallenge: string;
2159
+ }
2160
+ /** Generate a PKCE verifier + S256 challenge for the authorize→token exchange. */
2161
+ declare function generatePkce(): Promise<Pkce>;
2162
+ /** A random URL-safe value for the OAuth `state` (CSRF binding on the callback). */
2163
+ declare function randomState(): string;
2164
+
2006
2165
  /**
2007
2166
  * `SessionState` — the in-memory source of truth for "who is signed in" on a
2008
2167
  * {@link Client}.
@@ -2083,6 +2242,10 @@ declare function defaultSessionStore(keyPrefix?: string): SessionStore;
2083
2242
  declare class SessionState {
2084
2243
  private session;
2085
2244
  private _identity;
2245
+ /** The 32-byte master seed, in-memory only (never persisted). Held after
2246
+ * login/register so additional recovery factors (passkey, phrase) can be
2247
+ * enrolled, and a password can be changed, without re-deriving. */
2248
+ private _seed;
2086
2249
  private readonly store;
2087
2250
  constructor(store?: SessionStore);
2088
2251
  /** True once a token-bearing session exists (regardless of identity). */
@@ -2099,12 +2262,87 @@ declare class SessionState {
2099
2262
  setSession(session: StoredSession): Promise<void>;
2100
2263
  /** Attach derived identity material to the current session. */
2101
2264
  setIdentity(identity: ZkIdentity): void;
2265
+ /** The in-memory master seed (or null when locked / not held). */
2266
+ get seed(): Uint8Array | null;
2267
+ /** Hold the master seed in memory (set on login/register when available). */
2268
+ setSeed(seed: Uint8Array | null): void;
2102
2269
  /** Load a persisted session (used by `restore()`); identity stays null. */
2103
2270
  loadPersisted(): Promise<StoredSession | null>;
2104
2271
  /** Drop all session + identity state and clear persistence. */
2105
2272
  clear(): Promise<void>;
2106
2273
  }
2107
2274
 
2275
+ /**
2276
+ * `client.auth.hosted` — centralized hosted auth (auth.muhkoo.dev).
2277
+ * AUTH_HOSTED_PLAN.md.
2278
+ *
2279
+ * Two sides share this class:
2280
+ * - **Developer apps** call {@link login} (redirect to the hosted page) and
2281
+ * {@link handleCallback} (exchange the returned code → session + seed). The
2282
+ * app carries no snarkjs and never sees the user's credentials.
2283
+ * - **The hosted page** (the muhkoo/auth SPA), once it has authenticated the
2284
+ * user via the normal `client.auth.zk.*` flow, calls
2285
+ * {@link completeAuthorize} to seal the seed, mint the grant, and build the
2286
+ * redirect-back URL.
2287
+ *
2288
+ * Flow (authorization-code + PKCE + sealed-seed handoff):
2289
+ * app → GET auth.muhkoo.dev/authorize?app_id&redirect_uri&state&code_challenge
2290
+ * page → (user authenticates) → POST /api/auth/grant → redirect_uri?code&state#k=<K_t>
2291
+ * app → POST /api/auth/token {code, code_verifier} → {sessionToken, sealedKeys}
2292
+ * → unseal sealedKeys with K_t → master seed → session ready
2293
+ */
2294
+
2295
+ interface HostedAuthDeps {
2296
+ auth: AuthClient;
2297
+ session: SessionState;
2298
+ /** Base URL of the hosted auth SPA (e.g. https://auth.muhkoo.dev). */
2299
+ authBaseUrl: string;
2300
+ }
2301
+ interface AuthUser$1 {
2302
+ username: string;
2303
+ commitment: string;
2304
+ }
2305
+ declare class HostedAuth {
2306
+ private readonly deps;
2307
+ constructor(deps: HostedAuthDeps);
2308
+ /**
2309
+ * (App side) Begin hosted login: stash a PKCE verifier + state, then send
2310
+ * the browser to the hosted authorize page. Returns the URL it redirects to
2311
+ * (also useful for popup mode / tests). `redirectUri` must be registered for
2312
+ * the app in the portal.
2313
+ */
2314
+ login(opts: {
2315
+ appId: string;
2316
+ redirectUri: string;
2317
+ redirect?: boolean;
2318
+ }): Promise<string>;
2319
+ /** Whether the current URL looks like a hosted-auth callback (has code + state). */
2320
+ isCallback(): boolean;
2321
+ /**
2322
+ * (App side) Complete login on the callback URL: verify state, exchange the
2323
+ * code (+ PKCE verifier) for the session, unseal the seed from the fragment,
2324
+ * establish the session, and scrub the URL. Returns the signed-in user.
2325
+ */
2326
+ handleCallback(): Promise<AuthUser$1>;
2327
+ /**
2328
+ * (Hosted-page side) Finish an /authorize request after the user has
2329
+ * authenticated here: seal the in-memory seed, mint the grant, and return
2330
+ * the URL to redirect the browser back to. The seed must be held (the user
2331
+ * just signed in via `client.auth.zk.*`).
2332
+ */
2333
+ completeAuthorize(opts: {
2334
+ appId: string;
2335
+ redirectUri: string;
2336
+ codeChallenge: string;
2337
+ state: string;
2338
+ }): Promise<string>;
2339
+ private location;
2340
+ private store;
2341
+ private take;
2342
+ /** Drop `code`, `state`, and the `#k` fragment from the address bar. */
2343
+ private scrubUrl;
2344
+ }
2345
+
2108
2346
  /**
2109
2347
  * `client.auth` — user authentication, exposed today through the `zk`
2110
2348
  * sub-namespace (`client.auth.zk.login(...)`). Leaving room under `auth` for
@@ -2122,6 +2360,17 @@ declare class SessionState {
2122
2360
  * at-rest encryption and the messaging ratchet without re-deriving.
2123
2361
  */
2124
2362
 
2363
+ /**
2364
+ * Thrown when the identity vault can't be reached to unlock the seed — a network
2365
+ * failure, a 5xx, or the per-user rate limit (429). This is distinct from a wrong
2366
+ * password, and is surfaced so `login`/`unlock` do NOT silently fall back to the
2367
+ * legacy password derivation (which, for a vault account, yields a misleading
2368
+ * "commitment mismatch"). Callers should ask the user to retry, not re-enter a
2369
+ * password.
2370
+ */
2371
+ declare class VaultUnavailableError extends Error {
2372
+ constructor(cause?: unknown);
2373
+ }
2125
2374
  /** What the auth methods resolve to — the stable, non-secret user facts. */
2126
2375
  interface AuthUser {
2127
2376
  username: string;
@@ -2152,17 +2401,26 @@ declare class ZkAuth {
2152
2401
  private readonly deps;
2153
2402
  constructor(deps: ZkAuthDeps);
2154
2403
  /**
2155
- * Register a new user, then (by default) sign them in. The identity is
2156
- * derived deterministically from `(username, password)` — the same inputs
2157
- * reproduce it on any device, so there's no key material to ship around.
2404
+ * Register a new user, then (by default) sign them in.
2405
+ *
2406
+ * M1.0: the identity now descends from a **random master seed** (not the
2407
+ * password), so it's recoverable + the password can be changed. We register
2408
+ * the commitment, sign in with the in-memory identity to get a token, then
2409
+ * enroll the OPRF-gated **password factor** (the seed wrapped under a key the
2410
+ * server can't derive offline). Future logins unlock the seed via that factor.
2158
2411
  */
2159
2412
  register(params: RegisterParams): Promise<AuthUser>;
2160
2413
  /**
2161
- * Sign in: re-derive the identity, prove knowledge of it against a fresh
2162
- * server challenge, and trade the proof for a session token. On success
2163
- * both the session and the identity are stored on the client.
2414
+ * Sign in. M1.0 is **vault-first**: read the password factor and OPRF-unwrap
2415
+ * the master seed → identity. If that fails (a pre-vault account, whose factor
2416
+ * is absent so the server returns a decoy, or a wrong password), fall back to
2417
+ * the legacy password-derived identity. Either way we then prove knowledge of
2418
+ * the identity and trade it for a session token.
2164
2419
  */
2165
2420
  login(username: string, password: string, opts?: LoginOptions): Promise<AuthUser>;
2421
+ /** Enroll the password factor for a legacy (pre-vault) account. Idempotent +
2422
+ * best-effort; failure just means we retry on the next login. */
2423
+ private migrateLegacyPasswordFactor;
2166
2424
  /**
2167
2425
  * Silently re-authenticate the current user without prompting for a
2168
2426
  * password. Only possible while the client is **unlocked** — i.e. the
@@ -2200,6 +2458,113 @@ declare class ZkAuth {
2200
2458
  * the password by checking the derived commitment matches the session's.
2201
2459
  */
2202
2460
  unlock(password: string): Promise<void>;
2461
+ /**
2462
+ * Generate a 24-word **recovery phrase** that encodes the master seed, and
2463
+ * record a marker in the vault so the UI knows one exists. Show the returned
2464
+ * phrase to the user ONCE — it's never stored server-side (the phrase *is* the
2465
+ * seed). Requires being signed in (the seed must be held in memory).
2466
+ */
2467
+ enrollRecoveryPhrase(): Promise<string>;
2468
+ /**
2469
+ * Recover an account with its recovery phrase (the "forgot password" path).
2470
+ * Decodes the phrase to the master seed → identity → session. After this,
2471
+ * call {@link changePassword} to set a new password. No server lookup needed.
2472
+ */
2473
+ recoverWithPhrase(username: string, mnemonic: string, opts?: LoginOptions): Promise<AuthUser>;
2474
+ /**
2475
+ * Add an **email** as a recovery + login factor (M2 verification-gated:
2476
+ * the server only releases the split-key OPRF eval after the address is
2477
+ * OTP-verified). Sends the code, then returns a `confirm` continuation:
2478
+ *
2479
+ * const { confirm } = await client.auth.zk.enrollEmailFactor("me@x.com");
2480
+ * await confirm(codeFromInbox);
2481
+ *
2482
+ * Requires being signed in with the seed held (like the other enrolls).
2483
+ * Custody note: gated factors trade some custody for recoverability — the
2484
+ * trade-off should be disclosed in the enroll UI.
2485
+ */
2486
+ enrollEmailFactor(email: string): Promise<{
2487
+ email: string;
2488
+ confirm: (code: string) => Promise<void>;
2489
+ }>;
2490
+ /**
2491
+ * Recover an account via its enrolled **email** factor. Sends a code to the
2492
+ * enrolled address (the response never reveals whether one exists), then
2493
+ * `confirm(code)` completes: gated read + split-key eval → unwrap seed →
2494
+ * normal ZK login. After this, call {@link changePassword} to set a new
2495
+ * password.
2496
+ *
2497
+ * const { confirm } = await client.auth.zk.recoverWithEmail("alice");
2498
+ * const user = await confirm(codeFromInbox);
2499
+ */
2500
+ recoverWithEmail(username: string, opts?: LoginOptions): Promise<{
2501
+ confirm: (code: string) => Promise<AuthUser>;
2502
+ }>;
2503
+ /**
2504
+ * Link a Google account as a recovery + login factor. Requires being signed
2505
+ * in with the seed held. `idToken` is a fresh Google ID token (GIS).
2506
+ */
2507
+ enrollGoogleFactor(idToken: string): Promise<void>;
2508
+ /**
2509
+ * Sign in with Google — verify the ID token, unlock the seed via the gated
2510
+ * split-key eval, and establish a session. Same machinery serves "Sign in
2511
+ * with Google" and "recover with Google"; the account must have linked the
2512
+ * factor first.
2513
+ */
2514
+ loginWithGoogle(username: string, idToken: string, opts?: LoginOptions): Promise<AuthUser>;
2515
+ /**
2516
+ * Register a brand-new, **passwordless** account whose only factor is Google.
2517
+ * A random master seed is generated and wrapped under the Google-gated key;
2518
+ * the user can add a password / passkey / phrase later. The ZK registration
2519
+ * is identical to a password signup.
2520
+ */
2521
+ registerWithGoogle(username: string, idToken: string): Promise<AuthUser>;
2522
+ /**
2523
+ * Change the password — re-wraps the (unchanged) master seed under the new
2524
+ * password's OPRF-gated key. The identity/commitment never moves. Requires
2525
+ * being signed in (or freshly recovered) so the seed is held.
2526
+ */
2527
+ changePassword(newPassword: string): Promise<void>;
2528
+ /** Whether this browser can use passkeys (WebAuthn) at all. */
2529
+ passkeyAvailable(): boolean;
2530
+ /**
2531
+ * Whether this browser's authenticator can do the **PRF extension** our
2532
+ * passkey factor requires. A browser may support passkeys generally but not
2533
+ * PRF — in which case enrolling one would fail. Resolves `true`/`false`, or
2534
+ * `null` when it can't be determined (no capabilities API). Use this to gate
2535
+ * the passkey UI so users don't hit a dead-end error.
2536
+ */
2537
+ passkeyPrfAvailable(): Promise<boolean | null>;
2538
+ /**
2539
+ * Add a **passkey** recovery factor. Creates a WebAuthn passkey with PRF,
2540
+ * wraps the master seed under its PRF output, and stores the factor. The
2541
+ * passkey (synced via the platform's keychain) then unlocks the account on
2542
+ * any device — no password. Requires being signed in (seed held in memory).
2543
+ */
2544
+ enrollPasskey(opts?: {
2545
+ rpId?: string;
2546
+ rpName?: string;
2547
+ label?: string;
2548
+ }): Promise<void>;
2549
+ /**
2550
+ * Sign in with a passkey (no password). Reads the passkey factor, evaluates
2551
+ * its PRF, unwraps the master seed → identity → session.
2552
+ */
2553
+ loginWithPasskey(username: string, opts?: LoginOptions): Promise<AuthUser>;
2554
+ /**
2555
+ * List the user's enrolled login methods (metadata only). Session-authed.
2556
+ * Gated factors (email/google) carry a `masked` display hint
2557
+ * ("m•••@gmail.com"); the full value is never returned.
2558
+ */
2559
+ listFactors(): Promise<Array<{
2560
+ id: string;
2561
+ type: string;
2562
+ label?: string;
2563
+ createdAt?: number;
2564
+ masked?: string;
2565
+ }>>;
2566
+ /** Remove a login method by id (can't remove your only one). Session-authed. */
2567
+ removeFactor(id: string): Promise<void>;
2203
2568
  /** Sign out: clears the session, identity, and persisted token. */
2204
2569
  logout(): Promise<void>;
2205
2570
  /** The signed-in user, or `null`. Synchronous read of current state. */
@@ -2213,13 +2578,52 @@ declare class ZkAuth {
2213
2578
  get identity(): ZkIdentity | null;
2214
2579
  /** The current session token, or `null` when signed out. */
2215
2580
  get token(): string | null;
2581
+ /**
2582
+ * The master seed as base64, or `null` when locked. The host app uses this to
2583
+ * wrap app-level data (e.g. chat ratchet keys) to the **seed** instead of the
2584
+ * password — so that data survives password changes and can be unlocked by a
2585
+ * passwordless (passkey) login, both of which recover the same seed.
2586
+ */
2587
+ get seedBase64(): string | null;
2216
2588
  /** Poseidon commitment for an identity — the value the server stores. */
2217
2589
  private commitmentFor;
2590
+ /**
2591
+ * Derive the OPRF-gated wrap key for the password factor: `scrypt(password)`
2592
+ * is blinded, evaluated by the server's secret OPRF key (so it can't be
2593
+ * derived offline), unblinded, then HKDF'd into an AES-GCM key.
2594
+ */
2595
+ private passwordWrapKey;
2596
+ /**
2597
+ * Try to recover the master seed from the password factor.
2598
+ *
2599
+ * Returns `null` only when the vault read SUCCEEDS but the seed can't be
2600
+ * unwrapped — i.e. there's no real factor (a pre-vault/legacy account, whose
2601
+ * read returns a decoy that won't unwrap) or the password is wrong. The caller
2602
+ * then legitimately falls back to the legacy password-derived identity.
2603
+ *
2604
+ * Throws {@link VaultUnavailableError} when the vault itself can't be reached
2605
+ * (network / 5xx / rate-limit) — so a *transient* failure is NOT silently
2606
+ * turned into a legacy fallback (which would surface as "commitment mismatch").
2607
+ */
2608
+ private tryUnlockSeed;
2609
+ /**
2610
+ * Enroll/replace the password factor for `username` — wraps `seed` under the
2611
+ * OPRF-gated key and stores it (needs a session `token`).
2612
+ *
2613
+ * Retries: this is the step that persists the (otherwise in-memory-only)
2614
+ * master seed. If it fails after a fresh registration the account would be
2615
+ * unrecoverable, so we retry a few times before surfacing the error.
2616
+ */
2617
+ private enrollPasswordFactor;
2218
2618
  }
2219
2619
  /** `client.auth` — the auth namespace. Strategies hang off here. */
2220
2620
  declare class AuthNamespace {
2221
2621
  readonly zk: ZkAuth;
2222
- constructor(deps: ZkAuthDeps);
2622
+ /** Centralized hosted auth (auth.muhkoo.dev) — `client.auth.hosted.login(...)`. */
2623
+ readonly hosted: HostedAuth;
2624
+ constructor(deps: ZkAuthDeps & {
2625
+ authBaseUrl: string;
2626
+ });
2223
2627
  /** Convenience pass-through: the currently signed-in user, or `null`. */
2224
2628
  get user(): AuthUser | null;
2225
2629
  }
@@ -3459,6 +3863,7 @@ declare class AgentsNamespace {
3459
3863
  * });
3460
3864
  * // HTTP-reachable at hello--<slug>.fns.muhkoo.dev
3461
3865
  * await client.functions.enable(appId, config.functionId, spaceId); // Space-bound
3866
+ * const out = await client.functions.invoke({ name: 'hello', slug }, { body: { q: 1 } });
3462
3867
  *
3463
3868
  * These are MANAGEMENT calls — the function itself runs on the accelerator
3464
3869
  * (uploaded just-in-time, decrypted only at invocation). Only an app's owner or
@@ -3528,6 +3933,29 @@ interface FunctionDeployInput {
3528
3933
  type FunctionUpdateInput = Partial<FunctionDeployInput>;
3529
3934
  interface FunctionsNamespaceDeps {
3530
3935
  http: HttpClient;
3936
+ /**
3937
+ * Wildcard zone HTTP functions are served from
3938
+ * (`<name>--<slug>.<suffix>`). Defaults to the hosted platform's
3939
+ * {@link DEFAULT_FN_HOST_SUFFIX}; override for staging / self-hosted.
3940
+ */
3941
+ fnHostSuffix?: string;
3942
+ }
3943
+ /** The hosted platform's HTTP-function zone. */
3944
+ declare const DEFAULT_FN_HOST_SUFFIX = "fns.muhkoo.dev";
3945
+ /** Options for {@link FunctionsNamespace.invoke}. */
3946
+ interface FunctionInvokeOptions {
3947
+ /** HTTP method. Defaults to `POST` when `body` is present, else `GET`. */
3948
+ method?: string;
3949
+ /**
3950
+ * Request body. Plain objects/arrays are JSON-encoded (with
3951
+ * `Content-Type: application/json`); strings, `FormData`, `Blob`,
3952
+ * `ArrayBuffer`/views and `URLSearchParams` are sent as-is.
3953
+ */
3954
+ body?: unknown;
3955
+ /** Extra headers, merged over the SDK's credential headers. */
3956
+ headers?: HeadersInit;
3957
+ /** Sub-path (and query) on the function's host, e.g. `/reports?from=...`. */
3958
+ path?: string;
3531
3959
  }
3532
3960
  /** Common option: a management Space for delegated (non-owner) access. */
3533
3961
  interface FunctionScopeOpts {
@@ -3568,11 +3996,42 @@ declare class FunctionsNamespace {
3568
3996
  disable(appId: string, functionId: string, spaceId: string, opts?: FunctionScopeOpts): Promise<{
3569
3997
  config: FunctionConfig;
3570
3998
  }>;
3999
+ /**
4000
+ * Invoke an HTTP-triggered function and parse its JSON response.
4001
+ *
4002
+ * const out = await client.functions.invoke({ name: 'hello', slug: 'myapp' });
4003
+ * const out = await client.functions.invoke('hello--myapp', { body: { q: 1 } });
4004
+ * const out = await client.functions.invoke('https://hello--myapp.fns.muhkoo.dev/x');
4005
+ *
4006
+ * The request carries the SDK's credentials (`X-Muhkoo-Key` and, when a
4007
+ * user is signed in, `X-Muhkoo-Session`) — the platform passes the session
4008
+ * token through to the function, which can verify it server-side via
4009
+ * `POST /api/auth/verify` to establish the caller's identity.
4010
+ *
4011
+ * Non-2xx responses throw {@link HttpError}. For functions that return
4012
+ * something other than JSON, use {@link invokeRaw}.
4013
+ */
4014
+ invoke<T = unknown>(target: string | {
4015
+ name: string;
4016
+ slug: string;
4017
+ }, opts?: FunctionInvokeOptions): Promise<T>;
4018
+ /** Invoke an HTTP-triggered function and return the raw `Response`. */
4019
+ invokeRaw(target: string | {
4020
+ name: string;
4021
+ slug: string;
4022
+ }, opts?: FunctionInvokeOptions): Promise<Response>;
4023
+ /** Resolve an invoke target to the function's absolute URL. */
4024
+ invokeUrl(target: string | {
4025
+ name: string;
4026
+ slug: string;
4027
+ }, path?: string): string;
3571
4028
  private path;
3572
4029
  }
3573
4030
 
3574
4031
  /** The hosted Muhkoo Accelerator — the default {@link ClientOptions.baseUrl}. */
3575
4032
  declare const DEFAULT_BASE_URL = "https://api.muhkoo.dev";
4033
+ /** The hosted auth SPA — the default {@link ClientOptions.authBaseUrl}. */
4034
+ declare const DEFAULT_AUTH_BASE_URL = "https://auth.muhkoo.dev";
3576
4035
  interface ClientOptions {
3577
4036
  /**
3578
4037
  * App / publishable key issued by the accelerator (e.g. `mk_test_pk_…`).
@@ -3603,6 +4062,19 @@ interface ClientOptions {
3603
4062
  fetch?: typeof fetch;
3604
4063
  /** Optional log level for the SDK logger. */
3605
4064
  logLevel?: string;
4065
+ /**
4066
+ * Wildcard zone HTTP serverless functions are served from
4067
+ * (`<name>--<slug>.<suffix>`), used by `client.functions.invoke(...)`.
4068
+ * Defaults to the hosted platform's zone (`fns.muhkoo.dev`); override for
4069
+ * staging or a self-hosted deployment.
4070
+ */
4071
+ functionsHostSuffix?: string;
4072
+ /**
4073
+ * Base URL of the centralized hosted auth SPA (`client.auth.hosted`).
4074
+ * Defaults to {@link DEFAULT_AUTH_BASE_URL} (`auth.muhkoo.dev`); override for
4075
+ * staging (`auth.staging.muhkoo.dev`) or a self-hosted deployment.
4076
+ */
4077
+ authBaseUrl?: string;
3606
4078
  }
3607
4079
  declare class Client {
3608
4080
  readonly baseUrl: string;
@@ -3842,5 +4314,5 @@ declare class KeyringClient implements KeyringTransport {
3842
4314
  fetchMetadata(): Promise<SpaceMetadata | null>;
3843
4315
  }
3844
4316
 
3845
- 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, WSTransport, ZkAuth, buildCommitment, compilePrograms, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultSessionStore, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, 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 };
3846
- export type { ACL, ACLEntry, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, DehydratedKeypair, DehydratedKeys, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EphemeralEvent, EpochKeyProvider, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvNamespaceDeps, LoginOptions, MessageBody, MessageDeletedEvent, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, PacketCipher, PacketHeaders, PacketOptions, Participant, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, PublicKeyJWK, RatchetState, ReceiveResult, RegisterParams, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardClientOptions, SharedSpaceClientOptions, SnarkProof, SpaceChannelLike, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, StorageChangeEvent, StoredSession, Subscription, UserIdentity, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
4317
+ 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 };
4318
+ export type { ACL, ACLEntry, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, DehydratedKeypair, DehydratedKeys, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EphemeralEvent, EpochKeyProvider, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvNamespaceDeps, LoginOptions, MessageBody, MessageDeletedEvent, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, PacketCipher, PacketHeaders, PacketOptions, Participant, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, Pkce, PublicKeyJWK, RatchetState, ReceiveResult, RegisterParams, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, SealedHandoff, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardClientOptions, SharedSpaceClientOptions, SnarkProof, SpaceChannelLike, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, StorageChangeEvent, StoredSession, Subscription, UserIdentity, VaultFactor, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };