@muhkoo/connect 0.10.11-alpha.0 → 0.10.13-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +1380 -14
- package/dist/browser/index.js.map +1 -1
- package/dist/connect.d.ts +620 -7
- package/dist/server/index.js +1380 -14
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/connect.d.ts
CHANGED
|
@@ -2232,6 +2232,29 @@ declare function signMessage(message: string, privateKey: CryptoKey): Promise<st
|
|
|
2232
2232
|
* seed) while moving credential entry onto the Muhkoo origin. The envelope is
|
|
2233
2233
|
* versioned so a future per-app-key handoff (v2) can swap in without a protocol
|
|
2234
2234
|
* change.
|
|
2235
|
+
*
|
|
2236
|
+
* ## v2 — device pairing (TV / no-redirect handoff)
|
|
2237
|
+
*
|
|
2238
|
+
* v1's K_t rides in the redirect URL *fragment*, which works only because the
|
|
2239
|
+
* receiving app is a browser page that can read `location.hash`. A TV app has
|
|
2240
|
+
* no redirect to receive: it polls. So v2 keeps the *entire* v1 shape — the
|
|
2241
|
+
* server relays a sealed envelope it cannot read — and replaces only the key
|
|
2242
|
+
* agreement:
|
|
2243
|
+
*
|
|
2244
|
+
* - the TV generates an ephemeral P-256 ECDH keypair in memory and publishes
|
|
2245
|
+
* the public half ({@link generateDevicePairingKeypair});
|
|
2246
|
+
* - the hosted page (the component that already holds the unlocked seed and
|
|
2247
|
+
* already owns sealing) seals *to that public key* with a fresh ephemeral
|
|
2248
|
+
* keypair of its own ({@link sealSeedToDevice}) — ECDH → HKDF-SHA256 →
|
|
2249
|
+
* AES-256-GCM;
|
|
2250
|
+
* - the TV recovers the seed with its private key
|
|
2251
|
+
* ({@link unsealSeedFromDevice}).
|
|
2252
|
+
*
|
|
2253
|
+
* The server stores and relays the v2 envelope byte-for-byte, exactly as it
|
|
2254
|
+
* does for v1, and can read neither. The one thing ECDH cannot supply is proof
|
|
2255
|
+
* that the public key the hosted page sealed to is *the TV's* rather than an
|
|
2256
|
+
* attacker's — that is what {@link pairingVerificationCode} is for, and it is
|
|
2257
|
+
* the only MITM defence in the flow (see the entropy note on that function).
|
|
2235
2258
|
*/
|
|
2236
2259
|
interface SealedHandoff {
|
|
2237
2260
|
/** base64 JSON envelope — relayed by the server in the grant; server-opaque. */
|
|
@@ -2243,6 +2266,101 @@ interface SealedHandoff {
|
|
|
2243
2266
|
declare function sealSeed(seed: Uint8Array): Promise<SealedHandoff>;
|
|
2244
2267
|
/** Recover the master seed from the sealed envelope + the fragment key. */
|
|
2245
2268
|
declare function unsealSeed(sealedKeys: string, fragmentKey: string): Promise<Uint8Array>;
|
|
2269
|
+
/**
|
|
2270
|
+
* Alphabet for the pairing verification code: 30 symbols, every confusable pair
|
|
2271
|
+
* removed in BOTH directions — no `0`/`O`, no `1`/`I`/`L`, and no `U` (which is
|
|
2272
|
+
* read as `V` on the low-resolution, far-viewed type a TV renders). Because the
|
|
2273
|
+
* user compares this string across two screens by eye, unambiguity beats the
|
|
2274
|
+
* convenience of a power-of-two alphabet; the derivation below rejection-samples
|
|
2275
|
+
* to avoid the modulo bias that costs us.
|
|
2276
|
+
*/
|
|
2277
|
+
declare const PAIRING_CODE_ALPHABET = "ABCDEFGHJKMNPQRSTVWXYZ23456789";
|
|
2278
|
+
/** Length of the pairing verification code, in characters. 30^8 ≈ 39.3 bits. */
|
|
2279
|
+
declare const PAIRING_CODE_LENGTH = 8;
|
|
2280
|
+
/** An ephemeral ECDH keypair generated by the pairing device (the TV). */
|
|
2281
|
+
interface DevicePairingKeypair {
|
|
2282
|
+
/** base64 raw (SEC1 uncompressed, 65 bytes) public key — safe to publish. */
|
|
2283
|
+
publicKeyB64: string;
|
|
2284
|
+
/**
|
|
2285
|
+
* Non-extractable private half. In-memory only, one per pairing attempt:
|
|
2286
|
+
* it is never persisted and never leaves the device, so a captured pairing
|
|
2287
|
+
* transcript is undecryptable once the attempt ends.
|
|
2288
|
+
*/
|
|
2289
|
+
privateKey: CryptoKey;
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* (Device side) Generate the ephemeral P-256 ECDH keypair for one pairing
|
|
2293
|
+
* attempt. Call this once per attempt and throw it away when the attempt ends —
|
|
2294
|
+
* reusing a keypair across attempts would let an old, observed ciphertext be
|
|
2295
|
+
* decrypted by whoever later obtains the key.
|
|
2296
|
+
*/
|
|
2297
|
+
declare function generateDevicePairingKeypair(): Promise<DevicePairingKeypair>;
|
|
2298
|
+
/**
|
|
2299
|
+
* (Hosted-page side) Seal the master seed *to a device's* pairing public key.
|
|
2300
|
+
*
|
|
2301
|
+
* A fresh ephemeral keypair is generated per call and its public half is
|
|
2302
|
+
* carried in the envelope (`epk`), so sealing the same seed to the same device
|
|
2303
|
+
* twice yields unrelated ciphertext — and the sealer retains nothing that could
|
|
2304
|
+
* later reopen the envelope.
|
|
2305
|
+
*
|
|
2306
|
+
* Returns the v2 `sealedKeys` string, which slots into exactly the same server
|
|
2307
|
+
* field as v1: relayed verbatim, never read.
|
|
2308
|
+
*/
|
|
2309
|
+
declare function sealSeedToDevice(seed: Uint8Array, devicePublicKeyB64: string): Promise<string>;
|
|
2310
|
+
/**
|
|
2311
|
+
* (Device side) Recover the master seed from a v2 envelope using the ephemeral
|
|
2312
|
+
* private key from {@link generateDevicePairingKeypair}.
|
|
2313
|
+
*
|
|
2314
|
+
* Throws on any tamper: a substituted `epk` yields a different shared secret
|
|
2315
|
+
* (or fails point validation outright), and any edit to `iv`/`ct` fails the
|
|
2316
|
+
* AES-GCM tag. There is no path that returns partially-authenticated bytes.
|
|
2317
|
+
*
|
|
2318
|
+
* `devicePublicKeyB64` is the caller's OWN public half (from
|
|
2319
|
+
* {@link generateDevicePairingKeypair}). It is required, not optional, because
|
|
2320
|
+
* it is mixed into the KDF — see {@link deriveDeviceSealKey} for why binding the
|
|
2321
|
+
* recipient matters. A device always holds both halves, so this costs nothing.
|
|
2322
|
+
*/
|
|
2323
|
+
declare function unsealSeedFromDevice(sealedKeys: string, privateKey: CryptoKey, devicePublicKeyB64: string): Promise<Uint8Array>;
|
|
2324
|
+
/**
|
|
2325
|
+
* Short code derived deterministically from the device's pairing public key,
|
|
2326
|
+
* displayed on BOTH the TV and the approving browser so the human can confirm
|
|
2327
|
+
* they match before approving.
|
|
2328
|
+
*
|
|
2329
|
+
* ## What this is worth (be honest about it)
|
|
2330
|
+
*
|
|
2331
|
+
* ~39.3 bits (30^8), rendered in an unambiguous 30-symbol alphabet and grouped
|
|
2332
|
+
* as `XXXX-XXXX` for comparison. Alongside the recipient-key binding in
|
|
2333
|
+
* {@link deriveDeviceSealKey}, this is what stops a substituted device key: the
|
|
2334
|
+
* KDF rejects a *negated* key, and this code is what a human uses to reject a
|
|
2335
|
+
* wholly *attacker-generated* one, which the crypto cannot detect on its own —
|
|
2336
|
+
* ECDH proves nothing about WHOSE public key it is.
|
|
2337
|
+
*
|
|
2338
|
+
* Two attacks, and what each actually costs:
|
|
2339
|
+
*
|
|
2340
|
+
* 1. **Online grind.** Generate keypairs until one's code equals the honest
|
|
2341
|
+
* TV's. ~2^39 trials. Naively that reads as "keygen + hash", but a real
|
|
2342
|
+
* attacker never generates keys: walking `P_{i+1} = P_i + G` while tracking
|
|
2343
|
+
* `d_i = d_0 + i` makes each trial one point-add plus one derivation. With
|
|
2344
|
+
* a plain hash that is well under a core-day — which is why this uses
|
|
2345
|
+
* PBKDF2-SHA256 at {@link PAIRING_CODE_ITERATIONS} iterations instead. The
|
|
2346
|
+
* work factor applies per trial, pushing the same grind to the order of
|
|
2347
|
+
* 10^5 core-days. The target is also only learned when pairing starts, so
|
|
2348
|
+
* the grind is online against the pairing TTL (keep it minutes).
|
|
2349
|
+
* 2. **Precomputed table.** The reason this is not a plain hash. A table over
|
|
2350
|
+
* candidate keys is computed once and reused forever; with SHA-256 that is
|
|
2351
|
+
* cheap enough to be the preferred attack. The iteration count multiplies
|
|
2352
|
+
* the table's build cost by the same 2 * 10^5.
|
|
2353
|
+
*
|
|
2354
|
+
* Neither is a proof of security, and an adversary with ASIC-scale hardware
|
|
2355
|
+
* changes these numbers. If the margin is ever judged insufficient, raise
|
|
2356
|
+
* {@link PAIRING_CODE_LENGTH} — 10 characters buys ~49 bits for two more
|
|
2357
|
+
* characters of human comparison, and is display-only: it does not touch the
|
|
2358
|
+
* envelope or the wire format.
|
|
2359
|
+
*
|
|
2360
|
+
* The cost is paid once per pairing, on two idle devices, so ~200k iterations
|
|
2361
|
+
* is imperceptible here in a way it would not be on a hot path.
|
|
2362
|
+
*/
|
|
2363
|
+
declare function pairingVerificationCode(devicePublicKeyB64: string): Promise<string>;
|
|
2246
2364
|
interface Pkce {
|
|
2247
2365
|
codeVerifier: string;
|
|
2248
2366
|
codeChallenge: string;
|
|
@@ -2364,7 +2482,7 @@ declare class SessionState {
|
|
|
2364
2482
|
|
|
2365
2483
|
/**
|
|
2366
2484
|
* `client.auth.hosted` — centralized hosted auth (auth.muhkoo.dev).
|
|
2367
|
-
* AUTH_HOSTED_PLAN.md.
|
|
2485
|
+
* AUTH_HOSTED_PLAN.md; TV device pairing per `muhkoo/auth/docs/tv-device-pairing-spec.md`.
|
|
2368
2486
|
*
|
|
2369
2487
|
* Two sides share this class:
|
|
2370
2488
|
* - **Developer apps** call {@link login} (redirect to the hosted page) and
|
|
@@ -2380,6 +2498,22 @@ declare class SessionState {
|
|
|
2380
2498
|
* page → (user authenticates) → POST /api/auth/grant → redirect_uri?code&state#k=<K_t>
|
|
2381
2499
|
* app → POST /api/auth/token {code, code_verifier} → {sessionToken, sealedKeys}
|
|
2382
2500
|
* → unseal sealedKeys with K_t → master seed → session ready
|
|
2501
|
+
*
|
|
2502
|
+
* ## Device pairing (v2 — TVs and other keyboard-less devices)
|
|
2503
|
+
*
|
|
2504
|
+
* The redirect flow above is unchanged and remains the fallback for anything
|
|
2505
|
+
* with a browser and a keyboard. A device with no redirect to receive instead
|
|
2506
|
+
* displays a code and **polls**:
|
|
2507
|
+
*
|
|
2508
|
+
* TV → {@link startDevicePairing} → shows `userCode` + `verificationCode`
|
|
2509
|
+
* user → opens auth.muhkoo.dev/link, signs in, {@link lookupDevicePairing}
|
|
2510
|
+
* user → compares the verification code on both screens,
|
|
2511
|
+
* {@link approveDevicePairing} seals the seed TO the TV's public key
|
|
2512
|
+
* TV → {@link waitForDevicePairing} → unseal → own ZK session → persisted
|
|
2513
|
+
*
|
|
2514
|
+
* Same invariant as v1: the accelerator relays a sealed envelope it cannot read.
|
|
2515
|
+
* Only the key agreement changes (ECDH to a key the TV generated, instead of a
|
|
2516
|
+
* one-time key delivered in a URL fragment a TV can't receive).
|
|
2383
2517
|
*/
|
|
2384
2518
|
|
|
2385
2519
|
interface HostedAuthDeps {
|
|
@@ -2387,10 +2521,171 @@ interface HostedAuthDeps {
|
|
|
2387
2521
|
session: SessionState;
|
|
2388
2522
|
/** Base URL of the hosted auth SPA (e.g. https://auth.muhkoo.dev). */
|
|
2389
2523
|
authBaseUrl: string;
|
|
2524
|
+
/**
|
|
2525
|
+
* Absolute URL of the accelerator (e.g. https://api.muhkoo.dev) — the origin
|
|
2526
|
+
* the `/api/auth/device*` endpoints live on.
|
|
2527
|
+
*
|
|
2528
|
+
* Optional for backwards compatibility: `AuthNamespace` does not pass it
|
|
2529
|
+
* today, so it is resolved from the {@link AuthClient} at call time (see
|
|
2530
|
+
* `apiBase`). Pass it explicitly whenever you construct `HostedAuth` yourself.
|
|
2531
|
+
*/
|
|
2532
|
+
apiBaseUrl?: string;
|
|
2533
|
+
/**
|
|
2534
|
+
* `preimagePoK` circuit assets, used when a paired device mints its OWN ZK
|
|
2535
|
+
* session. Defaults to {@link defaultCircuitUrls} anchored at `apiBaseUrl` —
|
|
2536
|
+
* which matches `Client`'s own default. Supply it if the app overrode
|
|
2537
|
+
* `ClientOptions.circuits`.
|
|
2538
|
+
*/
|
|
2539
|
+
circuits?: CircuitUrls;
|
|
2540
|
+
/** Custom fetch — defaults to `globalThis.fetch`. */
|
|
2541
|
+
fetch?: typeof fetch;
|
|
2542
|
+
/** Sleep used by the poll loop. Injectable so tests don't burn wall-clock. */
|
|
2543
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
2544
|
+
}
|
|
2545
|
+
/** One in-flight pairing attempt. Hold it; pass it back into poll/wait/cancel. */
|
|
2546
|
+
interface DevicePairingSession {
|
|
2547
|
+
/** Opaque; carry it back into poll/wait/cancel. Never render it. */
|
|
2548
|
+
deviceCode: string;
|
|
2549
|
+
/** Show this, large. Formatted `"XXXX-XXXX"`. */
|
|
2550
|
+
userCode: string;
|
|
2551
|
+
/**
|
|
2552
|
+
* Show this too, and tell the user it must match the browser. Computed
|
|
2553
|
+
* LOCALLY from this device's own public key — the server's copy is never
|
|
2554
|
+
* trusted (that binding is the only MITM defence in the flow).
|
|
2555
|
+
*/
|
|
2556
|
+
verificationCode: string;
|
|
2557
|
+
verificationUri: string;
|
|
2558
|
+
/** Same URI with the code pre-filled — render as a QR code. */
|
|
2559
|
+
verificationUriComplete: string;
|
|
2560
|
+
/** Absolute epoch-ms. */
|
|
2561
|
+
expiresAt: number;
|
|
2562
|
+
/** Server-recommended seconds between polls; may grow via `slow_down`. */
|
|
2563
|
+
interval: number;
|
|
2564
|
+
/** Ephemeral private key for this attempt. In memory only, never persisted. */
|
|
2565
|
+
privateKey: CryptoKey;
|
|
2566
|
+
/**
|
|
2567
|
+
* This attempt's own public half, base64 raw SEC1.
|
|
2568
|
+
*
|
|
2569
|
+
* NOT in the spec's §7 listing, but REQUIRED: `unsealSeedFromDevice` takes
|
|
2570
|
+
* the recipient key as a third argument because it is mixed into the KDF to
|
|
2571
|
+
* defeat a point-negation substitution. The shipped crypto is authoritative;
|
|
2572
|
+
* carrying the public half here costs nothing (a device always holds both).
|
|
2573
|
+
*/
|
|
2574
|
+
devicePublicKeyB64: string;
|
|
2575
|
+
}
|
|
2576
|
+
/** The result of one poll. Discriminated on `status`. */
|
|
2577
|
+
type DevicePairingPoll = {
|
|
2578
|
+
status: "pending";
|
|
2579
|
+
interval: number;
|
|
2580
|
+
expiresIn: number;
|
|
2581
|
+
} | {
|
|
2582
|
+
status: "slow_down";
|
|
2583
|
+
interval: number;
|
|
2584
|
+
retryAfter: number;
|
|
2585
|
+
} | {
|
|
2586
|
+
status: "approved";
|
|
2587
|
+
user: AuthUser;
|
|
2588
|
+
} | {
|
|
2589
|
+
status: "denied";
|
|
2590
|
+
} | {
|
|
2591
|
+
status: "expired";
|
|
2592
|
+
};
|
|
2593
|
+
interface StartDevicePairingOptions {
|
|
2594
|
+
appId: string;
|
|
2595
|
+
/** Human label shown to the approver. Default: a UA-derived guess. */
|
|
2596
|
+
label?: string;
|
|
2390
2597
|
}
|
|
2391
|
-
interface
|
|
2392
|
-
|
|
2393
|
-
|
|
2598
|
+
interface WaitForDevicePairingOptions {
|
|
2599
|
+
signal?: AbortSignal;
|
|
2600
|
+
/** Called before each sleep — drive a countdown / "waiting…" UI. */
|
|
2601
|
+
onTick?: (remainingMs: number, interval: number) => void;
|
|
2602
|
+
}
|
|
2603
|
+
/** A pending pairing, resolved by the approver from the typed `user_code`. */
|
|
2604
|
+
interface DevicePairingRequest {
|
|
2605
|
+
userCode: string;
|
|
2606
|
+
devicePublicKeyB64: string;
|
|
2607
|
+
/** Recomputed locally by the SDK; compare with what the TV shows. */
|
|
2608
|
+
verificationCode: string;
|
|
2609
|
+
deviceFingerprint: string;
|
|
2610
|
+
deviceLabel: string;
|
|
2611
|
+
appId: string;
|
|
2612
|
+
appLabel: string;
|
|
2613
|
+
isKnownDevice: boolean;
|
|
2614
|
+
requiresReauth: boolean;
|
|
2615
|
+
expiresAt: number;
|
|
2616
|
+
}
|
|
2617
|
+
/** A device the account has paired, as listed on the security page. */
|
|
2618
|
+
interface PairedDevice {
|
|
2619
|
+
id: string;
|
|
2620
|
+
label: string;
|
|
2621
|
+
appId: string;
|
|
2622
|
+
pairedAt: number;
|
|
2623
|
+
lastSeenAt: number;
|
|
2624
|
+
activeSession: boolean;
|
|
2625
|
+
}
|
|
2626
|
+
/**
|
|
2627
|
+
* Why a pairing ended.
|
|
2628
|
+
*
|
|
2629
|
+
* `denied` / `expired` / `aborted` / `rate_limited` are the four the spec names
|
|
2630
|
+
* for {@link HostedAuth.waitForDevicePairing}. The rest are additive and exist
|
|
2631
|
+
* because the flow genuinely produces them:
|
|
2632
|
+
* - `network` — the reference loop in spec §9.2 throws it after 5 consecutive
|
|
2633
|
+
* transport failures;
|
|
2634
|
+
* - `commitment_mismatch` — spec §10.8's MANDATORY client check failed (the
|
|
2635
|
+
* seed does not derive the commitment the server sent). This is what turns
|
|
2636
|
+
* §10.2's substitution attack from silent into loud;
|
|
2637
|
+
* - `account_mismatch` — spec §10.2 detection 2: this device was previously
|
|
2638
|
+
* paired to a DIFFERENT account. Refuse rather than silently adopt;
|
|
2639
|
+
* - `reauth_required` — see {@link ReauthRequiredError};
|
|
2640
|
+
* - `invalid_user_code` — client-side canonicalization rejected the input;
|
|
2641
|
+
* - `error` — anything else; read `.code` for the server's machine code.
|
|
2642
|
+
*/
|
|
2643
|
+
type DevicePairingErrorReason = "denied" | "expired" | "aborted" | "rate_limited" | "network" | "commitment_mismatch" | "account_mismatch" | "reauth_required" | "invalid_user_code" | "error";
|
|
2644
|
+
/**
|
|
2645
|
+
* Any terminal failure of the pairing flow.
|
|
2646
|
+
*
|
|
2647
|
+
* `.reason` drives the device's UI state machine; `.code` carries the server's
|
|
2648
|
+
* machine error code verbatim (spec §6.7) for the cases the reason collapses —
|
|
2649
|
+
* e.g. `consent_required`, `too_many_devices`, `key_mismatch` all arrive as
|
|
2650
|
+
* `reason: "error"` with a distinct `.code`.
|
|
2651
|
+
*/
|
|
2652
|
+
declare class DevicePairingError extends Error {
|
|
2653
|
+
readonly reason: DevicePairingErrorReason;
|
|
2654
|
+
/** Machine code from the error envelope (`{error, message}`), when there was one. */
|
|
2655
|
+
readonly code?: string;
|
|
2656
|
+
readonly status?: number;
|
|
2657
|
+
/** Seconds, from `Retry-After`, when the server sent one. */
|
|
2658
|
+
readonly retryAfter?: number;
|
|
2659
|
+
/** Any extra fields the error envelope carried (e.g. `version`). */
|
|
2660
|
+
readonly details?: Record<string, unknown>;
|
|
2661
|
+
constructor(reason: DevicePairingErrorReason, opts?: {
|
|
2662
|
+
message?: string;
|
|
2663
|
+
code?: string;
|
|
2664
|
+
status?: number;
|
|
2665
|
+
retryAfter?: number;
|
|
2666
|
+
details?: Record<string, unknown>;
|
|
2667
|
+
cause?: unknown;
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
/**
|
|
2671
|
+
* Approving a **new** device needs a recently authenticated approver (spec
|
|
2672
|
+
* §10.7, `FRESH_AUTH_MAX_AGE_MS`). The hosted page catches this, prompts for a
|
|
2673
|
+
* password / passkey / Google factor, and retries the approval.
|
|
2674
|
+
*
|
|
2675
|
+
* Server-enforced, not a UI convention — `requiresReauth` on
|
|
2676
|
+
* {@link DevicePairingRequest} is only the hint.
|
|
2677
|
+
*/
|
|
2678
|
+
declare class ReauthRequiredError extends DevicePairingError {
|
|
2679
|
+
/** How long ago the approver's session authenticated, in seconds. */
|
|
2680
|
+
readonly authAgeSeconds?: number;
|
|
2681
|
+
/** The freshness window the server enforces, in seconds. */
|
|
2682
|
+
readonly maxAgeSeconds?: number;
|
|
2683
|
+
constructor(opts?: {
|
|
2684
|
+
message?: string;
|
|
2685
|
+
authAgeSeconds?: number;
|
|
2686
|
+
maxAgeSeconds?: number;
|
|
2687
|
+
status?: number;
|
|
2688
|
+
});
|
|
2394
2689
|
}
|
|
2395
2690
|
declare class HostedAuth {
|
|
2396
2691
|
private readonly deps;
|
|
@@ -2423,7 +2718,7 @@ declare class HostedAuth {
|
|
|
2423
2718
|
* code (+ PKCE verifier) for the session, unseal the seed from the fragment,
|
|
2424
2719
|
* establish the session, and scrub the URL. Returns the signed-in user.
|
|
2425
2720
|
*/
|
|
2426
|
-
handleCallback(): Promise<AuthUser
|
|
2721
|
+
handleCallback(): Promise<AuthUser>;
|
|
2427
2722
|
/**
|
|
2428
2723
|
* (Hosted-page side) Finish an /authorize request after the user has
|
|
2429
2724
|
* authenticated here: seal the in-memory seed, mint the grant, and return
|
|
@@ -2436,12 +2731,180 @@ declare class HostedAuth {
|
|
|
2436
2731
|
codeChallenge: string;
|
|
2437
2732
|
state: string;
|
|
2438
2733
|
}): Promise<string>;
|
|
2734
|
+
/**
|
|
2735
|
+
* (Device side) Begin pairing. Generates the ephemeral ECDH keypair, ensures
|
|
2736
|
+
* a persistent device identity key exists, signs a possession proof over it,
|
|
2737
|
+
* and calls `POST /api/auth/device/code`.
|
|
2738
|
+
*
|
|
2739
|
+
* Display `userCode` (large, monospace) **and** `verificationCode`, then call
|
|
2740
|
+
* {@link waitForDevicePairing}. The verification code returned here is
|
|
2741
|
+
* computed locally from this device's own public key — the server also
|
|
2742
|
+
* returns a copy, which is ignored on purpose: trusting it would forfeit the
|
|
2743
|
+
* entire MITM binding (spec §6.1, §10.1).
|
|
2744
|
+
*/
|
|
2745
|
+
startDevicePairing(opts: StartDevicePairingOptions): Promise<DevicePairingSession>;
|
|
2746
|
+
/**
|
|
2747
|
+
* (Device side) One poll of `POST /api/auth/device/token`.
|
|
2748
|
+
*
|
|
2749
|
+
* On `"approved"` this does the whole tail of the flow before returning: it
|
|
2750
|
+
* unseals the envelope with the ephemeral private key, derives the identity,
|
|
2751
|
+
* **verifies the commitment matches** (spec §10.8 — mandatory, and the check
|
|
2752
|
+
* that makes a substituted seed loud rather than silent), mints the device's
|
|
2753
|
+
* OWN long-lived ZK session, registers the device via `PUT /api/auth/devices`,
|
|
2754
|
+
* and persists the identity for cold starts.
|
|
2755
|
+
*
|
|
2756
|
+
* Prefer {@link waitForDevicePairing}; use this only for a custom loop. A
|
|
2757
|
+
* transport failure REJECTS (the caller decides whether to retry); protocol
|
|
2758
|
+
* outcomes resolve as a status.
|
|
2759
|
+
*/
|
|
2760
|
+
pollDevicePairing(session: DevicePairingSession): Promise<DevicePairingPoll>;
|
|
2761
|
+
/**
|
|
2762
|
+
* (Device side) Poll to completion with the interval + `slow_down` backoff of
|
|
2763
|
+
* spec §9.
|
|
2764
|
+
*
|
|
2765
|
+
* The server's `interval` is authoritative: on `slow_down` the client moves to
|
|
2766
|
+
* `max(serverInterval, own + 5, retryAfter)` so the two can't drift into a
|
|
2767
|
+
* permanent slow_down loop. Sleeps carry ±10 % jitter so a rack of devices
|
|
2768
|
+
* restarted together doesn't synchronise, and the loop stops at `expiresAt`
|
|
2769
|
+
* without waiting for the server to say so.
|
|
2770
|
+
*
|
|
2771
|
+
* Resolves with the signed-in user, or rejects with {@link DevicePairingError}.
|
|
2772
|
+
*/
|
|
2773
|
+
waitForDevicePairing(session: DevicePairingSession, opts?: WaitForDevicePairingOptions): Promise<AuthUser>;
|
|
2774
|
+
/**
|
|
2775
|
+
* (Device side) Abandon an attempt. Marks the session dead so an in-flight
|
|
2776
|
+
* {@link waitForDevicePairing} rejects with `aborted` on its next tick, and
|
|
2777
|
+
* clears the device code.
|
|
2778
|
+
*
|
|
2779
|
+
* The ephemeral `CryptoKey` cannot be zeroed from JS — dropping the last
|
|
2780
|
+
* reference to `session` is what makes it unrecoverable, so don't keep the
|
|
2781
|
+
* object around. Deliberately does NOT notify the server: the pairing is
|
|
2782
|
+
* single-use and expires on its own, and an unauthenticated "cancel" endpoint
|
|
2783
|
+
* would just be another way to grief someone else's pending code.
|
|
2784
|
+
*/
|
|
2785
|
+
cancelDevicePairing(session: DevicePairingSession): void;
|
|
2786
|
+
/**
|
|
2787
|
+
* (Device side) Cold-start path. Reads the persisted device identity,
|
|
2788
|
+
* re-derives the ZK identity from the stored seed, mints a fresh session, and
|
|
2789
|
+
* touches `PUT /api/auth/devices`.
|
|
2790
|
+
*
|
|
2791
|
+
* Returns `null` when nothing is persisted, when the blob doesn't derive its
|
|
2792
|
+
* own pinned commitment (corrupt/tampered storage), or when the account no
|
|
2793
|
+
* longer accepts the identity — call {@link startDevicePairing} then.
|
|
2794
|
+
*/
|
|
2795
|
+
resumeDeviceSession(): Promise<AuthUser | null>;
|
|
2796
|
+
/** (Device side) Wipe the persisted seed, the device identity key, and the session. */
|
|
2797
|
+
forgetDeviceSession(): Promise<void>;
|
|
2798
|
+
/** (Device side) Whether a persisted device identity exists. Cheap: no crypto, no network. */
|
|
2799
|
+
hasDeviceSession(): Promise<boolean>;
|
|
2800
|
+
/**
|
|
2801
|
+
* (Hosted page) Resolve a typed `user_code` into the pending request.
|
|
2802
|
+
* Session-authed.
|
|
2803
|
+
*
|
|
2804
|
+
* `verificationCode` is recomputed here from the returned device key — the
|
|
2805
|
+
* server's copy is discarded. Both screens must show independently derived
|
|
2806
|
+
* values or the comparison proves nothing.
|
|
2807
|
+
*/
|
|
2808
|
+
lookupDevicePairing(userCode: string): Promise<DevicePairingRequest>;
|
|
2809
|
+
/**
|
|
2810
|
+
* (Hosted page) Seal the in-memory seed to the device's public key and post
|
|
2811
|
+
* the ciphertext. Requires an unlocked seed — the user just authenticated
|
|
2812
|
+
* here — exactly like {@link completeAuthorize}. The server stores and relays
|
|
2813
|
+
* the envelope verbatim and can't read it.
|
|
2814
|
+
*
|
|
2815
|
+
* Show the user the verification code and the device label FIRST, and only
|
|
2816
|
+
* call this on an explicit tap. Throws {@link ReauthRequiredError} when the
|
|
2817
|
+
* account's last authentication is too old for a new device; the page should
|
|
2818
|
+
* prompt for a factor and retry. A stale legal agreement arrives as
|
|
2819
|
+
* `DevicePairingError` with `.code === "consent_required"` and
|
|
2820
|
+
* `.details.version`.
|
|
2821
|
+
*/
|
|
2822
|
+
approveDevicePairing(req: DevicePairingRequest, opts?: {
|
|
2823
|
+
label?: string;
|
|
2824
|
+
}): Promise<{
|
|
2825
|
+
deviceId: string;
|
|
2826
|
+
}>;
|
|
2827
|
+
/**
|
|
2828
|
+
* (Hosted page) Refuse a pairing. No re-auth and no consent gate — declining
|
|
2829
|
+
* must never be harder than accepting. Leaves a tombstone so the device is
|
|
2830
|
+
* told "declined" rather than an indistinguishable "expired".
|
|
2831
|
+
*/
|
|
2832
|
+
denyDevicePairing(userCode: string): Promise<void>;
|
|
2833
|
+
/**
|
|
2834
|
+
* The account's paired devices. Session-authed.
|
|
2835
|
+
*
|
|
2836
|
+
* UI copy note (spec §10.6): removing a device signs it out and forces a full
|
|
2837
|
+
* re-approval, but it **does not take back the encryption key the device
|
|
2838
|
+
* already holds**. Say so — users will assume otherwise.
|
|
2839
|
+
*/
|
|
2840
|
+
listPairedDevices(): Promise<PairedDevice[]>;
|
|
2841
|
+
/** Remove a paired device and revoke its session. Session-authed. */
|
|
2842
|
+
revokePairedDevice(id: string): Promise<void>;
|
|
2843
|
+
/**
|
|
2844
|
+
* The whole tail of an approved pairing: unseal → derive → **verify** →
|
|
2845
|
+
* authenticate → register → persist. Split out of `pollDevicePairing` so the
|
|
2846
|
+
* ordering (and the two refusals) stay readable.
|
|
2847
|
+
*/
|
|
2848
|
+
private adoptSealedSeed;
|
|
2849
|
+
/**
|
|
2850
|
+
* Run the standard ZK challenge → Groth16 proof → authenticate round-trip for
|
|
2851
|
+
* an identity derived from a seed the caller already holds, and store the
|
|
2852
|
+
* resulting session.
|
|
2853
|
+
*
|
|
2854
|
+
* Identical to `ZkAuth.login()`'s tail (`proveAndStore`) — same challenge,
|
|
2855
|
+
* same circuit, same signature — so the commitment is byte-identical to
|
|
2856
|
+
* `zk.login(username, password)` by construction. No new endpoint, no circuit
|
|
2857
|
+
* change, no alternative derivation.
|
|
2858
|
+
*
|
|
2859
|
+
* (Spec §7 puts this on `AuthNamespace` as `loginWithSeed`; see the report —
|
|
2860
|
+
* it lives here because `AuthNamespace.ts` was out of scope for this change,
|
|
2861
|
+
* and `proveAndStore` is private.)
|
|
2862
|
+
*/
|
|
2863
|
+
private mintSession;
|
|
2864
|
+
/**
|
|
2865
|
+
* `PUT /api/auth/devices` — the device checks in after each self-login so the
|
|
2866
|
+
* security page can show "last seen" and revoke the right session. Never
|
|
2867
|
+
* creates a record (only approve does), so a 404 here is expected on a device
|
|
2868
|
+
* whose pairing was revoked. Best-effort by design.
|
|
2869
|
+
*/
|
|
2870
|
+
private touchPairedDevice;
|
|
2871
|
+
/** Reject a session that {@link cancelDevicePairing} has retired. */
|
|
2872
|
+
private assertLive;
|
|
2873
|
+
private sleep;
|
|
2874
|
+
/**
|
|
2875
|
+
* One round-trip to a `/api/auth/device*` endpoint. Never throws on a non-2xx
|
|
2876
|
+
* — the poll path has to inspect 401/403/429 itself — so callers that don't
|
|
2877
|
+
* want that follow up with {@link throwForStatus}.
|
|
2878
|
+
*/
|
|
2879
|
+
private deviceApi;
|
|
2880
|
+
/** Turn a non-2xx device-endpoint response into the right typed error. */
|
|
2881
|
+
private throwForStatus;
|
|
2882
|
+
/**
|
|
2883
|
+
* Accelerator origin for the device endpoints.
|
|
2884
|
+
*
|
|
2885
|
+
* Prefers the explicit dep. Falls back to the {@link AuthClient}'s own base
|
|
2886
|
+
* URL, because `AuthNamespace` builds `HostedAuth` without one today and a
|
|
2887
|
+
* hard-coded default would silently point a staging or local build at
|
|
2888
|
+
* production. Last resort: map the auth SPA host (`auth.…` → `api.…`).
|
|
2889
|
+
*/
|
|
2890
|
+
private apiBase;
|
|
2891
|
+
private circuits;
|
|
2439
2892
|
private location;
|
|
2440
2893
|
private store;
|
|
2441
2894
|
private take;
|
|
2442
2895
|
/** Drop `code`, `state`, and the `#k` fragment from the address bar. */
|
|
2443
2896
|
private scrubUrl;
|
|
2444
2897
|
}
|
|
2898
|
+
/**
|
|
2899
|
+
* Canonicalize a typed `user_code`: uppercase, drop whitespace and `-`.
|
|
2900
|
+
*
|
|
2901
|
+
* We deliberately do NOT guess (`O → 0`, `I → 1`, …). Both members of every
|
|
2902
|
+
* confusable pair are excluded from the alphabet, so a typed `O` is a genuine
|
|
2903
|
+
* misread — telling the user is more useful than silently resolving it.
|
|
2904
|
+
*/
|
|
2905
|
+
declare function canonicalUserCode(input: string): string;
|
|
2906
|
+
/** Group a canonical code for display and for the wire: `"K7QD3MXR"` → `"K7QD-3MXR"`. */
|
|
2907
|
+
declare function formatUserCode(canonical: string): string;
|
|
2445
2908
|
|
|
2446
2909
|
/**
|
|
2447
2910
|
* `client.auth` — user authentication, exposed today through the `zk`
|
|
@@ -2749,6 +3212,7 @@ declare class AuthNamespace {
|
|
|
2749
3212
|
readonly hosted: HostedAuth;
|
|
2750
3213
|
constructor(deps: ZkAuthDeps & {
|
|
2751
3214
|
authBaseUrl: string;
|
|
3215
|
+
apiBaseUrl?: string;
|
|
2752
3216
|
});
|
|
2753
3217
|
/** Convenience pass-through: the currently signed-in user, or `null`. */
|
|
2754
3218
|
get user(): AuthUser | null;
|
|
@@ -5046,6 +5510,155 @@ declare class Client {
|
|
|
5046
5510
|
recoverSession(): Promise<boolean>;
|
|
5047
5511
|
}
|
|
5048
5512
|
|
|
5513
|
+
/**
|
|
5514
|
+
* `deviceStore` — at-rest persistence for a paired device (the TV).
|
|
5515
|
+
*
|
|
5516
|
+
* A device that has completed {@link HostedAuth.startDevicePairing} holds the
|
|
5517
|
+
* account's **master seed**. Android WebView has no WebAuthn, so the passkey
|
|
5518
|
+
* wrapping used elsewhere isn't available, and re-pairing on every cold start
|
|
5519
|
+
* is not acceptable UX for a set-top box. So the seed is persisted, encrypted:
|
|
5520
|
+
*
|
|
5521
|
+
* | Item | Where | Form |
|
|
5522
|
+
* | ------------------------ | ------------------------------------ | ---- |
|
|
5523
|
+
* | wrapping key | IndexedDB (`muhkoo.device.v1`) | `CryptoKey`, AES-GCM-256, **non-extractable** |
|
|
5524
|
+
* | device identity keypair | IndexedDB (`muhkoo.device.v1`) | `CryptoKeyPair`, ECDSA P-256, private half **non-extractable** |
|
|
5525
|
+
* | sealed identity blob | localStorage `muhkoo.tv.identity.v1` | `{"v":1,"iv":…,"ct":…}` over `JSON{username, commitment, seed(b64), pairedAt}` |
|
|
5526
|
+
*
|
|
5527
|
+
* The split is deliberate — ciphertext in one backend, key in another, and the
|
|
5528
|
+
* key non-extractable so JS can *use* it but not export it.
|
|
5529
|
+
*
|
|
5530
|
+
* ## Be honest about what this is worth
|
|
5531
|
+
*
|
|
5532
|
+
* **This is obfuscation, not protection.** It raises the cost of a casual
|
|
5533
|
+
* `localStorage` dump to "nothing useful", and that is all:
|
|
5534
|
+
*
|
|
5535
|
+
* - **XSS on the device origin** cannot *exfiltrate* the wrapping key (it is
|
|
5536
|
+
* non-extractable) but can absolutely call `decrypt` and read the seed. The
|
|
5537
|
+
* only mitigation is that there is no third-party script in the shell —
|
|
5538
|
+
* enforce a strict CSP with no `unsafe-inline`, no CDN, no remote fonts.
|
|
5539
|
+
* - **ADB / root / a backup extraction / a forensic image recovers the seed.**
|
|
5540
|
+
* The IndexedDB LevelDB files hold the AES key bytes in the clear; WebCrypto
|
|
5541
|
+
* "non-extractable" is a JS-API boundary, not an OS one. **A stolen device is
|
|
5542
|
+
* a stolen identity.**
|
|
5543
|
+
*
|
|
5544
|
+
* That is not an implementation shortcut, it is the shape of the problem: any
|
|
5545
|
+
* wrapping key usable without user interaction lives somewhere equally readable
|
|
5546
|
+
* on a keyboard-less device with no hardware key store exposed to WebView.
|
|
5547
|
+
* Reduce the blast radius with `android:allowBackup="false"`,
|
|
5548
|
+
* `android:debuggable="false"`, a strict CSP, and by storing *only* the 32-byte
|
|
5549
|
+
* seed + username + commitment (never a session token, never a recovery phrase).
|
|
5550
|
+
*
|
|
5551
|
+
* ## Documented upgrade path — no protocol change
|
|
5552
|
+
*
|
|
5553
|
+
* If the native shell exposes a Keystore bridge:
|
|
5554
|
+
*
|
|
5555
|
+
* ```java
|
|
5556
|
+
* @JavascriptInterface public String wrap(String b64plain); // AES-GCM, StrongBox when present
|
|
5557
|
+
* @JavascriptInterface public String unwrap(String b64cipher);
|
|
5558
|
+
* ```
|
|
5559
|
+
*
|
|
5560
|
+
* this module prefers `globalThis.MuhkooKeystore` automatically and falls back
|
|
5561
|
+
* to the WebCrypto scheme otherwise. The Keystore key is hardware-backed and
|
|
5562
|
+
* non-exportable at the OS level, so a copied filesystem image alone becomes
|
|
5563
|
+
* useless. Nothing on the wire, in the endpoint contracts, or in the SDK surface
|
|
5564
|
+
* changes — only the two functions below do, internally.
|
|
5565
|
+
*
|
|
5566
|
+
* ## Degradation
|
|
5567
|
+
*
|
|
5568
|
+
* Nothing here throws at import time. On a runtime with neither backend (SSR,
|
|
5569
|
+
* Workers, Node) the module still loads: {@link loadDeviceIdentity} returns
|
|
5570
|
+
* `null`, {@link persistDeviceIdentity} throws a typed
|
|
5571
|
+
* {@link DeviceStoreUnavailableError} the caller can swallow, and
|
|
5572
|
+
* {@link deviceIdentityKey} falls back to an **in-memory** keypair for the
|
|
5573
|
+
* lifetime of the process (so pairing still works; the device is just never
|
|
5574
|
+
* recognised as "known" on a later run).
|
|
5575
|
+
*/
|
|
5576
|
+
/** localStorage key holding the AES-GCM-sealed identity blob. */
|
|
5577
|
+
declare const DEVICE_IDENTITY_STORAGE_KEY = "muhkoo.tv.identity.v1";
|
|
5578
|
+
/** IndexedDB database holding the non-extractable keys. */
|
|
5579
|
+
declare const DEVICE_KEY_DB_NAME = "muhkoo.device.v1";
|
|
5580
|
+
/** What a paired device persists. The seed **is** the identity — treat it as such. */
|
|
5581
|
+
interface PersistedDeviceIdentity {
|
|
5582
|
+
username: string;
|
|
5583
|
+
/** Decimal-string Poseidon commitment. Pinned: a later pairing that yields a
|
|
5584
|
+
* different commitment for this device must be refused, not adopted. */
|
|
5585
|
+
commitment: string;
|
|
5586
|
+
/** The 32-byte master seed. */
|
|
5587
|
+
seed: Uint8Array;
|
|
5588
|
+
pairedAt: number;
|
|
5589
|
+
}
|
|
5590
|
+
/** Thrown when persistence is impossible (no IndexedDB and/or no localStorage). */
|
|
5591
|
+
declare class DeviceStoreUnavailableError extends Error {
|
|
5592
|
+
constructor(what: string);
|
|
5593
|
+
}
|
|
5594
|
+
/** Key/value store for live `CryptoKey` objects (IndexedDB in the browser). */
|
|
5595
|
+
interface DeviceKeyVault {
|
|
5596
|
+
get(id: string): Promise<unknown | null>;
|
|
5597
|
+
put(id: string, value: unknown): Promise<void>;
|
|
5598
|
+
/** Delete the entire store — used by `clearDeviceIdentity`. */
|
|
5599
|
+
destroy(): Promise<void>;
|
|
5600
|
+
}
|
|
5601
|
+
/** String store for the sealed blob (localStorage in the browser). */
|
|
5602
|
+
interface DeviceBlobStore {
|
|
5603
|
+
get(key: string): string | null;
|
|
5604
|
+
set(key: string, value: string): void;
|
|
5605
|
+
remove(key: string): void;
|
|
5606
|
+
}
|
|
5607
|
+
/** The native Keystore bridge, when the shell provides one (see module docs). */
|
|
5608
|
+
interface MuhkooKeystoreBridge {
|
|
5609
|
+
wrap(b64plain: string): string | Promise<string>;
|
|
5610
|
+
unwrap(b64cipher: string): string | Promise<string>;
|
|
5611
|
+
}
|
|
5612
|
+
/**
|
|
5613
|
+
* Point the store at explicit backends (or `null` to force "unavailable"), and
|
|
5614
|
+
* reset the memoized device keypair. Testing / embedding seam — pass `{}` to
|
|
5615
|
+
* restore the default browser backends.
|
|
5616
|
+
*/
|
|
5617
|
+
declare function configureDeviceStore(opts?: {
|
|
5618
|
+
keys?: DeviceKeyVault | null;
|
|
5619
|
+
blobs?: DeviceBlobStore | null;
|
|
5620
|
+
keystore?: MuhkooKeystoreBridge | null;
|
|
5621
|
+
}): void;
|
|
5622
|
+
/**
|
|
5623
|
+
* True when {@link deviceIdentityKey} had to fall back to an in-memory keypair
|
|
5624
|
+
* because no key vault was available. Such a device can pair, but will never be
|
|
5625
|
+
* recognised as "known" afterwards (so approval always needs a fresh factor).
|
|
5626
|
+
*/
|
|
5627
|
+
declare function deviceIdentityIsEphemeral(): boolean;
|
|
5628
|
+
/**
|
|
5629
|
+
* The device's **persistent** ECDSA P-256 identity keypair, created on first
|
|
5630
|
+
* call. This is not a browser fingerprint — it is a key, and every
|
|
5631
|
+
* `POST /api/auth/device/code` proves possession of it. Without that proof,
|
|
5632
|
+
* "known device" would be an unauthenticated string anyone could claim in order
|
|
5633
|
+
* to skip the approver's re-authentication.
|
|
5634
|
+
*
|
|
5635
|
+
* The private half is non-extractable and never leaves IndexedDB as bytes.
|
|
5636
|
+
*/
|
|
5637
|
+
declare function deviceIdentityKey(): Promise<CryptoKeyPair>;
|
|
5638
|
+
/** `base64url(SHA-256(raw SEC1 device identity public key))` — 43 chars. */
|
|
5639
|
+
declare function deviceFingerprint(): Promise<string>;
|
|
5640
|
+
/** Whether a sealed identity blob exists. Cheap: no crypto, no IndexedDB, no network. */
|
|
5641
|
+
declare function hasDeviceIdentity(): Promise<boolean>;
|
|
5642
|
+
/**
|
|
5643
|
+
* Seal and store the identity. Throws {@link DeviceStoreUnavailableError} when
|
|
5644
|
+
* there is nowhere to put it — callers should treat that as "this device won't
|
|
5645
|
+
* resume", not as a pairing failure.
|
|
5646
|
+
*/
|
|
5647
|
+
declare function persistDeviceIdentity(v: PersistedDeviceIdentity): Promise<void>;
|
|
5648
|
+
/**
|
|
5649
|
+
* Recover the persisted identity, or `null` when there is none / it can't be
|
|
5650
|
+
* opened (wiped IndexedDB, a different browser profile, a corrupted blob).
|
|
5651
|
+
* Never throws — a cold start with unreadable storage must fall back to a fresh
|
|
5652
|
+
* pairing, not to an error screen.
|
|
5653
|
+
*/
|
|
5654
|
+
declare function loadDeviceIdentity(): Promise<PersistedDeviceIdentity | null>;
|
|
5655
|
+
/**
|
|
5656
|
+
* Wipe everything: the sealed blob **and** the whole key database — so the
|
|
5657
|
+
* wrapping key never outlives the ciphertext it opens, and the device identity
|
|
5658
|
+
* key is regenerated (a re-pair is then correctly treated as a new device).
|
|
5659
|
+
*/
|
|
5660
|
+
declare function clearDeviceIdentity(): Promise<void>;
|
|
5661
|
+
|
|
5049
5662
|
/**
|
|
5050
5663
|
* Passkey factor (M1.1) — WebAuthn with the **PRF extension**.
|
|
5051
5664
|
*
|
|
@@ -6019,5 +6632,5 @@ declare class KeyringClient implements KeyringTransport {
|
|
|
6019
6632
|
fetchMetadata(): Promise<SpaceMetadata | null>;
|
|
6020
6633
|
}
|
|
6021
6634
|
|
|
6022
|
-
export { ACCESS_TOKEN_SCOPES, AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AccessTokensNamespace, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BlockEngine, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, ConnectivityManager, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_FN_HOST_SUFFIX, DbCache, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HLC, HashKnowledge, HttpClient, HttpError, IndexedDbStore, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvCache, KvNamespace, LocalEngineHost, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, NoopStore, OfflineLockedError, OfflineManager, OutboundQueue, P2P_SUBJECT, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PasskeyOriginError, PeerExchange, PeerNetwork, PersonalSpaceClient, Poseidon, PreimagePoK, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardCache, ShardClient, SharedSpaceClient, SnapshotNamespace, Space, SpaceCache, SpaceKeyring, SpaceNamespace, SpacePacketCipher, SpaceSignaler, StorageNamespace, SyncEngine, VaultUnavailableError, WSTransport, WebRtcTransport, WorkerEngineHost, ZERO_HLC, ZkAuth, buildCommitment, compareHlc, compilePrograms, createPasskeyWithPrf, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultRpId, defaultSessionStore, deleteBeatsWrite, deleteRegister, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, ejectAgentPrompt, ejectAgentTools, emptyMap, emptyRegister, encodeToHex, encryptAesGcm, evaluatePasskeyPrf, exportPublicKeyBase64, exportPublicKeyHex, generateAuthProof, generatePkce, getMuhkooAppDescriptor, getSubtle, initBn128Wasm, initRsWasm, initializeCircuits, isNewer, isOfflineCapable, isP2pCapable, isProvisional, manifestToStat, materializeRow, mergeMap, mergeMessage, mergeRegister, messageKey, pack, padHandle, passkeyPrfCapable, passkeySupported, poseidonHash, protocol, provisionalHandle, quickVerify, randomBytes, randomState, rpIdUsableForOrigin, sealSeed, shardHash, signMessage, stableStringify, toField, tombstoneRow, unpack, unsealSeed, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase, writeBeatsTombstone, writeColumns, writeRegister };
|
|
6023
|
-
export type { ACL, ACLEntry, AccessTokenInfo, AccessTokensNamespaceDeps, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AttachedPeerNetwork, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, BlockStore, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, CachedSpaceMessage, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, ConnectivityOptions, ConnectivityState, CreateAccessTokenInput, CreatedAccessToken, DbQueryPage, DehydratedKeypair, DehydratedKeys, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EngineHandle, EngineHost, EphemeralEvent, EpochKeyProvider, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, Hasher, HlcParts, HlcState, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvEntry, KvNamespaceDeps, LoginOptions, LwwMap, LwwRegister, MessageBody, MessageDeletedEvent, MessageEntry, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOp, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, OfflineEntry, OfflineManagerDeps, OfflineStore, OutSink, PacketCipher, PacketHeaders, PacketOptions, Participant, PasskeyEnrollment, PeerBlockSource, PeerExchangeOptions, PeerId, PeerNetworkOptions, PeerTransport, Permission, PermissionCheckRequest, PermissionCheckResult, PersonalSpaceClientOptions, Pkce, PublicKeyJWK, QueueEntry, RatchetState, ReceiveResult, RegisterParams, Replayer, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, Scope, SealedHandoff, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardByteCache, ShardClientOptions, SharedSpaceClientOptions, SignalEnvelope, SignalKind, SignalingSpace, SnapshotNamespaceDeps, SnarkProof, SpaceChannelLike, SpaceCursor, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, SpaceOfflineAdapter, StorageChangeEvent, StoreName, StoredSession, Subscription, UserIdentity, VaultFactor, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WebRtcTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|
|
6635
|
+
export { ACCESS_TOKEN_SCOPES, AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AccessTokensNamespace, AgentsNamespace, AuthClient, AuthNamespace, AuthPublicInput, Authenticator, BlockEngine, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, Client, ConnectivityManager, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_FN_HOST_SUFFIX, DEVICE_IDENTITY_STORAGE_KEY, DEVICE_KEY_DB_NAME, DbCache, DevicePairingError, DeviceStoreUnavailableError, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HLC, HashKnowledge, HostedAuth, HttpClient, HttpError, IndexedDbStore, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KvCache, KvNamespace, LocalEngineHost, LocalStorageSessionStore, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, NoopStore, OfflineLockedError, OfflineManager, OutboundQueue, P2P_SUBJECT, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, PBKDF2_DEFAULT_ITERATIONS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PasskeyOriginError, PeerExchange, PeerNetwork, PersonalSpaceClient, Poseidon, PreimagePoK, ReauthRequiredError, ReedSolomonCodec, Space as Room, SerializeMessage, SessionState, ShardCache, ShardClient, SharedSpaceClient, SnapshotNamespace, Space, SpaceCache, SpaceKeyring, SpaceNamespace, SpacePacketCipher, SpaceSignaler, StorageNamespace, SyncEngine, VaultUnavailableError, WSTransport, WebRtcTransport, WorkerEngineHost, ZERO_HLC, ZkAuth, buildCommitment, canonicalUserCode, clearDeviceIdentity, compareHlc, compilePrograms, configureDeviceStore, createPasskeyWithPrf, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultRpId, defaultSessionStore, deleteBeatsWrite, deleteRegister, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, deviceFingerprint, deviceIdentityIsEphemeral, deviceIdentityKey, ejectAgentPrompt, ejectAgentTools, emptyMap, emptyRegister, encodeToHex, encryptAesGcm, evaluatePasskeyPrf, exportPublicKeyBase64, exportPublicKeyHex, formatUserCode, generateAuthProof, generateDevicePairingKeypair, generatePkce, getMuhkooAppDescriptor, getSubtle, hasDeviceIdentity, initBn128Wasm, initRsWasm, initializeCircuits, isNewer, isOfflineCapable, isP2pCapable, isProvisional, loadDeviceIdentity, manifestToStat, materializeRow, mergeMap, mergeMessage, mergeRegister, messageKey, pack, padHandle, pairingVerificationCode, passkeyPrfCapable, passkeySupported, persistDeviceIdentity, poseidonHash, protocol, provisionalHandle, quickVerify, randomBytes, randomState, rpIdUsableForOrigin, sealSeed, sealSeedToDevice, shardHash, signMessage, stableStringify, toField, tombstoneRow, unpack, unsealSeed, unsealSeedFromDevice, unwrapWithPassphrase, verify, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase, writeBeatsTombstone, writeColumns, writeRegister };
|
|
6636
|
+
export type { ACL, ACLEntry, AccessTokenInfo, AccessTokensNamespaceDeps, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AttachedPeerNetwork, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, BlockStore, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, CachedSpaceMessage, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitUrls, ClientOptions, ConnectivityOptions, ConnectivityState, CreateAccessTokenInput, CreatedAccessToken, DbQueryPage, DehydratedKeypair, DehydratedKeys, DeviceBlobStore, DeviceKeyVault, DevicePairingErrorReason, DevicePairingKeypair, DevicePairingPoll, DevicePairingRequest, DevicePairingSession, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EngineHandle, EngineHost, EphemeralEvent, EpochKeyProvider, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, Hasher, HlcParts, HlcState, HostedAuthDeps, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvEntry, KvNamespaceDeps, LoginOptions, LwwMap, LwwRegister, MessageBody, MessageDeletedEvent, MessageEntry, MessageEvent, MessageHeaders, MessageNamespaceDeps, MessageOp, MessageOptions, MessageSubscription, MuhkooAgentMeta, MuhkooAgentToolsConfig, MuhkooAppDescriptor, MuhkooDBAccess, MuhkooDBMeta, MuhkooFunctionMeta, MuhkooKeystoreBridge, MuhkooMessageEvent, MuhkooSpaceMeta, OAuthProvider, OfflineEntry, OfflineManagerDeps, OfflineStore, OutSink, PacketCipher, PacketHeaders, PacketOptions, PairedDevice, Participant, PasskeyEnrollment, PeerBlockSource, PeerExchangeOptions, PeerId, PeerNetworkOptions, PeerTransport, Permission, PermissionCheckRequest, PermissionCheckResult, PersistedDeviceIdentity, PersonalSpaceClientOptions, Pkce, PublicKeyJWK, QueueEntry, RatchetState, ReceiveResult, RegisterParams, Replayer, Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, Scope, SealedHandoff, SealedHeaders, Session, SessionOptions, SessionStore, SetOptions, ShardByteCache, ShardClientOptions, SharedSpaceClientOptions, SignalEnvelope, SignalKind, SignalingSpace, SnapshotNamespaceDeps, SnarkProof, SpaceChannelLike, SpaceCursor, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, SpaceOfflineAdapter, StartDevicePairingOptions, StorageChangeEvent, StoreName, StoredSession, Subscription, UserIdentity, VaultFactor, VerificationKey, WSMessage, WSMessageType, WSTransportOptions, WaitForDevicePairingOptions, WebRtcTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|