@muhkoo/connect 0.14.0-alpha.8 → 0.15.0-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/README.md +53 -4
- package/dist/browser/index.js +4631 -990
- package/dist/browser/index.js.map +1 -1
- package/dist/connect.d.ts +1165 -55
- package/dist/connect.workers.d.ts +38 -1
- package/dist/server/index.js +4631 -990
- package/dist/server/index.js.map +1 -1
- package/dist/workers/index.js +111 -22
- package/dist/workers/index.js.map +1 -1
- package/package.json +1 -1
package/dist/connect.d.ts
CHANGED
|
@@ -160,14 +160,14 @@ type Permission = 'read' | 'write' | 'delete' | 'admin' | 'share' | 'invite';
|
|
|
160
160
|
/**
|
|
161
161
|
* Role types
|
|
162
162
|
*/
|
|
163
|
-
type Role = 'owner' | 'admin' | 'editor' | 'viewer' | 'member';
|
|
163
|
+
type Role$1 = 'owner' | 'admin' | 'editor' | 'viewer' | 'member';
|
|
164
164
|
/**
|
|
165
165
|
* ACL Entry - grants permissions to a user (identified by public key)
|
|
166
166
|
*/
|
|
167
167
|
interface ACLEntry {
|
|
168
168
|
subject: string;
|
|
169
169
|
permissions: Permission[];
|
|
170
|
-
role?: Role;
|
|
170
|
+
role?: Role$1;
|
|
171
171
|
expiresAt?: number;
|
|
172
172
|
conditions?: {
|
|
173
173
|
ipAllowlist?: string[];
|
|
@@ -194,7 +194,7 @@ interface ACL {
|
|
|
194
194
|
*/
|
|
195
195
|
interface Participant {
|
|
196
196
|
publicKey: string;
|
|
197
|
-
role: Role;
|
|
197
|
+
role: Role$1;
|
|
198
198
|
addedAt: number;
|
|
199
199
|
addedBy: string;
|
|
200
200
|
customPermissions?: Permission[];
|
|
@@ -434,20 +434,51 @@ interface DehydratedKeys {
|
|
|
434
434
|
ecdsaPub: string;
|
|
435
435
|
ecdsaPriv: string;
|
|
436
436
|
}
|
|
437
|
+
/** A member's own ECDH and ECDSA pairs, held outside the store until {@link KeyStore.replaceOwnKeyPair}. */
|
|
438
|
+
interface OwnKeyPair {
|
|
439
|
+
ecdh: KeyPair;
|
|
440
|
+
ecdsa: AuthKeyPair;
|
|
441
|
+
}
|
|
437
442
|
declare class KeyStore {
|
|
438
443
|
private static instance;
|
|
439
444
|
private _keys;
|
|
440
445
|
private _authKeys;
|
|
446
|
+
/** The exact pairs marked vault-backed, so a pair written over them later is never taken for them. */
|
|
447
|
+
private _vaultBacked;
|
|
441
448
|
private constructor();
|
|
442
449
|
static getInstance(): KeyStore;
|
|
443
450
|
get keys(): Map<string, KeyPair>;
|
|
444
451
|
get authKeys(): Map<string, AuthKeyPair>;
|
|
445
452
|
generateOwnKeyPair(id: string): Promise<KeyPair>;
|
|
446
453
|
storeRemotePublicKeys(id: string, ecdhPublicKey: CryptoKey, ecdsaPublicKey: CryptoKey): Promise<void>;
|
|
454
|
+
/** Generate an own pair without touching the store; {@link replaceOwnKeyPair} puts it in place. */
|
|
455
|
+
generateKeyPair(): Promise<OwnKeyPair>;
|
|
447
456
|
hydrateKeyPair(id: string, dehydrated: DehydratedKeys): Promise<void>;
|
|
457
|
+
/** Import a dehydrated pair without touching the store; {@link replaceOwnKeyPair} puts it in place. */
|
|
458
|
+
importKeyPair(dehydrated: DehydratedKeys): Promise<OwnKeyPair>;
|
|
448
459
|
getKeyPair(id: string): KeyPair | null;
|
|
449
460
|
getAuthKeyPair(id: string): AuthKeyPair | null;
|
|
461
|
+
/** Record that the pair `id` holds now came from the seed-wrapped vault (ChatKeyVault). */
|
|
462
|
+
markVaultBacked(id: string): void;
|
|
463
|
+
/**
|
|
464
|
+
* Put `pair` in place as `id`'s own pair and mark it vault-backed, in one
|
|
465
|
+
* synchronous step: whatever `id` held before is dropped, so a pair minted
|
|
466
|
+
* while the vault pair was being fetched or generated is never kept and
|
|
467
|
+
* marked in its place (design §3.3).
|
|
468
|
+
*/
|
|
469
|
+
replaceOwnKeyPair(id: string, pair: OwnKeyPair): void;
|
|
470
|
+
/**
|
|
471
|
+
* Whether `id`'s own pair was loaded from, or persisted to, the vault in this
|
|
472
|
+
* tab, and is still the very pair that was marked. A Space identity must be
|
|
473
|
+
* vault-backed (design §3.3): any other pair, such as one minted by a locked
|
|
474
|
+
* DM subscription, never takes part, even one that lands over the marked pair.
|
|
475
|
+
*/
|
|
476
|
+
isVaultBacked(id: string): boolean;
|
|
477
|
+
/** Drop every key held for `id` (SpecA §6.4 `KeyStore.forget`). */
|
|
478
|
+
forget(id: string): void;
|
|
450
479
|
dehydrateKeyPair(id: string): Promise<DehydratedKeys>;
|
|
480
|
+
/** Dehydrate a pair that is not (yet) in the store, in the same format as {@link dehydrateKeyPair}. */
|
|
481
|
+
exportKeyPair(pair: OwnKeyPair): Promise<DehydratedKeys>;
|
|
451
482
|
getRawEcdsaPublicKey(id: string): Promise<Uint8Array | null>;
|
|
452
483
|
packDehydratedKeys(id: string): Promise<string>;
|
|
453
484
|
hydrateFromPacked(id: string, masterKey: string): Promise<void>;
|
|
@@ -522,7 +553,6 @@ declare class DoubleRatchet {
|
|
|
522
553
|
private sessionType;
|
|
523
554
|
private maxSkip;
|
|
524
555
|
private overlapPeriod;
|
|
525
|
-
private windowSize;
|
|
526
556
|
constructor(senderId: string, recipientId: string, sessionType: 'global' | 'specific', isClient?: boolean);
|
|
527
557
|
private hkdf;
|
|
528
558
|
initializeSession(isClient?: boolean): Promise<void>;
|
|
@@ -885,6 +915,8 @@ interface WSTransportOptions {
|
|
|
885
915
|
* auto-response. Default `"pong"`.
|
|
886
916
|
*/
|
|
887
917
|
pongFrame?: string;
|
|
918
|
+
/** A close with one of these codes is terminal: no reconnect, `DISCONNECTED` carries `{code, reason}`; `resume()` restarts. */
|
|
919
|
+
terminalCloseCodes?: number[];
|
|
888
920
|
}
|
|
889
921
|
declare class WSTransport extends EventCore {
|
|
890
922
|
/**
|
|
@@ -917,11 +949,14 @@ declare class WSTransport extends EventCore {
|
|
|
917
949
|
private pongFrame;
|
|
918
950
|
private heartbeatTimer;
|
|
919
951
|
private pongTimer;
|
|
952
|
+
private readonly terminalCloseCodes;
|
|
920
953
|
constructor(opts: WSTransportOptions);
|
|
921
954
|
/** Open the socket. Resolves when CONNECTED fires. Rejects on first error. */
|
|
922
955
|
connect(): Promise<void>;
|
|
923
956
|
/** Close the socket and stop reconnecting. */
|
|
924
957
|
disconnect(): void;
|
|
958
|
+
/** Restart reconnecting after a terminal close or `disconnect()`; the next URL comes from `urlProvider`. */
|
|
959
|
+
resume(): void;
|
|
925
960
|
/**
|
|
926
961
|
* Send a raw frame. If currently disconnected, the frame is queued and sent
|
|
927
962
|
* after the next successful reconnect (up to `maxQueueSize`).
|
|
@@ -1035,6 +1070,8 @@ declare class BroadcastChannel {
|
|
|
1035
1070
|
connect(): Promise<void>;
|
|
1036
1071
|
/** Close the WebSocket and stop reconnecting. */
|
|
1037
1072
|
disconnect(): void;
|
|
1073
|
+
/** Reconnect after a terminal close (see `terminalCloseCodes`) or `disconnect()`, on a fresh `urlProvider` URL. */
|
|
1074
|
+
resume(): void;
|
|
1038
1075
|
/**
|
|
1039
1076
|
* Send our `keyExchange` frame to the room. Idempotent — only sends on the
|
|
1040
1077
|
* first call. Call this once your higher-level handshake (e.g. sending a
|
|
@@ -1214,6 +1251,68 @@ declare class DoubleRatchetManager {
|
|
|
1214
1251
|
private loadState;
|
|
1215
1252
|
}
|
|
1216
1253
|
|
|
1254
|
+
/**
|
|
1255
|
+
* OPRF (Oblivious PRF) — RFC 9497, ristretto255-SHA512, mode 0x00.
|
|
1256
|
+
*
|
|
1257
|
+
* Used to gate the password/recovery factors of the identity vault
|
|
1258
|
+
* ({@link ./vault}). The wrap key for a password factor is
|
|
1259
|
+
* `HKDF(OPRF(serverKey, scrypt(password)))`, so:
|
|
1260
|
+
* - the client cannot derive the wrap key offline (it needs the server's
|
|
1261
|
+
* secret-keyed evaluation), and
|
|
1262
|
+
* - the server learns nothing — it only ever sees a *blinded* group element,
|
|
1263
|
+
* never the password or the resulting key.
|
|
1264
|
+
*
|
|
1265
|
+
* Net effect: a stolen vault blob is NOT offline-crackable; every guess costs a
|
|
1266
|
+
* round-trip to a rate-limited server endpoint. The same OPRF backs the M2
|
|
1267
|
+
* Google/email factors.
|
|
1268
|
+
*
|
|
1269
|
+
* Thin wrapper over `@noble/curves`' vetted `ristretto255_oprf` so the protocol
|
|
1270
|
+
* (blind / blindEvaluate / finalize) lives in one named place. CLIENT uses
|
|
1271
|
+
* {@link oprfBlind} + {@link oprfFinalize}.
|
|
1272
|
+
*
|
|
1273
|
+
* The `oprfDeriveKey` / `oprfBlindEvaluate` helpers here are the SERVER half,
|
|
1274
|
+
* but note they are used only by this repo's tests. The accelerator does NOT
|
|
1275
|
+
* import them: it has an independent implementation in its own
|
|
1276
|
+
* `src/services/oprf.ts`, also built directly on `@noble/curves`. (An earlier
|
|
1277
|
+
* version of this comment claimed the accelerator's VaultDO imported them; it
|
|
1278
|
+
* does not — it imports `@muhkoo/connect` in exactly two files, both for
|
|
1279
|
+
* Groth16 verification.) Any protocol change therefore has to land in BOTH
|
|
1280
|
+
* repos in lockstep.
|
|
1281
|
+
*
|
|
1282
|
+
* KNOWN WEAKNESS — mode 0x00, tracked as audit finding C4 and NOT fixed here.
|
|
1283
|
+
* This uses RFC 9497 base mode: the server returns a bare evaluation with no
|
|
1284
|
+
* DLEQ proof, and {@link oprfFinalize} verifies nothing. A hostile server can
|
|
1285
|
+
* answer `{evaluated: blinded}` (i.e. k=1); the unblinded point is then just
|
|
1286
|
+
* `H(input)` and the wrap key collapses to a pure function of public strings,
|
|
1287
|
+
* destroying the offline-uncrackability guarantee stated above.
|
|
1288
|
+
*
|
|
1289
|
+
* The fix is `ristretto255_oprf.voprf` (mode 0x01) with the server's public key
|
|
1290
|
+
* pinned as a build-time constant and the proof verified on every finalize.
|
|
1291
|
+
* noble's `voprf` variant is already present and unused. It cannot be done
|
|
1292
|
+
* client-side alone: the wire response (`{evaluated}` / `{evaluated, evaluated2}`)
|
|
1293
|
+
* carries no proof field, and the pinned public keys must correspond to the
|
|
1294
|
+
* accelerator's actual K1/K2 secrets. Changing only this file would break login
|
|
1295
|
+
* against the live server.
|
|
1296
|
+
*/
|
|
1297
|
+
/**
|
|
1298
|
+
* The RFC 9497 mode this client speaks, declared on every evaluation request.
|
|
1299
|
+
*
|
|
1300
|
+
* Declaring it is what lets the server refuse loudly instead of answering in a
|
|
1301
|
+
* mode we did not ask for — the silent substitution that IS audit finding C4.
|
|
1302
|
+
* A server too old to know the field drops it and answers in base mode, which is
|
|
1303
|
+
* what we asked for anyway, so sending it is safe in both directions and imposes
|
|
1304
|
+
* no deploy ordering.
|
|
1305
|
+
*
|
|
1306
|
+
* NOT to be confused with the vault's KEY VERSION, a different axis that also
|
|
1307
|
+
* uses small integers: there, `2` means "per-account key". Here `2` means POPRF.
|
|
1308
|
+
* The server half of these constants is `accelerator/src/services/oprf.ts` and
|
|
1309
|
+
* MUST hold the same numbers.
|
|
1310
|
+
*/
|
|
1311
|
+
declare const OPRF_PROTOCOL_BASE = 0;
|
|
1312
|
+
/** RFC 9497 mode 0x02 — the fix for C4. Reserved; no deployment serves it yet. */
|
|
1313
|
+
declare const OPRF_PROTOCOL_POPRF = 2;
|
|
1314
|
+
type OprfProtocol = typeof OPRF_PROTOCOL_BASE | typeof OPRF_PROTOCOL_POPRF;
|
|
1315
|
+
|
|
1217
1316
|
/**
|
|
1218
1317
|
* HTTP client for the accelerator's `/api/auth/*` endpoints.
|
|
1219
1318
|
*
|
|
@@ -1227,6 +1326,21 @@ declare class DoubleRatchetManager {
|
|
|
1227
1326
|
* (with the worker's message inlined where available).
|
|
1228
1327
|
*/
|
|
1229
1328
|
|
|
1329
|
+
/**
|
|
1330
|
+
* A non-2xx response from `/api/auth/*`, carrying the HTTP status and the
|
|
1331
|
+
* worker's machine-readable `code` alongside its human `error` string.
|
|
1332
|
+
*
|
|
1333
|
+
* Exists because the status used to be discarded: every failure arrived as a
|
|
1334
|
+
* bare `Error` whose only distinguishing feature was its message, so classifying
|
|
1335
|
+
* one meant regex-matching a sentence the server is free to reword. See
|
|
1336
|
+
* `VaultUnavailableError`, which still does exactly that for rate limits.
|
|
1337
|
+
*/
|
|
1338
|
+
declare class AuthHttpError extends Error {
|
|
1339
|
+
readonly label: string;
|
|
1340
|
+
readonly status: number;
|
|
1341
|
+
readonly code: string | undefined;
|
|
1342
|
+
constructor(label: string, status: number, code: string | undefined, detail: string);
|
|
1343
|
+
}
|
|
1230
1344
|
interface ZkRegisterRequest {
|
|
1231
1345
|
username: string;
|
|
1232
1346
|
/** Decimal-string Poseidon commitment. */
|
|
@@ -1306,16 +1420,24 @@ declare class AuthClient {
|
|
|
1306
1420
|
verify(token: string): Promise<{
|
|
1307
1421
|
username: string;
|
|
1308
1422
|
}>;
|
|
1309
|
-
/**
|
|
1310
|
-
|
|
1423
|
+
/**
|
|
1424
|
+
* Blinded OPRF evaluation — the server-gated half of a password/recovery wrap key.
|
|
1425
|
+
*
|
|
1426
|
+
* `oprfProtocol` declares the RFC 9497 mode this client speaks so the server
|
|
1427
|
+
* can refuse rather than answer in a different one. A server too old to know
|
|
1428
|
+
* the field drops it and answers in base mode, so this is safe against any
|
|
1429
|
+
* deployment.
|
|
1430
|
+
*/
|
|
1431
|
+
oprfEvaluate(username: string, blinded: string, oprfProtocol?: OprfProtocol): Promise<{
|
|
1311
1432
|
evaluated: string;
|
|
1312
1433
|
}>;
|
|
1313
1434
|
/**
|
|
1314
|
-
* Gated
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
1435
|
+
* Gated OPRF evaluation (M2 — email/google factors). Requires a live
|
|
1436
|
+
* `verifyToken` from {@link emailOtpVerify}; returns BOTH keys'
|
|
1437
|
+
* evaluations (K1 and K2, both operated by Muhkoo). The recover token is
|
|
1438
|
+
* consumed by this call.
|
|
1317
1439
|
*/
|
|
1318
|
-
oprfEvaluateGated(username: string, blinded: string, factorType: "email" | "google", purpose: "enroll" | "recover", verifyToken: string): Promise<{
|
|
1440
|
+
oprfEvaluateGated(username: string, blinded: string, factorType: "email" | "google", purpose: "enroll" | "recover", verifyToken: string, oprfProtocol?: OprfProtocol): Promise<{
|
|
1319
1441
|
evaluated: string;
|
|
1320
1442
|
evaluated2: string;
|
|
1321
1443
|
}>;
|
|
@@ -2896,6 +3018,11 @@ interface HostedAuthDeps {
|
|
|
2896
3018
|
/** Sleep used by the poll loop. Injectable so tests don't burn wall-clock. */
|
|
2897
3019
|
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
2898
3020
|
}
|
|
3021
|
+
/**
|
|
3022
|
+
* A same-origin path the app may be sent back to after sign-in. It starts with
|
|
3023
|
+
* "/", not "//", and has no backslash or control characters (design §4.4.9).
|
|
3024
|
+
*/
|
|
3025
|
+
declare function isSameOriginPath(p: unknown): p is string;
|
|
2899
3026
|
/** One in-flight pairing attempt. Hold it; pass it back into poll/wait/cancel. */
|
|
2900
3027
|
interface DevicePairingSession {
|
|
2901
3028
|
/** Opaque; carry it back into poll/wait/cancel. Never render it. */
|
|
@@ -3048,13 +3175,15 @@ declare class HostedAuth {
|
|
|
3048
3175
|
* (App side) Begin hosted login: stash a PKCE verifier + state, then send
|
|
3049
3176
|
* the browser to the hosted authorize page. Returns the URL it redirects to
|
|
3050
3177
|
* (also useful for popup mode / tests). `redirectUri` must be registered for
|
|
3051
|
-
* the app in the portal.
|
|
3178
|
+
* the app in the portal. `returnTo`, a same-origin path, rides the stash and
|
|
3179
|
+
* comes back from {@link handleCallback}; anything else is refused here.
|
|
3052
3180
|
*/
|
|
3053
3181
|
login(opts: {
|
|
3054
3182
|
appId: string;
|
|
3055
3183
|
redirectUri: string;
|
|
3056
3184
|
redirect?: boolean;
|
|
3057
3185
|
prompt?: "login";
|
|
3186
|
+
returnTo?: string;
|
|
3058
3187
|
}): Promise<string>;
|
|
3059
3188
|
/**
|
|
3060
3189
|
* (App side) Send the user to centralized account & security management on
|
|
@@ -3071,9 +3200,12 @@ declare class HostedAuth {
|
|
|
3071
3200
|
/**
|
|
3072
3201
|
* (App side) Complete login on the callback URL: verify state, exchange the
|
|
3073
3202
|
* code (+ PKCE verifier) for the session, unseal the seed from the fragment,
|
|
3074
|
-
* establish the session, and scrub the URL. Returns the signed-in user
|
|
3203
|
+
* establish the session, and scrub the URL. Returns the signed-in user, with
|
|
3204
|
+
* the `returnTo` passed to {@link login} when it is still a same-origin path.
|
|
3075
3205
|
*/
|
|
3076
|
-
handleCallback(): Promise<AuthUser
|
|
3206
|
+
handleCallback(): Promise<AuthUser & {
|
|
3207
|
+
returnTo?: string;
|
|
3208
|
+
}>;
|
|
3077
3209
|
/**
|
|
3078
3210
|
* (Hosted-page side) Finish an /authorize request after the user has
|
|
3079
3211
|
* authenticated here: seal the in-memory seed, mint the grant, and return
|
|
@@ -3299,6 +3431,24 @@ declare function formatUserCode(canonical: string): string;
|
|
|
3299
3431
|
declare class VaultUnavailableError extends Error {
|
|
3300
3432
|
constructor(cause?: unknown);
|
|
3301
3433
|
}
|
|
3434
|
+
/**
|
|
3435
|
+
* The vault refused the OPRF protocol this client speaks.
|
|
3436
|
+
*
|
|
3437
|
+
* Distinct from {@link VaultUnavailableError} because the remedy is different and
|
|
3438
|
+
* the two used to be conflated: "unavailable" means retry, and a version mismatch
|
|
3439
|
+
* never resolves by retrying — it resolves by updating the app (or the server).
|
|
3440
|
+
*
|
|
3441
|
+
* Critically, this must NOT be caught into the
|
|
3442
|
+
* `vaultSeed ?? deriveMasterSeedFromPassword(...)` fallback in `login`/`unlock`.
|
|
3443
|
+
* That fallback is correct for an account with no password factor, and wrong for
|
|
3444
|
+
* a refusal: it derives a different seed, and on a legacy-migrated account —
|
|
3445
|
+
* where the fallback seed happens to be the right one — it would let
|
|
3446
|
+
* `migrateLegacyPasswordFactor` rewrite the factor as a side effect of a failure
|
|
3447
|
+
* the user was never shown.
|
|
3448
|
+
*/
|
|
3449
|
+
declare class VaultProtocolMismatchError extends Error {
|
|
3450
|
+
constructor(cause?: unknown);
|
|
3451
|
+
}
|
|
3302
3452
|
/**
|
|
3303
3453
|
* A stored device pairing that the account has since revoked.
|
|
3304
3454
|
*
|
|
@@ -3428,15 +3578,16 @@ declare class ZkAuth {
|
|
|
3428
3578
|
recoverWithPhrase(username: string, mnemonic: string, opts?: LoginOptions): Promise<AuthUser>;
|
|
3429
3579
|
/**
|
|
3430
3580
|
* Add an **email** as a recovery + login factor (M2 verification-gated:
|
|
3431
|
-
* the server only releases the
|
|
3581
|
+
* the server only releases the gated OPRF eval after the address is
|
|
3432
3582
|
* OTP-verified). Sends the code, then returns a `confirm` continuation:
|
|
3433
3583
|
*
|
|
3434
3584
|
* const { confirm } = await client.auth.zk.enrollEmailFactor("me@x.com");
|
|
3435
3585
|
* await confirm(codeFromInbox);
|
|
3436
3586
|
*
|
|
3437
3587
|
* Requires being signed in with the seed held (like the other enrolls).
|
|
3438
|
-
* Custody note:
|
|
3439
|
-
*
|
|
3588
|
+
* Custody note: Muhkoo holds the keys behind gated factors, so it can
|
|
3589
|
+
* recover the seed of any account with an email or Google factor. The
|
|
3590
|
+
* hosted sign-in UI discloses this; a custom enroll UI must too.
|
|
3440
3591
|
*/
|
|
3441
3592
|
enrollEmailFactor(email: string): Promise<{
|
|
3442
3593
|
email: string;
|
|
@@ -3445,7 +3596,7 @@ declare class ZkAuth {
|
|
|
3445
3596
|
/**
|
|
3446
3597
|
* Recover an account via its enrolled **email** factor. Sends a code to the
|
|
3447
3598
|
* enrolled address (the response never reveals whether one exists), then
|
|
3448
|
-
* `confirm(code)` completes: gated read +
|
|
3599
|
+
* `confirm(code)` completes: gated read + gated OPRF eval → unwrap seed →
|
|
3449
3600
|
* normal ZK login. After this, call {@link changePassword} to set a new
|
|
3450
3601
|
* password.
|
|
3451
3602
|
*
|
|
@@ -3458,11 +3609,14 @@ declare class ZkAuth {
|
|
|
3458
3609
|
/**
|
|
3459
3610
|
* Link a Google account as a recovery + login factor. Requires being signed
|
|
3460
3611
|
* in with the seed held. `idToken` is a fresh Google ID token (GIS).
|
|
3612
|
+
* Custody note: as with {@link enrollEmailFactor}, Muhkoo holds the keys
|
|
3613
|
+
* behind this factor and can recover the seed of an account that has it.
|
|
3614
|
+
* The hosted sign-in UI discloses this; a custom enroll UI must too.
|
|
3461
3615
|
*/
|
|
3462
3616
|
enrollGoogleFactor(idToken: string): Promise<void>;
|
|
3463
3617
|
/**
|
|
3464
3618
|
* Sign in with Google — verify the ID token, unlock the seed via the gated
|
|
3465
|
-
*
|
|
3619
|
+
* OPRF eval, and establish a session. Same machinery serves "Sign in
|
|
3466
3620
|
* with Google" and "recover with Google"; the account must have linked the
|
|
3467
3621
|
* factor first.
|
|
3468
3622
|
*/
|
|
@@ -3471,7 +3625,8 @@ declare class ZkAuth {
|
|
|
3471
3625
|
* Register a brand-new, **passwordless** account whose only factor is Google.
|
|
3472
3626
|
* A random master seed is generated and wrapped under the Google-gated key;
|
|
3473
3627
|
* the user can add a password / passkey / phrase later. The ZK registration
|
|
3474
|
-
* is identical to a password signup.
|
|
3628
|
+
* is identical to a password signup. Custody note: see
|
|
3629
|
+
* {@link enrollGoogleFactor} — Muhkoo can recover this account's seed.
|
|
3475
3630
|
*/
|
|
3476
3631
|
registerWithGoogle(username: string, idToken: string): Promise<AuthUser>;
|
|
3477
3632
|
/**
|
|
@@ -3726,9 +3881,10 @@ interface HttpClientOptions {
|
|
|
3726
3881
|
* token) or `false` if it couldn't (the `401` propagates as an
|
|
3727
3882
|
* {@link HttpError}). Wired by {@link Client} to `auth.zk.recover()`.
|
|
3728
3883
|
*
|
|
3729
|
-
* Only consulted by the JSON helpers (`get`/`post`/`del`)
|
|
3730
|
-
* {@link
|
|
3731
|
-
* power recovery itself
|
|
3884
|
+
* Only consulted by the JSON helpers (`get`/`post`/`del`) and
|
|
3885
|
+
* {@link HttpClient.fetchWithRecovery}; the raw {@link fetch} passes 401s
|
|
3886
|
+
* straight through so the auth wire calls that power recovery itself
|
|
3887
|
+
* can't recurse.
|
|
3732
3888
|
*/
|
|
3733
3889
|
onUnauthorized?: () => Promise<boolean>;
|
|
3734
3890
|
}
|
|
@@ -3755,6 +3911,15 @@ declare class HttpClient {
|
|
|
3755
3911
|
* up credentials without knowing about them.
|
|
3756
3912
|
*/
|
|
3757
3913
|
readonly fetch: typeof fetch;
|
|
3914
|
+
/**
|
|
3915
|
+
* {@link fetch} with the JSON helpers' stale-session recovery: a 401 on a
|
|
3916
|
+
* request made while a session token is held runs `onUnauthorized` once
|
|
3917
|
+
* and, if that recovered, replays the request once with the fresh token.
|
|
3918
|
+
* The final response is returned whatever its status, never thrown. The
|
|
3919
|
+
* replay re-sends `init` as given, so its body must be re-sendable (a
|
|
3920
|
+
* string or bytes, not a stream).
|
|
3921
|
+
*/
|
|
3922
|
+
readonly fetchWithRecovery: typeof fetch;
|
|
3758
3923
|
/** True when `input` resolves to {@link baseOrigin} (relative URLs always do). */
|
|
3759
3924
|
private isSameOrigin;
|
|
3760
3925
|
get<T = unknown>(path: string): Promise<T>;
|
|
@@ -3919,8 +4084,9 @@ interface OfflineStore {
|
|
|
3919
4084
|
* mutation by re-invoking the real namespace method (with an `isReplay` flag so
|
|
3920
4085
|
* it doesn't re-enqueue). `drain()` replays entries in HLC order:
|
|
3921
4086
|
* - success → remove the entry;
|
|
3922
|
-
* - permanent failure (4xx
|
|
3923
|
-
*
|
|
4087
|
+
* - permanent failure (4xx, or a Space whose member list refuses this user)
|
|
4088
|
+
* → drop it and surface a `SYNC_ERROR` (a write the server will never
|
|
4089
|
+
* accept must not wedge the queue forever);
|
|
3924
4090
|
* - transient/network failure → stop draining and leave the rest for the next
|
|
3925
4091
|
* reconnect.
|
|
3926
4092
|
*
|
|
@@ -4500,6 +4666,140 @@ declare class StorageNamespace {
|
|
|
4500
4666
|
private space;
|
|
4501
4667
|
}
|
|
4502
4668
|
|
|
4669
|
+
/**
|
|
4670
|
+
* Wire types of the e2e-channels trust layer (spec 2026-09-27 §3.2, §3.5,
|
|
4671
|
+
* §4.2.4, §5.3.1). These shapes are signed and stored: never rename a field.
|
|
4672
|
+
*
|
|
4673
|
+
* Every string field holds the exact string that was transmitted; the
|
|
4674
|
+
* canonical encoders in `canonical.ts` sign those strings as they are.
|
|
4675
|
+
*
|
|
4676
|
+
* Ported from the accelerator's `src/trust/types.ts` (the server-only app
|
|
4677
|
+
* roster types are left out); the shared shapes must stay identical.
|
|
4678
|
+
*/
|
|
4679
|
+
type Visibility = "private" | "public";
|
|
4680
|
+
type HistoryPolicy$1 = "static" | "rotate";
|
|
4681
|
+
type MemberRole = "viewer" | "editor";
|
|
4682
|
+
type SvcMode = "addressed" | "all";
|
|
4683
|
+
type MlogOp = "genesis" | "add" | "role" | "remove" | "leave" | "join" | "link" | "unlink" | "redeem" | "svc" | "retire";
|
|
4684
|
+
/** `legacyCommits` is `{}` for a new Space; values are b64url of 32 bytes, keys canonical decimal epochs. */
|
|
4685
|
+
interface GenesisBody {
|
|
4686
|
+
visibility: Visibility;
|
|
4687
|
+
policy: HistoryPolicy$1;
|
|
4688
|
+
appId: string;
|
|
4689
|
+
baseEpoch: number;
|
|
4690
|
+
legacyCommits: Record<string, string>;
|
|
4691
|
+
}
|
|
4692
|
+
interface AddBody {
|
|
4693
|
+
member: string;
|
|
4694
|
+
memberIk: string;
|
|
4695
|
+
role: MemberRole;
|
|
4696
|
+
}
|
|
4697
|
+
interface RoleBody {
|
|
4698
|
+
member: string;
|
|
4699
|
+
role: MemberRole;
|
|
4700
|
+
}
|
|
4701
|
+
interface RemoveBody {
|
|
4702
|
+
member: string;
|
|
4703
|
+
}
|
|
4704
|
+
type LeaveBody = Record<string, never>;
|
|
4705
|
+
/** `via` is a linkId, or "" for no link. */
|
|
4706
|
+
interface JoinBody {
|
|
4707
|
+
via: string;
|
|
4708
|
+
}
|
|
4709
|
+
interface LinkBody {
|
|
4710
|
+
linkId: string;
|
|
4711
|
+
role: MemberRole;
|
|
4712
|
+
standing: boolean;
|
|
4713
|
+
expiresAt: number;
|
|
4714
|
+
maxUses: number;
|
|
4715
|
+
linkEcdhPub: string;
|
|
4716
|
+
linkSignPub: string;
|
|
4717
|
+
keyCommits: Record<string, string>;
|
|
4718
|
+
}
|
|
4719
|
+
interface UnlinkBody {
|
|
4720
|
+
linkId: string;
|
|
4721
|
+
}
|
|
4722
|
+
interface RedeemBody {
|
|
4723
|
+
linkId: string;
|
|
4724
|
+
}
|
|
4725
|
+
interface SvcBody {
|
|
4726
|
+
member: string;
|
|
4727
|
+
action: "add" | "remove";
|
|
4728
|
+
ecdhPub: string;
|
|
4729
|
+
ecdsaPub: string;
|
|
4730
|
+
mode: SvcMode;
|
|
4731
|
+
rosterSeq: number;
|
|
4732
|
+
}
|
|
4733
|
+
interface RetireBody {
|
|
4734
|
+
member: string;
|
|
4735
|
+
reason: string;
|
|
4736
|
+
}
|
|
4737
|
+
interface MlogBodies {
|
|
4738
|
+
genesis: GenesisBody;
|
|
4739
|
+
add: AddBody;
|
|
4740
|
+
role: RoleBody;
|
|
4741
|
+
remove: RemoveBody;
|
|
4742
|
+
leave: LeaveBody;
|
|
4743
|
+
join: JoinBody;
|
|
4744
|
+
link: LinkBody;
|
|
4745
|
+
unlink: UnlinkBody;
|
|
4746
|
+
redeem: RedeemBody;
|
|
4747
|
+
svc: SvcBody;
|
|
4748
|
+
retire: RetireBody;
|
|
4749
|
+
}
|
|
4750
|
+
interface EntryBase {
|
|
4751
|
+
v: 1;
|
|
4752
|
+
spaceId: string;
|
|
4753
|
+
/** 0 = genesis; −1 only in an enable intent (§5.3.3), never stored. */
|
|
4754
|
+
seq: number;
|
|
4755
|
+
/** hash of entry seq−1; "" at genesis and in an intent. */
|
|
4756
|
+
prev: string;
|
|
4757
|
+
/** Signer member id; "" in a server-written `retire`. */
|
|
4758
|
+
by: string;
|
|
4759
|
+
/** Signer IK (b64 or hex raw P-256), exactly as sent; "" in a `retire`. */
|
|
4760
|
+
ik: string;
|
|
4761
|
+
/** Signer clock, informational. */
|
|
4762
|
+
at: number;
|
|
4763
|
+
/** b64 P1363 ECDSA-P256-SHA256 by `ik` over entryBytes; "" only in a `retire`. */
|
|
4764
|
+
sig: string;
|
|
4765
|
+
/** Link-sign signature over entryBytes (`link`, `redeem`, `join` with `via`). */
|
|
4766
|
+
lsig?: string;
|
|
4767
|
+
}
|
|
4768
|
+
type MembershipEntry<O extends MlogOp = MlogOp> = {
|
|
4769
|
+
[K in O]: EntryBase & {
|
|
4770
|
+
op: K;
|
|
4771
|
+
body: MlogBodies[K];
|
|
4772
|
+
};
|
|
4773
|
+
}[O];
|
|
4774
|
+
type UnsignedEntry<O extends MlogOp = MlogOp> = {
|
|
4775
|
+
[K in O]: Omit<MembershipEntry<K>, "sig" | "lsig">;
|
|
4776
|
+
}[O];
|
|
4777
|
+
interface ChatKeyCert {
|
|
4778
|
+
v: 1;
|
|
4779
|
+
ik: string;
|
|
4780
|
+
ecdh: string;
|
|
4781
|
+
ecdsa: string;
|
|
4782
|
+
sig: string;
|
|
4783
|
+
}
|
|
4784
|
+
interface EpochCommit {
|
|
4785
|
+
v: 1;
|
|
4786
|
+
spaceId: string;
|
|
4787
|
+
epoch: number;
|
|
4788
|
+
prevKeyed: number;
|
|
4789
|
+
keyCommit: string;
|
|
4790
|
+
headSeq: number;
|
|
4791
|
+
headHash: string;
|
|
4792
|
+
sole: boolean;
|
|
4793
|
+
by: string;
|
|
4794
|
+
ik: string;
|
|
4795
|
+
at: number;
|
|
4796
|
+
sig: string;
|
|
4797
|
+
}
|
|
4798
|
+
interface MlogHead {
|
|
4799
|
+
seq: number;
|
|
4800
|
+
hash: string;
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4503
4803
|
/**
|
|
4504
4804
|
* Shared types for the Space fan-out group-encryption layer.
|
|
4505
4805
|
*
|
|
@@ -4509,6 +4809,7 @@ declare class StorageNamespace {
|
|
|
4509
4809
|
* which is what lets the server persist + replay messages as history while
|
|
4510
4810
|
* staying blind to their contents.
|
|
4511
4811
|
*/
|
|
4812
|
+
|
|
4512
4813
|
/**
|
|
4513
4814
|
* How a space treats history across membership changes.
|
|
4514
4815
|
* - `static`: one group key for all time (epoch 0). New members can read the
|
|
@@ -4556,6 +4857,8 @@ interface JoinRequest {
|
|
|
4556
4857
|
/** Epoch the newcomer wants a key for (defaults to current). */
|
|
4557
4858
|
desiredEpoch?: number;
|
|
4558
4859
|
ts?: number;
|
|
4860
|
+
/** The account IK's certificate over the two identity strings above (design §3.2). Optional: a join never requires one. */
|
|
4861
|
+
keyCert?: ChatKeyCert;
|
|
4559
4862
|
}
|
|
4560
4863
|
/** A space member's published identity keys (the keyring directory). */
|
|
4561
4864
|
interface RosterMember {
|
|
@@ -4710,6 +5013,18 @@ interface KeyringTransport {
|
|
|
4710
5013
|
/** Add `username` to a private channel's membership allowlist. */
|
|
4711
5014
|
invite(username: string): Promise<void>;
|
|
4712
5015
|
fetchMetadata(): Promise<SpaceMetadata | null>;
|
|
5016
|
+
/** GET /keyring/log (design §3.6): entries with seq > since, ascending, ≤ limit; head null = no log. */
|
|
5017
|
+
fetchLog?(since?: number, limit?: number): Promise<{
|
|
5018
|
+
entries: MembershipEntry[];
|
|
5019
|
+
head: MlogHead | null;
|
|
5020
|
+
}>;
|
|
5021
|
+
/** POST /keyring/log {entry, ec?}. */
|
|
5022
|
+
postLogEntry?(entry: MembershipEntry, ec?: EpochCommit): Promise<{
|
|
5023
|
+
ok: true;
|
|
5024
|
+
head: MlogHead;
|
|
5025
|
+
}>;
|
|
5026
|
+
/** Status of GET /keyring/state, or "network" when the request fails. */
|
|
5027
|
+
probeState?(): Promise<number | "network">;
|
|
4713
5028
|
}
|
|
4714
5029
|
/** PersonalSpace-backed cache of this member's group keys (epoch → base64 key). */
|
|
4715
5030
|
interface SpaceKeyCache {
|
|
@@ -4729,6 +5044,10 @@ interface SpaceKeyringDeps {
|
|
|
4729
5044
|
transport: KeyringTransport;
|
|
4730
5045
|
cache?: SpaceKeyCache;
|
|
4731
5046
|
historyPolicy?: HistoryPolicy;
|
|
5047
|
+
/** Mints the IK certificate for this member's chat keys (design §3.2). Absent, or resolving undefined, means none. */
|
|
5048
|
+
keyCert?: () => Promise<ChatKeyCert | undefined>;
|
|
5049
|
+
/** Whether a key may be wrapped to `memberId`; absent means anyone. On a signed Space: log members and the keeper only. */
|
|
5050
|
+
canWrapTo?: (memberId: string) => boolean;
|
|
4732
5051
|
}
|
|
4733
5052
|
declare class SpaceKeyring implements EpochKeyProvider {
|
|
4734
5053
|
private readonly deps;
|
|
@@ -4745,7 +5064,11 @@ declare class SpaceKeyring implements EpochKeyProvider {
|
|
|
4745
5064
|
* this window won't re-fetch (the roster hasn't changed that fast); genuine
|
|
4746
5065
|
* membership changes use `{ force: true }` (join requests / history loads). */
|
|
4747
5066
|
private static readonly DIR_COOLDOWN_MS;
|
|
5067
|
+
private readonly keyListeners;
|
|
4748
5068
|
constructor(deps: SpaceKeyringDeps);
|
|
5069
|
+
/** Notified whenever this keyring gains a key (bootstrap, cache hit, pull). Returns an unsubscribe. */
|
|
5070
|
+
onKeysChanged(h: () => void): () => void;
|
|
5071
|
+
private notifyKeys;
|
|
4749
5072
|
/** The cached ECDSA verify key for a member, if known. */
|
|
4750
5073
|
ecdsaKeyFor(memberId: string): CryptoKey | undefined;
|
|
4751
5074
|
/**
|
|
@@ -4763,6 +5086,10 @@ declare class SpaceKeyring implements EpochKeyProvider {
|
|
|
4763
5086
|
currentEpoch(): number;
|
|
4764
5087
|
keyForEpoch(epoch: number): Uint8Array | undefined;
|
|
4765
5088
|
hasAnyKey(): boolean;
|
|
5089
|
+
/** The raw 32-byte group key for `epoch`, for its key commitment (design §4.2.4). */
|
|
5090
|
+
rawKey(epoch: number): Uint8Array | undefined;
|
|
5091
|
+
/** Whether a key may be wrapped to `memberId` (design §4.5.1: on a signed Space, log members and the keeper only). */
|
|
5092
|
+
canWrapTo(memberId: string): boolean;
|
|
4766
5093
|
/** Hydrate keys from the PersonalSpace cache, if available. Best-effort —
|
|
4767
5094
|
* a cache miss/failure (e.g. not unlocked) must not block joining. */
|
|
4768
5095
|
loadFromCache(): Promise<boolean>;
|
|
@@ -4772,7 +5099,8 @@ declare class SpaceKeyring implements EpochKeyProvider {
|
|
|
4772
5099
|
/**
|
|
4773
5100
|
* Post a join request so an online key-holder can wrap a key for us. Also
|
|
4774
5101
|
* publishes our identity keys in the member directory (so others can verify
|
|
4775
|
-
* our message signatures, and the keeper can wrap to us).
|
|
5102
|
+
* our message signatures, and the keeper can wrap to us). With a `keyCert`
|
|
5103
|
+
* dep it also carries the IK certificate over exactly those two strings.
|
|
4776
5104
|
*/
|
|
4777
5105
|
requestKey(desiredEpoch?: number): Promise<void>;
|
|
4778
5106
|
/**
|
|
@@ -4810,6 +5138,124 @@ declare class SpaceKeyring implements EpochKeyProvider {
|
|
|
4810
5138
|
invite(username: string): Promise<void>;
|
|
4811
5139
|
}
|
|
4812
5140
|
|
|
5141
|
+
type Role = "owner" | "editor" | "viewer";
|
|
5142
|
+
interface LogMember {
|
|
5143
|
+
id: string;
|
|
5144
|
+
ik: string;
|
|
5145
|
+
role: Role;
|
|
5146
|
+
since: number;
|
|
5147
|
+
}
|
|
5148
|
+
|
|
5149
|
+
/** What LogSync needs from core (kept structural so spaces/ never imports core/). */
|
|
5150
|
+
interface LogSyncDeps {
|
|
5151
|
+
spaceId: string;
|
|
5152
|
+
me: () => string;
|
|
5153
|
+
appId: () => Promise<string>;
|
|
5154
|
+
commitment: () => string | null;
|
|
5155
|
+
transport: {
|
|
5156
|
+
fetchLog(since?: number, limit?: number): Promise<{
|
|
5157
|
+
entries: MembershipEntry[];
|
|
5158
|
+
head: MlogHead | null;
|
|
5159
|
+
}>;
|
|
5160
|
+
postLogEntry(entry: MembershipEntry, ec?: EpochCommit): Promise<{
|
|
5161
|
+
head: MlogHead;
|
|
5162
|
+
}>;
|
|
5163
|
+
};
|
|
5164
|
+
signer: {
|
|
5165
|
+
sign(b: Uint8Array): Promise<string>;
|
|
5166
|
+
identityKeyRaw(): Promise<string>;
|
|
5167
|
+
};
|
|
5168
|
+
directory: {
|
|
5169
|
+
lookup(username: string): Promise<string>;
|
|
5170
|
+
};
|
|
5171
|
+
pins: {
|
|
5172
|
+
get(doc: "identity" | "spaces", key: string): Promise<string | undefined>;
|
|
5173
|
+
pin(doc: "identity" | "spaces", entries: Record<string, string>): Promise<{
|
|
5174
|
+
conflicts: string[];
|
|
5175
|
+
}>;
|
|
5176
|
+
};
|
|
5177
|
+
store: () => Promise<{
|
|
5178
|
+
get<T>(k: string): Promise<T | undefined>;
|
|
5179
|
+
put(k: string, v: unknown): Promise<void>;
|
|
5180
|
+
}>;
|
|
5181
|
+
}
|
|
5182
|
+
interface MemberView {
|
|
5183
|
+
id: string;
|
|
5184
|
+
role: Role;
|
|
5185
|
+
ik: string;
|
|
5186
|
+
fingerprint: string;
|
|
5187
|
+
identityChanged: boolean;
|
|
5188
|
+
}
|
|
5189
|
+
type IntegrityState = {
|
|
5190
|
+
broken: false;
|
|
5191
|
+
} | {
|
|
5192
|
+
broken: true;
|
|
5193
|
+
reason: string;
|
|
5194
|
+
};
|
|
5195
|
+
/**
|
|
5196
|
+
* One Space's membership log, as this client has verified it (design §3.6–§3.7).
|
|
5197
|
+
* Syncs the whole log from −1 on each call (slice logs are tiny, so no entry
|
|
5198
|
+
* cache), checks the head against the high-water mark kept in `muhkoo.trust`,
|
|
5199
|
+
* checks the genesis against the pins (TOFU against the directory the first
|
|
5200
|
+
* time), and pins every IK a verified `add` names (design §3.4).
|
|
5201
|
+
*/
|
|
5202
|
+
declare class LogSync {
|
|
5203
|
+
private readonly deps;
|
|
5204
|
+
private log;
|
|
5205
|
+
private enabled;
|
|
5206
|
+
private integrityState;
|
|
5207
|
+
private changed;
|
|
5208
|
+
private fps;
|
|
5209
|
+
private listeners;
|
|
5210
|
+
private timer;
|
|
5211
|
+
private running;
|
|
5212
|
+
private followUp;
|
|
5213
|
+
constructor(deps: LogSyncDeps);
|
|
5214
|
+
get logEnabled(): boolean;
|
|
5215
|
+
get integrity(): IntegrityState;
|
|
5216
|
+
get signer(): LogSyncDeps["signer"];
|
|
5217
|
+
get directory(): LogSyncDeps["directory"];
|
|
5218
|
+
member(id: string): LogMember | undefined;
|
|
5219
|
+
isMember(id: string): boolean;
|
|
5220
|
+
members(): MemberView[];
|
|
5221
|
+
onChange(h: () => void): () => void;
|
|
5222
|
+
/**
|
|
5223
|
+
* An `mlog` frame marks the log stale; sync once a burst of frames settles.
|
|
5224
|
+
* A failure is logged with its code and reported through `onChange`, never
|
|
5225
|
+
* thrown; integrity is left as it was, and the next frame or open retries.
|
|
5226
|
+
*/
|
|
5227
|
+
scheduleSync(): void;
|
|
5228
|
+
private hwKey;
|
|
5229
|
+
/** Whether this client holds a high-water mark for this Space, i.e. has verified its log before (design §3.7 item 3). */
|
|
5230
|
+
hasHighWater(): Promise<boolean>;
|
|
5231
|
+
/**
|
|
5232
|
+
* Fetch and verify the whole log. Single-flight: a call made while a sync
|
|
5233
|
+
* runs does not start a second one alongside it. It waits for exactly one
|
|
5234
|
+
* follow-up run, which starts once the running one ends, so it sees whatever
|
|
5235
|
+
* prompted the call; every call made in the meantime shares that follow-up.
|
|
5236
|
+
*/
|
|
5237
|
+
sync(): Promise<void>;
|
|
5238
|
+
private startSync;
|
|
5239
|
+
private runSync;
|
|
5240
|
+
/** Genesis trust (design §3.7 item 2): the pinned genesis hash, else TOFU with the owner's IK checked against the directory. */
|
|
5241
|
+
private checkGenesis;
|
|
5242
|
+
/** "s:" + b64url(SHA-256(utf8(spaceId))), the spaces-v1 key; computed exactly as PinStore.spacePinKey, which this layer may not import. */
|
|
5243
|
+
private spacePinKey;
|
|
5244
|
+
/** Pin every IK a verified add names (design §3.4 row 2); a different pinned IK flags the member (identity-changed). */
|
|
5245
|
+
private pinMembers;
|
|
5246
|
+
/**
|
|
5247
|
+
* Sign and append one entry. On `409 Log moved`: re-sync, re-sign against the
|
|
5248
|
+
* new head and retry, at most 3 times with jitter (design §9.2 I9). Nothing is
|
|
5249
|
+
* signed while integrity is broken, including after a re-sync that breaks it.
|
|
5250
|
+
* Any other refusal → SpaceTrustError("log-refused") carrying the server's code and reason.
|
|
5251
|
+
* Once the server has stored the entry, its `{seq}` is returned even if the sync confirming it fails.
|
|
5252
|
+
*/
|
|
5253
|
+
append(op: MembershipEntry["op"], body: Record<string, unknown>, ec?: (hash: string, entry: UnsignedEntry) => Promise<EpochCommit>): Promise<{
|
|
5254
|
+
seq: number;
|
|
5255
|
+
}>;
|
|
5256
|
+
private emit;
|
|
5257
|
+
}
|
|
5258
|
+
|
|
4813
5259
|
/**
|
|
4814
5260
|
* `Space` — a client handle for one shared space.
|
|
4815
5261
|
*
|
|
@@ -4927,6 +5373,11 @@ interface EphemeralEvent {
|
|
|
4927
5373
|
interface MessageDeletedEvent {
|
|
4928
5374
|
handle: number;
|
|
4929
5375
|
}
|
|
5376
|
+
type AdmissionState = "locked" | "requesting" | "keyed" | "not-member" | "identity-mismatch" | "integrity";
|
|
5377
|
+
/** WebSocket close code for a refused private-Space socket (design §3.6; accelerator NOT_MEMBER_CLOSE_CODE). */
|
|
5378
|
+
declare const NOT_MEMBER_CLOSE_CODE = 4403;
|
|
5379
|
+
/** At most one resume after a 4403 per Space per this interval (design §6.7). */
|
|
5380
|
+
declare const NOT_MEMBER_RECHECK_MS = 60000;
|
|
4930
5381
|
interface SpaceDeps {
|
|
4931
5382
|
/** Transport room id. For fan-out spaces this is the encoded space pubkey. */
|
|
4932
5383
|
name: string;
|
|
@@ -4961,6 +5412,21 @@ interface SpaceDeps {
|
|
|
4961
5412
|
deferShardUpload?: (hash: string) => Promise<void>;
|
|
4962
5413
|
/** Offline message cache + send queue. Undefined ⇒ no offline behavior. */
|
|
4963
5414
|
offline?: SpaceOfflineAdapter;
|
|
5415
|
+
/** Locked session (design §3.3): no keyring, never connects; the app re-opens after sign-in. */
|
|
5416
|
+
locked?: boolean;
|
|
5417
|
+
/** The Space's membership log (a signed private Space), when this client has trust wired. */
|
|
5418
|
+
logSync?: LogSync;
|
|
5419
|
+
/** Visibility the Space was created with; only a private Space gets a genesis (design §3.8). */
|
|
5420
|
+
visibility?: "private" | "public";
|
|
5421
|
+
/** This app's id, which a genesis names (design §3.5). */
|
|
5422
|
+
appId?: () => Promise<string>;
|
|
5423
|
+
/**
|
|
5424
|
+
* Status of GET /keyring/state after the client's silent 401 recovery, or "network": asked after a
|
|
5425
|
+
* 4403 close before believing it (design §6.9).
|
|
5426
|
+
*/
|
|
5427
|
+
probeState?: () => Promise<number | "network">;
|
|
5428
|
+
/** True while the session holds a token, so a reconnect must never go ticketless. */
|
|
5429
|
+
hasSession?: () => boolean;
|
|
4964
5430
|
}
|
|
4965
5431
|
/** The subset of {@link BroadcastChannel} a {@link Space} drives. */
|
|
4966
5432
|
interface SpaceChannelLike {
|
|
@@ -4973,13 +5439,22 @@ interface SpaceChannelLike {
|
|
|
4973
5439
|
sendRaw(frame: unknown): void;
|
|
4974
5440
|
peers?(): string[];
|
|
4975
5441
|
isConnected?(): boolean;
|
|
5442
|
+
/** Reconnect after a terminal close, on a fresh URL. */
|
|
5443
|
+
resume?(): void;
|
|
4976
5444
|
}
|
|
4977
5445
|
declare class Space {
|
|
4978
5446
|
private readonly deps;
|
|
4979
5447
|
readonly name: string;
|
|
4980
5448
|
readonly historyPolicy: HistoryPolicy;
|
|
4981
5449
|
private channel;
|
|
4982
|
-
|
|
5450
|
+
/**
|
|
5451
|
+
* The log turned this user away and closed `channel` (design §3.7). Its
|
|
5452
|
+
* single-use ticket is spent, so it is never dialled again: the next
|
|
5453
|
+
* `connect()` checks the log and opens a new channel on a fresh ticket.
|
|
5454
|
+
*/
|
|
5455
|
+
private channelSpent;
|
|
5456
|
+
/** Every raw channel listener (`on`), attached to each channel this Space opens. */
|
|
5457
|
+
private readonly listeners;
|
|
4983
5458
|
private readonly cipher;
|
|
4984
5459
|
/** Per-instance event bus for fan-out events (isolated across spaces). */
|
|
4985
5460
|
private readonly events;
|
|
@@ -4987,12 +5462,52 @@ declare class Space {
|
|
|
4987
5462
|
private catchUpUnsub;
|
|
4988
5463
|
/** Optional P2P swarm for this Space (block exchange among members). */
|
|
4989
5464
|
private peerNet;
|
|
5465
|
+
private _admission;
|
|
5466
|
+
/** Whether admission follows the log's changes yet (subscribed once, on the first open the log admits). */
|
|
5467
|
+
private admissionWired;
|
|
5468
|
+
/** When the last resume after a 4403 ran (design §6.7: at most one per {@link NOT_MEMBER_RECHECK_MS}). */
|
|
5469
|
+
private lastResumeAt;
|
|
5470
|
+
private resumeTimer;
|
|
5471
|
+
/** Bumped by `disconnect()`: a /keyring/state answer that arrives after it is dropped. */
|
|
5472
|
+
private teardowns;
|
|
4990
5473
|
constructor(deps: SpaceDeps);
|
|
4991
5474
|
/** The encoded space id (same as the transport name for fan-out spaces). */
|
|
4992
5475
|
get id(): string;
|
|
4993
5476
|
/** The keyring backing this space's group keys, if any. */
|
|
4994
5477
|
get keyring(): SpaceKeyring | undefined;
|
|
4995
|
-
/**
|
|
5478
|
+
/** Where this member stands in this Space (design §4.5.12, as the slice ships it). */
|
|
5479
|
+
get admission(): AdmissionState;
|
|
5480
|
+
/** Subscribe to admission changes. Returns an unsubscribe. */
|
|
5481
|
+
onAdmission(h: (s: AdmissionState) => void): () => void;
|
|
5482
|
+
protected setAdmission(s: AdmissionState): void;
|
|
5483
|
+
/** The log turned this user away; nothing reconnects until it admits them again. */
|
|
5484
|
+
private denied;
|
|
5485
|
+
/**
|
|
5486
|
+
* Sync the membership log and derive admission from it, before any ticket
|
|
5487
|
+
* (design §3.4, §3.7, §4.5.12). A 403 from the log route is not-member and a
|
|
5488
|
+
* 401 is locked. Any other failure is fatal only where this client has
|
|
5489
|
+
* verified the log before (it holds a high-water mark). Otherwise the Space
|
|
5490
|
+
* opens as unlogged this time, and the next open or `mlog` frame syncs again.
|
|
5491
|
+
*/
|
|
5492
|
+
private admitByLog;
|
|
5493
|
+
/** Re-derive admission from the verified log (design §3.4 rows, §3.7, §4.5.12). */
|
|
5494
|
+
private evaluateAdmission;
|
|
5495
|
+
/**
|
|
5496
|
+
* A 4403 alone never shows not-member: ask /keyring/state first (design §6.9, AV12).
|
|
5497
|
+
* 403 is not-member and 401, which `probeState` returns only once a silent re-auth has failed, is
|
|
5498
|
+
* locked; either closes the channel for good, like a log refusal. Any
|
|
5499
|
+
* other answer (404 while step I has no state route, 2xx, 5xx, a network error) resumes, at most
|
|
5500
|
+
* once per {@link NOT_MEMBER_RECHECK_MS}.
|
|
5501
|
+
*/
|
|
5502
|
+
private onNotMemberClose;
|
|
5503
|
+
/** Close the socket of a user the log turned away, and never dial that channel again (design §3.7). */
|
|
5504
|
+
private dropChannel;
|
|
5505
|
+
/**
|
|
5506
|
+
* Open the space websocket (resolves the ticket + identity lazily). On a
|
|
5507
|
+
* Space with a membership log, the log is checked first: a user it does not
|
|
5508
|
+
* list, or lists under another identity key, is refused with
|
|
5509
|
+
* {@link AdmissionDeniedError} before any ticket is fetched (design §4.5.12).
|
|
5510
|
+
*/
|
|
4996
5511
|
connect(): Promise<void>;
|
|
4997
5512
|
disconnect(): void;
|
|
4998
5513
|
/** Attach a P2P swarm so this Space's file reads/writes can use peers. */
|
|
@@ -5008,7 +5523,7 @@ declare class Space {
|
|
|
5008
5523
|
gossipToPeers(data: Uint8Array): void;
|
|
5009
5524
|
/** Subscribe to peer gossip ({@link gossipToPeers}). Returns an unsubscribe fn. */
|
|
5010
5525
|
onPeerGossip(handler: (from: string, data: Uint8Array) => void): () => void;
|
|
5011
|
-
/** Subscribe to a raw channel event. Buffered until `connect()` if early. */
|
|
5526
|
+
/** Subscribe to a raw channel event. Buffered until `connect()` if early, and kept across a new channel. */
|
|
5012
5527
|
on(event: string, handler: Listener): void;
|
|
5013
5528
|
off(event: string, handler: Listener): void;
|
|
5014
5529
|
/** Advertise our keyExchange to the room (idempotent per connection). */
|
|
@@ -5092,7 +5607,7 @@ declare class Space {
|
|
|
5092
5607
|
timeoutMs?: number;
|
|
5093
5608
|
pollMs?: number;
|
|
5094
5609
|
}): Promise<boolean>;
|
|
5095
|
-
/** Creator path: mint
|
|
5610
|
+
/** Creator path: mint epoch 0; for a private Space, sign the genesis + ec(0) BEFORE connecting (design §4.5.10). */
|
|
5096
5611
|
create(): Promise<void>;
|
|
5097
5612
|
/** Wrap the group key for a newcomer and post it (key-holder action). */
|
|
5098
5613
|
admit(memberId: string, identityEcdhPub: string): Promise<void>;
|
|
@@ -5103,6 +5618,29 @@ declare class Space {
|
|
|
5103
5618
|
memberId: string;
|
|
5104
5619
|
identityEcdhPub: string;
|
|
5105
5620
|
}>): Promise<number>;
|
|
5621
|
+
/** Whether this Space has a verified membership log. */
|
|
5622
|
+
get logEnabled(): boolean;
|
|
5623
|
+
/** The verified member list, or null when this Space has no log. */
|
|
5624
|
+
membership(): MemberView[] | null;
|
|
5625
|
+
/** Called with the member list (null when unlogged) each time the log is re-synced. Returns an unsubscribe. */
|
|
5626
|
+
onMembership(h: (m: MemberView[] | null) => void): () => void;
|
|
5627
|
+
private requireLog;
|
|
5628
|
+
/** Add by exact username (design §3.8; the owner, or an editor for viewers). Looks the IK up in the directory. */
|
|
5629
|
+
add(username: string, opts?: {
|
|
5630
|
+
role?: "viewer" | "editor";
|
|
5631
|
+
}): Promise<{
|
|
5632
|
+
seq: number;
|
|
5633
|
+
}>;
|
|
5634
|
+
/** Remove a member. In this build it cuts them off at the relay; it does not rotate the key. */
|
|
5635
|
+
remove(memberId: string): Promise<{
|
|
5636
|
+
seq: number;
|
|
5637
|
+
}>;
|
|
5638
|
+
setRole(memberId: string, role: "viewer" | "editor"): Promise<{
|
|
5639
|
+
seq: number;
|
|
5640
|
+
}>;
|
|
5641
|
+
leave(): Promise<{
|
|
5642
|
+
seq: number;
|
|
5643
|
+
}>;
|
|
5106
5644
|
putFile(file: File | Blob | Uint8Array, metadata: SpaceFileMetadata, opts?: {
|
|
5107
5645
|
onProgress?: (completed: number, total: number) => void;
|
|
5108
5646
|
/**
|
|
@@ -5295,19 +5833,27 @@ declare class ChatKeyVault {
|
|
|
5295
5833
|
private readonly store;
|
|
5296
5834
|
constructor(store: ChatKeyStore);
|
|
5297
5835
|
/**
|
|
5298
|
-
* Register-time: ensure a keypair exists for `memberId`,
|
|
5299
|
-
*
|
|
5300
|
-
*
|
|
5836
|
+
* Register-time: ensure a vault-backed keypair exists for `memberId`,
|
|
5837
|
+
* seed-wrap it and persist it. Idempotent for a pair that is already
|
|
5838
|
+
* vault-backed. Otherwise a fresh pair is generated, persisted, and only
|
|
5839
|
+
* then swapped in, replacing whatever the tab held: a throwaway key is never
|
|
5840
|
+
* persisted as the stable one, and a failed persist leaves the tab's keys
|
|
5841
|
+
* as they were, with nothing marked.
|
|
5301
5842
|
*/
|
|
5302
5843
|
provision(memberId: string, seedSecret: string): Promise<void>;
|
|
5303
5844
|
/**
|
|
5304
|
-
* Sign-in / unlock time: fetch the wrapped blob, unwrap with the seed, and
|
|
5305
|
-
* hydrate the `KeyStore
|
|
5306
|
-
* Returns `true` when a keypair is now in hand
|
|
5307
|
-
*
|
|
5308
|
-
*
|
|
5845
|
+
* Sign-in / unlock time: fetch the wrapped blob, unwrap it with the seed, and
|
|
5846
|
+
* hydrate the `KeyStore`, restoring the SAME keypair the member had before.
|
|
5847
|
+
* Returns `true` when a vault-backed keypair is now in hand, and `false` when
|
|
5848
|
+
* there is no blob yet (a brand-new member; the caller provisions). A store
|
|
5849
|
+
* failure propagates as thrown; a blob that does not unwrap, parse or import
|
|
5850
|
+
* throws `ChatKeysUnreadableError`. Nothing in the `KeyStore` changes until
|
|
5851
|
+
* the saved pair is imported; it then replaces whatever pair the tab held,
|
|
5852
|
+
* in one step, so a pair minted meanwhile is never kept in its place.
|
|
5309
5853
|
*/
|
|
5310
5854
|
rehydrate(memberId: string, seedSecret: string): Promise<boolean>;
|
|
5855
|
+
/** Unwrap, parse and import a saved pair, outside the `KeyStore`. */
|
|
5856
|
+
private unwrapPair;
|
|
5311
5857
|
/**
|
|
5312
5858
|
* Ensure a stable keypair is in hand: rehydrate from the vault if a blob
|
|
5313
5859
|
* exists, otherwise mint one and persist it (so it's stable next session).
|
|
@@ -5316,6 +5862,101 @@ declare class ChatKeyVault {
|
|
|
5316
5862
|
ensure(memberId: string, seedSecret: string): Promise<void>;
|
|
5317
5863
|
}
|
|
5318
5864
|
|
|
5865
|
+
/**
|
|
5866
|
+
* POST /api/auth/identity (SpecA §4.1 as amended by CS-7). An unknown name
|
|
5867
|
+
* answers a deterministic decoy, so a lookup never says whether an account
|
|
5868
|
+
* exists. Responses are cached for the life of the Client. Any failure,
|
|
5869
|
+
* including a 429, fails closed (SpecA §4.3).
|
|
5870
|
+
*/
|
|
5871
|
+
declare class IdentityDirectory {
|
|
5872
|
+
private readonly http;
|
|
5873
|
+
private readonly cache;
|
|
5874
|
+
constructor(http: Pick<HttpClient, "post">);
|
|
5875
|
+
lookup(username: string): Promise<string>;
|
|
5876
|
+
lookupMany(usernames: string[]): Promise<Record<string, string>>;
|
|
5877
|
+
}
|
|
5878
|
+
|
|
5879
|
+
/** Signs with the account identity key (IK). Never exposes the seed (SpecA §6.1). */
|
|
5880
|
+
interface IdentitySigner {
|
|
5881
|
+
/** b64 IEEE-P1363 ECDSA-P256-SHA256 over `bytes`. */
|
|
5882
|
+
sign(bytes: Uint8Array): Promise<string>;
|
|
5883
|
+
/** The IK as standard padded base64 of the raw 65-byte point. */
|
|
5884
|
+
identityKeyRaw(): Promise<string>;
|
|
5885
|
+
}
|
|
5886
|
+
|
|
5887
|
+
/**
|
|
5888
|
+
* IndexedDB `muhkoo.trust` (design §6.6): pin high-water marks and per-Space log
|
|
5889
|
+
* heads. When IndexedDB is absent, throws on open, or has not opened within 2 s,
|
|
5890
|
+
* it is an in-memory store for the life of the Client, and `persistent` is
|
|
5891
|
+
* false (KI1).
|
|
5892
|
+
*/
|
|
5893
|
+
interface TrustStore {
|
|
5894
|
+
readonly persistent: boolean;
|
|
5895
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
5896
|
+
put(key: string, value: unknown): Promise<void>;
|
|
5897
|
+
del(key: string): Promise<void>;
|
|
5898
|
+
}
|
|
5899
|
+
|
|
5900
|
+
type PinDocName = "identity" | "spaces";
|
|
5901
|
+
interface PinDoc {
|
|
5902
|
+
v: 1;
|
|
5903
|
+
ownerCommitment: string;
|
|
5904
|
+
counter: number;
|
|
5905
|
+
entries: Record<string, string>;
|
|
5906
|
+
sig: string;
|
|
5907
|
+
}
|
|
5908
|
+
interface PinStoreDeps {
|
|
5909
|
+
http: Pick<HttpClient, "post">;
|
|
5910
|
+
commitment: () => string | null;
|
|
5911
|
+
signer: IdentitySigner;
|
|
5912
|
+
store: () => Promise<TrustStore>;
|
|
5913
|
+
}
|
|
5914
|
+
declare class PinStore {
|
|
5915
|
+
private readonly deps;
|
|
5916
|
+
constructor(deps: PinStoreDeps);
|
|
5917
|
+
private commitment;
|
|
5918
|
+
private markKey;
|
|
5919
|
+
private kvPath;
|
|
5920
|
+
private fetchDoc;
|
|
5921
|
+
private verify;
|
|
5922
|
+
/** The current verified document; an empty bootstrap at counter 0 when none exists and no mark does either. */
|
|
5923
|
+
read(doc: PinDocName): Promise<PinDoc>;
|
|
5924
|
+
get(doc: PinDocName, key: string): Promise<string | undefined>;
|
|
5925
|
+
/** Pin new keys. A key already pinned to a different value is reported, never overwritten (design §3.4). */
|
|
5926
|
+
pin(doc: PinDocName, entries: Record<string, string>): Promise<{
|
|
5927
|
+
conflicts: string[];
|
|
5928
|
+
}>;
|
|
5929
|
+
unpin(doc: PinDocName, keys: string[]): Promise<void>;
|
|
5930
|
+
/** User-confirmed recovery (design §9.1 "Your saved trust records are missing."): forget the high-water mark. */
|
|
5931
|
+
resetMark(doc: PinDocName): Promise<void>;
|
|
5932
|
+
private mutate;
|
|
5933
|
+
}
|
|
5934
|
+
|
|
5935
|
+
/** One per Client: shared by every Space (and, in sub-project 4, DMs). */
|
|
5936
|
+
declare class TrustContext {
|
|
5937
|
+
private readonly deps;
|
|
5938
|
+
readonly directory: IdentityDirectory;
|
|
5939
|
+
readonly signer: IdentitySigner;
|
|
5940
|
+
readonly pins: PinStore;
|
|
5941
|
+
private storeP;
|
|
5942
|
+
private opened;
|
|
5943
|
+
private appIdP;
|
|
5944
|
+
constructor(deps: {
|
|
5945
|
+
http: HttpClient;
|
|
5946
|
+
session: SessionState;
|
|
5947
|
+
openStore?: () => Promise<TrustStore>;
|
|
5948
|
+
});
|
|
5949
|
+
/** Whether muhkoo.trust survives a restart (KI1): false until the open settles, and false for an in-memory store. */
|
|
5950
|
+
get persistent(): boolean;
|
|
5951
|
+
store(): Promise<TrustStore>;
|
|
5952
|
+
/** This app's id, from GET /api/app/trust (a genesis must name it, D4). Cached; a failure is retried next call. */
|
|
5953
|
+
appId(): Promise<string>;
|
|
5954
|
+
/** User-confirmed "Reset trust for this space" (design §9.1): drop its genesis pin and log high-water. */
|
|
5955
|
+
resetSpacePins(spaceId: string): Promise<void>;
|
|
5956
|
+
/** `client.trust.unpin(username)`: forget a member's pinned identity after comparing safety numbers (design §3.4). */
|
|
5957
|
+
unpinUser(username: string): Promise<void>;
|
|
5958
|
+
}
|
|
5959
|
+
|
|
5319
5960
|
/**
|
|
5320
5961
|
* `client.space` — the fan-out group-encryption surface.
|
|
5321
5962
|
*
|
|
@@ -5354,6 +5995,9 @@ interface SpaceNamespaceDeps {
|
|
|
5354
5995
|
/** SDK-owned ratchet-keypair vault — gives a STABLE member keypair across
|
|
5355
5996
|
* reloads (rehydrate-or-provision) instead of a fresh one each session. */
|
|
5356
5997
|
chatKeys?: ChatKeyVault;
|
|
5998
|
+
/** The Client's trust context. Present, every join request carries the IK
|
|
5999
|
+
* certificate over this member's chat keys (design §3.2). */
|
|
6000
|
+
trust?: TrustContext;
|
|
5357
6001
|
/** P2P swarm config — enables peer block exchange among Space members. */
|
|
5358
6002
|
p2p?: {
|
|
5359
6003
|
enabled?: boolean;
|
|
@@ -5369,6 +6013,10 @@ declare class SpaceNamespace {
|
|
|
5369
6013
|
private spaceCache?;
|
|
5370
6014
|
/** Spaces built this session, by id — used to route offline send replays. */
|
|
5371
6015
|
private readonly openSpaces;
|
|
6016
|
+
/** In-flight identity loads, by member id; each entry is deleted once it settles. */
|
|
6017
|
+
private readonly identityInFlight;
|
|
6018
|
+
/** Chat-key certificates, by member and the exact key strings; a failed mint is dropped so the next join retries. */
|
|
6019
|
+
private readonly certs;
|
|
5372
6020
|
/** Unique id for THIS device/session — the P2P peer identity. A user's own
|
|
5373
6021
|
* devices share a member id, so the mesh MUST distinguish them by this or a
|
|
5374
6022
|
* device treats its siblings' signals as its own echo and never connects. */
|
|
@@ -5397,8 +6045,14 @@ declare class SpaceNamespace {
|
|
|
5397
6045
|
joinSpace(spaceId: string, opts?: {
|
|
5398
6046
|
timeoutMs?: number;
|
|
5399
6047
|
}): Promise<Space>;
|
|
5400
|
-
/**
|
|
5401
|
-
|
|
6048
|
+
/**
|
|
6049
|
+
* A handle to a space without auto-connecting (uses cached keys on demand).
|
|
6050
|
+
* `messaging: false` opens a files-only space, as {@link createSpace} makes:
|
|
6051
|
+
* no keyring and no identity, so it opens whether or not the session is locked.
|
|
6052
|
+
*/
|
|
6053
|
+
get(spaceId: string, historyPolicy?: HistoryPolicy, opts?: {
|
|
6054
|
+
messaging?: boolean;
|
|
6055
|
+
}): Promise<Space>;
|
|
5402
6056
|
/** List this app's channels (name → space-id) for the key's environment. */
|
|
5403
6057
|
listChannels(): Promise<Array<{
|
|
5404
6058
|
name: string;
|
|
@@ -5457,8 +6111,22 @@ declare class SpaceNamespace {
|
|
|
5457
6111
|
}>>;
|
|
5458
6112
|
private keyringFor;
|
|
5459
6113
|
private build;
|
|
5460
|
-
/**
|
|
6114
|
+
/**
|
|
6115
|
+
* The member's stable chat identity (design §3.3). A locked session (no
|
|
6116
|
+
* seed) throws `IdentityLockedError`. A vault failure throws
|
|
6117
|
+
* `IdentityUnavailableError`: `retryable` when the vault could not be
|
|
6118
|
+
* reached (§9.2 I8), and not when the saved keys do not unwrap under this
|
|
6119
|
+
* seed. A throwaway pair is never minted: doing so is what hid a reloaded
|
|
6120
|
+
* member's earlier messages from everyone else.
|
|
6121
|
+
*
|
|
6122
|
+
* Concurrent calls for one member share a single load, so two first opens
|
|
6123
|
+
* can never interleave their forget/generate steps and persist a second or
|
|
6124
|
+
* mixed pair.
|
|
6125
|
+
*/
|
|
5461
6126
|
private ensureIdentity;
|
|
6127
|
+
private loadIdentity;
|
|
6128
|
+
/** One certificate per (member, chat keys) for the life of the Client (design §3.2: global, not per Space). */
|
|
6129
|
+
private certFor;
|
|
5462
6130
|
private fetchPolicy;
|
|
5463
6131
|
private fetchTicket;
|
|
5464
6132
|
private myId;
|
|
@@ -5920,6 +6588,8 @@ interface FileEntry {
|
|
|
5920
6588
|
* would defeat the point of keeping history out of the directory record.
|
|
5921
6589
|
*/
|
|
5922
6590
|
versions?: number;
|
|
6591
|
+
/** See `DirEntry.moving`. */
|
|
6592
|
+
moving?: true;
|
|
5923
6593
|
}
|
|
5924
6594
|
interface DirEntry {
|
|
5925
6595
|
kind: "dir";
|
|
@@ -5928,6 +6598,25 @@ interface DirEntry {
|
|
|
5928
6598
|
/** Base64 AES-256 key for that record. See the capability note above. */
|
|
5929
6599
|
key: string;
|
|
5930
6600
|
mtime: number;
|
|
6601
|
+
/**
|
|
6602
|
+
* Set on both copies while a move between two directories is under way.
|
|
6603
|
+
*
|
|
6604
|
+
* A move writes two records, attaching before it detaches, and one that
|
|
6605
|
+
* is interrupted leaves the same entry - the same directory record, or
|
|
6606
|
+
* the same file id, content and history - named in both. This is how a
|
|
6607
|
+
* delete of either copy knows to look for the other before letting go of
|
|
6608
|
+
* anything. Cleared by the move's last write, and from the one copy left
|
|
6609
|
+
* when a delete removes the other. Left in place, it costs a delete of
|
|
6610
|
+
* that entry one read of the tree, and a file keeps every version that
|
|
6611
|
+
* falls off its history.
|
|
6612
|
+
*
|
|
6613
|
+
* Not cleared when that last write is the one that fails: the move has
|
|
6614
|
+
* otherwise finished, only one copy is named anywhere, and nothing comes
|
|
6615
|
+
* back to unmark it. A retried move of the same entry carries the mark
|
|
6616
|
+
* over. That copy then keeps what falls off its history for good - a
|
|
6617
|
+
* storage cost, never a loss.
|
|
6618
|
+
*/
|
|
6619
|
+
moving?: true;
|
|
5931
6620
|
}
|
|
5932
6621
|
type Entry = FileEntry | DirEntry;
|
|
5933
6622
|
/** One directory record, as stored (before encryption). */
|
|
@@ -5983,6 +6672,57 @@ declare class VfsNotFoundError extends Error {
|
|
|
5983
6672
|
declare class VfsConflictError extends Error {
|
|
5984
6673
|
constructor(message: string);
|
|
5985
6674
|
}
|
|
6675
|
+
/**
|
|
6676
|
+
* Raised when a directory record cannot be read: it does not decrypt or parse
|
|
6677
|
+
* with the key its parent holds, or it does but is not a shape this SDK can
|
|
6678
|
+
* read - not an object, a version other than 1, `entries` missing or not a map.
|
|
6679
|
+
*
|
|
6680
|
+
* Thrown, never read as an empty directory. The next write into an "empty"
|
|
6681
|
+
* directory would seal an empty record over the real one, and a record this
|
|
6682
|
+
* version cannot read may simply be one a NEWER version wrote. Nor is the
|
|
6683
|
+
* directory skipped: a sync that cannot see a subtree reads its absence as a
|
|
6684
|
+
* deletion. The record is left exactly as stored; a recursive delete
|
|
6685
|
+
* (`delete(path, { recursive: true })`) detaches it when the user wants it
|
|
6686
|
+
* gone.
|
|
6687
|
+
*/
|
|
6688
|
+
declare class VfsCorruptRecordError extends Error {
|
|
6689
|
+
/** The record's id - `dirKey(id)` is where it is stored. */
|
|
6690
|
+
readonly id: string;
|
|
6691
|
+
/** The path it was reached through, when known. A moved subtree can have two. */
|
|
6692
|
+
readonly path?: string;
|
|
6693
|
+
constructor(id: string, path: string | undefined, reason: string);
|
|
6694
|
+
}
|
|
6695
|
+
/**
|
|
6696
|
+
* Raised when a walk or a copy would go past a size or depth ceiling, or a
|
|
6697
|
+
* write would put a directory deeper than a walk reads.
|
|
6698
|
+
*
|
|
6699
|
+
* Thrown rather than returning what was gathered so far, because every caller
|
|
6700
|
+
* of a walk treats a path that is missing from the result as a path that does
|
|
6701
|
+
* not exist - and a sync then plans a deletion for it. A tree this large or
|
|
6702
|
+
* deep is almost always one built to be expensive to read: directory records
|
|
6703
|
+
* can point at the same directory many times over, so a handful of tiny
|
|
6704
|
+
* records can describe more paths than there are atoms.
|
|
6705
|
+
*/
|
|
6706
|
+
declare class VfsLimitError extends Error {
|
|
6707
|
+
/**
|
|
6708
|
+
* Which ceiling was hit: total entries, total characters of path text,
|
|
6709
|
+
* nesting depth, or - for a copy - how many places one directory may
|
|
6710
|
+
* appear. Entries alone do not bound memory - a name may be 255 bytes, so
|
|
6711
|
+
* one path can be ~16KB - which is why path text has its own.
|
|
6712
|
+
*/
|
|
6713
|
+
readonly limit: "entries" | "pathChars" | "depth" | "shared";
|
|
6714
|
+
/** The ceiling itself. */
|
|
6715
|
+
readonly max: number;
|
|
6716
|
+
/** Where the operation started, or for a write what it was asked to write. */
|
|
6717
|
+
readonly path: string;
|
|
6718
|
+
/**
|
|
6719
|
+
* Where the ceiling was reached: the directory being listed, or for depth
|
|
6720
|
+
* the entry one level too deep. Walking the tree to find the culprit would
|
|
6721
|
+
* hit the same ceiling, so this is the only pointer the caller gets.
|
|
6722
|
+
*/
|
|
6723
|
+
readonly at: string;
|
|
6724
|
+
constructor(limit: VfsLimitError["limit"], max: number, path: string, at: string, doing?: string);
|
|
6725
|
+
}
|
|
5986
6726
|
|
|
5987
6727
|
/**
|
|
5988
6728
|
* `client.vfs` — a real filesystem over Muhkoo primitives.
|
|
@@ -6053,6 +6793,18 @@ interface VfsNamespaceDeps {
|
|
|
6053
6793
|
seed: () => Uint8Array | null;
|
|
6054
6794
|
/** Versions retained per file. */
|
|
6055
6795
|
historyLimit?: number;
|
|
6796
|
+
/**
|
|
6797
|
+
* Ceilings for the operations that visit a whole subtree - `walk` (and so
|
|
6798
|
+
* `glob`) and `copy`. `entries` is the most entries one call may visit;
|
|
6799
|
+
* `pathChars` the most characters of path text, summed over every path it
|
|
6800
|
+
* visits. The defaults are far past any real project; tests lower them so
|
|
6801
|
+
* a hostile tree hits them in milliseconds. `Client` does not pass these,
|
|
6802
|
+
* so `client.vfs` always has the defaults.
|
|
6803
|
+
*/
|
|
6804
|
+
limits?: {
|
|
6805
|
+
entries?: number;
|
|
6806
|
+
pathChars?: number;
|
|
6807
|
+
};
|
|
6056
6808
|
}
|
|
6057
6809
|
declare class VfsNamespace {
|
|
6058
6810
|
private readonly deps;
|
|
@@ -6066,6 +6818,8 @@ declare class VfsNamespace {
|
|
|
6066
6818
|
*/
|
|
6067
6819
|
private cache;
|
|
6068
6820
|
private readonly historyLimit;
|
|
6821
|
+
private readonly maxEntries;
|
|
6822
|
+
private readonly maxPathChars;
|
|
6069
6823
|
constructor(deps: VfsNamespaceDeps);
|
|
6070
6824
|
/**
|
|
6071
6825
|
* The working directory relative paths resolve against.
|
|
@@ -6117,14 +6871,46 @@ declare class VfsNamespace {
|
|
|
6117
6871
|
stat(path: string): Promise<VfsStat>;
|
|
6118
6872
|
/** Immediate children of a directory. One record fetch. */
|
|
6119
6873
|
list(path?: string): Promise<VfsStat[]>;
|
|
6874
|
+
/**
|
|
6875
|
+
* A directory's entries in listing order: directories first, then by name.
|
|
6876
|
+
* Shared by `list` and `walk`, so a walk's output order is exactly the order
|
|
6877
|
+
* of the listings it is made of.
|
|
6878
|
+
*/
|
|
6879
|
+
private children;
|
|
6120
6880
|
private warn;
|
|
6121
6881
|
/**
|
|
6122
6882
|
* Every file path under a prefix, depth-first.
|
|
6123
6883
|
*
|
|
6124
6884
|
* Deliberately separate from `list`: this walks the whole subtree, which is
|
|
6125
6885
|
* one fetch per directory. Fine for a project, wrong as a default.
|
|
6886
|
+
*
|
|
6887
|
+
* Throws VfsLimitError past the entry budget or the depth ceiling, and
|
|
6888
|
+
* VfsCorruptRecordError for a directory it cannot read, rather than
|
|
6889
|
+
* returning part of the tree. Skips (with a warning) a directory that is
|
|
6890
|
+
* one of its own ancestors.
|
|
6126
6891
|
*/
|
|
6127
6892
|
walk(path?: string): Promise<string[]>;
|
|
6893
|
+
/**
|
|
6894
|
+
* Every entry under a prefix - directories as well as files - depth-first,
|
|
6895
|
+
* each directory just before its contents. `walk` is this with the
|
|
6896
|
+
* directories filtered out, so the two can never disagree about what is in
|
|
6897
|
+
* a tree; version control uses this one because a commit records empty
|
|
6898
|
+
* directories too.
|
|
6899
|
+
*/
|
|
6900
|
+
walkEntries(path?: string): Promise<VfsStat[]>;
|
|
6901
|
+
/**
|
|
6902
|
+
* `walkEntries` without the list: `visit` is called with each entry in
|
|
6903
|
+
* the same order, and for a file with its manifest - the handle
|
|
6904
|
+
* `statManifest` gives, without resolving the path all over again, which
|
|
6905
|
+
* on a deep tree cost far more than the walk itself. Null for a directory.
|
|
6906
|
+
*
|
|
6907
|
+
* For a caller that does not need every entry at once. Version control
|
|
6908
|
+
* compares the working tree with a commit as it goes, where holding the
|
|
6909
|
+
* whole walk beside the whole commit, and a map of one beside a map of
|
|
6910
|
+
* the other, ran a 512MB heap out of memory inside the default budgets.
|
|
6911
|
+
* The same guards and budgets as `walk`, which is built on this.
|
|
6912
|
+
*/
|
|
6913
|
+
eachEntry(path: string, visit: (stat: VfsStat, manifest: FileManifest | null) => void): Promise<void>;
|
|
6128
6914
|
readFile(path: string): Promise<Uint8Array>;
|
|
6129
6915
|
readText(path: string): Promise<string>;
|
|
6130
6916
|
/**
|
|
@@ -6170,6 +6956,16 @@ declare class VfsNamespace {
|
|
|
6170
6956
|
mkdir(path: string, opts?: {
|
|
6171
6957
|
recursive?: boolean;
|
|
6172
6958
|
}): Promise<void>;
|
|
6959
|
+
/**
|
|
6960
|
+
* Refuse now what creating these directories with `mkdir -p` would refuse
|
|
6961
|
+
* part way: one deeper than MAX_DEPTH, or a name on the way held by an
|
|
6962
|
+
* entry this version cannot read. Writes nothing.
|
|
6963
|
+
*
|
|
6964
|
+
* For an operation that removes things before it writes - a checkout -
|
|
6965
|
+
* so that a refusal comes before anything is gone. A FILE in the way is
|
|
6966
|
+
* not refused: such an operation removes those first.
|
|
6967
|
+
*/
|
|
6968
|
+
assertCreatable(dirs: Iterable<string>): Promise<void>;
|
|
6173
6969
|
/**
|
|
6174
6970
|
* Write a file, creating parent directories as needed.
|
|
6175
6971
|
*
|
|
@@ -6183,10 +6979,10 @@ declare class VfsNamespace {
|
|
|
6183
6979
|
/**
|
|
6184
6980
|
* Delete a file, or a directory when `recursive`.
|
|
6185
6981
|
*
|
|
6186
|
-
* `keepContent` removes the entry WITHOUT releasing its shards
|
|
6187
|
-
*
|
|
6188
|
-
*
|
|
6189
|
-
* depends on.
|
|
6982
|
+
* `keepContent` removes the entry WITHOUT releasing its shards - or, for
|
|
6983
|
+
* a directory, the shards of anything in it. A checkout needs that: the
|
|
6984
|
+
* file is leaving the working tree, but commits still reference the same
|
|
6985
|
+
* manifest, and releasing would free content history depends on.
|
|
6190
6986
|
*/
|
|
6191
6987
|
delete(path: string, opts?: {
|
|
6192
6988
|
recursive?: boolean;
|
|
@@ -6198,9 +6994,21 @@ declare class VfsNamespace {
|
|
|
6198
6994
|
*
|
|
6199
6995
|
* A directory move is O(1) regardless of how much is inside it — the
|
|
6200
6996
|
* subtree records are not touched at all, because nothing in them records
|
|
6201
|
-
* its own path.
|
|
6997
|
+
* its own path. Except a move to a DEEPER place, which reads the subtree
|
|
6998
|
+
* first: what is below must still be inside MAX_DEPTH once it is moved.
|
|
6202
6999
|
*/
|
|
6203
7000
|
rename(from: string, to: string): Promise<void>;
|
|
7001
|
+
/**
|
|
7002
|
+
* Refuse to move the directory at `src` to `dst` when something in it
|
|
7003
|
+
* would then be deeper than MAX_DEPTH from the root. Writes nothing.
|
|
7004
|
+
*
|
|
7005
|
+
* Measured only moving deeper, because it reads the whole subtree: one
|
|
7006
|
+
* move of a 40-deep tree under a 30-deep directory was accepted when only
|
|
7007
|
+
* the moved directory was measured, and every walk of the root threw from
|
|
7008
|
+
* then on. A part that cannot be read cannot be measured, and does not
|
|
7009
|
+
* stop the move - that record stops every walk above it already.
|
|
7010
|
+
*/
|
|
7011
|
+
private assertFitsAt;
|
|
6204
7012
|
/**
|
|
6205
7013
|
* Copy a file or a directory tree.
|
|
6206
7014
|
*
|
|
@@ -6262,25 +7070,158 @@ declare class VfsNamespace {
|
|
|
6262
7070
|
/** Open a directory by path, creating nothing. Throws if it is missing. */
|
|
6263
7071
|
private openDir;
|
|
6264
7072
|
private openDirOrNull;
|
|
7073
|
+
/** Open a directory by path, along with the id of every directory on the way to it. */
|
|
7074
|
+
private openChain;
|
|
7075
|
+
/**
|
|
7076
|
+
* `path` is only for error messages. The cache is keyed by id, so when two
|
|
7077
|
+
* paths reach one record - a move interrupted between attach and detach -
|
|
7078
|
+
* the error names whichever asked first.
|
|
7079
|
+
*/
|
|
6265
7080
|
private openDirById;
|
|
7081
|
+
/**
|
|
7082
|
+
* Turn a decrypted record into an OpenDir, or throw.
|
|
7083
|
+
*
|
|
7084
|
+
* The record is THROWN on, never read as empty, when its shape is wrong -
|
|
7085
|
+
* see VfsCorruptRecordError. An individual entry of the wrong shape is set
|
|
7086
|
+
* aside in `hidden` instead: out of every read, but kept for the write.
|
|
7087
|
+
*/
|
|
7088
|
+
private readRecord;
|
|
6266
7089
|
/** Apply an edit to a directory record and persist it. */
|
|
6267
7090
|
private mutate;
|
|
6268
7091
|
private createDir;
|
|
6269
|
-
/**
|
|
7092
|
+
/**
|
|
7093
|
+
* Every directory record in a subtree, each once, opened - and nothing
|
|
7094
|
+
* changed. The first half of a recursive delete; see `delete`.
|
|
7095
|
+
*
|
|
7096
|
+
* Once per id, so a record that references another inside the subtree
|
|
7097
|
+
* cannot recurse forever, and a subtree reachable twice is not released
|
|
7098
|
+
* twice. And never a directory in `above` - the root and everything else
|
|
7099
|
+
* on the way down to the target - nor the root by any route. Deduplicating
|
|
7100
|
+
* alone let an entry naming the root pull the whole filesystem into the
|
|
7101
|
+
* delete, the root record included; a walk shows the same pointer as a
|
|
7102
|
+
* cycle and skips it, so this skips it too.
|
|
7103
|
+
*
|
|
7104
|
+
* `moving` says whether anything in it is marked as half of an
|
|
7105
|
+
* interrupted move, which is what makes a delete look for the other half.
|
|
7106
|
+
*/
|
|
7107
|
+
private subtree;
|
|
7108
|
+
/**
|
|
7109
|
+
* Delete these records and let go of every file in them. The second half
|
|
7110
|
+
* of a recursive delete, run only once the subtree is detached. Cannot
|
|
7111
|
+
* throw: every step is best-effort, because unreachable is not fatal.
|
|
7112
|
+
*
|
|
7113
|
+
* With `elsewhere` - what the rest of the tree still names, read because
|
|
7114
|
+
* something here is half of an interrupted move - a record still named
|
|
7115
|
+
* elsewhere is kept, and so is a file.
|
|
7116
|
+
*/
|
|
6270
7117
|
private purge;
|
|
7118
|
+
/**
|
|
7119
|
+
* Unmark what is left of an interrupted move once a delete has removed
|
|
7120
|
+
* the other copy. Marked, a file keeps every version that falls off its
|
|
7121
|
+
* history, because the other copy might hold it - for good, once there
|
|
7122
|
+
* is no other copy - and every delete of the entry, or of a directory
|
|
7123
|
+
* above it, reads the whole tree. Only when exactly one marked copy is
|
|
7124
|
+
* left: more, and they are still shared. Best-effort, like everything
|
|
7125
|
+
* after a detach; failing, it stays marked, which costs only that.
|
|
7126
|
+
*/
|
|
7127
|
+
private settle;
|
|
7128
|
+
/**
|
|
7129
|
+
* Let go of files that are leaving the tree: each one's content, every
|
|
7130
|
+
* version its history holds, and the history record. Best-effort, like
|
|
7131
|
+
* everything after a detach.
|
|
7132
|
+
*
|
|
7133
|
+
* A file id still named elsewhere is one file with two names, left by an
|
|
7134
|
+
* interrupted move: its content and history belong to the name that is
|
|
7135
|
+
* staying, and all that changes hands is the key its history opens with.
|
|
7136
|
+
* `keepContent` keeps every file's content and drops only its history
|
|
7137
|
+
* record, as a checkout needs.
|
|
7138
|
+
*/
|
|
7139
|
+
private letGo;
|
|
7140
|
+
/**
|
|
7141
|
+
* What the tree still names with one entry left out: every directory
|
|
7142
|
+
* record, and every file id with a directory that holds it. Null when
|
|
7143
|
+
* part of the tree cannot be read or followed, so the answer is unknown.
|
|
7144
|
+
*/
|
|
7145
|
+
private namedElsewhere;
|
|
7146
|
+
/**
|
|
7147
|
+
* Everything reachable from the root: every directory record's id and
|
|
7148
|
+
* key, and every file id with a directory that names it - following the
|
|
7149
|
+
* entries hidden only for their name, which still reference what they
|
|
7150
|
+
* point at. `skip` leaves one entry out: one about to be detached.
|
|
7151
|
+
*
|
|
7152
|
+
* Fetches up to WALK_CONCURRENCY records at once, each record once
|
|
7153
|
+
* however often it is named. Throws VfsCorruptRecordError for a record a
|
|
7154
|
+
* path reaches that cannot be read, unless `lenient`. `unknown` counts
|
|
7155
|
+
* what could not be followed at all: an entry this version cannot read,
|
|
7156
|
+
* and a record behind an unusable name that does not open - which no path
|
|
7157
|
+
* can reach, and so no recursive delete can remove - or, `lenient`, any
|
|
7158
|
+
* record that does not open.
|
|
7159
|
+
*/
|
|
7160
|
+
private references;
|
|
6271
7161
|
/**
|
|
6272
7162
|
* Deep-copy an entry. Files reuse their manifest (no bytes move); each
|
|
6273
7163
|
* directory gets a fresh id and key, so the copy is independent of the
|
|
6274
7164
|
* original — writing into one must never appear in the other.
|
|
7165
|
+
*
|
|
7166
|
+
* `at` is the entry's SOURCE path, and `depth` how many directories deep
|
|
7167
|
+
* from the root the copy of it will be. With `run.write` false this is the
|
|
7168
|
+
* dry run: every check, no reference taken and no record stored.
|
|
6275
7169
|
*/
|
|
6276
7170
|
private cloneEntry;
|
|
6277
7171
|
/**
|
|
6278
|
-
* Let go of
|
|
6279
|
-
*
|
|
7172
|
+
* Let go of one file's bytes: its content, every version its history
|
|
7173
|
+
* retains, and the history record itself.
|
|
7174
|
+
*
|
|
7175
|
+
* Each distinct manifest once. `copies` is every entry naming the file -
|
|
7176
|
+
* two after an interrupted move, which may by now hold different
|
|
7177
|
+
* versions - and `restore` puts a version back without taking a second
|
|
7178
|
+
* reference, so its manifest is in the entry and in the history both.
|
|
7179
|
+
* Releasing per mention released one reference twice, and so freed bytes
|
|
7180
|
+
* a copy elsewhere, holding its own reference, still needed.
|
|
6280
7181
|
*/
|
|
6281
7182
|
private releaseFile;
|
|
7183
|
+
/**
|
|
7184
|
+
* A file's history record, opened with the first of `keys` that opens
|
|
7185
|
+
* it, and that key. Null when there is none, or none of them does.
|
|
7186
|
+
*/
|
|
7187
|
+
private openHistory;
|
|
7188
|
+
/**
|
|
7189
|
+
* Seal a file's history with the key of the directory that holds it now.
|
|
7190
|
+
*
|
|
7191
|
+
* A history record is sealed with its directory's key, so a file moved
|
|
7192
|
+
* to another directory carried a history its new directory could not
|
|
7193
|
+
* open: every read of it, and every write to the file, failed from then
|
|
7194
|
+
* on. Nothing to do when there is no history, when it opens with `to`
|
|
7195
|
+
* already, or when none of the keys opens it.
|
|
7196
|
+
*/
|
|
7197
|
+
private rekeyHistory;
|
|
7198
|
+
/**
|
|
7199
|
+
* The history of the file `entry` names as `name` in `parent`, and the
|
|
7200
|
+
* key to seal it with from now on.
|
|
7201
|
+
*
|
|
7202
|
+
* A history is sealed with its directory's key - usually. The two halves
|
|
7203
|
+
* of an interrupted move share one history, sealed with ONE of their
|
|
7204
|
+
* directories' keys, and a file moved between directories by a version
|
|
7205
|
+
* before `rename` re-sealed it still has its old directory's. So when the
|
|
7206
|
+
* directory's own key does not open it, every key in the tree is tried,
|
|
7207
|
+
* the other half's first: a read of the whole tree, and only then. It
|
|
7208
|
+
* need not be read in full, or understood - an entry this version cannot
|
|
7209
|
+
* follow may hide a key, but a key that opens the record is proof
|
|
7210
|
+
* enough. Found, it is re-sealed with this directory's key, so the search
|
|
7211
|
+
* is made once - except for half of a move, which keeps the key that
|
|
7212
|
+
* opened it and so keeps working for both halves.
|
|
7213
|
+
*
|
|
7214
|
+
* No record, or a value that is not a sealed record at all, is an empty
|
|
7215
|
+
* history, as it always was. One that no key opens is thrown on, with a
|
|
7216
|
+
* message rather than a bare decryption error: writing an empty history
|
|
7217
|
+
* over it would lose every version it holds.
|
|
7218
|
+
*/
|
|
6282
7219
|
private readHistory;
|
|
6283
|
-
/**
|
|
7220
|
+
/**
|
|
7221
|
+
* Push `entry` onto its history record. Returns the new version count.
|
|
7222
|
+
* `next` is the manifest the entry will hold once the caller has written
|
|
7223
|
+
* it.
|
|
7224
|
+
*/
|
|
6284
7225
|
private pushHistory;
|
|
6285
7226
|
}
|
|
6286
7227
|
|
|
@@ -6386,21 +7327,88 @@ interface VcsNamespaceDeps {
|
|
|
6386
7327
|
key: () => Promise<Uint8Array>;
|
|
6387
7328
|
/** Who to record as the author. */
|
|
6388
7329
|
author: () => string;
|
|
7330
|
+
/**
|
|
7331
|
+
* `entries` is the most tree entries one read of history may visit - one
|
|
7332
|
+
* `status`, `diff` or `checkout` - and `pathChars` the most characters of
|
|
7333
|
+
* path those entries may add up to. The defaults are far past any real
|
|
7334
|
+
* project; tests lower them so a hostile tree hits them in milliseconds.
|
|
7335
|
+
* `Client` does not pass these, so `client.vcs` always has the defaults.
|
|
7336
|
+
*/
|
|
7337
|
+
limits?: {
|
|
7338
|
+
entries?: number;
|
|
7339
|
+
pathChars?: number;
|
|
7340
|
+
};
|
|
6389
7341
|
}
|
|
6390
7342
|
declare class Repo {
|
|
6391
7343
|
private readonly slug;
|
|
6392
7344
|
private readonly deps;
|
|
7345
|
+
private readonly maxEntries;
|
|
7346
|
+
private readonly maxPathChars;
|
|
7347
|
+
/**
|
|
7348
|
+
* How many directories deep below the project a tree may nest. The
|
|
7349
|
+
* working tree may be written MAX_DEPTH directories below the root, and
|
|
7350
|
+
* the project itself is some of those: a deeper tree would check out into
|
|
7351
|
+
* something no walk will read, and `status` - and every checkout after,
|
|
7352
|
+
* which walks the tree first - would throw from then on.
|
|
7353
|
+
*/
|
|
7354
|
+
private readonly maxTreeDepth;
|
|
6393
7355
|
constructor(slug: string, deps: VcsNamespaceDeps);
|
|
6394
7356
|
private get root();
|
|
6395
7357
|
/** Store an object, returning its hash and whether it was new. */
|
|
6396
7358
|
private put;
|
|
7359
|
+
/** Does this stored record open to an object with this hash? */
|
|
7360
|
+
private holds;
|
|
7361
|
+
/**
|
|
7362
|
+
* Fetch an object, and prove it is the one that was asked for.
|
|
7363
|
+
*
|
|
7364
|
+
* Content addressing is only a guarantee if the address is checked. Without
|
|
7365
|
+
* this a stored object could claim any hash - including its own, so a tree
|
|
7366
|
+
* could contain itself and `status` would follow it for ever. Every object
|
|
7367
|
+
* this repository has ever written verifies: `put` hashes exactly the value
|
|
7368
|
+
* it seals, `canonical` is independent of key order and drops `undefined`
|
|
7369
|
+
* exactly as JSON does, and hashing has never been versioned.
|
|
7370
|
+
*/
|
|
6397
7371
|
private get;
|
|
7372
|
+
/** A commit, checked to be one. See `commitProblem`. */
|
|
7373
|
+
private readCommit;
|
|
7374
|
+
/**
|
|
7375
|
+
* Start a traversal of history: a budget shared by every branch of it, and
|
|
7376
|
+
* a cache of the tree objects it has fetched.
|
|
7377
|
+
*
|
|
7378
|
+
* The cache matters for the same reason the budget does. Identical
|
|
7379
|
+
* subtrees are one object, so a tree of honest-but-hostile shape is a
|
|
7380
|
+
* handful of fetches and a vast number of paths; caching keeps it to the
|
|
7381
|
+
* handful, and the budget stops the paths. It caches OBJECTS, never output
|
|
7382
|
+
* - each name a shared subtree appears at is still listed.
|
|
7383
|
+
*/
|
|
7384
|
+
private treeWalk;
|
|
7385
|
+
/**
|
|
7386
|
+
* A tree object's entries, checked and ready to walk.
|
|
7387
|
+
*
|
|
7388
|
+
* Returned in a null-prototype map, so a name like `constructor` is looked
|
|
7389
|
+
* up as an entry and not found as an inherited property.
|
|
7390
|
+
*/
|
|
7391
|
+
private readTree;
|
|
7392
|
+
/**
|
|
7393
|
+
* A record that is not an object - HEAD, a branch ref, the branch list,
|
|
7394
|
+
* MERGE - opened. Null when there is none; `problem` when there is one
|
|
7395
|
+
* that does not open. What is inside is the caller's to check.
|
|
7396
|
+
*/
|
|
7397
|
+
private openRecord;
|
|
6398
7398
|
private head;
|
|
6399
7399
|
private setHead;
|
|
6400
7400
|
private readRef;
|
|
7401
|
+
/** A branch's commit, null when there is no such branch, or "unreadable". */
|
|
7402
|
+
private refState;
|
|
6401
7403
|
private writeRef;
|
|
6402
7404
|
/** Every branch name in this repository. */
|
|
6403
7405
|
branches(): Promise<string[]>;
|
|
7406
|
+
/**
|
|
7407
|
+
* The branch list, less any name a branch could not have been created
|
|
7408
|
+
* with - left out with a warning, like an unreadable entry in a tree, so
|
|
7409
|
+
* one planted name does not hide every real one.
|
|
7410
|
+
*/
|
|
7411
|
+
private readIndex;
|
|
6404
7412
|
/** The commit HEAD resolves to, or null in an empty repository. */
|
|
6405
7413
|
current(): Promise<string | null>;
|
|
6406
7414
|
/**
|
|
@@ -6418,7 +7426,27 @@ declare class Repo {
|
|
|
6418
7426
|
theirs: string;
|
|
6419
7427
|
name: string;
|
|
6420
7428
|
} | null>;
|
|
6421
|
-
/**
|
|
7429
|
+
/**
|
|
7430
|
+
* Give up on a merge that stopped for conflicts: the next commit is an
|
|
7431
|
+
* ordinary one, with one parent. The working tree is left as it is,
|
|
7432
|
+
* conflict markers and all. To put it back, switch to the current branch
|
|
7433
|
+
* with `discardChanges` — not `checkout`, which always leaves HEAD
|
|
7434
|
+
* detached, so later commits would stop moving the branch.
|
|
7435
|
+
*
|
|
7436
|
+
* Also the way out when the record of the merge cannot be read, which
|
|
7437
|
+
* would otherwise stop every commit.
|
|
7438
|
+
*/
|
|
7439
|
+
abortMerge(): Promise<void>;
|
|
7440
|
+
/**
|
|
7441
|
+
* Write a directory and everything under it, returning the tree's hash.
|
|
7442
|
+
*
|
|
7443
|
+
* Reads the working tree through ONE `walkEntries`, rather than listing
|
|
7444
|
+
* each directory by path and recursing. The recursion had no guard at all:
|
|
7445
|
+
* a directory record that names itself - which anything holding the seed
|
|
7446
|
+
* can plant - sent `commit` down /loop/loop/loop/... for ever. The walk has
|
|
7447
|
+
* the cycle guard, the budget and the depth ceiling, so a commit records
|
|
7448
|
+
* exactly the tree `status` compares it against.
|
|
7449
|
+
*/
|
|
6422
7450
|
private writeTree;
|
|
6423
7451
|
/** Commits reachable from HEAD, newest first. */
|
|
6424
7452
|
log(limit?: number): Promise<LogEntry[]>;
|
|
@@ -6446,8 +7474,6 @@ declare class Repo {
|
|
|
6446
7474
|
* trees for states nobody chose to keep.
|
|
6447
7475
|
*/
|
|
6448
7476
|
status(): Promise<Change[]>;
|
|
6449
|
-
/** The working tree as a path → entry map, mirroring `filesOf`. */
|
|
6450
|
-
private workingFiles;
|
|
6451
7477
|
/**
|
|
6452
7478
|
* Turn what a person typed into a commit hash.
|
|
6453
7479
|
*
|
|
@@ -6539,6 +7565,11 @@ interface VcsNamespaceOptions {
|
|
|
6539
7565
|
/** The in-memory master seed, or null when locked. Read per call. */
|
|
6540
7566
|
seed: () => Uint8Array | null;
|
|
6541
7567
|
author: () => string;
|
|
7568
|
+
/** See `VcsNamespaceDeps.limits`. */
|
|
7569
|
+
limits?: {
|
|
7570
|
+
entries?: number;
|
|
7571
|
+
pathChars?: number;
|
|
7572
|
+
};
|
|
6542
7573
|
}
|
|
6543
7574
|
|
|
6544
7575
|
/** The hosted Muhkoo Accelerator — the default {@link ClientOptions.baseUrl}. */
|
|
@@ -6646,6 +7677,12 @@ declare class Client {
|
|
|
6646
7677
|
readonly message: MessageNamespace;
|
|
6647
7678
|
/** Fan-out group spaces — `client.space.createSpace(...)`, etc. */
|
|
6648
7679
|
readonly space: SpaceNamespace;
|
|
7680
|
+
/** Trust records shared by every Space: pins and per-Space log heads (design §3.4, §6.9). */
|
|
7681
|
+
readonly trust: {
|
|
7682
|
+
readonly persistent: boolean;
|
|
7683
|
+
unpin(username: string): Promise<void>;
|
|
7684
|
+
resetSpacePins(spaceId: string): Promise<void>;
|
|
7685
|
+
};
|
|
6649
7686
|
/** Programmable Agents — `client.agents.create(appId, …)`, etc. */
|
|
6650
7687
|
readonly agents: AgentsNamespace;
|
|
6651
7688
|
/** Serverless functions — `client.functions.deploy(appId, …)`, etc. */
|
|
@@ -7582,14 +8619,29 @@ declare namespace protocol {
|
|
|
7582
8619
|
* GET /api/spaces/:id/keyring/pending -> JoinRequest[]
|
|
7583
8620
|
* GET /api/spaces/:id/keyring/roster -> { memberId, identityEcdhPub }[]
|
|
7584
8621
|
* POST /api/spaces/:id/keyring/rotate { nextEpoch } -> { epoch }
|
|
8622
|
+
* GET /api/spaces/:id/keyring/log ?since&limit -> { entries, head }
|
|
8623
|
+
* POST /api/spaces/:id/keyring/log { entry, ec? } -> { ok, head }
|
|
8624
|
+
* GET /api/spaces/:id/keyring/state (status only)
|
|
7585
8625
|
* GET /api/spaces/:id/metadata -> SpaceMetadata
|
|
7586
8626
|
*/
|
|
7587
8627
|
|
|
8628
|
+
/** A keyring route answered non-2xx; status and JSON body preserved (the existing methods keep their plain Errors). */
|
|
8629
|
+
declare class KeyringHttpError extends Error {
|
|
8630
|
+
readonly status: number;
|
|
8631
|
+
readonly body: Record<string, unknown> | null;
|
|
8632
|
+
constructor(status: number, body: Record<string, unknown> | null, route: string);
|
|
8633
|
+
}
|
|
7588
8634
|
interface KeyringClientDeps {
|
|
7589
8635
|
spaceId: string;
|
|
7590
8636
|
httpBaseUrl: string;
|
|
7591
8637
|
/** Header-injecting fetch from the client's HttpClient. */
|
|
7592
8638
|
fetch: typeof fetch;
|
|
8639
|
+
/**
|
|
8640
|
+
* The same fetch with the HttpClient's silent 401 recovery (`HttpClient.fetchWithRecovery`).
|
|
8641
|
+
* `probeState` and the log routes use it, so a 401 reads as locked only once a re-auth has failed (design §6.9).
|
|
8642
|
+
* Defaults to {@link fetch}.
|
|
8643
|
+
*/
|
|
8644
|
+
fetchWithRecovery?: typeof fetch;
|
|
7593
8645
|
}
|
|
7594
8646
|
declare class KeyringClient implements KeyringTransport {
|
|
7595
8647
|
private readonly deps;
|
|
@@ -7597,6 +8649,27 @@ declare class KeyringClient implements KeyringTransport {
|
|
|
7597
8649
|
private url;
|
|
7598
8650
|
private post;
|
|
7599
8651
|
private get;
|
|
8652
|
+
/**
|
|
8653
|
+
* The log routes. Sent through `fetchWithRecovery` when given, as `probeState` is, so a stale session is
|
|
8654
|
+
* re-established before a 401 reads as locked (design §6.9): a GET replays safely, and a POST body is a string.
|
|
8655
|
+
*/
|
|
8656
|
+
private request;
|
|
8657
|
+
/** GET /keyring/log (design §3.6): entries with seq > since, ascending, ≤ limit; head null = no log. */
|
|
8658
|
+
fetchLog(since?: number, limit?: number): Promise<{
|
|
8659
|
+
entries: MembershipEntry[];
|
|
8660
|
+
head: MlogHead | null;
|
|
8661
|
+
}>;
|
|
8662
|
+
/** POST /keyring/log {entry, ec?} → {ok, head}; `409 {error:"Log moved", head}` (no code) on a compare-and-set miss. */
|
|
8663
|
+
postLogEntry(entry: MembershipEntry, ec?: EpochCommit): Promise<{
|
|
8664
|
+
ok: true;
|
|
8665
|
+
head: MlogHead;
|
|
8666
|
+
}>;
|
|
8667
|
+
/**
|
|
8668
|
+
* Status of GET /keyring/state, or "network" when the request fails: 403 is not-member, 401 is locked,
|
|
8669
|
+
* anything else resumes (design §6.9). Sent through `fetchWithRecovery` when given, so a stale session
|
|
8670
|
+
* is re-established before a 401 is believed.
|
|
8671
|
+
*/
|
|
8672
|
+
probeState(): Promise<number | "network">;
|
|
7600
8673
|
postJoinRequest(req: JoinRequest): Promise<void>;
|
|
7601
8674
|
fetchBlobs(memberId: string): Promise<WrappedKey[]>;
|
|
7602
8675
|
postWrappedKey(targetMemberId: string, wrapped: WrappedKey, fromMemberId: string): Promise<void>;
|
|
@@ -7631,6 +8704,43 @@ declare class KeyringClient implements KeyringTransport {
|
|
|
7631
8704
|
fetchMetadata(): Promise<SpaceMetadata | null>;
|
|
7632
8705
|
}
|
|
7633
8706
|
|
|
8707
|
+
/**
|
|
8708
|
+
* Trust-layer errors shared by Spaces and, later, DMs (design §3.3, §6.9;
|
|
8709
|
+
* SpecA §6.6). Class names are public API and are frozen at their first
|
|
8710
|
+
* release (design Q16).
|
|
8711
|
+
*/
|
|
8712
|
+
/** The session holds no seed, so it has no stable identity and may not take part (design §3.3, T7). */
|
|
8713
|
+
declare class IdentityLockedError extends Error {
|
|
8714
|
+
readonly code: "identity-locked";
|
|
8715
|
+
constructor(message?: string);
|
|
8716
|
+
}
|
|
8717
|
+
/**
|
|
8718
|
+
* The stable chat keys could not be loaded (design §3.3). `retryable` says
|
|
8719
|
+
* whether the same call can succeed later. It is true when the vault could not
|
|
8720
|
+
* be reached or written (a 5xx or a network error on `chat-keys`), which the
|
|
8721
|
+
* next open retries (§9.2 I8). It is false when the saved keys were read but do
|
|
8722
|
+
* not unwrap under this session's seed, because they are damaged or were saved
|
|
8723
|
+
* under another seed, and when the client has no vault: retrying fails the same way.
|
|
8724
|
+
*/
|
|
8725
|
+
declare class IdentityUnavailableError extends Error {
|
|
8726
|
+
readonly reason?: unknown | undefined;
|
|
8727
|
+
readonly retryable: boolean;
|
|
8728
|
+
readonly code: "identity-unavailable";
|
|
8729
|
+
constructor(message?: string, reason?: unknown | undefined, retryable?: boolean);
|
|
8730
|
+
}
|
|
8731
|
+
type SpaceTrustCode = "integrity" | "log-conflict" | "log-refused" | "lookup-failed" | "pins-missing" | "pins-rollback" | "pins-stale" | "pins-invalid" | "pins-write-failed";
|
|
8732
|
+
/** A membership-log or pin failure. `detail` carries server fields (`reason`, `head`, ...). */
|
|
8733
|
+
declare class SpaceTrustError extends Error {
|
|
8734
|
+
readonly code: SpaceTrustCode;
|
|
8735
|
+
readonly detail?: Record<string, unknown> | undefined;
|
|
8736
|
+
constructor(code: SpaceTrustCode, message?: string, detail?: Record<string, unknown> | undefined);
|
|
8737
|
+
}
|
|
8738
|
+
/** This user may not open this Space (design §4.5.12, V25). */
|
|
8739
|
+
declare class AdmissionDeniedError extends Error {
|
|
8740
|
+
readonly code: "not-member" | "identity-mismatch";
|
|
8741
|
+
constructor(code: "not-member" | "identity-mismatch", message?: string);
|
|
8742
|
+
}
|
|
8743
|
+
|
|
7634
8744
|
declare class Logger {
|
|
7635
8745
|
private prefix;
|
|
7636
8746
|
private logLevel;
|
|
@@ -7865,7 +8975,7 @@ declare function getId(row: {
|
|
|
7865
8975
|
* expect (and isn't serving a stale cached bundle). Keep this in sync with
|
|
7866
8976
|
* `package.json` `version`.
|
|
7867
8977
|
*/
|
|
7868
|
-
declare const VERSION = "0.
|
|
8978
|
+
declare const VERSION = "0.15.0-alpha.0";
|
|
7869
8979
|
|
|
7870
8980
|
/**
|
|
7871
8981
|
* The SDK's process-global logger.
|
|
@@ -7897,5 +9007,5 @@ declare global {
|
|
|
7897
9007
|
}
|
|
7898
9008
|
declare const appLogger: InstanceType<typeof Logger>;
|
|
7899
9009
|
|
|
7900
|
-
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, CircuitIntegrityError, Client, ConnectivityManager, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_BRANCH, DEFAULT_FN_HOST_SUFFIX, DEVICE_IDENTITY_STORAGE_KEY, DEVICE_KEY_DB_NAME, DbCache, DbNamespace, DbTable, DevicePairingError, DeviceRevokedError, 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, Logger, 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, PINNED_CIRCUIT_DIGESTS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PasskeyOriginError, PeerExchange, PeerNetwork, PersonalSpaceClient, Poseidon, PreimagePoK, ROOT_ID, Ready, ReauthRequiredError, ReedSolomonCodec, Repo, Retry, Space as Room, SerializeMessage, SessionState, ShardCache, ShardClient, SharedSpaceClient, SnapshotNamespace, Space, SpaceCache, SpaceKeyring, SpaceNamespace, SpacePacketCipher, SpaceSignaler, StorageNamespace, SyncEngine, VERSION, VFS_ROOT_INFO, VaultUnavailableError, VcsError, VcsNamespace, VfsConflictError, VfsLockedError, VfsNamespace, VfsNotFoundError, WSTransport, WebRtcTransport, WorkerEngineHost, ZERO_HLC, ZkAuth, _accountId, _formatBytes, _messageId, _objectId, _packetId, _socketId, _userId, appLogger, assertType, base58Decode, base58Encode, buildCommitment, canonicalUserCode, clearDeviceIdentity, compareHlc, compilePrograms, concatBytes, configureDeviceStore, createPasskeyWithPrf, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultRpId, defaultSessionStore, deleteBeatsWrite, deleteRegister, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, deserialize, deviceFingerprint, deviceIdentityIsEphemeral, deviceIdentityKey, dirKey, ejectAgentPrompt, ejectAgentTools, emptyMap, emptyRegister, encodeToHex, encryptAesGcm, evaluatePasskeyPrf, exportPublicKeyBase64, exportPublicKeyHex, formatUserCode, fromBase64, fromBase64Url, fromHex, generateAuthProof, generateChecksum, generateDevicePairingKeypair, generateId, generatePkce, getIPAddress, getId, getMuhkooAppDescriptor, getSubtle, hasDeviceIdentity, headKey, historyKey, initBn128Wasm, initRsWasm, initializeCircuits, isNewer, isNumber, isOfflineCapable, isP2pCapable, isProvisional, loadDeviceIdentity, log, manifestToStat, materializeRow, merge3, merge3Text, mergeKey, mergeMap, mergeMessage, mergeRegister, messageKey, objectKey, pack, padHandle, pairingVerificationCode, passkeyPrfCapable, passkeySupported, passkeyUsableFromOrigin, persistDeviceIdentity, poseidonHash, protocol, provisionalHandle, quickVerify, randomBytes, randomState, refKey, refsIndexKey, rpIdUsableForOrigin, sealSeed, sealSeedToDevice, serialize, shardHash, signMessage, stableStringify, toBase64, toBase64Url, toField, toHex, tombstoneRow, unpack, unsealSeed, unsealSeedFromDevice, unwrapWithPassphrase, utf8Decode, utf8Encode, verify, verifyChecksum, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase, writeBeatsTombstone, writeColumns, writeRegister };
|
|
7901
|
-
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, Change, ChannelLike, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitIntegrity, CircuitUrls, ClientOptions, Commit, Conflict, ConnectivityOptions, ConnectivityState, CreateAccessTokenInput, CreatedAccessToken, DbFilterOp, DbNamespaceDeps, DbQuery, DbQueryPage, DbQueryResult, DbWhereCondition, DehydratedKeypair, DehydratedKeys, DeviceBlobStore, DeviceKeyVault, DevicePairingErrorReason, DevicePairingKeypair, DevicePairingPoll, DevicePairingRequest, DevicePairingSession, DirEntry, DirNode, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EngineHandle, EngineHost, Entry, EphemeralEvent, EpochKeyProvider, FileEntry, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, Hasher, Head, HistoryRecord, HlcParts, HlcState, HostedAuthDeps, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvEntry, KvNamespaceDeps, LogEntry, LoginOptions, LwwMap, LwwRegister, Merge3Result, MergeRegionConflict, MergeResult, 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, SealedRecord, 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, Tree, TreeDir, TreeEntry, TreeFile, UserIdentity, VaultFactor, VcsNamespaceDeps, VcsNamespaceOptions, VerificationKey, VfsContentStore, VfsNamespaceDeps, VfsStat, VfsStore, WSMessage, WSMessageType, WSTransportOptions, WaitForDevicePairingOptions, WebRtcTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|
|
9010
|
+
export { ACCESS_TOKEN_SCOPES, AES_GCM_IV_BYTES, AES_GCM_KEY_BYTES, AES_GCM_TAG_BYTES, AccessTokensNamespace, AdmissionDeniedError, AgentsNamespace, AuthClient, AuthHttpError, AuthNamespace, AuthPublicInput, Authenticator, BlockEngine, BroadcastChannel, BroadcastChannelEvents, ChannelExistsError, ChannelNotFoundError, ChunkCipher, CircuitIntegrityError, Client, ConnectivityManager, DEFAULT_AUTH_BASE_URL, DEFAULT_BASE_URL, DEFAULT_BRANCH, DEFAULT_FN_HOST_SUFFIX, DEVICE_IDENTITY_STORAGE_KEY, DEVICE_KEY_DB_NAME, DbCache, DbNamespace, DbTable, DevicePairingError, DeviceRevokedError, DeviceStoreUnavailableError, DoubleRatchet, DoubleRatchetCipher, DoubleRatchetManager, EncryptedSession, EventCore, EventCoreEvents, Field, Field as FieldElement, FileStorage, FunctionsNamespace, HLC, HashKnowledge, HostedAuth, HttpClient, HttpError, IdentityLockedError, IdentityUnavailableError, IndexedDbStore, KEEPER_MEMBER_ID, KeyStore, KeyringClient, KeyringHttpError, KvCache, KvNamespace, LocalEngineHost, LocalStorageSessionStore, Logger, MemorySessionStore, Message, Message as MessageDefault, MessageNamespace, MuhkooAgent, MuhkooDB, MuhkooFunction, MuhkooSpace, NOT_MEMBER_CLOSE_CODE, NOT_MEMBER_RECHECK_MS, NoopStore, OfflineLockedError, OfflineManager, OutboundQueue, P2P_SUBJECT, PAIRING_CODE_ALPHABET, PAIRING_CODE_LENGTH, PBKDF2_DEFAULT_ITERATIONS, PINNED_CIRCUIT_DIGESTS, PREIMAGE_POK_VERIFICATION_KEY, Packet, PasskeyOriginError, PeerExchange, PeerNetwork, PersonalSpaceClient, Poseidon, PreimagePoK, ROOT_ID, Ready, ReauthRequiredError, ReedSolomonCodec, Repo, Retry, Space as Room, SerializeMessage, SessionState, ShardCache, ShardClient, SharedSpaceClient, SnapshotNamespace, Space, SpaceCache, SpaceKeyring, SpaceNamespace, SpacePacketCipher, SpaceSignaler, SpaceTrustError, StorageNamespace, SyncEngine, VERSION, VFS_ROOT_INFO, VaultProtocolMismatchError, VaultUnavailableError, VcsError, VcsNamespace, VfsConflictError, VfsCorruptRecordError, VfsLimitError, VfsLockedError, VfsNamespace, VfsNotFoundError, WSTransport, WebRtcTransport, WorkerEngineHost, ZERO_HLC, ZkAuth, _accountId, _formatBytes, _messageId, _objectId, _packetId, _socketId, _userId, appLogger, assertType, base58Decode, base58Encode, buildCommitment, canonicalUserCode, clearDeviceIdentity, compareHlc, compilePrograms, concatBytes, configureDeviceStore, createPasskeyWithPrf, decodeFromHex, decorators, decryptAesGcm, defaultCircuitUrls, defaultRpId, defaultSessionStore, deleteBeatsWrite, deleteRegister, deriveAesKeyFromPassphrase, deriveBitsHkdf, deriveIdentity, deriveIdentityFromSeed, deriveMasterSeedFromPassword, deserialize, deviceFingerprint, deviceIdentityIsEphemeral, deviceIdentityKey, dirKey, ejectAgentPrompt, ejectAgentTools, emptyMap, emptyRegister, encodeToHex, encryptAesGcm, evaluatePasskeyPrf, exportPublicKeyBase64, exportPublicKeyHex, formatUserCode, fromBase64, fromBase64Url, fromHex, generateAuthProof, generateChecksum, generateDevicePairingKeypair, generateId, generatePkce, getIPAddress, getId, getMuhkooAppDescriptor, getSubtle, hasDeviceIdentity, headKey, historyKey, initBn128Wasm, initRsWasm, initializeCircuits, isNewer, isNumber, isOfflineCapable, isP2pCapable, isProvisional, isSameOriginPath, loadDeviceIdentity, log, manifestToStat, materializeRow, merge3, merge3Text, mergeKey, mergeMap, mergeMessage, mergeRegister, messageKey, objectKey, pack, padHandle, pairingVerificationCode, passkeyPrfCapable, passkeySupported, passkeyUsableFromOrigin, persistDeviceIdentity, poseidonHash, protocol, provisionalHandle, quickVerify, randomBytes, randomState, refKey, refsIndexKey, rpIdUsableForOrigin, sealSeed, sealSeedToDevice, serialize, shardHash, signMessage, stableStringify, toBase64, toBase64Url, toField, toHex, tombstoneRow, unpack, unsealSeed, unsealSeedFromDevice, unwrapWithPassphrase, utf8Decode, utf8Encode, verify, verifyChecksum, verifyGroth16, verifyHashKnowledge, verifyPreimagePoK, wrapWithPassphrase, writeBeatsTombstone, writeColumns, writeRegister };
|
|
9011
|
+
export type { ACL, ACLEntry, AccessTokenInfo, AccessTokensNamespaceDeps, AdmissionState, AgentConfig, AgentCreateInput, AgentDbToolMode, AgentProvisioned, AgentScopeOpts, AgentSkill, AgentToolsConfig, AgentTrigger, AgentTriggerType, AgentUpdateInput, AgentsNamespaceDeps, AttachedPeerNetwork, AuthClientOptions, AuthKeyPair, AuthSuccess, AuthToken, AuthType, AuthUser, BlockStore, Bn128WasmInstance, BroadcastChannelEvent, BroadcastChannelOptions, CachedSpaceMessage, Change, ChannelLike, ChatKeyCert, ChunkKeyMaterial, ChunkManifest, CipherFrame, CipherMessage, CipherMessageHeader, CircuitBufferConfig, CircuitIntegrity, CircuitUrls, ClientOptions, Commit, Conflict, ConnectivityOptions, ConnectivityState, CreateAccessTokenInput, CreatedAccessToken, DbFilterOp, DbNamespaceDeps, DbQuery, DbQueryPage, DbQueryResult, DbWhereCondition, DehydratedKeypair, DehydratedKeys, DeviceBlobStore, DeviceKeyVault, DevicePairingErrorReason, DevicePairingKeypair, DevicePairingPoll, DevicePairingRequest, DevicePairingSession, DirEntry, DirNode, DoubleRatchetCipherDeps, ECDHCurve, EncodeResult, EngineHandle, EngineHost, Entry, EphemeralEvent, EpochKeyProvider, FileEntry, FileManifest, FileNamespaceDeps, FileStat, FileStorageOptions, FunctionCaps, FunctionConfig, FunctionDeployInput, FunctionInvokeOptions, FunctionScopeOpts, FunctionTrigger, FunctionTriggerType, FunctionTriggers, FunctionUpdateInput, FunctionsNamespaceDeps, Groth16Proof, Hasher, Head, HistoryRecord, HlcParts, HlcState, HostedAuthDeps, HttpClientOptions, IncomingFrame, KeyExchangeFrame, KeyPair, Keypair, KeyringClientDeps, KeyringTransport, KvEntry, KvNamespaceDeps, LogEntry, LoginOptions, LwwMap, LwwRegister, MemberView, MembershipEntry, Merge3Result, MergeRegionConflict, MergeResult, 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$1 as Role, SpaceDeps as RoomDeps, SpaceFileMetadata as RoomFileMetadata, Scope, SealedHandoff, SealedHeaders, SealedRecord, Session, SessionOptions, SessionStore, SetOptions, ShardByteCache, ShardClientOptions, SharedSpaceClientOptions, SignalEnvelope, SignalKind, SignalingSpace, SnapshotNamespaceDeps, SnarkProof, SpaceChannelLike, SpaceCursor, SpaceDeps, SpaceFileMetadata, SpaceKeyCache, SpaceKeyringDeps, SpaceMessageEvent, SpaceNamespaceDeps, SpaceOfflineAdapter, SpaceTrustCode, StartDevicePairingOptions, StorageChangeEvent, StoreName, StoredSession, Subscription, Tree, TreeDir, TreeEntry, TreeFile, UserIdentity, VaultFactor, VcsNamespaceDeps, VcsNamespaceOptions, VerificationKey, VfsContentStore, VfsNamespaceDeps, VfsStat, VfsStore, WSMessage, WSMessageType, WSTransportOptions, WaitForDevicePairingOptions, WebRtcTransportOptions, WrappedPayload, WriteFileInput, WriteFileOptions, WriteToShardsInput, ZkAuthDeps, ZkAuthenticateRequest, ZkChallengeResponse, ZkCompiled, ZkIdentity, ZkRegisterRequest, ZkRegisterResponse };
|