@tinycloud/node-sdk 3.8.0-beta.1 → 3.8.0-beta.11

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.
Files changed (39) hide show
  1. package/dist/{TinyCloudNode-Ckqu_fqH.d.ts → TinyCloudNode-BVDUdEcf.d.cts} +53 -5
  2. package/dist/{TinyCloudNode-DDfx8Fx5.d.cts → TinyCloudNode-jMK_2Uj8.d.ts} +53 -5
  3. package/dist/{authority-BwM_s3Ya.d.cts → authority-DJ-PE4Xo.d.cts} +20 -4
  4. package/dist/{authority-DcyJpajL.d.ts → authority-i22vHbbp.d.ts} +20 -4
  5. package/dist/core.cjs +300 -32
  6. package/dist/core.cjs.map +1 -1
  7. package/dist/core.d.cts +5 -4
  8. package/dist/core.d.ts +5 -4
  9. package/dist/core.js +292 -27
  10. package/dist/core.js.map +1 -1
  11. package/dist/credentials-core.d.cts +1 -1
  12. package/dist/credentials-core.d.ts +1 -1
  13. package/dist/credentials.d.cts +1 -1
  14. package/dist/credentials.d.ts +1 -1
  15. package/dist/{delegation-0LKZkmP2.d.cts → delegation-Ckc3UETl.d.cts} +1 -1
  16. package/dist/{delegation-0LKZkmP2.d.ts → delegation-Ckc3UETl.d.ts} +1 -1
  17. package/dist/encryption-keys.d.cts +2 -2
  18. package/dist/encryption-keys.d.ts +2 -2
  19. package/dist/index.cjs +304 -44
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +4 -3
  22. package/dist/index.d.ts +4 -3
  23. package/dist/index.js +291 -31
  24. package/dist/index.js.map +1 -1
  25. package/dist/replication/authority.cjs +39 -0
  26. package/dist/replication/authority.cjs.map +1 -1
  27. package/dist/replication/authority.d.cts +4 -3
  28. package/dist/replication/authority.d.ts +4 -3
  29. package/dist/replication/authority.js +38 -0
  30. package/dist/replication/authority.js.map +1 -1
  31. package/dist/replication/runtime.cjs +1 -0
  32. package/dist/replication/runtime.cjs.map +1 -1
  33. package/dist/replication/runtime.d.cts +1 -1
  34. package/dist/replication/runtime.d.ts +1 -1
  35. package/dist/replication/runtime.js +1 -0
  36. package/dist/replication/runtime.js.map +1 -1
  37. package/dist/{runtime-v62FzQHz.d.ts → runtime-CO4VXnPq.d.ts} +2 -1
  38. package/dist/{runtime-D1o9Eybt.d.cts → runtime-mRhSu-vJ.d.cts} +2 -1
  39. package/package.json +4 -4
@@ -25,6 +25,7 @@ __export(authority_exports, {
25
25
  assertValidReplicationConfig: () => assertValidReplicationConfig,
26
26
  augmentSignInEntriesWithReplication: () => augmentSignInEntriesWithReplication,
27
27
  createReplicationAuthority: () => createReplicationAuthority,
28
+ eraseReplicaLocalCopy: () => eraseReplicaLocalCopy,
28
29
  establishReplicaSessionStamp: () => establishReplicaSessionStamp,
29
30
  hasUnrestrictedGetCoverage: () => hasUnrestrictedGetCoverage,
30
31
  mintNodeReplicationGrant: () => mintNodeReplicationGrant,
@@ -47,6 +48,9 @@ function assertValidReplicationConfig(replication) {
47
48
  if (replication?.syncScope !== void 0 && replication.syncScope !== "prefixes") {
48
49
  throw new TypeError('replication.syncScope must be "prefixes"');
49
50
  }
51
+ if (replication?.contentFetch !== void 0 && replication.contentFetch !== "eager" && replication.contentFetch !== "lazy") {
52
+ throw new TypeError('replication.contentFetch must be "eager" or "lazy"');
53
+ }
50
54
  if (replication === void 0 || !replication.enabled) return;
51
55
  if (replication.storage === void 0 || replication.storage === null) {
52
56
  throw new TypeError("replication.enabled requires replication.storage");
@@ -151,6 +155,40 @@ function reportReplicaDenial(result, reason, onEvent, context) {
151
155
  } catch {
152
156
  }
153
157
  }
158
+ var ERASE_RETRY_DELAYS_MS = [150, 450];
159
+ var ERASE_ATTEMPT_MS = 3e3;
160
+ async function eraseReplicaLocalCopy(lifecycle, trigger, denyStatus, handlers) {
161
+ if (lifecycle?.kind !== "indexeddb" || lifecycle.eraseLocalCopy === void 0) return void 0;
162
+ let outcome = { status: "failed", code: "STORAGE_ERROR", marker: false };
163
+ for (let attempt = 0; attempt <= ERASE_RETRY_DELAYS_MS.length; attempt++) {
164
+ if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, ERASE_RETRY_DELAYS_MS[attempt - 1]));
165
+ try {
166
+ outcome = await lifecycle.eraseLocalCopy({ signal: AbortSignal.timeout(ERASE_ATTEMPT_MS) });
167
+ } catch {
168
+ outcome = { status: "failed", code: "STORAGE_ERROR", marker: false };
169
+ }
170
+ if (outcome.status !== "failed") break;
171
+ }
172
+ const keysKept = denyStatus === "marker" || denyStatus === "unguarded";
173
+ const purge = outcome.status === "erased" ? { ok: true } : { ok: false, retryNow: outcome.status === "failed" };
174
+ if (outcome.status === "erased" || outcome.status === "refused" && !keysKept) return { outcome, purge };
175
+ const logoutPending = outcome.status === "failed" && outcome.marker;
176
+ const event = {
177
+ type: "replica.purge.failed",
178
+ trigger,
179
+ reason: logoutPending ? "logout_pending" : "gate_unavailable",
180
+ code: outcome.code,
181
+ retryNow: outcome.status === "failed"
182
+ };
183
+ for (const handler of handlers) {
184
+ try {
185
+ handler(event);
186
+ } catch (error) {
187
+ console.error('Error in "replica.purge.failed" handler:', error);
188
+ }
189
+ }
190
+ return logoutPending ? { outcome, purge, logoutPending } : { outcome, purge };
191
+ }
154
192
  var KV_GET2 = import_sdk_core2.KV.GET;
155
193
  var KV_SYNC2 = import_sdk_core2.KV.SYNC;
156
194
  var KV_SERVICE2 = "tinycloud.kv";
@@ -420,6 +458,7 @@ function createReplicationAuthority(host) {
420
458
  assertValidReplicationConfig,
421
459
  augmentSignInEntriesWithReplication,
422
460
  createReplicationAuthority,
461
+ eraseReplicaLocalCopy,
423
462
  establishReplicaSessionStamp,
424
463
  hasUnrestrictedGetCoverage,
425
464
  mintNodeReplicationGrant,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/replication/authority.ts","../../src/replication/config.ts","../../src/replication/authority-sign-in.ts"],"sourcesContent":["/**\n * Replication authority (TC-858, plan v3 §4).\n *\n * `ReplicationAuthority` answers, per configured prefix, whether this node's\n * session can back a local replica: the session UCAN itself (delegate\n * posture), a child UCAN minted through `delegateTo`, or nothing (the\n * controller then reports `grant_missing` and reads from the network).\n *\n * The unrestricted-authority rule is enforced here, before any network work:\n * a prefix covered only by a caveated `get`/`sync` refuses with\n * `CAVEATED_AUTHORITY` — on the session path, the runtime-grant path, and on\n * any reuse of an installed grant (the adapter marks every grant's\n * `unconstrained` flag; the controller enforces it on reuse).\n */\nimport { SiweMessage } from \"siwe\";\n\nimport {\n actionContains,\n KV,\n canonicalizeRecapCaveats,\n isCapabilitySubset,\n principalDid,\n parseSpaceUri,\n type PermissionEntry,\n type IWasmBindings,\n type WasmRecapEntry,\n type TinyCloudSession,\n type KVReplicaStorage,\n type ServiceContext,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport {\n type AuthorityRefusal,\n type ReplicaConsentAnchor,\n type ReplicaDevice,\n type ReplicationAuthority,\n type ReplicationEvent,\n} from \"@tinycloud/sdk-services\";\nimport type { ReplicaDenyInput, ReplicaSessionStamp } from \"@tinycloud/sdk-services/kv/replication\";\nimport type { DelegateToOptions, DelegateToResult, TinyCloudNode } from \"../TinyCloudNode\";\nimport type { ReplicaRuntime } from \"./replica-contract\";\n// @ts-ignore -- node-sdk builds before replica declarations; keep the literal import for bundlers.\nimport { parseUcanGrant as replicaParseUcanGrant } from \"@tinycloud/replica\";\nconst parseUcanGrant = replicaParseUcanGrant as ReplicaRuntime[\"parseUcanGrant\"];\nexport { assertValidReplicationConfig } from \"./config\";\nexport { augmentSignInEntriesWithReplication, hasUnrestrictedGetCoverage } from \"./authority-sign-in\";\n\n/** Report a completed deny from the already-loaded authority; delivery was revoked synchronously. */\nexport function reportReplicaDenial(\n result: { status: string; code?: string; intents?: number },\n reason: ReplicaDenyInput[\"reason\"],\n onEvent: ReplicationOptions[\"onEvent\"],\n context?: ServiceContext,\n): void {\n if (result.status !== \"denied\" && result.status !== \"deleted\" && result.status !== \"marker\") return;\n const event: Extract<ReplicationEvent, { type: \"replication.lifecycle\" }> = {\n type: \"replication.lifecycle\",\n at: new Date().toISOString(),\n reason,\n action: result.status === \"denied\" ? \"denied\" : result.status === \"deleted\" ? \"database_deleted\" : \"marker_set\",\n ...(result.status === \"denied\" ? { partitions: result.intents } : {}),\n ...(result.status === \"marker\" ? { code: result.code } : {}),\n };\n try { onEvent?.(event); } catch { /* Observability cannot undo committed denial. */ }\n const { type, ...data } = event;\n try { context?.emit(type, data); } catch { /* Observability cannot undo committed denial. */ }\n}\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\n/** The verified owner proof is signed, finite, exact-space, and uncaveated on one get+sync scope. */\nexport function verifiedOwnerConsentAnchor(\n session: TinyCloudSession,\n wasmBindings: Pick<IWasmBindings, \"validatePersistedSession\">,\n exactVerifiedRecap: (verified: { verifiedRecap?: unknown }) => WasmRecapEntry[],\n): ReplicaConsentAnchor | null {\n if (typeof session.siwe !== \"string\" || typeof session.signature !== \"string\" ||\n typeof session.address !== \"string\" || typeof session.chainId !== \"number\") return null;\n try {\n const verified = wasmBindings.validatePersistedSession?.({\n delegationHeader: session.delegationHeader,\n delegationCid: session.delegationCid,\n spaceId: session.spaceId,\n jwk: session.jwk,\n address: session.address,\n chainId: session.chainId,\n siwe: session.siwe,\n signature: session.signature,\n });\n if (!verified || !Number.isSafeInteger(Date.parse(verified.expiresAt ?? \"\"))) return null;\n const signedIssuedAt = new SiweMessage(session.siwe).issuedAt;\n if (typeof signedIssuedAt !== \"string\") return null;\n const issuedAt = Date.parse(signedIssuedAt);\n if (!Number.isSafeInteger(issuedAt) || issuedAt < 0) return null;\n const granted = exactVerifiedRecap(verified)\n .filter((entry) => entry.space === session.spaceId && entry.service === \"kv\")\n .map((entry) => ({ ...entry, service: \"tinycloud.kv\" }));\n const coversGetAndSync = granted.some((entry) => isCapabilitySubset([{\n service: \"tinycloud.kv\",\n space: session.spaceId,\n path: entry.path,\n actions: [KV.GET, KV.SYNC],\n }], granted).subset);\n return coversGetAndSync ? { at: issuedAt, source: \"siwe\" } : null;\n } catch {\n return null;\n }\n}\n\n/** Fresh sign-in only: the lifecycle creates keys; restore never calls this. */\nexport function establishReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n anchor: ReplicaConsentAnchor,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n throw Object.assign(new Error(\"Replica sign-in requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** Existing persisted stamp only: never manufactures an authority on restore. */\nexport function restoreReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n persisted: ReplicaSessionStamp | undefined,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n throw Object.assign(new Error(\"Replica restore requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** The delegation the replication grant delegates: get + sync on one folder prefix. */\nfunction replicationEntries(spaceId: string, prefix: string): PermissionEntry[] {\n return [{\n service: KV_SERVICE,\n space: spaceId,\n path: prefix === \"/\" ? \"\" : prefix,\n actions: [KV_GET, KV_SYNC],\n }];\n}\n\n\n\n\n/**\n * The signed UCAN attenuation (`att`: resource → ability → caveat branches).\n * Only array branch sets are known; anything else is treated as constrained —\n * fail closed, never assume unconstrained.\n */\nfunction caveatBranchesUnconstrained(value: unknown): boolean {\n if (value === undefined || value === null) return true;\n if (typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length === 0) {\n return true;\n }\n if (!Array.isArray(value)) return false;\n try {\n return (\n canonicalizeRecapCaveats(value as Record<string, unknown>[]) ===\n canonicalizeRecapCaveats(undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** The KV path a signed UCAN resource names in `space`, like replica's `kvPathIn`. */\nfunction kvPathInResource(resource: string, space: string): string | undefined {\n for (const root of [`${space}/kv`, `tinycloud://${space}/kv`]) {\n if (resource === root || resource === `${root}/`) return \"/\";\n }\n for (const base of [`${space}/kv/`, `tinycloud://${space}/kv/`]) {\n if (resource.startsWith(base)) return resource.slice(base.length);\n }\n return undefined;\n\n}\n/**\n * Whether the signed `att` grants `ability` over the folder prefix, with\n * unrestricted authority unless explicitly asked to ignore caveats.\n */\nexport function ucanAttCovers(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n ability: string,\n options?: { ignoreCaveats?: boolean },\n): boolean {\n for (const [resource, abilities] of Object.entries(att)) {\n const path = kvPathInResource(resource, space);\n if (path === undefined) continue;\n for (const [granted, caveats] of Object.entries(abilities)) {\n if (!actionContains(granted, ability)) continue;\n if (!isCapabilitySubset(\n [{ service: KV_SERVICE, space, path: prefix, actions: [ability] }],\n [{ service: KV_SERVICE, space, path, actions: [granted] }],\n ).subset) continue;\n if (options?.ignoreCaveats === true || caveatBranchesUnconstrained(caveats)) return true;\n }\n }\n return false;\n}\n\n/** `unconstrained` for `ReplicaGrantInfo` (§2.1): get AND sync on the prefix carry no caveats. */\nexport function ucanAttUnconstrainedFor(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n): boolean {\n return (\n ucanAttCovers(att, space, prefix, KV_GET) &&\n ucanAttCovers(att, space, prefix, KV_SYNC)\n );\n}\n\nexport interface DelegationPlanResult {\n path: \"session\" | \"runtime\";\n parentCid: string;\n expiresAt: number;\n}\n\n/**\n * The `TinyCloudNode` internals the authority needs. `planDelegation` is the\n * pure extraction of `delegateTo`'s parent selection (§4.3), so `plan` and\n * `mint` can never disagree about which parent or expiry a delegation gets.\n */\nexport type ReplicationAuthoritySession = Pick<\n TinyCloudSession,\n \"delegationHeader\" | \"delegationCid\" | \"spaceId\" | \"verificationMethod\" | \"jwk\"\n> & Partial<Pick<TinyCloudSession, \"siwe\" | \"signature\" | \"address\" | \"chainId\">>;\n\nexport interface ReplicationAuthorityHost {\n replicationSession(): ReplicationAuthoritySession | undefined;\n /** Validated owner proof only; never infer consent from a compact child UCAN. */\n verifiedConsentAnchor?(session: ReplicationAuthoritySession): ReplicaConsentAnchor | null;\n /** The SIWE \"Expiration Time\" of the session — the delegateTo delegation cap. */\n siweExpiration(siwe: string): Date | undefined;\n planDelegation(\n entries: PermissionEntry[],\n options?: { expiry?: string | number },\n ): ({ grant?: unknown } & DelegationPlanResult) | AuthorityRefusal;\n mintDelegation(\n deviceDid: string,\n entries: PermissionEntry[],\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; expiresAt: number }>;\n}\n\n/** Delegate a replica grant only through the selected owner parent for silent renewal. */\nexport async function mintNodeReplicationGrant(\n deviceDid: string,\n entries: PermissionEntry[],\n options: { expiryMs?: number; renewalOnly?: boolean } | undefined,\n renewalSession: ReplicationAuthoritySession | undefined,\n node: Pick<TinyCloudNode, \"delegateTo\">,\n): Promise<{ ucan: string; expiresAt: number }> {\n if (options?.renewalOnly && !renewalSession) throw new Error(\"replication renewal lost its owner session\");\n const delegateOptions: DelegateToOptions | undefined = options?.renewalOnly ? {\n expiry: options.expiryMs,\n onPrepared: async (prepared) => {\n assertRenewalParent(prepared.delegationHeader.Authorization, renewalSession!);\n },\n } : options?.expiryMs === undefined ? undefined : { expiry: options.expiryMs };\n const result: DelegateToResult = await node.delegateTo(principalDid(deviceDid), entries, delegateOptions);\n return {\n ucan: result.delegation.delegationHeader.Authorization,\n expiresAt: result.delegation.expiry.getTime(),\n };\n}\n\n/** Milliseconds of validity a session or parent must have left for authority reuse (delegateTo's margin). */\nexport const AUTHORITY_EXPIRY_MARGIN_MS = 60_000;\n\n\n/** Verify a compact session delegation before using any persisted space metadata. */\nexport function replicationScopeFromSignedSession(input: {\n delegationHeader: { Authorization: string };\n delegationCid: string;\n verificationMethod: string;\n persistedSpace: string;\n}): { space: string; principal: string } | undefined {\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n grant = parseUcanGrant(input.delegationHeader.Authorization);\n } catch {\n return undefined;\n }\n const bareDid = (value: string): string => value.split(\"#\", 1)[0]!;\n if (\n grant.cid !== input.delegationCid ||\n bareDid(grant.audience) !== bareDid(input.verificationMethod)\n ) return undefined;\n for (const resource of Object.keys(grant.att)) {\n const space = resource.match(/^(?:tinycloud:\\/\\/)?(.+?)\\/kv(?:\\/|$)/)?.[1];\n if (space !== input.persistedSpace) continue;\n const owner = parseSpaceUri(space);\n if (owner?.owner && owner.address && owner.chainId) return { space, principal: owner.owner };\n }\n return undefined;\n}\n/** Runs before host activation; a replan cannot silently switch a renewal to a runtime parent. */\nexport function assertRenewalParent(authorization: string, session: ReplicationAuthoritySession): void {\n const grant = parseUcanGrant(authorization);\n if (\n grant.issuer !== session.verificationMethod.split(\"#\", 1)[0] ||\n grant.prf.length !== 1 ||\n grant.prf[0] !== session.delegationCid\n ) {\n throw Object.assign(new Error(\"replication renewal requires the bound owner session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n}\n\nexport function createReplicationAuthority(host: ReplicationAuthorityHost): ReplicationAuthority {\n const boundSession = host.replicationSession();\n let verifiedAnchor: ReplicaConsentAnchor | null = null;\n try {\n const candidate = boundSession && host.verifiedConsentAnchor?.(boundSession);\n if (candidate && Number.isFinite(candidate.at) && candidate.source !== \"first_install\") {\n verifiedAnchor = { at: candidate.at, source: candidate.source };\n }\n } catch {\n // Network sessions remain usable when the optional replica proof cannot be verified.\n }\n const activeSession = (): ReplicationAuthoritySession | undefined => {\n const session = host.replicationSession();\n return session !== undefined && session === boundSession ? session : undefined;\n };\n const planOrRefusal = (entries: PermissionEntry[], expiryMs?: number): DelegationPlanResult | AuthorityRefusal => {\n const plan = host.planDelegation(entries, expiryMs === undefined ? undefined : { expiry: expiryMs });\n if (\"refused\" in plan) return plan;\n return { path: plan.path, parentCid: plan.parentCid, expiresAt: plan.expiresAt };\n };\n const sessionAudience = (value: string): string => value.split(\"#\", 1)[0]!;\n const signedSessionGrant = (session: ReplicationAuthoritySession) => {\n const grant = parseUcanGrant(session.delegationHeader.Authorization);\n if (\n grant.cid !== session.delegationCid ||\n sessionAudience(grant.audience) !== sessionAudience(session.verificationMethod)\n ) return undefined;\n return grant;\n };\n const authority: ReplicationAuthority = {\n get sessionOnly() {\n const session = activeSession();\n return session === undefined || session.siwe === undefined;\n },\n consentAnchor(): ReplicaConsentAnchor | null {\n return activeSession() ? verifiedAnchor : null;\n },\n get canRenew() {\n const session = activeSession();\n return verifiedAnchor?.source === \"siwe\" && session !== undefined && session.siwe !== undefined;\n },\n sessionGrant(prefix: string): { ucan: string; device: ReplicaDevice } | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n // A compact session proves current signed coverage, not consent to install or mint.\n if (verifiedAnchor === null && session.siwe !== undefined) return { refused: \"NOT_COVERED\" };\n const siweExpiry = session.siwe ? host.siweExpiration(session.siwe)?.getTime() : undefined;\n if (siweExpiry !== undefined && siweExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n const verified = signedSessionGrant(session);\n if (!verified) return { refused: \"NOT_COVERED\" };\n grant = verified;\n } catch {\n return { refused: \"NOT_COVERED\" };\n }\n const tokenExpiry = grant.expiresAt === null ? undefined : grant.expiresAt * 1000;\n if (tokenExpiry === undefined || tokenExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n const covered = ucanAttCovers(grant.att, session.spaceId, prefix, KV_GET, { ignoreCaveats: true }) &&\n ucanAttCovers(grant.att, session.spaceId, prefix, KV_SYNC, { ignoreCaveats: true });\n if (!covered) return { refused: \"NOT_COVERED\" };\n if (!ucanAttUnconstrainedFor(grant.att, session.spaceId, prefix)) {\n return { refused: \"CAVEATED_AUTHORITY\" };\n }\n return {\n ucan: grant.jwt,\n device: { did: session.verificationMethod, jwk: session.jwk },\n };\n },\n plan(prefix: string): DelegationPlanResult | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n if (verifiedAnchor === null) return { refused: \"NOT_COVERED\" };\n const sessionGrant = this.sessionGrant(prefix);\n if (!(\"refused\" in sessionGrant)) {\n const signed = signedSessionGrant(session);\n if (!signed || signed.expiresAt === null) return { refused: \"NOT_COVERED\" };\n return {\n path: \"session\",\n parentCid: session.delegationCid,\n expiresAt: signed.expiresAt * 1000,\n };\n }\n if (authority.sessionOnly) return sessionGrant;\n return planOrRefusal(replicationEntries(session.spaceId, prefix));\n },\n async mint(\n deviceDid: string,\n prefix: string,\n signal: AbortSignal,\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; parentCid: string; expiresAt: number }> {\n const session = activeSession();\n if (session === undefined) throw new Error(\"replication mint without the bound session\");\n if (session.siwe === undefined) throw new Error(\"compact delegate sessions cannot mint device grants\");\n if (verifiedAnchor?.source !== \"siwe\") throw new Error(\"replication mint requires verified owner consent\");\n const entries = replicationEntries(session.spaceId, prefix);\n const plan = planOrRefusal(entries, options?.expiryMs);\n if (\"refused\" in plan) {\n throw Object.assign(\n new Error(`replication authority refused the mint: ${plan.refused}`),\n { refused: plan.refused },\n );\n }\n if (options?.renewalOnly && plan.path !== \"session\") {\n throw Object.assign(new Error(\"replication renewal requires a session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n signal.throwIfAborted();\n const minted = await new Promise<{ ucan: string; expiresAt: number }>((resolve, reject) => {\n const onAbort = (): void => reject(signal.reason ?? new Error(\"replication mint aborted\"));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n host.mintDelegation(deviceDid, entries, options).then(\n (result) => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve(result);\n },\n (error: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error instanceof Error ? error : new Error(String(error)));\n },\n );\n });\n return { ucan: minted.ucan, parentCid: plan.parentCid, expiresAt: minted.expiresAt };\n },\n };\n return authority;\n}\n","import { MAX_GRANT_TTL_MS } from \"@tinycloud/sdk-services/kv/replication/grantPolicyConstants\";\nimport { assertFolderPrefix as assertPureFolderPrefix, kvPrefixCovers } from \"@tinycloud/sdk-services/kv/replication/prefix\";\nimport type { ReplicationOptions } from \"@tinycloud/sdk-core\";\n\n/** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */\nexport function assertValidReplicationConfig(\n replication: (ReplicationOptions & { storage?: unknown }) | undefined,\n): void {\n if (replication?.syncScope !== undefined && replication.syncScope !== \"prefixes\") {\n throw new TypeError('replication.syncScope must be \"prefixes\"');\n }\n if (replication === undefined || !replication.enabled) return;\n if (replication.storage === undefined || replication.storage === null) {\n throw new TypeError(\"replication.enabled requires replication.storage\");\n }\n if (!Array.isArray(replication.prefixes) || replication.prefixes.length === 0) {\n throw new TypeError(\"replication.prefixes must be a non-empty array\");\n }\n const grantTtlMs = replication.grantTtlMs;\n if (grantTtlMs !== undefined && (!Number.isFinite(grantTtlMs) || grantTtlMs <= 0 || grantTtlMs > MAX_GRANT_TTL_MS)) {\n throw new TypeError(`replication.grantTtlMs must be a positive number of milliseconds no greater than ${MAX_GRANT_TTL_MS}`);\n }\n for (const prefix of replication.prefixes) {\n try {\n assertPureFolderPrefix(prefix);\n } catch (error) {\n throw new TypeError(error instanceof Error ? error.message : String(error));\n }\n }\n for (const [index, prefix] of replication.prefixes.entries()) {\n for (const other of replication.prefixes.slice(index + 1)) {\n if (kvPrefixCovers(prefix, other) || kvPrefixCovers(other, prefix)) {\n throw new TypeError(\n `replication.prefixes must not overlap: ${JSON.stringify(prefix)} and ${JSON.stringify(other)}`,\n );\n }\n }\n if ((prefix === \"/\" || prefix.split(\"/\", 1)[0] === \"vault\") && replication.allowSecrets !== true) {\n throw new TypeError(\n `replication prefix ${JSON.stringify(prefix)} overlaps the vault namespace; pass allowSecrets to opt in`,\n );\n }\n }\n}\n","import {\n KV,\n isCapabilitySubset,\n type PermissionEntry,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport { requiresSecretsOptIn } from \"@tinycloud/sdk-services\";\nimport { assertFolderPrefix } from \"@tinycloud/sdk-services/kv/replication/prefix\";\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\nexport function hasUnrestrictedGetCoverage(\n entries: readonly PermissionEntry[],\n spaceId: string,\n prefix: string,\n): boolean {\n return isCapabilitySubset(\n [{ service: KV_SERVICE, space: spaceId, path: prefix, actions: [KV_GET] }],\n [...entries],\n ).subset;\n}\n\nexport function augmentSignInEntriesWithReplication(input: {\n entries: readonly PermissionEntry[];\n primarySpaceId: string;\n replication?: Pick<ReplicationOptions, \"prefixes\" | \"allowSecrets\" | \"syncScope\">;\n}): PermissionEntry[] {\n const out: PermissionEntry[] = [];\n const replication = input.replication;\n if (replication === undefined) return out;\n const eligiblePaths: string[] = [];\n for (const prefix of replication.prefixes) {\n assertFolderPrefix(prefix);\n const path = prefix === \"/\" ? \"\" : prefix;\n if (\n requiresSecretsOptIn(input.primarySpaceId, prefix) &&\n replication.allowSecrets !== true\n ) {\n continue;\n }\n if (!hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, path)) {\n continue;\n }\n eligiblePaths.push(path);\n }\n if (\n replication.syncScope !== \"prefixes\" &&\n eligiblePaths.length === replication.prefixes.length &&\n eligiblePaths.length > 0 &&\n hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, \"\")\n ) {\n return [{\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path: \"\",\n actions: [KV_GET, KV_SYNC],\n }];\n }\n for (const path of eligiblePaths) {\n out.push({\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path,\n actions: [KV_GET, KV_SYNC],\n });\n }\n return out;\n}\n"],"mappings":"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","names":["import_sdk_core","assertPureFolderPrefix","import_prefix","replicaParseUcanGrant","KV_GET","KV_SYNC","KV_SERVICE"]}
1
+ {"version":3,"sources":["../../src/replication/authority.ts","../../src/replication/config.ts","../../src/replication/authority-sign-in.ts"],"sourcesContent":["/**\n * Replication authority (TC-858, plan v3 §4).\n *\n * `ReplicationAuthority` answers, per configured prefix, whether this node's\n * session can back a local replica: the session UCAN itself (delegate\n * posture), a child UCAN minted through `delegateTo`, or nothing (the\n * controller then reports `grant_missing` and reads from the network).\n *\n * The unrestricted-authority rule is enforced here, before any network work:\n * a prefix covered only by a caveated `get`/`sync` refuses with\n * `CAVEATED_AUTHORITY` — on the session path, the runtime-grant path, and on\n * any reuse of an installed grant (the adapter marks every grant's\n * `unconstrained` flag; the controller enforces it on reuse).\n */\nimport { SiweMessage } from \"siwe\";\n\nimport {\n actionContains,\n KV,\n canonicalizeRecapCaveats,\n isCapabilitySubset,\n principalDid,\n parseSpaceUri,\n type PermissionEntry,\n type IWasmBindings,\n type WasmRecapEntry,\n type TinyCloudSession,\n type KVReplicaStorage,\n type ServiceContext,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport {\n type AuthorityRefusal,\n type ReplicaConsentAnchor,\n type ReplicaDevice,\n type ReplicationAuthority,\n type ReplicationEvent,\n} from \"@tinycloud/sdk-services\";\nimport type {\n ReplicaDenyInput,\n ReplicaLocalEraseOutcome,\n ReplicaPurgeFailedEvent,\n ReplicaPurgeLocalCopyResult,\n ReplicaPurgeTrigger,\n ReplicaSessionStamp,\n} from \"@tinycloud/sdk-services/kv/replication\";\nimport type { DelegateToOptions, DelegateToResult, TinyCloudNode } from \"../TinyCloudNode\";\nimport type { ReplicaRuntime } from \"./replica-contract\";\n// @ts-ignore -- node-sdk builds before replica declarations; keep the literal import for bundlers.\nimport { parseUcanGrant as replicaParseUcanGrant } from \"@tinycloud/replica\";\nconst parseUcanGrant = replicaParseUcanGrant as ReplicaRuntime[\"parseUcanGrant\"];\nexport { assertValidReplicationConfig } from \"./config\";\nexport { augmentSignInEntriesWithReplication, hasUnrestrictedGetCoverage } from \"./authority-sign-in\";\n\n/** Report a completed deny from the already-loaded authority; delivery was revoked synchronously. */\nexport function reportReplicaDenial(\n result: { status: string; code?: string; intents?: number },\n reason: ReplicaDenyInput[\"reason\"],\n onEvent: ReplicationOptions[\"onEvent\"],\n context?: ServiceContext,\n): void {\n if (result.status !== \"denied\" && result.status !== \"deleted\" && result.status !== \"marker\") return;\n const event: Extract<ReplicationEvent, { type: \"replication.lifecycle\" }> = {\n type: \"replication.lifecycle\",\n at: new Date().toISOString(),\n reason,\n action: result.status === \"denied\" ? \"denied\" : result.status === \"deleted\" ? \"database_deleted\" : \"marker_set\",\n ...(result.status === \"denied\" ? { partitions: result.intents } : {}),\n ...(result.status === \"marker\" ? { code: result.code } : {}),\n };\n try { onEvent?.(event); } catch { /* Observability cannot undo committed denial. */ }\n const { type, ...data } = event;\n try { context?.emit(type, data); } catch { /* Observability cannot undo committed denial. */ }\n}\n\n/** What the root needs after a local-copy erase: the outcome, the `purgeLocalCopy()` result and the marker state. */\nexport interface ReplicaLocalEraseReport {\n outcome: ReplicaLocalEraseOutcome;\n purge: ReplicaPurgeLocalCopyResult;\n /** The logout marker is set: the next load finishes the erase and the replica stays gated meanwhile. */\n logoutPending?: true;\n}\n\n/** Local-copy erase after a deny: 3 attempts in total, retries 150 ms then 450 ms apart, 3 s per attempt. */\nconst ERASE_RETRY_DELAYS_MS = [150, 450] as const;\nconst ERASE_ATTEMPT_MS = 3_000;\n\n/**\n * Erases the browser local copy after a deny, retrying a `failed` outcome with the delays above. After\n * the last failure (or a refusal while the deny kept the keys) it notifies `handlers` with\n * `replica.purge.failed`; the logout marker the lifecycle set makes the next load finish the erase.\n * Never rejects. `undefined`: this storage has no browser local copy to erase.\n */\nexport async function eraseReplicaLocalCopy(\n lifecycle: KVReplicaStorage[\"lifecycle\"],\n trigger: ReplicaPurgeTrigger,\n denyStatus: string | undefined,\n handlers: ReadonlySet<(event: ReplicaPurgeFailedEvent) => void>,\n): Promise<ReplicaLocalEraseReport | undefined> {\n if (lifecycle?.kind !== \"indexeddb\" || lifecycle.eraseLocalCopy === undefined) return undefined;\n let outcome: ReplicaLocalEraseOutcome = { status: \"failed\", code: \"STORAGE_ERROR\", marker: false };\n for (let attempt = 0; attempt <= ERASE_RETRY_DELAYS_MS.length; attempt++) {\n if (attempt > 0) await new Promise<void>((resolve) => setTimeout(resolve, ERASE_RETRY_DELAYS_MS[attempt - 1]));\n try {\n outcome = await lifecycle.eraseLocalCopy({ signal: AbortSignal.timeout(ERASE_ATTEMPT_MS) });\n } catch {\n outcome = { status: \"failed\", code: \"STORAGE_ERROR\", marker: false };\n }\n if (outcome.status !== \"failed\") break;\n }\n const keysKept = denyStatus === \"marker\" || denyStatus === \"unguarded\";\n const purge: ReplicaPurgeLocalCopyResult = outcome.status === \"erased\"\n ? { ok: true }\n : { ok: false, retryNow: outcome.status === \"failed\" };\n if (outcome.status === \"erased\" || (outcome.status === \"refused\" && !keysKept)) return { outcome, purge };\n const logoutPending = outcome.status === \"failed\" && outcome.marker;\n const event: ReplicaPurgeFailedEvent = {\n type: \"replica.purge.failed\",\n trigger,\n reason: logoutPending ? \"logout_pending\" : \"gate_unavailable\",\n code: outcome.code,\n retryNow: outcome.status === \"failed\",\n };\n for (const handler of handlers) {\n try {\n handler(event);\n } catch (error) {\n console.error('Error in \"replica.purge.failed\" handler:', error);\n }\n }\n return logoutPending ? { outcome, purge, logoutPending } : { outcome, purge };\n}\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\n/** The verified owner proof is signed, finite, exact-space, and uncaveated on one get+sync scope. */\nexport function verifiedOwnerConsentAnchor(\n session: TinyCloudSession,\n wasmBindings: Pick<IWasmBindings, \"validatePersistedSession\">,\n exactVerifiedRecap: (verified: { verifiedRecap?: unknown }) => WasmRecapEntry[],\n): ReplicaConsentAnchor | null {\n if (typeof session.siwe !== \"string\" || typeof session.signature !== \"string\" ||\n typeof session.address !== \"string\" || typeof session.chainId !== \"number\") return null;\n try {\n const verified = wasmBindings.validatePersistedSession?.({\n delegationHeader: session.delegationHeader,\n delegationCid: session.delegationCid,\n spaceId: session.spaceId,\n jwk: session.jwk,\n address: session.address,\n chainId: session.chainId,\n siwe: session.siwe,\n signature: session.signature,\n });\n if (!verified || !Number.isSafeInteger(Date.parse(verified.expiresAt ?? \"\"))) return null;\n const signedIssuedAt = new SiweMessage(session.siwe).issuedAt;\n if (typeof signedIssuedAt !== \"string\") return null;\n const issuedAt = Date.parse(signedIssuedAt);\n if (!Number.isSafeInteger(issuedAt) || issuedAt < 0) return null;\n const granted = exactVerifiedRecap(verified)\n .filter((entry) => entry.space === session.spaceId && entry.service === \"kv\")\n .map((entry) => ({ ...entry, service: \"tinycloud.kv\" }));\n const coversGetAndSync = granted.some((entry) => isCapabilitySubset([{\n service: \"tinycloud.kv\",\n space: session.spaceId,\n path: entry.path,\n actions: [KV.GET, KV.SYNC],\n }], granted).subset);\n return coversGetAndSync ? { at: issuedAt, source: \"siwe\" } : null;\n } catch {\n return null;\n }\n}\n\n/** Fresh sign-in only: the lifecycle creates keys; restore never calls this. */\nexport function establishReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n anchor: ReplicaConsentAnchor,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n throw Object.assign(new Error(\"Replica sign-in requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** Existing persisted stamp only: never manufactures an authority on restore. */\nexport function restoreReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n persisted: ReplicaSessionStamp | undefined,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n throw Object.assign(new Error(\"Replica restore requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** The delegation the replication grant delegates: get + sync on one folder prefix. */\nfunction replicationEntries(spaceId: string, prefix: string): PermissionEntry[] {\n return [{\n service: KV_SERVICE,\n space: spaceId,\n path: prefix === \"/\" ? \"\" : prefix,\n actions: [KV_GET, KV_SYNC],\n }];\n}\n\n\n\n\n/**\n * The signed UCAN attenuation (`att`: resource → ability → caveat branches).\n * Only array branch sets are known; anything else is treated as constrained —\n * fail closed, never assume unconstrained.\n */\nfunction caveatBranchesUnconstrained(value: unknown): boolean {\n if (value === undefined || value === null) return true;\n if (typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length === 0) {\n return true;\n }\n if (!Array.isArray(value)) return false;\n try {\n return (\n canonicalizeRecapCaveats(value as Record<string, unknown>[]) ===\n canonicalizeRecapCaveats(undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** The KV path a signed UCAN resource names in `space`, like replica's `kvPathIn`. */\nfunction kvPathInResource(resource: string, space: string): string | undefined {\n for (const root of [`${space}/kv`, `tinycloud://${space}/kv`]) {\n if (resource === root || resource === `${root}/`) return \"/\";\n }\n for (const base of [`${space}/kv/`, `tinycloud://${space}/kv/`]) {\n if (resource.startsWith(base)) return resource.slice(base.length);\n }\n return undefined;\n\n}\n/**\n * Whether the signed `att` grants `ability` over the folder prefix, with\n * unrestricted authority unless explicitly asked to ignore caveats.\n */\nexport function ucanAttCovers(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n ability: string,\n options?: { ignoreCaveats?: boolean },\n): boolean {\n for (const [resource, abilities] of Object.entries(att)) {\n const path = kvPathInResource(resource, space);\n if (path === undefined) continue;\n for (const [granted, caveats] of Object.entries(abilities)) {\n if (!actionContains(granted, ability)) continue;\n if (!isCapabilitySubset(\n [{ service: KV_SERVICE, space, path: prefix, actions: [ability] }],\n [{ service: KV_SERVICE, space, path, actions: [granted] }],\n ).subset) continue;\n if (options?.ignoreCaveats === true || caveatBranchesUnconstrained(caveats)) return true;\n }\n }\n return false;\n}\n\n/** `unconstrained` for `ReplicaGrantInfo` (§2.1): get AND sync on the prefix carry no caveats. */\nexport function ucanAttUnconstrainedFor(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n): boolean {\n return (\n ucanAttCovers(att, space, prefix, KV_GET) &&\n ucanAttCovers(att, space, prefix, KV_SYNC)\n );\n}\n\nexport interface DelegationPlanResult {\n path: \"session\" | \"runtime\";\n parentCid: string;\n expiresAt: number;\n}\n\n/**\n * The `TinyCloudNode` internals the authority needs. `planDelegation` is the\n * pure extraction of `delegateTo`'s parent selection (§4.3), so `plan` and\n * `mint` can never disagree about which parent or expiry a delegation gets.\n */\nexport type ReplicationAuthoritySession = Pick<\n TinyCloudSession,\n \"delegationHeader\" | \"delegationCid\" | \"spaceId\" | \"verificationMethod\" | \"jwk\"\n> & Partial<Pick<TinyCloudSession, \"siwe\" | \"signature\" | \"address\" | \"chainId\">>;\n\nexport interface ReplicationAuthorityHost {\n replicationSession(): ReplicationAuthoritySession | undefined;\n /** Validated owner proof only; never infer consent from a compact child UCAN. */\n verifiedConsentAnchor?(session: ReplicationAuthoritySession): ReplicaConsentAnchor | null;\n /** The SIWE \"Expiration Time\" of the session — the delegateTo delegation cap. */\n siweExpiration(siwe: string): Date | undefined;\n planDelegation(\n entries: PermissionEntry[],\n options?: { expiry?: string | number },\n ): ({ grant?: unknown } & DelegationPlanResult) | AuthorityRefusal;\n mintDelegation(\n deviceDid: string,\n entries: PermissionEntry[],\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; expiresAt: number }>;\n}\n\n/** Delegate a replica grant only through the selected owner parent for silent renewal. */\nexport async function mintNodeReplicationGrant(\n deviceDid: string,\n entries: PermissionEntry[],\n options: { expiryMs?: number; renewalOnly?: boolean } | undefined,\n renewalSession: ReplicationAuthoritySession | undefined,\n node: Pick<TinyCloudNode, \"delegateTo\">,\n): Promise<{ ucan: string; expiresAt: number }> {\n if (options?.renewalOnly && !renewalSession) throw new Error(\"replication renewal lost its owner session\");\n const delegateOptions: DelegateToOptions | undefined = options?.renewalOnly ? {\n expiry: options.expiryMs,\n onPrepared: async (prepared) => {\n assertRenewalParent(prepared.delegationHeader.Authorization, renewalSession!);\n },\n } : options?.expiryMs === undefined ? undefined : { expiry: options.expiryMs };\n const result: DelegateToResult = await node.delegateTo(principalDid(deviceDid), entries, delegateOptions);\n return {\n ucan: result.delegation.delegationHeader.Authorization,\n expiresAt: result.delegation.expiry.getTime(),\n };\n}\n\n/** Milliseconds of validity a session or parent must have left for authority reuse (delegateTo's margin). */\nexport const AUTHORITY_EXPIRY_MARGIN_MS = 60_000;\n\n\n/** Verify a compact session delegation before using any persisted space metadata. */\nexport function replicationScopeFromSignedSession(input: {\n delegationHeader: { Authorization: string };\n delegationCid: string;\n verificationMethod: string;\n persistedSpace: string;\n}): { space: string; principal: string } | undefined {\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n grant = parseUcanGrant(input.delegationHeader.Authorization);\n } catch {\n return undefined;\n }\n const bareDid = (value: string): string => value.split(\"#\", 1)[0]!;\n if (\n grant.cid !== input.delegationCid ||\n bareDid(grant.audience) !== bareDid(input.verificationMethod)\n ) return undefined;\n for (const resource of Object.keys(grant.att)) {\n const space = resource.match(/^(?:tinycloud:\\/\\/)?(.+?)\\/kv(?:\\/|$)/)?.[1];\n if (space !== input.persistedSpace) continue;\n const owner = parseSpaceUri(space);\n if (owner?.owner && owner.address && owner.chainId) return { space, principal: owner.owner };\n }\n return undefined;\n}\n/** Runs before host activation; a replan cannot silently switch a renewal to a runtime parent. */\nexport function assertRenewalParent(authorization: string, session: ReplicationAuthoritySession): void {\n const grant = parseUcanGrant(authorization);\n if (\n grant.issuer !== session.verificationMethod.split(\"#\", 1)[0] ||\n grant.prf.length !== 1 ||\n grant.prf[0] !== session.delegationCid\n ) {\n throw Object.assign(new Error(\"replication renewal requires the bound owner session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n}\n\nexport function createReplicationAuthority(host: ReplicationAuthorityHost): ReplicationAuthority {\n const boundSession = host.replicationSession();\n let verifiedAnchor: ReplicaConsentAnchor | null = null;\n try {\n const candidate = boundSession && host.verifiedConsentAnchor?.(boundSession);\n if (candidate && Number.isFinite(candidate.at) && candidate.source !== \"first_install\") {\n verifiedAnchor = { at: candidate.at, source: candidate.source };\n }\n } catch {\n // Network sessions remain usable when the optional replica proof cannot be verified.\n }\n const activeSession = (): ReplicationAuthoritySession | undefined => {\n const session = host.replicationSession();\n return session !== undefined && session === boundSession ? session : undefined;\n };\n const planOrRefusal = (entries: PermissionEntry[], expiryMs?: number): DelegationPlanResult | AuthorityRefusal => {\n const plan = host.planDelegation(entries, expiryMs === undefined ? undefined : { expiry: expiryMs });\n if (\"refused\" in plan) return plan;\n return { path: plan.path, parentCid: plan.parentCid, expiresAt: plan.expiresAt };\n };\n const sessionAudience = (value: string): string => value.split(\"#\", 1)[0]!;\n const signedSessionGrant = (session: ReplicationAuthoritySession) => {\n const grant = parseUcanGrant(session.delegationHeader.Authorization);\n if (\n grant.cid !== session.delegationCid ||\n sessionAudience(grant.audience) !== sessionAudience(session.verificationMethod)\n ) return undefined;\n return grant;\n };\n const authority: ReplicationAuthority = {\n get sessionOnly() {\n const session = activeSession();\n return session === undefined || session.siwe === undefined;\n },\n consentAnchor(): ReplicaConsentAnchor | null {\n return activeSession() ? verifiedAnchor : null;\n },\n get canRenew() {\n const session = activeSession();\n return verifiedAnchor?.source === \"siwe\" && session !== undefined && session.siwe !== undefined;\n },\n sessionGrant(prefix: string): { ucan: string; device: ReplicaDevice } | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n // A compact session proves current signed coverage, not consent to install or mint.\n if (verifiedAnchor === null && session.siwe !== undefined) return { refused: \"NOT_COVERED\" };\n const siweExpiry = session.siwe ? host.siweExpiration(session.siwe)?.getTime() : undefined;\n if (siweExpiry !== undefined && siweExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n const verified = signedSessionGrant(session);\n if (!verified) return { refused: \"NOT_COVERED\" };\n grant = verified;\n } catch {\n return { refused: \"NOT_COVERED\" };\n }\n const tokenExpiry = grant.expiresAt === null ? undefined : grant.expiresAt * 1000;\n if (tokenExpiry === undefined || tokenExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n const covered = ucanAttCovers(grant.att, session.spaceId, prefix, KV_GET, { ignoreCaveats: true }) &&\n ucanAttCovers(grant.att, session.spaceId, prefix, KV_SYNC, { ignoreCaveats: true });\n if (!covered) return { refused: \"NOT_COVERED\" };\n if (!ucanAttUnconstrainedFor(grant.att, session.spaceId, prefix)) {\n return { refused: \"CAVEATED_AUTHORITY\" };\n }\n return {\n ucan: grant.jwt,\n device: { did: session.verificationMethod, jwk: session.jwk },\n };\n },\n plan(prefix: string): DelegationPlanResult | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n if (verifiedAnchor === null) return { refused: \"NOT_COVERED\" };\n const sessionGrant = this.sessionGrant(prefix);\n if (!(\"refused\" in sessionGrant)) {\n const signed = signedSessionGrant(session);\n if (!signed || signed.expiresAt === null) return { refused: \"NOT_COVERED\" };\n return {\n path: \"session\",\n parentCid: session.delegationCid,\n expiresAt: signed.expiresAt * 1000,\n };\n }\n if (authority.sessionOnly) return sessionGrant;\n return planOrRefusal(replicationEntries(session.spaceId, prefix));\n },\n async mint(\n deviceDid: string,\n prefix: string,\n signal: AbortSignal,\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; parentCid: string; expiresAt: number }> {\n const session = activeSession();\n if (session === undefined) throw new Error(\"replication mint without the bound session\");\n if (session.siwe === undefined) throw new Error(\"compact delegate sessions cannot mint device grants\");\n if (verifiedAnchor?.source !== \"siwe\") throw new Error(\"replication mint requires verified owner consent\");\n const entries = replicationEntries(session.spaceId, prefix);\n const plan = planOrRefusal(entries, options?.expiryMs);\n if (\"refused\" in plan) {\n throw Object.assign(\n new Error(`replication authority refused the mint: ${plan.refused}`),\n { refused: plan.refused },\n );\n }\n if (options?.renewalOnly && plan.path !== \"session\") {\n throw Object.assign(new Error(\"replication renewal requires a session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n signal.throwIfAborted();\n const minted = await new Promise<{ ucan: string; expiresAt: number }>((resolve, reject) => {\n const onAbort = (): void => reject(signal.reason ?? new Error(\"replication mint aborted\"));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n host.mintDelegation(deviceDid, entries, options).then(\n (result) => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve(result);\n },\n (error: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error instanceof Error ? error : new Error(String(error)));\n },\n );\n });\n return { ucan: minted.ucan, parentCid: plan.parentCid, expiresAt: minted.expiresAt };\n },\n };\n return authority;\n}\n","import { MAX_GRANT_TTL_MS } from \"@tinycloud/sdk-services/kv/replication/grantPolicyConstants\";\nimport { assertFolderPrefix as assertPureFolderPrefix, kvPrefixCovers } from \"@tinycloud/sdk-services/kv/replication/prefix\";\nimport type { ReplicationOptions } from \"@tinycloud/sdk-core\";\n\n/** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */\nexport function assertValidReplicationConfig(\n replication: (ReplicationOptions & { storage?: unknown }) | undefined,\n): void {\n if (replication?.syncScope !== undefined && replication.syncScope !== \"prefixes\") {\n throw new TypeError('replication.syncScope must be \"prefixes\"');\n }\n if (replication?.contentFetch !== undefined && replication.contentFetch !== \"eager\" && replication.contentFetch !== \"lazy\") {\n throw new TypeError('replication.contentFetch must be \"eager\" or \"lazy\"');\n }\n if (replication === undefined || !replication.enabled) return;\n if (replication.storage === undefined || replication.storage === null) {\n throw new TypeError(\"replication.enabled requires replication.storage\");\n }\n if (!Array.isArray(replication.prefixes) || replication.prefixes.length === 0) {\n throw new TypeError(\"replication.prefixes must be a non-empty array\");\n }\n const grantTtlMs = replication.grantTtlMs;\n if (grantTtlMs !== undefined && (!Number.isFinite(grantTtlMs) || grantTtlMs <= 0 || grantTtlMs > MAX_GRANT_TTL_MS)) {\n throw new TypeError(`replication.grantTtlMs must be a positive number of milliseconds no greater than ${MAX_GRANT_TTL_MS}`);\n }\n for (const prefix of replication.prefixes) {\n try {\n assertPureFolderPrefix(prefix);\n } catch (error) {\n throw new TypeError(error instanceof Error ? error.message : String(error));\n }\n }\n for (const [index, prefix] of replication.prefixes.entries()) {\n for (const other of replication.prefixes.slice(index + 1)) {\n if (kvPrefixCovers(prefix, other) || kvPrefixCovers(other, prefix)) {\n throw new TypeError(\n `replication.prefixes must not overlap: ${JSON.stringify(prefix)} and ${JSON.stringify(other)}`,\n );\n }\n }\n if ((prefix === \"/\" || prefix.split(\"/\", 1)[0] === \"vault\") && replication.allowSecrets !== true) {\n throw new TypeError(\n `replication prefix ${JSON.stringify(prefix)} overlaps the vault namespace; pass allowSecrets to opt in`,\n );\n }\n }\n}\n","import {\n KV,\n isCapabilitySubset,\n type PermissionEntry,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport { requiresSecretsOptIn } from \"@tinycloud/sdk-services\";\nimport { assertFolderPrefix } from \"@tinycloud/sdk-services/kv/replication/prefix\";\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\nexport function hasUnrestrictedGetCoverage(\n entries: readonly PermissionEntry[],\n spaceId: string,\n prefix: string,\n): boolean {\n return isCapabilitySubset(\n [{ service: KV_SERVICE, space: spaceId, path: prefix, actions: [KV_GET] }],\n [...entries],\n ).subset;\n}\n\nexport function augmentSignInEntriesWithReplication(input: {\n entries: readonly PermissionEntry[];\n primarySpaceId: string;\n replication?: Pick<ReplicationOptions, \"prefixes\" | \"allowSecrets\" | \"syncScope\">;\n}): PermissionEntry[] {\n const out: PermissionEntry[] = [];\n const replication = input.replication;\n if (replication === undefined) return out;\n const eligiblePaths: string[] = [];\n for (const prefix of replication.prefixes) {\n assertFolderPrefix(prefix);\n const path = prefix === \"/\" ? \"\" : prefix;\n if (\n requiresSecretsOptIn(input.primarySpaceId, prefix) &&\n replication.allowSecrets !== true\n ) {\n continue;\n }\n if (!hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, path)) {\n continue;\n }\n eligiblePaths.push(path);\n }\n if (\n replication.syncScope !== \"prefixes\" &&\n eligiblePaths.length === replication.prefixes.length &&\n eligiblePaths.length > 0 &&\n hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, \"\")\n ) {\n return [{\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path: \"\",\n actions: [KV_GET, KV_SYNC],\n }];\n }\n for (const path of eligiblePaths) {\n out.push({\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path,\n actions: [KV_GET, KV_SYNC],\n });\n }\n return out;\n}\n"],"mappings":"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","names":["import_sdk_core","assertPureFolderPrefix","import_prefix","replicaParseUcanGrant","KV_GET","KV_SYNC","KV_SERVICE"]}
@@ -1,7 +1,8 @@
1
1
  import '@tinycloud/sdk-core';
2
2
  import '@tinycloud/sdk-services';
3
3
  import '@tinycloud/sdk-services/kv/replication';
4
- import '../TinyCloudNode-DDfx8Fx5.cjs';
5
- export { A as AUTHORITY_EXPIRY_MARGIN_MS, D as DelegationPlanResult, R as ReplicationAuthorityHost, a as ReplicationAuthoritySession, b as assertRenewalParent, c as assertValidReplicationConfig, d as augmentSignInEntriesWithReplication, e as createReplicationAuthority, f as establishReplicaSessionStamp, h as hasUnrestrictedGetCoverage, m as mintNodeReplicationGrant, r as replicationScopeFromSignedSession, g as reportReplicaDenial, i as restoreReplicaSessionStamp, u as ucanAttCovers, j as ucanAttUnconstrainedFor, v as verifiedOwnerConsentAnchor } from '../authority-BwM_s3Ya.cjs';
4
+ import '../TinyCloudNode-BVDUdEcf.cjs';
5
+ export { A as AUTHORITY_EXPIRY_MARGIN_MS, D as DelegationPlanResult, R as ReplicaLocalEraseReport, a as ReplicationAuthorityHost, b as ReplicationAuthoritySession, c as assertRenewalParent, d as assertValidReplicationConfig, e as augmentSignInEntriesWithReplication, f as createReplicationAuthority, g as eraseReplicaLocalCopy, h as establishReplicaSessionStamp, i as hasUnrestrictedGetCoverage, m as mintNodeReplicationGrant, r as replicationScopeFromSignedSession, j as reportReplicaDenial, k as restoreReplicaSessionStamp, u as ucanAttCovers, l as ucanAttUnconstrainedFor, v as verifiedOwnerConsentAnchor } from '../authority-DJ-PE4Xo.cjs';
6
+ import '@tinycloud/sdk-services/encryption/named-keys';
6
7
  import 'events';
7
- import '../delegation-0LKZkmP2.cjs';
8
+ import '../delegation-Ckc3UETl.cjs';
@@ -1,7 +1,8 @@
1
1
  import '@tinycloud/sdk-core';
2
2
  import '@tinycloud/sdk-services';
3
3
  import '@tinycloud/sdk-services/kv/replication';
4
- import '../TinyCloudNode-Ckqu_fqH.js';
5
- export { A as AUTHORITY_EXPIRY_MARGIN_MS, D as DelegationPlanResult, R as ReplicationAuthorityHost, a as ReplicationAuthoritySession, b as assertRenewalParent, c as assertValidReplicationConfig, d as augmentSignInEntriesWithReplication, e as createReplicationAuthority, f as establishReplicaSessionStamp, h as hasUnrestrictedGetCoverage, m as mintNodeReplicationGrant, r as replicationScopeFromSignedSession, g as reportReplicaDenial, i as restoreReplicaSessionStamp, u as ucanAttCovers, j as ucanAttUnconstrainedFor, v as verifiedOwnerConsentAnchor } from '../authority-DcyJpajL.js';
4
+ import '../TinyCloudNode-jMK_2Uj8.js';
5
+ export { A as AUTHORITY_EXPIRY_MARGIN_MS, D as DelegationPlanResult, R as ReplicaLocalEraseReport, a as ReplicationAuthorityHost, b as ReplicationAuthoritySession, c as assertRenewalParent, d as assertValidReplicationConfig, e as augmentSignInEntriesWithReplication, f as createReplicationAuthority, g as eraseReplicaLocalCopy, h as establishReplicaSessionStamp, i as hasUnrestrictedGetCoverage, m as mintNodeReplicationGrant, r as replicationScopeFromSignedSession, j as reportReplicaDenial, k as restoreReplicaSessionStamp, u as ucanAttCovers, l as ucanAttUnconstrainedFor, v as verifiedOwnerConsentAnchor } from '../authority-i22vHbbp.js';
6
+ import '@tinycloud/sdk-services/encryption/named-keys';
6
7
  import 'events';
7
- import '../delegation-0LKZkmP2.js';
8
+ import '../delegation-Ckc3UETl.js';
@@ -17,6 +17,9 @@ function assertValidReplicationConfig(replication) {
17
17
  if (replication?.syncScope !== void 0 && replication.syncScope !== "prefixes") {
18
18
  throw new TypeError('replication.syncScope must be "prefixes"');
19
19
  }
20
+ if (replication?.contentFetch !== void 0 && replication.contentFetch !== "eager" && replication.contentFetch !== "lazy") {
21
+ throw new TypeError('replication.contentFetch must be "eager" or "lazy"');
22
+ }
20
23
  if (replication === void 0 || !replication.enabled) return;
21
24
  if (replication.storage === void 0 || replication.storage === null) {
22
25
  throw new TypeError("replication.enabled requires replication.storage");
@@ -124,6 +127,40 @@ function reportReplicaDenial(result, reason, onEvent, context) {
124
127
  } catch {
125
128
  }
126
129
  }
130
+ var ERASE_RETRY_DELAYS_MS = [150, 450];
131
+ var ERASE_ATTEMPT_MS = 3e3;
132
+ async function eraseReplicaLocalCopy(lifecycle, trigger, denyStatus, handlers) {
133
+ if (lifecycle?.kind !== "indexeddb" || lifecycle.eraseLocalCopy === void 0) return void 0;
134
+ let outcome = { status: "failed", code: "STORAGE_ERROR", marker: false };
135
+ for (let attempt = 0; attempt <= ERASE_RETRY_DELAYS_MS.length; attempt++) {
136
+ if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, ERASE_RETRY_DELAYS_MS[attempt - 1]));
137
+ try {
138
+ outcome = await lifecycle.eraseLocalCopy({ signal: AbortSignal.timeout(ERASE_ATTEMPT_MS) });
139
+ } catch {
140
+ outcome = { status: "failed", code: "STORAGE_ERROR", marker: false };
141
+ }
142
+ if (outcome.status !== "failed") break;
143
+ }
144
+ const keysKept = denyStatus === "marker" || denyStatus === "unguarded";
145
+ const purge = outcome.status === "erased" ? { ok: true } : { ok: false, retryNow: outcome.status === "failed" };
146
+ if (outcome.status === "erased" || outcome.status === "refused" && !keysKept) return { outcome, purge };
147
+ const logoutPending = outcome.status === "failed" && outcome.marker;
148
+ const event = {
149
+ type: "replica.purge.failed",
150
+ trigger,
151
+ reason: logoutPending ? "logout_pending" : "gate_unavailable",
152
+ code: outcome.code,
153
+ retryNow: outcome.status === "failed"
154
+ };
155
+ for (const handler of handlers) {
156
+ try {
157
+ handler(event);
158
+ } catch (error) {
159
+ console.error('Error in "replica.purge.failed" handler:', error);
160
+ }
161
+ }
162
+ return logoutPending ? { outcome, purge, logoutPending } : { outcome, purge };
163
+ }
127
164
  var KV_GET2 = KV2.GET;
128
165
  var KV_SYNC2 = KV2.SYNC;
129
166
  var KV_SERVICE2 = "tinycloud.kv";
@@ -392,6 +429,7 @@ export {
392
429
  assertValidReplicationConfig,
393
430
  augmentSignInEntriesWithReplication,
394
431
  createReplicationAuthority,
432
+ eraseReplicaLocalCopy,
395
433
  establishReplicaSessionStamp,
396
434
  hasUnrestrictedGetCoverage,
397
435
  mintNodeReplicationGrant,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/replication/authority.ts","../../src/replication/config.ts","../../src/replication/authority-sign-in.ts"],"sourcesContent":["/**\n * Replication authority (TC-858, plan v3 §4).\n *\n * `ReplicationAuthority` answers, per configured prefix, whether this node's\n * session can back a local replica: the session UCAN itself (delegate\n * posture), a child UCAN minted through `delegateTo`, or nothing (the\n * controller then reports `grant_missing` and reads from the network).\n *\n * The unrestricted-authority rule is enforced here, before any network work:\n * a prefix covered only by a caveated `get`/`sync` refuses with\n * `CAVEATED_AUTHORITY` — on the session path, the runtime-grant path, and on\n * any reuse of an installed grant (the adapter marks every grant's\n * `unconstrained` flag; the controller enforces it on reuse).\n */\nimport { SiweMessage } from \"siwe\";\n\nimport {\n actionContains,\n KV,\n canonicalizeRecapCaveats,\n isCapabilitySubset,\n principalDid,\n parseSpaceUri,\n type PermissionEntry,\n type IWasmBindings,\n type WasmRecapEntry,\n type TinyCloudSession,\n type KVReplicaStorage,\n type ServiceContext,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport {\n type AuthorityRefusal,\n type ReplicaConsentAnchor,\n type ReplicaDevice,\n type ReplicationAuthority,\n type ReplicationEvent,\n} from \"@tinycloud/sdk-services\";\nimport type { ReplicaDenyInput, ReplicaSessionStamp } from \"@tinycloud/sdk-services/kv/replication\";\nimport type { DelegateToOptions, DelegateToResult, TinyCloudNode } from \"../TinyCloudNode\";\nimport type { ReplicaRuntime } from \"./replica-contract\";\n// @ts-ignore -- node-sdk builds before replica declarations; keep the literal import for bundlers.\nimport { parseUcanGrant as replicaParseUcanGrant } from \"@tinycloud/replica\";\nconst parseUcanGrant = replicaParseUcanGrant as ReplicaRuntime[\"parseUcanGrant\"];\nexport { assertValidReplicationConfig } from \"./config\";\nexport { augmentSignInEntriesWithReplication, hasUnrestrictedGetCoverage } from \"./authority-sign-in\";\n\n/** Report a completed deny from the already-loaded authority; delivery was revoked synchronously. */\nexport function reportReplicaDenial(\n result: { status: string; code?: string; intents?: number },\n reason: ReplicaDenyInput[\"reason\"],\n onEvent: ReplicationOptions[\"onEvent\"],\n context?: ServiceContext,\n): void {\n if (result.status !== \"denied\" && result.status !== \"deleted\" && result.status !== \"marker\") return;\n const event: Extract<ReplicationEvent, { type: \"replication.lifecycle\" }> = {\n type: \"replication.lifecycle\",\n at: new Date().toISOString(),\n reason,\n action: result.status === \"denied\" ? \"denied\" : result.status === \"deleted\" ? \"database_deleted\" : \"marker_set\",\n ...(result.status === \"denied\" ? { partitions: result.intents } : {}),\n ...(result.status === \"marker\" ? { code: result.code } : {}),\n };\n try { onEvent?.(event); } catch { /* Observability cannot undo committed denial. */ }\n const { type, ...data } = event;\n try { context?.emit(type, data); } catch { /* Observability cannot undo committed denial. */ }\n}\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\n/** The verified owner proof is signed, finite, exact-space, and uncaveated on one get+sync scope. */\nexport function verifiedOwnerConsentAnchor(\n session: TinyCloudSession,\n wasmBindings: Pick<IWasmBindings, \"validatePersistedSession\">,\n exactVerifiedRecap: (verified: { verifiedRecap?: unknown }) => WasmRecapEntry[],\n): ReplicaConsentAnchor | null {\n if (typeof session.siwe !== \"string\" || typeof session.signature !== \"string\" ||\n typeof session.address !== \"string\" || typeof session.chainId !== \"number\") return null;\n try {\n const verified = wasmBindings.validatePersistedSession?.({\n delegationHeader: session.delegationHeader,\n delegationCid: session.delegationCid,\n spaceId: session.spaceId,\n jwk: session.jwk,\n address: session.address,\n chainId: session.chainId,\n siwe: session.siwe,\n signature: session.signature,\n });\n if (!verified || !Number.isSafeInteger(Date.parse(verified.expiresAt ?? \"\"))) return null;\n const signedIssuedAt = new SiweMessage(session.siwe).issuedAt;\n if (typeof signedIssuedAt !== \"string\") return null;\n const issuedAt = Date.parse(signedIssuedAt);\n if (!Number.isSafeInteger(issuedAt) || issuedAt < 0) return null;\n const granted = exactVerifiedRecap(verified)\n .filter((entry) => entry.space === session.spaceId && entry.service === \"kv\")\n .map((entry) => ({ ...entry, service: \"tinycloud.kv\" }));\n const coversGetAndSync = granted.some((entry) => isCapabilitySubset([{\n service: \"tinycloud.kv\",\n space: session.spaceId,\n path: entry.path,\n actions: [KV.GET, KV.SYNC],\n }], granted).subset);\n return coversGetAndSync ? { at: issuedAt, source: \"siwe\" } : null;\n } catch {\n return null;\n }\n}\n\n/** Fresh sign-in only: the lifecycle creates keys; restore never calls this. */\nexport function establishReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n anchor: ReplicaConsentAnchor,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n throw Object.assign(new Error(\"Replica sign-in requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** Existing persisted stamp only: never manufactures an authority on restore. */\nexport function restoreReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n persisted: ReplicaSessionStamp | undefined,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n throw Object.assign(new Error(\"Replica restore requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** The delegation the replication grant delegates: get + sync on one folder prefix. */\nfunction replicationEntries(spaceId: string, prefix: string): PermissionEntry[] {\n return [{\n service: KV_SERVICE,\n space: spaceId,\n path: prefix === \"/\" ? \"\" : prefix,\n actions: [KV_GET, KV_SYNC],\n }];\n}\n\n\n\n\n/**\n * The signed UCAN attenuation (`att`: resource → ability → caveat branches).\n * Only array branch sets are known; anything else is treated as constrained —\n * fail closed, never assume unconstrained.\n */\nfunction caveatBranchesUnconstrained(value: unknown): boolean {\n if (value === undefined || value === null) return true;\n if (typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length === 0) {\n return true;\n }\n if (!Array.isArray(value)) return false;\n try {\n return (\n canonicalizeRecapCaveats(value as Record<string, unknown>[]) ===\n canonicalizeRecapCaveats(undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** The KV path a signed UCAN resource names in `space`, like replica's `kvPathIn`. */\nfunction kvPathInResource(resource: string, space: string): string | undefined {\n for (const root of [`${space}/kv`, `tinycloud://${space}/kv`]) {\n if (resource === root || resource === `${root}/`) return \"/\";\n }\n for (const base of [`${space}/kv/`, `tinycloud://${space}/kv/`]) {\n if (resource.startsWith(base)) return resource.slice(base.length);\n }\n return undefined;\n\n}\n/**\n * Whether the signed `att` grants `ability` over the folder prefix, with\n * unrestricted authority unless explicitly asked to ignore caveats.\n */\nexport function ucanAttCovers(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n ability: string,\n options?: { ignoreCaveats?: boolean },\n): boolean {\n for (const [resource, abilities] of Object.entries(att)) {\n const path = kvPathInResource(resource, space);\n if (path === undefined) continue;\n for (const [granted, caveats] of Object.entries(abilities)) {\n if (!actionContains(granted, ability)) continue;\n if (!isCapabilitySubset(\n [{ service: KV_SERVICE, space, path: prefix, actions: [ability] }],\n [{ service: KV_SERVICE, space, path, actions: [granted] }],\n ).subset) continue;\n if (options?.ignoreCaveats === true || caveatBranchesUnconstrained(caveats)) return true;\n }\n }\n return false;\n}\n\n/** `unconstrained` for `ReplicaGrantInfo` (§2.1): get AND sync on the prefix carry no caveats. */\nexport function ucanAttUnconstrainedFor(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n): boolean {\n return (\n ucanAttCovers(att, space, prefix, KV_GET) &&\n ucanAttCovers(att, space, prefix, KV_SYNC)\n );\n}\n\nexport interface DelegationPlanResult {\n path: \"session\" | \"runtime\";\n parentCid: string;\n expiresAt: number;\n}\n\n/**\n * The `TinyCloudNode` internals the authority needs. `planDelegation` is the\n * pure extraction of `delegateTo`'s parent selection (§4.3), so `plan` and\n * `mint` can never disagree about which parent or expiry a delegation gets.\n */\nexport type ReplicationAuthoritySession = Pick<\n TinyCloudSession,\n \"delegationHeader\" | \"delegationCid\" | \"spaceId\" | \"verificationMethod\" | \"jwk\"\n> & Partial<Pick<TinyCloudSession, \"siwe\" | \"signature\" | \"address\" | \"chainId\">>;\n\nexport interface ReplicationAuthorityHost {\n replicationSession(): ReplicationAuthoritySession | undefined;\n /** Validated owner proof only; never infer consent from a compact child UCAN. */\n verifiedConsentAnchor?(session: ReplicationAuthoritySession): ReplicaConsentAnchor | null;\n /** The SIWE \"Expiration Time\" of the session — the delegateTo delegation cap. */\n siweExpiration(siwe: string): Date | undefined;\n planDelegation(\n entries: PermissionEntry[],\n options?: { expiry?: string | number },\n ): ({ grant?: unknown } & DelegationPlanResult) | AuthorityRefusal;\n mintDelegation(\n deviceDid: string,\n entries: PermissionEntry[],\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; expiresAt: number }>;\n}\n\n/** Delegate a replica grant only through the selected owner parent for silent renewal. */\nexport async function mintNodeReplicationGrant(\n deviceDid: string,\n entries: PermissionEntry[],\n options: { expiryMs?: number; renewalOnly?: boolean } | undefined,\n renewalSession: ReplicationAuthoritySession | undefined,\n node: Pick<TinyCloudNode, \"delegateTo\">,\n): Promise<{ ucan: string; expiresAt: number }> {\n if (options?.renewalOnly && !renewalSession) throw new Error(\"replication renewal lost its owner session\");\n const delegateOptions: DelegateToOptions | undefined = options?.renewalOnly ? {\n expiry: options.expiryMs,\n onPrepared: async (prepared) => {\n assertRenewalParent(prepared.delegationHeader.Authorization, renewalSession!);\n },\n } : options?.expiryMs === undefined ? undefined : { expiry: options.expiryMs };\n const result: DelegateToResult = await node.delegateTo(principalDid(deviceDid), entries, delegateOptions);\n return {\n ucan: result.delegation.delegationHeader.Authorization,\n expiresAt: result.delegation.expiry.getTime(),\n };\n}\n\n/** Milliseconds of validity a session or parent must have left for authority reuse (delegateTo's margin). */\nexport const AUTHORITY_EXPIRY_MARGIN_MS = 60_000;\n\n\n/** Verify a compact session delegation before using any persisted space metadata. */\nexport function replicationScopeFromSignedSession(input: {\n delegationHeader: { Authorization: string };\n delegationCid: string;\n verificationMethod: string;\n persistedSpace: string;\n}): { space: string; principal: string } | undefined {\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n grant = parseUcanGrant(input.delegationHeader.Authorization);\n } catch {\n return undefined;\n }\n const bareDid = (value: string): string => value.split(\"#\", 1)[0]!;\n if (\n grant.cid !== input.delegationCid ||\n bareDid(grant.audience) !== bareDid(input.verificationMethod)\n ) return undefined;\n for (const resource of Object.keys(grant.att)) {\n const space = resource.match(/^(?:tinycloud:\\/\\/)?(.+?)\\/kv(?:\\/|$)/)?.[1];\n if (space !== input.persistedSpace) continue;\n const owner = parseSpaceUri(space);\n if (owner?.owner && owner.address && owner.chainId) return { space, principal: owner.owner };\n }\n return undefined;\n}\n/** Runs before host activation; a replan cannot silently switch a renewal to a runtime parent. */\nexport function assertRenewalParent(authorization: string, session: ReplicationAuthoritySession): void {\n const grant = parseUcanGrant(authorization);\n if (\n grant.issuer !== session.verificationMethod.split(\"#\", 1)[0] ||\n grant.prf.length !== 1 ||\n grant.prf[0] !== session.delegationCid\n ) {\n throw Object.assign(new Error(\"replication renewal requires the bound owner session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n}\n\nexport function createReplicationAuthority(host: ReplicationAuthorityHost): ReplicationAuthority {\n const boundSession = host.replicationSession();\n let verifiedAnchor: ReplicaConsentAnchor | null = null;\n try {\n const candidate = boundSession && host.verifiedConsentAnchor?.(boundSession);\n if (candidate && Number.isFinite(candidate.at) && candidate.source !== \"first_install\") {\n verifiedAnchor = { at: candidate.at, source: candidate.source };\n }\n } catch {\n // Network sessions remain usable when the optional replica proof cannot be verified.\n }\n const activeSession = (): ReplicationAuthoritySession | undefined => {\n const session = host.replicationSession();\n return session !== undefined && session === boundSession ? session : undefined;\n };\n const planOrRefusal = (entries: PermissionEntry[], expiryMs?: number): DelegationPlanResult | AuthorityRefusal => {\n const plan = host.planDelegation(entries, expiryMs === undefined ? undefined : { expiry: expiryMs });\n if (\"refused\" in plan) return plan;\n return { path: plan.path, parentCid: plan.parentCid, expiresAt: plan.expiresAt };\n };\n const sessionAudience = (value: string): string => value.split(\"#\", 1)[0]!;\n const signedSessionGrant = (session: ReplicationAuthoritySession) => {\n const grant = parseUcanGrant(session.delegationHeader.Authorization);\n if (\n grant.cid !== session.delegationCid ||\n sessionAudience(grant.audience) !== sessionAudience(session.verificationMethod)\n ) return undefined;\n return grant;\n };\n const authority: ReplicationAuthority = {\n get sessionOnly() {\n const session = activeSession();\n return session === undefined || session.siwe === undefined;\n },\n consentAnchor(): ReplicaConsentAnchor | null {\n return activeSession() ? verifiedAnchor : null;\n },\n get canRenew() {\n const session = activeSession();\n return verifiedAnchor?.source === \"siwe\" && session !== undefined && session.siwe !== undefined;\n },\n sessionGrant(prefix: string): { ucan: string; device: ReplicaDevice } | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n // A compact session proves current signed coverage, not consent to install or mint.\n if (verifiedAnchor === null && session.siwe !== undefined) return { refused: \"NOT_COVERED\" };\n const siweExpiry = session.siwe ? host.siweExpiration(session.siwe)?.getTime() : undefined;\n if (siweExpiry !== undefined && siweExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n const verified = signedSessionGrant(session);\n if (!verified) return { refused: \"NOT_COVERED\" };\n grant = verified;\n } catch {\n return { refused: \"NOT_COVERED\" };\n }\n const tokenExpiry = grant.expiresAt === null ? undefined : grant.expiresAt * 1000;\n if (tokenExpiry === undefined || tokenExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n const covered = ucanAttCovers(grant.att, session.spaceId, prefix, KV_GET, { ignoreCaveats: true }) &&\n ucanAttCovers(grant.att, session.spaceId, prefix, KV_SYNC, { ignoreCaveats: true });\n if (!covered) return { refused: \"NOT_COVERED\" };\n if (!ucanAttUnconstrainedFor(grant.att, session.spaceId, prefix)) {\n return { refused: \"CAVEATED_AUTHORITY\" };\n }\n return {\n ucan: grant.jwt,\n device: { did: session.verificationMethod, jwk: session.jwk },\n };\n },\n plan(prefix: string): DelegationPlanResult | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n if (verifiedAnchor === null) return { refused: \"NOT_COVERED\" };\n const sessionGrant = this.sessionGrant(prefix);\n if (!(\"refused\" in sessionGrant)) {\n const signed = signedSessionGrant(session);\n if (!signed || signed.expiresAt === null) return { refused: \"NOT_COVERED\" };\n return {\n path: \"session\",\n parentCid: session.delegationCid,\n expiresAt: signed.expiresAt * 1000,\n };\n }\n if (authority.sessionOnly) return sessionGrant;\n return planOrRefusal(replicationEntries(session.spaceId, prefix));\n },\n async mint(\n deviceDid: string,\n prefix: string,\n signal: AbortSignal,\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; parentCid: string; expiresAt: number }> {\n const session = activeSession();\n if (session === undefined) throw new Error(\"replication mint without the bound session\");\n if (session.siwe === undefined) throw new Error(\"compact delegate sessions cannot mint device grants\");\n if (verifiedAnchor?.source !== \"siwe\") throw new Error(\"replication mint requires verified owner consent\");\n const entries = replicationEntries(session.spaceId, prefix);\n const plan = planOrRefusal(entries, options?.expiryMs);\n if (\"refused\" in plan) {\n throw Object.assign(\n new Error(`replication authority refused the mint: ${plan.refused}`),\n { refused: plan.refused },\n );\n }\n if (options?.renewalOnly && plan.path !== \"session\") {\n throw Object.assign(new Error(\"replication renewal requires a session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n signal.throwIfAborted();\n const minted = await new Promise<{ ucan: string; expiresAt: number }>((resolve, reject) => {\n const onAbort = (): void => reject(signal.reason ?? new Error(\"replication mint aborted\"));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n host.mintDelegation(deviceDid, entries, options).then(\n (result) => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve(result);\n },\n (error: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error instanceof Error ? error : new Error(String(error)));\n },\n );\n });\n return { ucan: minted.ucan, parentCid: plan.parentCid, expiresAt: minted.expiresAt };\n },\n };\n return authority;\n}\n","import { MAX_GRANT_TTL_MS } from \"@tinycloud/sdk-services/kv/replication/grantPolicyConstants\";\nimport { assertFolderPrefix as assertPureFolderPrefix, kvPrefixCovers } from \"@tinycloud/sdk-services/kv/replication/prefix\";\nimport type { ReplicationOptions } from \"@tinycloud/sdk-core\";\n\n/** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */\nexport function assertValidReplicationConfig(\n replication: (ReplicationOptions & { storage?: unknown }) | undefined,\n): void {\n if (replication?.syncScope !== undefined && replication.syncScope !== \"prefixes\") {\n throw new TypeError('replication.syncScope must be \"prefixes\"');\n }\n if (replication === undefined || !replication.enabled) return;\n if (replication.storage === undefined || replication.storage === null) {\n throw new TypeError(\"replication.enabled requires replication.storage\");\n }\n if (!Array.isArray(replication.prefixes) || replication.prefixes.length === 0) {\n throw new TypeError(\"replication.prefixes must be a non-empty array\");\n }\n const grantTtlMs = replication.grantTtlMs;\n if (grantTtlMs !== undefined && (!Number.isFinite(grantTtlMs) || grantTtlMs <= 0 || grantTtlMs > MAX_GRANT_TTL_MS)) {\n throw new TypeError(`replication.grantTtlMs must be a positive number of milliseconds no greater than ${MAX_GRANT_TTL_MS}`);\n }\n for (const prefix of replication.prefixes) {\n try {\n assertPureFolderPrefix(prefix);\n } catch (error) {\n throw new TypeError(error instanceof Error ? error.message : String(error));\n }\n }\n for (const [index, prefix] of replication.prefixes.entries()) {\n for (const other of replication.prefixes.slice(index + 1)) {\n if (kvPrefixCovers(prefix, other) || kvPrefixCovers(other, prefix)) {\n throw new TypeError(\n `replication.prefixes must not overlap: ${JSON.stringify(prefix)} and ${JSON.stringify(other)}`,\n );\n }\n }\n if ((prefix === \"/\" || prefix.split(\"/\", 1)[0] === \"vault\") && replication.allowSecrets !== true) {\n throw new TypeError(\n `replication prefix ${JSON.stringify(prefix)} overlaps the vault namespace; pass allowSecrets to opt in`,\n );\n }\n }\n}\n","import {\n KV,\n isCapabilitySubset,\n type PermissionEntry,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport { requiresSecretsOptIn } from \"@tinycloud/sdk-services\";\nimport { assertFolderPrefix } from \"@tinycloud/sdk-services/kv/replication/prefix\";\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\nexport function hasUnrestrictedGetCoverage(\n entries: readonly PermissionEntry[],\n spaceId: string,\n prefix: string,\n): boolean {\n return isCapabilitySubset(\n [{ service: KV_SERVICE, space: spaceId, path: prefix, actions: [KV_GET] }],\n [...entries],\n ).subset;\n}\n\nexport function augmentSignInEntriesWithReplication(input: {\n entries: readonly PermissionEntry[];\n primarySpaceId: string;\n replication?: Pick<ReplicationOptions, \"prefixes\" | \"allowSecrets\" | \"syncScope\">;\n}): PermissionEntry[] {\n const out: PermissionEntry[] = [];\n const replication = input.replication;\n if (replication === undefined) return out;\n const eligiblePaths: string[] = [];\n for (const prefix of replication.prefixes) {\n assertFolderPrefix(prefix);\n const path = prefix === \"/\" ? \"\" : prefix;\n if (\n requiresSecretsOptIn(input.primarySpaceId, prefix) &&\n replication.allowSecrets !== true\n ) {\n continue;\n }\n if (!hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, path)) {\n continue;\n }\n eligiblePaths.push(path);\n }\n if (\n replication.syncScope !== \"prefixes\" &&\n eligiblePaths.length === replication.prefixes.length &&\n eligiblePaths.length > 0 &&\n hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, \"\")\n ) {\n return [{\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path: \"\",\n actions: [KV_GET, KV_SYNC],\n }];\n }\n for (const path of eligiblePaths) {\n out.push({\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path,\n actions: [KV_GET, KV_SYNC],\n });\n }\n return out;\n}\n"],"mappings":"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","names":["KV","isCapabilitySubset","KV_GET","KV","KV_SYNC","KV_SERVICE","isCapabilitySubset"]}
1
+ {"version":3,"sources":["../../src/replication/authority.ts","../../src/replication/config.ts","../../src/replication/authority-sign-in.ts"],"sourcesContent":["/**\n * Replication authority (TC-858, plan v3 §4).\n *\n * `ReplicationAuthority` answers, per configured prefix, whether this node's\n * session can back a local replica: the session UCAN itself (delegate\n * posture), a child UCAN minted through `delegateTo`, or nothing (the\n * controller then reports `grant_missing` and reads from the network).\n *\n * The unrestricted-authority rule is enforced here, before any network work:\n * a prefix covered only by a caveated `get`/`sync` refuses with\n * `CAVEATED_AUTHORITY` — on the session path, the runtime-grant path, and on\n * any reuse of an installed grant (the adapter marks every grant's\n * `unconstrained` flag; the controller enforces it on reuse).\n */\nimport { SiweMessage } from \"siwe\";\n\nimport {\n actionContains,\n KV,\n canonicalizeRecapCaveats,\n isCapabilitySubset,\n principalDid,\n parseSpaceUri,\n type PermissionEntry,\n type IWasmBindings,\n type WasmRecapEntry,\n type TinyCloudSession,\n type KVReplicaStorage,\n type ServiceContext,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport {\n type AuthorityRefusal,\n type ReplicaConsentAnchor,\n type ReplicaDevice,\n type ReplicationAuthority,\n type ReplicationEvent,\n} from \"@tinycloud/sdk-services\";\nimport type {\n ReplicaDenyInput,\n ReplicaLocalEraseOutcome,\n ReplicaPurgeFailedEvent,\n ReplicaPurgeLocalCopyResult,\n ReplicaPurgeTrigger,\n ReplicaSessionStamp,\n} from \"@tinycloud/sdk-services/kv/replication\";\nimport type { DelegateToOptions, DelegateToResult, TinyCloudNode } from \"../TinyCloudNode\";\nimport type { ReplicaRuntime } from \"./replica-contract\";\n// @ts-ignore -- node-sdk builds before replica declarations; keep the literal import for bundlers.\nimport { parseUcanGrant as replicaParseUcanGrant } from \"@tinycloud/replica\";\nconst parseUcanGrant = replicaParseUcanGrant as ReplicaRuntime[\"parseUcanGrant\"];\nexport { assertValidReplicationConfig } from \"./config\";\nexport { augmentSignInEntriesWithReplication, hasUnrestrictedGetCoverage } from \"./authority-sign-in\";\n\n/** Report a completed deny from the already-loaded authority; delivery was revoked synchronously. */\nexport function reportReplicaDenial(\n result: { status: string; code?: string; intents?: number },\n reason: ReplicaDenyInput[\"reason\"],\n onEvent: ReplicationOptions[\"onEvent\"],\n context?: ServiceContext,\n): void {\n if (result.status !== \"denied\" && result.status !== \"deleted\" && result.status !== \"marker\") return;\n const event: Extract<ReplicationEvent, { type: \"replication.lifecycle\" }> = {\n type: \"replication.lifecycle\",\n at: new Date().toISOString(),\n reason,\n action: result.status === \"denied\" ? \"denied\" : result.status === \"deleted\" ? \"database_deleted\" : \"marker_set\",\n ...(result.status === \"denied\" ? { partitions: result.intents } : {}),\n ...(result.status === \"marker\" ? { code: result.code } : {}),\n };\n try { onEvent?.(event); } catch { /* Observability cannot undo committed denial. */ }\n const { type, ...data } = event;\n try { context?.emit(type, data); } catch { /* Observability cannot undo committed denial. */ }\n}\n\n/** What the root needs after a local-copy erase: the outcome, the `purgeLocalCopy()` result and the marker state. */\nexport interface ReplicaLocalEraseReport {\n outcome: ReplicaLocalEraseOutcome;\n purge: ReplicaPurgeLocalCopyResult;\n /** The logout marker is set: the next load finishes the erase and the replica stays gated meanwhile. */\n logoutPending?: true;\n}\n\n/** Local-copy erase after a deny: 3 attempts in total, retries 150 ms then 450 ms apart, 3 s per attempt. */\nconst ERASE_RETRY_DELAYS_MS = [150, 450] as const;\nconst ERASE_ATTEMPT_MS = 3_000;\n\n/**\n * Erases the browser local copy after a deny, retrying a `failed` outcome with the delays above. After\n * the last failure (or a refusal while the deny kept the keys) it notifies `handlers` with\n * `replica.purge.failed`; the logout marker the lifecycle set makes the next load finish the erase.\n * Never rejects. `undefined`: this storage has no browser local copy to erase.\n */\nexport async function eraseReplicaLocalCopy(\n lifecycle: KVReplicaStorage[\"lifecycle\"],\n trigger: ReplicaPurgeTrigger,\n denyStatus: string | undefined,\n handlers: ReadonlySet<(event: ReplicaPurgeFailedEvent) => void>,\n): Promise<ReplicaLocalEraseReport | undefined> {\n if (lifecycle?.kind !== \"indexeddb\" || lifecycle.eraseLocalCopy === undefined) return undefined;\n let outcome: ReplicaLocalEraseOutcome = { status: \"failed\", code: \"STORAGE_ERROR\", marker: false };\n for (let attempt = 0; attempt <= ERASE_RETRY_DELAYS_MS.length; attempt++) {\n if (attempt > 0) await new Promise<void>((resolve) => setTimeout(resolve, ERASE_RETRY_DELAYS_MS[attempt - 1]));\n try {\n outcome = await lifecycle.eraseLocalCopy({ signal: AbortSignal.timeout(ERASE_ATTEMPT_MS) });\n } catch {\n outcome = { status: \"failed\", code: \"STORAGE_ERROR\", marker: false };\n }\n if (outcome.status !== \"failed\") break;\n }\n const keysKept = denyStatus === \"marker\" || denyStatus === \"unguarded\";\n const purge: ReplicaPurgeLocalCopyResult = outcome.status === \"erased\"\n ? { ok: true }\n : { ok: false, retryNow: outcome.status === \"failed\" };\n if (outcome.status === \"erased\" || (outcome.status === \"refused\" && !keysKept)) return { outcome, purge };\n const logoutPending = outcome.status === \"failed\" && outcome.marker;\n const event: ReplicaPurgeFailedEvent = {\n type: \"replica.purge.failed\",\n trigger,\n reason: logoutPending ? \"logout_pending\" : \"gate_unavailable\",\n code: outcome.code,\n retryNow: outcome.status === \"failed\",\n };\n for (const handler of handlers) {\n try {\n handler(event);\n } catch (error) {\n console.error('Error in \"replica.purge.failed\" handler:', error);\n }\n }\n return logoutPending ? { outcome, purge, logoutPending } : { outcome, purge };\n}\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\n/** The verified owner proof is signed, finite, exact-space, and uncaveated on one get+sync scope. */\nexport function verifiedOwnerConsentAnchor(\n session: TinyCloudSession,\n wasmBindings: Pick<IWasmBindings, \"validatePersistedSession\">,\n exactVerifiedRecap: (verified: { verifiedRecap?: unknown }) => WasmRecapEntry[],\n): ReplicaConsentAnchor | null {\n if (typeof session.siwe !== \"string\" || typeof session.signature !== \"string\" ||\n typeof session.address !== \"string\" || typeof session.chainId !== \"number\") return null;\n try {\n const verified = wasmBindings.validatePersistedSession?.({\n delegationHeader: session.delegationHeader,\n delegationCid: session.delegationCid,\n spaceId: session.spaceId,\n jwk: session.jwk,\n address: session.address,\n chainId: session.chainId,\n siwe: session.siwe,\n signature: session.signature,\n });\n if (!verified || !Number.isSafeInteger(Date.parse(verified.expiresAt ?? \"\"))) return null;\n const signedIssuedAt = new SiweMessage(session.siwe).issuedAt;\n if (typeof signedIssuedAt !== \"string\") return null;\n const issuedAt = Date.parse(signedIssuedAt);\n if (!Number.isSafeInteger(issuedAt) || issuedAt < 0) return null;\n const granted = exactVerifiedRecap(verified)\n .filter((entry) => entry.space === session.spaceId && entry.service === \"kv\")\n .map((entry) => ({ ...entry, service: \"tinycloud.kv\" }));\n const coversGetAndSync = granted.some((entry) => isCapabilitySubset([{\n service: \"tinycloud.kv\",\n space: session.spaceId,\n path: entry.path,\n actions: [KV.GET, KV.SYNC],\n }], granted).subset);\n return coversGetAndSync ? { at: issuedAt, source: \"siwe\" } : null;\n } catch {\n return null;\n }\n}\n\n/** Fresh sign-in only: the lifecycle creates keys; restore never calls this. */\nexport function establishReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n anchor: ReplicaConsentAnchor,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.establishSessionStamp(principal, { consentAnchor: anchor });\n }\n throw Object.assign(new Error(\"Replica sign-in requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** Existing persisted stamp only: never manufactures an authority on restore. */\nexport function restoreReplicaSessionStamp(\n storage: KVReplicaStorage,\n principal: string,\n persisted: ReplicaSessionStamp | undefined,\n): Promise<ReplicaSessionStamp> {\n if (storage.kind === \"indexeddb\" && storage.lifecycle?.kind === \"indexeddb\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n if (storage.kind === \"sqlite\" && storage.lifecycle?.kind === \"file\") {\n return storage.lifecycle.restoreSessionStamp(principal, persisted);\n }\n throw Object.assign(new Error(\"Replica restore requires its platform lifecycle gate.\"), {\n code: \"REPLICA_GATED\" as const, gate: \"gate_unavailable\" as const,\n });\n}\n\n/** The delegation the replication grant delegates: get + sync on one folder prefix. */\nfunction replicationEntries(spaceId: string, prefix: string): PermissionEntry[] {\n return [{\n service: KV_SERVICE,\n space: spaceId,\n path: prefix === \"/\" ? \"\" : prefix,\n actions: [KV_GET, KV_SYNC],\n }];\n}\n\n\n\n\n/**\n * The signed UCAN attenuation (`att`: resource → ability → caveat branches).\n * Only array branch sets are known; anything else is treated as constrained —\n * fail closed, never assume unconstrained.\n */\nfunction caveatBranchesUnconstrained(value: unknown): boolean {\n if (value === undefined || value === null) return true;\n if (typeof value === \"object\" && !Array.isArray(value) && Object.keys(value).length === 0) {\n return true;\n }\n if (!Array.isArray(value)) return false;\n try {\n return (\n canonicalizeRecapCaveats(value as Record<string, unknown>[]) ===\n canonicalizeRecapCaveats(undefined)\n );\n } catch {\n return false;\n }\n}\n\n/** The KV path a signed UCAN resource names in `space`, like replica's `kvPathIn`. */\nfunction kvPathInResource(resource: string, space: string): string | undefined {\n for (const root of [`${space}/kv`, `tinycloud://${space}/kv`]) {\n if (resource === root || resource === `${root}/`) return \"/\";\n }\n for (const base of [`${space}/kv/`, `tinycloud://${space}/kv/`]) {\n if (resource.startsWith(base)) return resource.slice(base.length);\n }\n return undefined;\n\n}\n/**\n * Whether the signed `att` grants `ability` over the folder prefix, with\n * unrestricted authority unless explicitly asked to ignore caveats.\n */\nexport function ucanAttCovers(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n ability: string,\n options?: { ignoreCaveats?: boolean },\n): boolean {\n for (const [resource, abilities] of Object.entries(att)) {\n const path = kvPathInResource(resource, space);\n if (path === undefined) continue;\n for (const [granted, caveats] of Object.entries(abilities)) {\n if (!actionContains(granted, ability)) continue;\n if (!isCapabilitySubset(\n [{ service: KV_SERVICE, space, path: prefix, actions: [ability] }],\n [{ service: KV_SERVICE, space, path, actions: [granted] }],\n ).subset) continue;\n if (options?.ignoreCaveats === true || caveatBranchesUnconstrained(caveats)) return true;\n }\n }\n return false;\n}\n\n/** `unconstrained` for `ReplicaGrantInfo` (§2.1): get AND sync on the prefix carry no caveats. */\nexport function ucanAttUnconstrainedFor(\n att: Record<string, Record<string, unknown>>,\n space: string,\n prefix: string,\n): boolean {\n return (\n ucanAttCovers(att, space, prefix, KV_GET) &&\n ucanAttCovers(att, space, prefix, KV_SYNC)\n );\n}\n\nexport interface DelegationPlanResult {\n path: \"session\" | \"runtime\";\n parentCid: string;\n expiresAt: number;\n}\n\n/**\n * The `TinyCloudNode` internals the authority needs. `planDelegation` is the\n * pure extraction of `delegateTo`'s parent selection (§4.3), so `plan` and\n * `mint` can never disagree about which parent or expiry a delegation gets.\n */\nexport type ReplicationAuthoritySession = Pick<\n TinyCloudSession,\n \"delegationHeader\" | \"delegationCid\" | \"spaceId\" | \"verificationMethod\" | \"jwk\"\n> & Partial<Pick<TinyCloudSession, \"siwe\" | \"signature\" | \"address\" | \"chainId\">>;\n\nexport interface ReplicationAuthorityHost {\n replicationSession(): ReplicationAuthoritySession | undefined;\n /** Validated owner proof only; never infer consent from a compact child UCAN. */\n verifiedConsentAnchor?(session: ReplicationAuthoritySession): ReplicaConsentAnchor | null;\n /** The SIWE \"Expiration Time\" of the session — the delegateTo delegation cap. */\n siweExpiration(siwe: string): Date | undefined;\n planDelegation(\n entries: PermissionEntry[],\n options?: { expiry?: string | number },\n ): ({ grant?: unknown } & DelegationPlanResult) | AuthorityRefusal;\n mintDelegation(\n deviceDid: string,\n entries: PermissionEntry[],\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; expiresAt: number }>;\n}\n\n/** Delegate a replica grant only through the selected owner parent for silent renewal. */\nexport async function mintNodeReplicationGrant(\n deviceDid: string,\n entries: PermissionEntry[],\n options: { expiryMs?: number; renewalOnly?: boolean } | undefined,\n renewalSession: ReplicationAuthoritySession | undefined,\n node: Pick<TinyCloudNode, \"delegateTo\">,\n): Promise<{ ucan: string; expiresAt: number }> {\n if (options?.renewalOnly && !renewalSession) throw new Error(\"replication renewal lost its owner session\");\n const delegateOptions: DelegateToOptions | undefined = options?.renewalOnly ? {\n expiry: options.expiryMs,\n onPrepared: async (prepared) => {\n assertRenewalParent(prepared.delegationHeader.Authorization, renewalSession!);\n },\n } : options?.expiryMs === undefined ? undefined : { expiry: options.expiryMs };\n const result: DelegateToResult = await node.delegateTo(principalDid(deviceDid), entries, delegateOptions);\n return {\n ucan: result.delegation.delegationHeader.Authorization,\n expiresAt: result.delegation.expiry.getTime(),\n };\n}\n\n/** Milliseconds of validity a session or parent must have left for authority reuse (delegateTo's margin). */\nexport const AUTHORITY_EXPIRY_MARGIN_MS = 60_000;\n\n\n/** Verify a compact session delegation before using any persisted space metadata. */\nexport function replicationScopeFromSignedSession(input: {\n delegationHeader: { Authorization: string };\n delegationCid: string;\n verificationMethod: string;\n persistedSpace: string;\n}): { space: string; principal: string } | undefined {\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n grant = parseUcanGrant(input.delegationHeader.Authorization);\n } catch {\n return undefined;\n }\n const bareDid = (value: string): string => value.split(\"#\", 1)[0]!;\n if (\n grant.cid !== input.delegationCid ||\n bareDid(grant.audience) !== bareDid(input.verificationMethod)\n ) return undefined;\n for (const resource of Object.keys(grant.att)) {\n const space = resource.match(/^(?:tinycloud:\\/\\/)?(.+?)\\/kv(?:\\/|$)/)?.[1];\n if (space !== input.persistedSpace) continue;\n const owner = parseSpaceUri(space);\n if (owner?.owner && owner.address && owner.chainId) return { space, principal: owner.owner };\n }\n return undefined;\n}\n/** Runs before host activation; a replan cannot silently switch a renewal to a runtime parent. */\nexport function assertRenewalParent(authorization: string, session: ReplicationAuthoritySession): void {\n const grant = parseUcanGrant(authorization);\n if (\n grant.issuer !== session.verificationMethod.split(\"#\", 1)[0] ||\n grant.prf.length !== 1 ||\n grant.prf[0] !== session.delegationCid\n ) {\n throw Object.assign(new Error(\"replication renewal requires the bound owner session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n}\n\nexport function createReplicationAuthority(host: ReplicationAuthorityHost): ReplicationAuthority {\n const boundSession = host.replicationSession();\n let verifiedAnchor: ReplicaConsentAnchor | null = null;\n try {\n const candidate = boundSession && host.verifiedConsentAnchor?.(boundSession);\n if (candidate && Number.isFinite(candidate.at) && candidate.source !== \"first_install\") {\n verifiedAnchor = { at: candidate.at, source: candidate.source };\n }\n } catch {\n // Network sessions remain usable when the optional replica proof cannot be verified.\n }\n const activeSession = (): ReplicationAuthoritySession | undefined => {\n const session = host.replicationSession();\n return session !== undefined && session === boundSession ? session : undefined;\n };\n const planOrRefusal = (entries: PermissionEntry[], expiryMs?: number): DelegationPlanResult | AuthorityRefusal => {\n const plan = host.planDelegation(entries, expiryMs === undefined ? undefined : { expiry: expiryMs });\n if (\"refused\" in plan) return plan;\n return { path: plan.path, parentCid: plan.parentCid, expiresAt: plan.expiresAt };\n };\n const sessionAudience = (value: string): string => value.split(\"#\", 1)[0]!;\n const signedSessionGrant = (session: ReplicationAuthoritySession) => {\n const grant = parseUcanGrant(session.delegationHeader.Authorization);\n if (\n grant.cid !== session.delegationCid ||\n sessionAudience(grant.audience) !== sessionAudience(session.verificationMethod)\n ) return undefined;\n return grant;\n };\n const authority: ReplicationAuthority = {\n get sessionOnly() {\n const session = activeSession();\n return session === undefined || session.siwe === undefined;\n },\n consentAnchor(): ReplicaConsentAnchor | null {\n return activeSession() ? verifiedAnchor : null;\n },\n get canRenew() {\n const session = activeSession();\n return verifiedAnchor?.source === \"siwe\" && session !== undefined && session.siwe !== undefined;\n },\n sessionGrant(prefix: string): { ucan: string; device: ReplicaDevice } | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n // A compact session proves current signed coverage, not consent to install or mint.\n if (verifiedAnchor === null && session.siwe !== undefined) return { refused: \"NOT_COVERED\" };\n const siweExpiry = session.siwe ? host.siweExpiration(session.siwe)?.getTime() : undefined;\n if (siweExpiry !== undefined && siweExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n let grant: ReturnType<typeof parseUcanGrant>;\n try {\n const verified = signedSessionGrant(session);\n if (!verified) return { refused: \"NOT_COVERED\" };\n grant = verified;\n } catch {\n return { refused: \"NOT_COVERED\" };\n }\n const tokenExpiry = grant.expiresAt === null ? undefined : grant.expiresAt * 1000;\n if (tokenExpiry === undefined || tokenExpiry <= Date.now() + AUTHORITY_EXPIRY_MARGIN_MS) {\n return { refused: \"SESSION_EXPIRING\" };\n }\n const covered = ucanAttCovers(grant.att, session.spaceId, prefix, KV_GET, { ignoreCaveats: true }) &&\n ucanAttCovers(grant.att, session.spaceId, prefix, KV_SYNC, { ignoreCaveats: true });\n if (!covered) return { refused: \"NOT_COVERED\" };\n if (!ucanAttUnconstrainedFor(grant.att, session.spaceId, prefix)) {\n return { refused: \"CAVEATED_AUTHORITY\" };\n }\n return {\n ucan: grant.jwt,\n device: { did: session.verificationMethod, jwk: session.jwk },\n };\n },\n plan(prefix: string): DelegationPlanResult | AuthorityRefusal {\n const session = activeSession();\n if (session === undefined) return { refused: \"SESSION_EXPIRING\" };\n if (verifiedAnchor === null) return { refused: \"NOT_COVERED\" };\n const sessionGrant = this.sessionGrant(prefix);\n if (!(\"refused\" in sessionGrant)) {\n const signed = signedSessionGrant(session);\n if (!signed || signed.expiresAt === null) return { refused: \"NOT_COVERED\" };\n return {\n path: \"session\",\n parentCid: session.delegationCid,\n expiresAt: signed.expiresAt * 1000,\n };\n }\n if (authority.sessionOnly) return sessionGrant;\n return planOrRefusal(replicationEntries(session.spaceId, prefix));\n },\n async mint(\n deviceDid: string,\n prefix: string,\n signal: AbortSignal,\n options?: { expiryMs?: number; renewalOnly?: boolean },\n ): Promise<{ ucan: string; parentCid: string; expiresAt: number }> {\n const session = activeSession();\n if (session === undefined) throw new Error(\"replication mint without the bound session\");\n if (session.siwe === undefined) throw new Error(\"compact delegate sessions cannot mint device grants\");\n if (verifiedAnchor?.source !== \"siwe\") throw new Error(\"replication mint requires verified owner consent\");\n const entries = replicationEntries(session.spaceId, prefix);\n const plan = planOrRefusal(entries, options?.expiryMs);\n if (\"refused\" in plan) {\n throw Object.assign(\n new Error(`replication authority refused the mint: ${plan.refused}`),\n { refused: plan.refused },\n );\n }\n if (options?.renewalOnly && plan.path !== \"session\") {\n throw Object.assign(new Error(\"replication renewal requires a session parent\"), { refused: \"NOT_COVERED\" as const });\n }\n signal.throwIfAborted();\n const minted = await new Promise<{ ucan: string; expiresAt: number }>((resolve, reject) => {\n const onAbort = (): void => reject(signal.reason ?? new Error(\"replication mint aborted\"));\n signal.addEventListener(\"abort\", onAbort, { once: true });\n host.mintDelegation(deviceDid, entries, options).then(\n (result) => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve(result);\n },\n (error: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(error instanceof Error ? error : new Error(String(error)));\n },\n );\n });\n return { ucan: minted.ucan, parentCid: plan.parentCid, expiresAt: minted.expiresAt };\n },\n };\n return authority;\n}\n","import { MAX_GRANT_TTL_MS } from \"@tinycloud/sdk-services/kv/replication/grantPolicyConstants\";\nimport { assertFolderPrefix as assertPureFolderPrefix, kvPrefixCovers } from \"@tinycloud/sdk-services/kv/replication/prefix\";\nimport type { ReplicationOptions } from \"@tinycloud/sdk-core\";\n\n/** Constructor-time validation; the space-dependent secrets check runs at sign-in and open. */\nexport function assertValidReplicationConfig(\n replication: (ReplicationOptions & { storage?: unknown }) | undefined,\n): void {\n if (replication?.syncScope !== undefined && replication.syncScope !== \"prefixes\") {\n throw new TypeError('replication.syncScope must be \"prefixes\"');\n }\n if (replication?.contentFetch !== undefined && replication.contentFetch !== \"eager\" && replication.contentFetch !== \"lazy\") {\n throw new TypeError('replication.contentFetch must be \"eager\" or \"lazy\"');\n }\n if (replication === undefined || !replication.enabled) return;\n if (replication.storage === undefined || replication.storage === null) {\n throw new TypeError(\"replication.enabled requires replication.storage\");\n }\n if (!Array.isArray(replication.prefixes) || replication.prefixes.length === 0) {\n throw new TypeError(\"replication.prefixes must be a non-empty array\");\n }\n const grantTtlMs = replication.grantTtlMs;\n if (grantTtlMs !== undefined && (!Number.isFinite(grantTtlMs) || grantTtlMs <= 0 || grantTtlMs > MAX_GRANT_TTL_MS)) {\n throw new TypeError(`replication.grantTtlMs must be a positive number of milliseconds no greater than ${MAX_GRANT_TTL_MS}`);\n }\n for (const prefix of replication.prefixes) {\n try {\n assertPureFolderPrefix(prefix);\n } catch (error) {\n throw new TypeError(error instanceof Error ? error.message : String(error));\n }\n }\n for (const [index, prefix] of replication.prefixes.entries()) {\n for (const other of replication.prefixes.slice(index + 1)) {\n if (kvPrefixCovers(prefix, other) || kvPrefixCovers(other, prefix)) {\n throw new TypeError(\n `replication.prefixes must not overlap: ${JSON.stringify(prefix)} and ${JSON.stringify(other)}`,\n );\n }\n }\n if ((prefix === \"/\" || prefix.split(\"/\", 1)[0] === \"vault\") && replication.allowSecrets !== true) {\n throw new TypeError(\n `replication prefix ${JSON.stringify(prefix)} overlaps the vault namespace; pass allowSecrets to opt in`,\n );\n }\n }\n}\n","import {\n KV,\n isCapabilitySubset,\n type PermissionEntry,\n type ReplicationOptions,\n} from \"@tinycloud/sdk-core\";\nimport { requiresSecretsOptIn } from \"@tinycloud/sdk-services\";\nimport { assertFolderPrefix } from \"@tinycloud/sdk-services/kv/replication/prefix\";\n\nconst KV_GET = KV.GET;\nconst KV_SYNC = KV.SYNC;\nconst KV_SERVICE = \"tinycloud.kv\";\n\nexport function hasUnrestrictedGetCoverage(\n entries: readonly PermissionEntry[],\n spaceId: string,\n prefix: string,\n): boolean {\n return isCapabilitySubset(\n [{ service: KV_SERVICE, space: spaceId, path: prefix, actions: [KV_GET] }],\n [...entries],\n ).subset;\n}\n\nexport function augmentSignInEntriesWithReplication(input: {\n entries: readonly PermissionEntry[];\n primarySpaceId: string;\n replication?: Pick<ReplicationOptions, \"prefixes\" | \"allowSecrets\" | \"syncScope\">;\n}): PermissionEntry[] {\n const out: PermissionEntry[] = [];\n const replication = input.replication;\n if (replication === undefined) return out;\n const eligiblePaths: string[] = [];\n for (const prefix of replication.prefixes) {\n assertFolderPrefix(prefix);\n const path = prefix === \"/\" ? \"\" : prefix;\n if (\n requiresSecretsOptIn(input.primarySpaceId, prefix) &&\n replication.allowSecrets !== true\n ) {\n continue;\n }\n if (!hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, path)) {\n continue;\n }\n eligiblePaths.push(path);\n }\n if (\n replication.syncScope !== \"prefixes\" &&\n eligiblePaths.length === replication.prefixes.length &&\n eligiblePaths.length > 0 &&\n hasUnrestrictedGetCoverage(input.entries, input.primarySpaceId, \"\")\n ) {\n return [{\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path: \"\",\n actions: [KV_GET, KV_SYNC],\n }];\n }\n for (const path of eligiblePaths) {\n out.push({\n service: KV_SERVICE,\n space: input.primarySpaceId,\n path,\n actions: [KV_GET, KV_SYNC],\n });\n }\n return out;\n}\n"],"mappings":"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","names":["KV","isCapabilitySubset","KV_GET","KV","KV_SYNC","KV_SERVICE","isCapabilitySubset"]}
@@ -49,6 +49,7 @@ function resolvedOptions(options) {
49
49
  maxStalenessMs: options.maxStalenessMs ?? 12e4,
50
50
  staleSyncTimeoutMs: options.staleSyncTimeoutMs ?? 5e3,
51
51
  allowSecrets: options.allowSecrets === true,
52
+ contentFetch: options.contentFetch ?? "eager",
52
53
  verify: options.verify === true
53
54
  };
54
55
  }