@tinycloud/operations 0.4.0-beta.7 → 0.4.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifacts.cjs +493 -6680
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.d.cts +1 -1
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +476 -6661
- package/dist/artifacts.js.map +1 -1
- package/dist/cli-runtime.cjs +727 -6915
- package/dist/cli-runtime.cjs.map +1 -1
- package/dist/cli-runtime.d.cts +1 -1
- package/dist/cli-runtime.d.ts +1 -1
- package/dist/cli-runtime.js +723 -6909
- package/dist/cli-runtime.js.map +1 -1
- package/dist/{contract-xMIIMtpF.d.cts → contract-CjuEjE-M.d.cts} +1 -1
- package/dist/{contract-xMIIMtpF.d.ts → contract-CjuEjE-M.d.ts} +1 -1
- package/dist/delegation-binding.cjs +492 -6679
- package/dist/delegation-binding.cjs.map +1 -1
- package/dist/delegation-binding.js +480 -6665
- package/dist/delegation-binding.js.map +1 -1
- package/dist/index.cjs +727 -6915
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +723 -6909
- package/dist/index.js.map +1 -1
- package/dist/profile.cjs +5 -3
- package/dist/profile.cjs.map +1 -1
- package/dist/profile.d.cts +1 -1
- package/dist/profile.d.ts +1 -1
- package/dist/profile.js +7 -3
- package/dist/profile.js.map +1 -1
- package/dist/state.cjs +422 -51
- package/dist/state.cjs.map +1 -1
- package/dist/state.d.cts +38 -5
- package/dist/state.d.ts +38 -5
- package/dist/state.js +420 -51
- package/dist/state.js.map +1 -1
- package/generated/operations.json +66 -0
- package/package.json +4 -3
package/dist/artifacts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../node_modules/ms/index.js","../../sdk-services/src/encryption/DecryptTransportResponseError.ts","../src/artifacts.ts","../../sdk-core/src/manifest.ts","../../bootstrap/src/index.ts","../../bootstrap/src/generated/capabilities.ts","../../bootstrap/src/capabilities.ts","../../sdk-services/src/types.ts","../../sdk-services/src/diagnostics.ts","../../sdk-services/src/debug.ts","../../sdk-services/src/types.schema.ts","../../sdk-services/src/context.ts","../../sdk-services/src/errors.ts","../../sdk-services/src/base/BaseService.ts","../../sdk-services/src/kv/PrefixedKVService.ts","../../sdk-services/src/kv/types.ts","../../sdk-services/src/kv/KVService.ts","../../sdk-services/src/responseErrors.ts","../../sdk-services/src/sql/SQLMigrations.ts","../../sdk-services/src/sql/DatabaseHandle.ts","../../sdk-services/src/sql/types.ts","../../sdk-services/src/sql/SQLService.ts","../../sdk-services/src/duckdb/DuckDbDatabaseHandle.ts","../../sdk-services/src/duckdb/types.ts","../../sdk-services/src/duckdb/DuckDbService.ts","../../sdk-services/src/hooks/HooksService.ts","../../sdk-services/src/quota/TinyCloudQuota.ts","../../sdk-services/src/vault/types.ts","../../sdk-services/src/vault/SignatureCache.ts","../../sdk-services/src/vault/DataVaultService.ts","../../sdk-services/src/vault/createVaultCrypto.ts","../../sdk-services/src/secrets/paths.ts","../../sdk-services/src/secrets/SecretsService.ts","../../sdk-services/src/encryption/canonical.ts","../../sdk-services/src/encryption/networkId.ts","../../sdk-services/src/encryption/types.ts","../../sdk-services/src/encryption/discovery.ts","../../sdk-services/src/encryption/envelope.ts","../../sdk-services/src/encryption/invocation.ts","../../sdk-services/src/encryption/receiverKey.ts","../../sdk-services/src/encryption/response.ts","../../sdk-services/src/encryption/EncryptionService.ts","../../sdk-services/src/encryption/index.ts","../../sdk-core/src/capabilities.ts","../src/authority.ts","../src/state.ts"],"sourcesContent":["/**\n * Helpers.\n */\n\nvar s = 1000;\nvar m = s * 60;\nvar h = m * 60;\nvar d = h * 24;\nvar w = d * 7;\nvar y = d * 365.25;\n\n/**\n * Parse or format the given `val`.\n *\n * Options:\n *\n * - `long` verbose formatting [false]\n *\n * @param {String|Number} val\n * @param {Object} [options]\n * @throws {Error} throw an error if val is not a non-empty string or a number\n * @return {String|Number}\n * @api public\n */\n\nmodule.exports = function (val, options) {\n options = options || {};\n var type = typeof val;\n if (type === 'string' && val.length > 0) {\n return parse(val);\n } else if (type === 'number' && isFinite(val)) {\n return options.long ? fmtLong(val) : fmtShort(val);\n }\n throw new Error(\n 'val is not a non-empty string or a valid number. val=' +\n JSON.stringify(val)\n );\n};\n\n/**\n * Parse the given `str` and return milliseconds.\n *\n * @param {String} str\n * @return {Number}\n * @api private\n */\n\nfunction parse(str) {\n str = String(str);\n if (str.length > 100) {\n return;\n }\n var match = /^(-?(?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(\n str\n );\n if (!match) {\n return;\n }\n var n = parseFloat(match[1]);\n var type = (match[2] || 'ms').toLowerCase();\n switch (type) {\n case 'years':\n case 'year':\n case 'yrs':\n case 'yr':\n case 'y':\n return n * y;\n case 'weeks':\n case 'week':\n case 'w':\n return n * w;\n case 'days':\n case 'day':\n case 'd':\n return n * d;\n case 'hours':\n case 'hour':\n case 'hrs':\n case 'hr':\n case 'h':\n return n * h;\n case 'minutes':\n case 'minute':\n case 'mins':\n case 'min':\n case 'm':\n return n * m;\n case 'seconds':\n case 'second':\n case 'secs':\n case 'sec':\n case 's':\n return n * s;\n case 'milliseconds':\n case 'millisecond':\n case 'msecs':\n case 'msec':\n case 'ms':\n return n;\n default:\n return undefined;\n }\n}\n\n/**\n * Short format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtShort(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return Math.round(ms / d) + 'd';\n }\n if (msAbs >= h) {\n return Math.round(ms / h) + 'h';\n }\n if (msAbs >= m) {\n return Math.round(ms / m) + 'm';\n }\n if (msAbs >= s) {\n return Math.round(ms / s) + 's';\n }\n return ms + 'ms';\n}\n\n/**\n * Long format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtLong(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return plural(ms, msAbs, d, 'day');\n }\n if (msAbs >= h) {\n return plural(ms, msAbs, h, 'hour');\n }\n if (msAbs >= m) {\n return plural(ms, msAbs, m, 'minute');\n }\n if (msAbs >= s) {\n return plural(ms, msAbs, s, 'second');\n }\n return ms + ' ms';\n}\n\n/**\n * Pluralization helper.\n */\n\nfunction plural(ms, msAbs, n, name) {\n var isPlural = msAbs >= n * 1.5;\n return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');\n}\n","import type { PermissionHint } from \"../types\";\n\n/** A reachable node returned an HTTP failure for a decrypt request. */\nexport class DecryptTransportResponseError extends Error {\n constructor(readonly status: number, readonly permissionHint?: PermissionHint) {\n super(\"Node decrypt request failed\");\n this.name = \"DecryptTransportResponseError\";\n }\n}\n","import { randomUUID } from \"node:crypto\";\n\nimport type { PermissionEntry } from \"@tinycloud/sdk-core\";\nimport { z } from \"zod\";\n\nimport {\n canonicalizeCapabilities,\n evaluateOperationAuthority,\n evaluateAuthority,\n} from \"./authority.js\";\nimport type { OperationSpaceResolver } from \"./authority.js\";\nimport type {\n OperationOperatorType,\n TinyCloudPosture,\n} from \"./contract.js\";\nimport { readAuthRequests, updateProfileStore } from \"./state.js\";\n\nexport { evaluateAuthority } from \"./authority.js\";\n\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1_000;\n\n/** The shared wire schema for SDK PermissionEntry values, including signed ReCap caveats. */\nexport const PermissionEntrySchema = z.object({\n service: z.string().min(1),\n space: z.string().min(1).optional(),\n path: z.string(),\n actions: z.array(z.string().min(1)).min(1),\n caveats: z.array(z.record(z.unknown())).optional(),\n skipPrefix: z.boolean().optional(),\n expiry: z.string().min(1).optional(),\n description: z.string().optional(),\n}).strict();\n\nconst CommandSchema = z.object({\n argv: z.array(z.string()).min(1),\n cwd: z.string().min(1),\n}).strict();\n\nconst LegacyPermissionRequestArtifactSchema = z.object({\n kind: z.literal(\"tinycloud.auth.request\"),\n version: z.literal(1),\n requestId: z.string(),\n sessionDid: z.string().min(1),\n requested: z.array(PermissionEntrySchema),\n requestedExpiry: z.union([z.string(), z.number().finite()]).optional(),\n}).strict();\n\nconst PortableDelegationSchema: z.ZodType<Record<string, unknown>> = z.lazy(() => z.object({\n cid: z.string().min(1),\n spaceId: z.string().min(1),\n path: z.string(),\n actions: z.array(z.string().min(1)).min(1),\n delegateDID: z.string().min(1),\n ownerAddress: z.string().min(1),\n chainId: z.number().int().positive(),\n expiry: z.union([z.string().datetime({ offset: true }), z.date()]),\n delegationHeader: z.object({ Authorization: z.string().min(1) }).strict(),\n host: z.string().min(1).optional(),\n disableSubDelegation: z.boolean().optional(),\n publicDelegation: PortableDelegationSchema.optional(),\n resources: z.array(PermissionEntrySchema).optional(),\n}).passthrough());\n\n/** Canonical wire-format v1 request retained for CLI and OpenKey compatibility. */\nexport const PermissionRequestArtifactSchema = z.object({\n kind: z.literal(\"tinycloud.auth.request\"),\n version: z.literal(1),\n requestId: z.string().min(1),\n createdAt: z.string().datetime({ offset: true }),\n profile: z.string().min(1),\n posture: z.enum([\"owner-openkey\", \"delegate-session\", \"local-owner-key\", \"unauthenticated\"]),\n operatorType: z.enum([\"human\", \"agent\"]),\n host: z.string().min(1),\n sessionDid: z.string().min(1),\n ownerDid: z.string().min(1).optional(),\n spaceId: z.string().min(1).optional(),\n requestedExpiry: z.union([z.string().min(1), z.number().finite()]).optional(),\n requested: z.array(PermissionEntrySchema),\n command: CommandSchema.optional(),\n}).strict();\n\n/** Canonical structured v1 delegation import accepted by the auth operation. */\nexport const DelegationImportArtifactSchema = z.object({\n kind: z.literal(\"tinycloud.auth.delegation\"),\n version: z.literal(1),\n requestId: z.string().min(1),\n delegationCid: z.string().min(1).optional(),\n delegation: PortableDelegationSchema,\n // This field is presentation metadata only. A later import operation obtains\n // effective permissions from validated, installed delegation authority.\n permissions: z.array(PermissionEntrySchema).optional(),\n expiry: z.string().datetime({ offset: true }).optional(),\n prompted: z.boolean().optional(),\n}).strict();\n\n/**\n * The stored request an imported delegation was checked against, persisted\n * with the delegation. Its fields are exactly the request artifact's, so\n * replay accepts every binding import can write.\n */\nexport const DelegationRequestBindingSchema = PermissionRequestArtifactSchema\n .pick({ requestId: true, requested: true })\n .strict();\nexport type DelegationRequestBinding = z.infer<typeof DelegationRequestBindingSchema>;\n\nexport type PermissionRequestArtifact = z.infer<typeof PermissionRequestArtifactSchema>;\nexport type DelegationImportArtifact = z.infer<typeof DelegationImportArtifactSchema>;\n/** The minimal public node-sdk request shape accepted by the legacy reader. */\nexport type LegacyPermissionRequestArtifact = z.infer<typeof LegacyPermissionRequestArtifactSchema>;\nexport type ArtifactClock = () => Date;\nexport type RequestIdSource = () => string;\n\n/** Invocation-owned values used to safely complete a legacy request record. */\nexport interface LegacyRequestContext {\n readonly profile: string;\n readonly host: string;\n readonly sessionDid: string;\n readonly posture: TinyCloudPosture;\n readonly operatorType: OperationOperatorType;\n readonly ownerDid?: string;\n readonly spaceId?: string;\n readonly now?: Date;\n}\n\nexport interface PermissionRequestIdentityInput {\n readonly profile: string;\n readonly sessionDid: string;\n readonly host: string;\n readonly missing: readonly PermissionEntry[];\n}\n\nexport interface BuildPermissionRequestArtifactInput extends PermissionRequestIdentityInput {\n readonly posture: TinyCloudPosture;\n readonly operatorType: OperationOperatorType;\n readonly ownerDid?: string;\n readonly spaceId?: string;\n readonly requestedExpiry?: string | number;\n readonly command?: { readonly argv: readonly string[]; readonly cwd: string };\n readonly now?: ArtifactClock;\n readonly createRequestId?: RequestIdSource;\n}\n\nexport interface CreateOrReusePermissionRequestInput extends BuildPermissionRequestArtifactInput {\n readonly granted: readonly PermissionEntry[];\n /** Owner-aware identity resolver used by registered operation callers. */\n readonly resolveSpace?: OperationSpaceResolver;\n readonly replace?: boolean;\n}\n\nexport type PermissionRequestResolution =\n | Readonly<{ status: \"satisfied\"; reused: false }>\n | Readonly<{ status: \"created\"; reused: boolean; request: PermissionRequestArtifact }>;\n\n/** Parses a request artifact without accepting a newer wire format as v1. */\nexport function validatePermissionRequestArtifact(value: unknown): PermissionRequestArtifact {\n return PermissionRequestArtifactSchema.parse(value);\n}\n\n/** Parses a structured active-session import artifact without accepting a newer wire format as v1. */\nexport function validateDelegationImportArtifact(value: unknown): DelegationImportArtifact {\n return DelegationImportArtifactSchema.parse(value);\n}\n\nexport function isPermissionRequestArtifact(value: unknown): value is PermissionRequestArtifact {\n return PermissionRequestArtifactSchema.safeParse(value).success;\n}\n\nexport function isDelegationImportArtifact(value: unknown): value is DelegationImportArtifact {\n return DelegationImportArtifactSchema.safeParse(value).success;\n}\n\nfunction normalizeLegacyPermissionRequest(\n value: LegacyPermissionRequestArtifact,\n context: LegacyRequestContext,\n): PermissionRequestArtifact {\n if (principal(value.sessionDid) !== principal(context.sessionDid)) {\n throw new TypeError(\"Stored authority request belongs to a different session.\");\n }\n const createdAt = context.now ?? new Date();\n if (Number.isNaN(createdAt.getTime())) {\n throw new TypeError(\"The legacy authority request has no usable creation time.\");\n }\n return {\n kind: \"tinycloud.auth.request\",\n version: 1,\n requestId: value.requestId,\n createdAt: createdAt.toISOString(),\n profile: context.profile,\n posture: context.posture,\n operatorType: context.operatorType,\n host: context.host,\n sessionDid: context.sessionDid,\n ...(context.ownerDid === undefined ? {} : { ownerDid: context.ownerDid }),\n ...(context.spaceId === undefined ? {} : { spaceId: context.spaceId }),\n ...(value.requestedExpiry === undefined ? {} : { requestedExpiry: value.requestedExpiry }),\n requested: canonicalizeCapabilities(value.requested),\n } as PermissionRequestArtifact;\n}\n\nfunction principal(value: string): string {\n return value.split(\"#\", 1)[0]!;\n}\n\n/**\n * Stable exact identity used only for request reuse. It binds a request to its\n * selected profile, active session DID, host, and canonical missing subset.\n */\nexport function createPermissionRequestIdentity(input: PermissionRequestIdentityInput): string {\n return JSON.stringify({\n profile: input.profile,\n sessionDid: input.sessionDid,\n host: input.host,\n missing: canonicalizeCapabilities(input.missing),\n });\n}\n\n/** Builds an on-disk-compatible v1 record. Omitted values stay omitted. */\nexport function buildPermissionRequestArtifact(\n input: BuildPermissionRequestArtifactInput,\n): PermissionRequestArtifact {\n const now = input.now ?? (() => new Date());\n const createdAt = now();\n if (Number.isNaN(createdAt.getTime())) throw new TypeError(\"The artifact clock returned an invalid date.\");\n const requested = canonicalizeCapabilities(input.missing);\n if (requested.length === 0) throw new TypeError(\"Permission requests require at least one missing capability.\");\n\n return validatePermissionRequestArtifact({\n kind: \"tinycloud.auth.request\",\n version: 1,\n requestId: input.createRequestId?.() ?? defaultRequestId(createdAt),\n createdAt: createdAt.toISOString(),\n profile: input.profile,\n posture: input.posture,\n operatorType: input.operatorType,\n host: input.host,\n sessionDid: input.sessionDid,\n ...(input.ownerDid !== undefined ? { ownerDid: input.ownerDid } : {}),\n ...(input.spaceId !== undefined ? { spaceId: input.spaceId } : {}),\n ...(input.requestedExpiry !== undefined ? { requestedExpiry: input.requestedExpiry } : {}),\n requested,\n ...(input.command !== undefined\n ? { command: { argv: [...input.command.argv], cwd: input.command.cwd } }\n : {}),\n });\n}\n\n/** Reads only valid canonical v1 records and refuses malformed stored artifacts. */\nexport async function listPermissionRequests(\n profile: string,\n context?: LegacyRequestContext,\n): Promise<PermissionRequestArtifact[]> {\n const records: PermissionRequestArtifact[] = [];\n for (const value of await readAuthRequests<unknown>(profile)) {\n const canonical = PermissionRequestArtifactSchema.safeParse(value);\n if (canonical.success) {\n if (context !== undefined && principal(canonical.data.sessionDid) !== principal(context.sessionDid)) {\n continue;\n }\n records.push(canonical.data);\n continue;\n }\n const legacy = LegacyPermissionRequestArtifactSchema.safeParse(value);\n if (!legacy.success) throw new TypeError(\"Stored authority request is malformed.\");\n if (context === undefined) {\n throw new TypeError(\"Legacy authority requests require the selected invocation context.\");\n }\n if (principal(legacy.data.sessionDid) !== principal(context.sessionDid)) continue;\n records.push(normalizeLegacyPermissionRequest(legacy.data, context));\n }\n return records;\n}\n\nexport async function findPermissionRequest(\n profile: string,\n requestId: string,\n identity: Pick<PermissionRequestIdentityInput, \"sessionDid\" | \"host\"> &\n Partial<Omit<LegacyRequestContext, \"profile\" | \"host\" | \"sessionDid\">>,\n): Promise<PermissionRequestArtifact | null> {\n const records = await listPermissionRequests(profile, {\n profile,\n host: identity.host,\n sessionDid: identity.sessionDid,\n posture: identity.posture ?? \"delegate-session\",\n operatorType: identity.operatorType ?? \"human\",\n ...(identity.ownerDid === undefined ? {} : { ownerDid: identity.ownerDid }),\n ...(identity.spaceId === undefined ? {} : { spaceId: identity.spaceId }),\n });\n return records.find((record) =>\n record.requestId === requestId &&\n record.sessionDid === identity.sessionDid &&\n record.host === identity.host,\n ) ?? null;\n}\n\n/**\n * Atomically returns the newest identical unresolved request, or writes a new\n * v1 record. Retention, stale-session pruning, covered-record pruning, and\n * partial-grant supersession all happen in the same existing profile lock.\n */\nexport async function createOrReusePermissionRequest(\n input: CreateOrReusePermissionRequestInput,\n): Promise<PermissionRequestResolution> {\n // Registered operations already computed this exact missing set during\n // canonical preflight. Do not recompute it with the legacy global matcher:\n // that matcher intentionally treats same-named short/full spaces as equal.\n const missing = input.resolveSpace === undefined\n ? evaluateAuthority(input.granted, input.missing).missing\n : canonicalizeCapabilities(input.missing);\n const now = input.now ?? (() => new Date());\n const observedNow = now();\n if (Number.isNaN(observedNow.getTime())) throw new TypeError(\"The artifact clock returned an invalid date.\");\n\n return updateProfileStore<unknown, PermissionRequestResolution>(\n input.profile,\n \"auth-requests\",\n (rawRecords) => {\n const records: PermissionRequestArtifact[] = [];\n for (const value of rawRecords) {\n const canonical = PermissionRequestArtifactSchema.safeParse(value);\n if (canonical.success) {\n if (principal(canonical.data.sessionDid) !== principal(input.sessionDid)) continue;\n records.push(canonical.data);\n continue;\n }\n const legacy = LegacyPermissionRequestArtifactSchema.safeParse(value);\n if (!legacy.success) throw new TypeError(\"Stored authority request is malformed.\");\n if (principal(legacy.data.sessionDid) !== principal(input.sessionDid)) continue;\n records.push(normalizeLegacyPermissionRequest(legacy.data, {\n profile: input.profile,\n host: input.host,\n sessionDid: input.sessionDid,\n posture: input.posture,\n operatorType: input.operatorType,\n ...(input.ownerDid === undefined ? {} : { ownerDid: input.ownerDid }),\n ...(input.spaceId === undefined ? {} : { spaceId: input.spaceId }),\n now: observedNow,\n }));\n }\n const retained = prunePermissionRequests(records, {\n profile: input.profile,\n sessionDid: input.sessionDid,\n granted: input.granted,\n resolveSpace: input.resolveSpace,\n now: observedNow,\n });\n\n if (missing.length === 0) {\n return { records: retained, result: { status: \"satisfied\", reused: false } };\n }\n\n const identity = createPermissionRequestIdentity({ ...input, missing });\n const withoutSuperseded = retained.filter((record) => !isSupersededBy(record, input, missing));\n const equivalent = newestEquivalentRequest(withoutSuperseded, identity);\n if (equivalent !== undefined && input.replace !== true) {\n return { records: withoutSuperseded, result: { status: \"created\", reused: true, request: equivalent } };\n }\n\n const next = withoutSuperseded.filter((record) =>\n input.replace !== true || permissionRequestArtifactIdentity(record) !== identity,\n );\n const request = buildPermissionRequestArtifact({ ...input, missing, now: () => observedNow });\n return {\n records: [...next, request],\n result: { status: \"created\", reused: false, request },\n };\n },\n );\n}\n\nexport interface PrunePermissionRequestsInput {\n readonly profile: string;\n readonly sessionDid: string;\n readonly granted: readonly PermissionEntry[];\n readonly resolveSpace?: OperationSpaceResolver;\n readonly now: Date;\n}\n\n/** Pure retention policy used by the locked persistence operation. */\nexport function prunePermissionRequests(\n records: readonly PermissionRequestArtifact[],\n input: PrunePermissionRequestsInput,\n): PermissionRequestArtifact[] {\n const cutoff = input.now.getTime() - THIRTY_DAYS_MS;\n return records.filter((record) => {\n const createdAt = Date.parse(record.createdAt);\n if (!Number.isFinite(createdAt) || createdAt < cutoff) return false;\n if (record.profile === input.profile && principal(record.sessionDid) !== principal(input.sessionDid)) return false;\n // Empty request arrays are historical CLI records. They carry no\n // authority, but remain readable and are retained byte-for-byte by the\n // shared format-1 writer for compatibility.\n if (record.requested.length === 0) return true;\n return !evaluateArtifactAuthority(\n input.granted,\n record.requested,\n input.resolveSpace,\n ).satisfied;\n });\n}\n\nfunction newestEquivalentRequest(\n records: readonly PermissionRequestArtifact[],\n identity: string,\n): PermissionRequestArtifact | undefined {\n let newest: PermissionRequestArtifact | undefined;\n for (const record of records) {\n if (permissionRequestArtifactIdentity(record) !== identity) continue;\n if (newest === undefined || record.createdAt >= newest.createdAt) newest = record;\n }\n return newest;\n}\n\nfunction isSupersededBy(\n record: PermissionRequestArtifact,\n input: CreateOrReusePermissionRequestInput,\n missing: readonly PermissionEntry[],\n): boolean {\n if (\n record.profile !== input.profile ||\n record.sessionDid !== input.sessionDid ||\n record.host !== input.host\n ) {\n return false;\n }\n const remaining = evaluateArtifactAuthority(\n input.granted,\n record.requested,\n input.resolveSpace,\n ).missing;\n return remaining.length > 0 &&\n permissionRequestArtifactIdentity(record) !==\n createPermissionRequestIdentity({ ...input, missing }) &&\n createPermissionRequestIdentity({ ...input, missing: remaining }) ===\n createPermissionRequestIdentity({ ...input, missing });\n}\n\nfunction permissionRequestArtifactIdentity(record: PermissionRequestArtifact): string {\n return createPermissionRequestIdentity({\n profile: record.profile,\n sessionDid: record.sessionDid,\n host: record.host,\n missing: record.requested,\n });\n}\n\nfunction evaluateArtifactAuthority(\n granted: readonly PermissionEntry[],\n required: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n) {\n return resolveSpace === undefined\n ? evaluateAuthority(granted, required)\n : evaluateOperationAuthority(granted, required, resolveSpace);\n}\n\nfunction defaultRequestId(now: Date): string {\n return `req_${now.getTime().toString(36)}_${randomUUID().replace(/-/g, \"\")}`;\n}\n","/**\n * TinyCloud App Manifest\n *\n * A declarative description of an app's identity and the capabilities it\n * needs. The manifest drives the SIWE recap at sign-in time, enabling a\n * single wallet prompt that covers the app's own permissions plus any\n * pre-declared delegations.\n *\n * The SDK does NOT fetch external manifests. Apps compose their own manifest\n * (optionally including backend or agent addenda) before handing it to the\n * SDK.\n *\n * Canonical spec: `.claude/specs/manifest.md`.\n *\n * @packageDocumentation\n */\n\nimport ms from \"ms\";\nimport { ACCOUNT_MANIFEST_PERMISSIONS } from \"@tinycloud/bootstrap\";\nimport { resolveSecretPath, SECRET_NAME_RE } from \"@tinycloud/sdk-services\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A single permission entry inside a manifest. This is the shape apps write\n * in their `manifest.json` and the shape we compare against when performing\n * the capability-subset derivability check in the delegation flow.\n *\n * `service` uses the long form (e.g. `\"tinycloud.kv\"`, `\"tinycloud.sql\"`).\n * `\"tinycloud.vault\"` is an SDK-only shorthand that expands to the KV\n * resources the vault service uses; it is never encoded as a recap service.\n */\nexport interface PermissionEntry {\n /** Service namespace, e.g. \"tinycloud.kv\", \"tinycloud.sql\", \"tinycloud.duckdb\", \"tinycloud.capabilities\". */\n service: string;\n /** Space name or full space URI. Defaults to \"applications\" inside manifests. */\n space?: string;\n /**\n * Service-specific path.\n * - tinycloud.kv: hierarchical prefix. \"/\" = all, \"foo/\" = prefix match, \"foo\" = exact key\n * - tinycloud.sql: database name/file (e.g. \"data.sqlite\") or \"/\" for all\n * - tinycloud.duckdb: database name/file\n * - tinycloud.capabilities: capability key URI or \"/\" for all\n */\n path: string;\n /**\n * Short action names (e.g. \"get\", \"put\", \"read\", \"schema\"). The SDK expands\n * these to full URNs (e.g. `tinycloud.kv/get`) during resolution.\n * Already-expanded URNs are passed through unchanged.\n */\n actions: string[];\n /** Exact ReCap caveat branches when this authority came from a signed ReCap. */\n caveats?: Record<string, unknown>[];\n /** When true, the manifest prefix is NOT prepended to `path`. Default false. */\n skipPrefix?: boolean;\n /** Per-entry expiry override, ms-format. */\n expiry?: string;\n /** User/agent-facing context for why this permission is requested. */\n description?: string;\n}\n\nexport type ManifestSecretActions =\n | true\n | string\n | string[]\n | {\n /** Actual vault secret name. Defaults to the manifest object key. */\n name?: string;\n /** Optional scoped secret namespace. Omit for global secrets. */\n scope?: string;\n actions?: string | string[];\n expiry?: string;\n description?: string;\n };\n\n/**\n * The valid values for `Manifest.defaults`.\n *\n * - `false` → no auto-included permissions\n * - `true` → standard tier (KV + SQL read/write + capabilities:read)\n * - `\"admin\"` → standard + SQL schema\n * - `\"all\"` → everything the SDK supports (including DuckDB)\n *\n * Unknown string values silently fall back to `true`. Values are normalized\n * (lowercase + trim) before matching.\n */\nexport type ManifestDefaults = boolean | \"admin\" | \"all\";\n\n/**\n * Agent-readable app knowledge bundle pointer.\n *\n * - `true` means the default root: `knowledge/index.md`\n * - string values must point at a markdown root under `knowledge/`\n */\nexport type ManifestKnowledge = true | string;\n\n/** Default root used when `manifest.knowledge === true`. */\nexport const DEFAULT_KNOWLEDGE_ROOT = \"knowledge/index.md\";\n\n/**\n * The raw manifest shape an app declares. See `.claude/specs/manifest.md`.\n */\nexport interface Manifest {\n /** Schema version. Optional, defaults to 1. */\n manifest_version?: 1;\n /** Application identifier / namespace prefix. Required. */\n app_id: string;\n /** Display name. Required. */\n name: string;\n /** Description of what the app or delegate does. Optional. */\n description?: string;\n /** DID of this manifest's delegate target. Optional. Required only for delegation materialization. */\n did?: string;\n /** URL to app icon. Optional. */\n icon?: string;\n /** App version string. Optional. */\n appVersion?: string;\n /** Default expiry for permissions. ms-format (\"30d\", \"2h\", \"1y\"). Default \"30d\". */\n expiry?: string;\n /** Space name or full space URI. Optional, defaults to \"applications\". */\n space?: string;\n /**\n * Path prefix auto-prepended to permission paths. Optional, defaults to\n * `app_id`. Set to `\"\"` to disable entirely. Individual permissions can opt\n * out with `skipPrefix: true`.\n */\n prefix?: string;\n /**\n * Default permission set to auto-include. Optional, defaults to `true`.\n * See {@link ManifestDefaults}.\n */\n defaults?: ManifestDefaults | string;\n /** Whether to include the public-space companion delegation. Default `true`. */\n includePublicSpace?: boolean;\n /** Agent-readable knowledge bundle root. `true` means `knowledge/index.md`. */\n knowledge?: ManifestKnowledge;\n /**\n * Additional permissions beyond the defaults. Use for cross-space access,\n * DuckDB (opt-in), or `skipPrefix: true` entries.\n */\n permissions?: PermissionEntry[];\n /**\n * Secret name shorthand. Entries resolve to encrypted vault KV resources in\n * the `secrets` space.\n */\n secrets?: Record<string, ManifestSecretActions>;\n}\n\n/**\n * A resolved permission entry with fully-expanded paths and action URNs.\n * This is the shape the delegation flow compares against parsed recap\n * capabilities, and the shape the session-key delegation path actually uses.\n */\nexport interface ResourceCapability {\n /** Long-form service, e.g. \"tinycloud.kv\". */\n service: string;\n /** Space name or URI. Short names are resolved to full SpaceIds at sign-in time. */\n space: string;\n /** Path with the manifest prefix applied (or skipped per `skipPrefix`). */\n path: string;\n /** Full-URN actions, e.g. [\"tinycloud.kv/get\", \"tinycloud.kv/put\"]. */\n actions: string[];\n /** Per-entry expiry override in milliseconds. */\n expiryMs?: number;\n /** User/agent-facing context copied from the source permission entry. */\n description?: string;\n}\n\n/**\n * A resolved delegation entry with fully-expanded permissions.\n */\nexport interface ResolvedDelegate {\n /** DID of the delegate. */\n did: string;\n /** Informational display name. Optional. */\n name?: string;\n /** Expiry in milliseconds (per-delegation > manifest default > 30 days). */\n expiryMs: number;\n /** Fully resolved permissions. */\n permissions: ResourceCapability[];\n}\n\n/**\n * The output of {@link resolveManifest}: a fully-expanded capability set\n * ready to drive the SIWE recap.\n */\nexport interface ResolvedCapabilities {\n /** Application identifier copied from manifest.app_id. */\n app_id: string;\n /** Delegate DID copied from manifest.did, when present. */\n did?: string;\n /** Effective default space for this manifest. */\n space: string;\n /** All session-key resources with paths fully resolved (prefix applied). */\n resources: ResourceCapability[];\n /** Default expiry for the session, in milliseconds. */\n expiryMs: number;\n /** Whether to include the public-space companion. */\n includePublicSpace: boolean;\n /** Delegate targets derived from manifests that declare `did`. */\n additionalDelegates: ResolvedDelegate[];\n}\n\nexport interface ManifestRegistryRecord {\n /** KV key inside the account space. */\n key: string;\n /** App id this record describes. */\n app_id: string;\n /** Latest manifest payloads composed for this app id. */\n manifests: Manifest[];\n}\n\nexport interface ComposeManifestOptions {\n /** Include implicit account-space registry permissions. Default true. */\n includeAccountRegistryPermissions?: boolean;\n}\n\nexport interface ComposedManifestRequest {\n /** Validated manifests that were composed. */\n manifests: Manifest[];\n /** Full permission union requested from the user in one SIWE. */\n resources: ResourceCapability[];\n /** Delegations that can be materialized after sign-in. */\n delegationTargets: ResolvedDelegate[];\n /** Account-space registry records to write after successful sign-in. */\n registryRecords: ManifestRegistryRecord[];\n /** Effective session expiry, using the longest composed manifest expiry. */\n expiryMs: number;\n /** Whether to include the public-space companion behavior. */\n includePublicSpace: boolean;\n}\n\n/**\n * Thrown when the manifest fails validation (missing id/name, bad expiry,\n * empty actions on a permission, etc).\n */\nexport class ManifestValidationError extends Error {\n constructor(message: string) {\n super(`Manifest validation failed: ${message}`);\n this.name = \"ManifestValidationError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Default expiry when neither the manifest, delegation, nor permission\n * specifies one. APP tier — see `expiry.ts`. Spec: 30 days.\n *\n * Kept as an ms-format string because the manifest schema stores expiry\n * as a string and the parser is shared between this default and\n * caller-provided values; converting `EXPIRY.APP_MS` back to a string\n * here would duplicate that same `30d` literal in another form.\n */\nexport const DEFAULT_EXPIRY = \"30d\";\n\n/**\n * Default `defaults` value when the manifest omits it. Spec: standard tier.\n */\nexport const DEFAULT_DEFAULTS: ManifestDefaults = true;\n\n/** Default manifest schema version. */\nexport const DEFAULT_MANIFEST_VERSION = 1;\n\n/** Default space for manifest-declared app data. */\nexport const DEFAULT_MANIFEST_SPACE = \"applications\";\n\n/** Account-space name used for installed-application registry records. */\nexport const ACCOUNT_REGISTRY_SPACE = \"account\";\n\n/** Account-space KV prefix used for installed-application registry records. */\nexport const ACCOUNT_REGISTRY_PATH = \"applications/\";\n\nexport const SECRETS_SPACE = \"secrets\";\n\n/** SDK-only permission service for encrypted vault resources. */\nexport const VAULT_PERMISSION_SERVICE = \"tinycloud.vault\";\n\ntype VaultKVBase = \"vault\";\n\ninterface VaultActionExpansion {\n bases: readonly VaultKVBase[];\n action: string;\n}\n\n/**\n * Known services and their short-form (recap URI) names. The TinyCloud\n * node encodes the recap resource URI with the short service name, while\n * action URNs and manifest entries use the long `tinycloud.<short>` form.\n * This table is the canonical bridge between the two.\n */\nexport const SERVICE_SHORT_TO_LONG: Readonly<Record<string, string>> =\n Object.freeze({\n kv: \"tinycloud.kv\",\n sql: \"tinycloud.sql\",\n duckdb: \"tinycloud.duckdb\",\n capabilities: \"tinycloud.capabilities\",\n hooks: \"tinycloud.hooks\",\n encryption: \"tinycloud.encryption\",\n delegation: \"tinycloud.delegation\",\n });\n\n/**\n * Manifest service identifier for TinyCloud encryption network grants.\n *\n * Encryption permissions live on a network id URN\n * (`urn:tinycloud:encryption:<ownerDid>:<network>`), not on a space.\n * The `path` field is the literal networkId; `actions` are\n * `[\"decrypt\"]` (expanded to `[\"tinycloud.encryption/decrypt\"]`).\n *\n * Apps should omit `space` for encryption permissions. The SDK may emit\n * an internal `\"encryption\"` compatibility label after expansion so the\n * older `PermissionEntry`/`ResourceCapability` shape can still carry the\n * raw network URN through subset checks.\n */\nexport const ENCRYPTION_PERMISSION_SERVICE = \"tinycloud.encryption\";\n\n/** Synthetic space label used by encryption manifest entries. */\nexport const ENCRYPTION_MANIFEST_SPACE = \"encryption\";\n\n/**\n * Inverse of {@link SERVICE_SHORT_TO_LONG}.\n */\nexport const SERVICE_LONG_TO_SHORT: Readonly<Record<string, string>> =\n Object.freeze(\n Object.fromEntries(\n Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s]),\n ),\n );\n\n/**\n * Default permission entries for the `true` / standard tier.\n *\n * `tinycloud.capabilities/read` is added separately for every requested\n * space, without the app path prefix. That keeps capability introspection\n * space-scoped instead of app-data scoped.\n */\nconst DEFAULT_STANDARD_ENTRIES: readonly PermissionEntry[] = [\n {\n service: \"tinycloud.kv\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\"],\n },\n];\n\n/**\n * Default permission entries for the `\"admin\"` tier: standard + sql/schema.\n */\nconst DEFAULT_ADMIN_ENTRIES: readonly PermissionEntry[] = [\n {\n service: \"tinycloud.kv\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\", \"schema\"],\n },\n];\n\n/**\n * Default permission entries for the `\"all\"` tier: admin + DuckDB.\n *\n * DuckDB is opt-in and only appears in this tier or in explicit manifest\n * `permissions` entries.\n */\nconst DEFAULT_ALL_ENTRIES: readonly PermissionEntry[] = [\n {\n service: \"tinycloud.kv\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\", \"schema\"],\n },\n {\n service: \"tinycloud.duckdb\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\"],\n },\n];\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse an ms-format duration string (e.g. \"30d\", \"2h\", \"1y\") into\n * milliseconds.\n *\n * @throws {ManifestValidationError} on empty string, non-string input, or\n * any input `ms()` cannot parse.\n */\nexport function parseExpiry(duration: string): number {\n if (typeof duration !== \"string\" || duration.length === 0) {\n throw new ManifestValidationError(\n `expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`,\n );\n }\n // `ms` returns `undefined` for unparseable input and can return a number\n // or a string depending on the call signature; cast explicitly.\n const parsed = (ms as unknown as (v: string) => number | undefined)(duration);\n if (typeof parsed !== \"number\" || !Number.isFinite(parsed) || parsed <= 0) {\n throw new ManifestValidationError(\n `invalid expiry duration: ${JSON.stringify(duration)}`,\n );\n }\n return parsed;\n}\n\n/**\n * Expand a list of action short names (or already-expanded URNs) into full\n * ability URNs of the form `<service>/<action>`.\n *\n * Examples:\n * `expandActionShortNames(\"tinycloud.kv\", [\"get\", \"put\"])`\n * → `[\"tinycloud.kv/get\", \"tinycloud.kv/put\"]`\n * `expandActionShortNames(\"tinycloud.kv\", [\"tinycloud.kv/get\"])`\n * → `[\"tinycloud.kv/get\"]` (passed through unchanged)\n */\nexport function expandActionShortNames(\n service: string,\n actions: readonly string[],\n): string[] {\n return actions.map((a) => {\n if (a.includes(\"/\")) {\n // Already a full URN — pass through.\n return a;\n }\n return `${service}/${a}`;\n });\n}\n\n/**\n * Expand SDK virtual permission services into concrete recap-capable services.\n *\n * Today this handles `\"tinycloud.vault\"`, which is backed by inline\n * network-encrypted KV records:\n * - read/get: `vault/<path>` with `tinycloud.kv/get`\n * - write/put: `vault/<path>` with `tinycloud.kv/put`\n * - delete/del: `vault/<path>` with `tinycloud.kv/del`\n * - list: `vault/<path>` with `tinycloud.kv/list`\n * - head: `vault/<path>` with `tinycloud.kv/get`\n * - metadata: `vault/<path>` with `tinycloud.kv/metadata`\n */\nexport function expandPermissionEntry(entry: PermissionEntry): PermissionEntry[] {\n if (entry.service === ENCRYPTION_PERMISSION_SERVICE) {\n return expandEncryptionPermissionEntry(entry);\n }\n if (entry.service !== VAULT_PERMISSION_SERVICE) {\n return [\n {\n ...entry,\n actions: expandActionShortNames(entry.service, entry.actions),\n },\n ];\n }\n\n return expandVaultPermissionEntry(entry);\n}\n\n/**\n * Expand a `tinycloud.encryption` manifest entry.\n *\n * - `path` MUST be a networkId URN (`urn:tinycloud:encryption:...`).\n * The manifest prefix is always skipped — network ids are top-level\n * owner-scoped resources, not space-scoped paths.\n * - `actions` must be `decrypt`, `network.create`, or `network.revoke`\n * (or their already-expanded `tinycloud.encryption/*` forms).\n *\n * The returned entry uses an internal `space = \"encryption\"` compatibility\n * label so old manifest grouping code does not collapse it into the app's\n * data space, and `skipPrefix` is forced to `true` so the prefix application\n * step is a no-op.\n */\nfunction expandEncryptionPermissionEntry(\n entry: PermissionEntry,\n): PermissionEntry[] {\n if (\n typeof entry.path !== \"string\" ||\n !entry.path.startsWith(\"urn:tinycloud:encryption:\")\n ) {\n throw new ManifestValidationError(\n `tinycloud.encryption entries require path to be a networkId URN ` +\n `(got ${JSON.stringify(entry.path)})`,\n );\n }\n const normalizedActions: string[] = [];\n for (const action of entry.actions) {\n if (action === \"decrypt\" || action === \"tinycloud.encryption/decrypt\") {\n normalizedActions.push(\"tinycloud.encryption/decrypt\");\n continue;\n }\n if (\n action === \"network.create\" ||\n action === \"tinycloud.encryption/network.create\"\n ) {\n normalizedActions.push(\"tinycloud.encryption/network.create\");\n continue;\n }\n if (\n action === \"network.revoke\" ||\n action === \"tinycloud.encryption/network.revoke\"\n ) {\n normalizedActions.push(\"tinycloud.encryption/network.revoke\");\n continue;\n }\n if (action.includes(\"/\")) {\n throw new ManifestValidationError(\n `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`,\n );\n }\n throw new ManifestValidationError(\n `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`,\n );\n }\n // Dedupe while preserving order\n const dedupedActions: string[] = [];\n const seen = new Set<string>();\n for (const a of normalizedActions) {\n if (!seen.has(a)) {\n dedupedActions.push(a);\n seen.add(a);\n }\n }\n // Encryption resources are network-scoped, not space-scoped. We\n // always emit the synthetic \"encryption\" space label so manifest\n // grouping can deal with it uniformly without collapsing the\n // networkId URN into an app data space.\n return [\n {\n service: ENCRYPTION_PERMISSION_SERVICE,\n space: ENCRYPTION_MANIFEST_SPACE,\n path: entry.path,\n actions: dedupedActions,\n skipPrefix: true,\n ...(entry.expiry !== undefined ? { expiry: entry.expiry } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n },\n ];\n}\n\n/**\n * Expand a list of permission entries using {@link expandPermissionEntry}.\n */\nexport function expandPermissionEntries(\n entries: readonly PermissionEntry[],\n): PermissionEntry[] {\n return entries.flatMap(expandPermissionEntry);\n}\n\n/**\n * Apply the manifest prefix to a permission path per the spec rules.\n *\n * - `skipPrefix: true` → path is returned as-is\n * - `prefix === \"\"` → path is returned as-is\n * - path starts with \"/\" → `prefix + path` (e.g. \"com.listen.app\" + \"/\" → \"com.listen.app/\")\n * - otherwise → `prefix + \"/\" + path` (e.g. \"com.listen.app\" + \"data.sqlite\" → \"com.listen.app/data.sqlite\")\n */\nexport function applyPrefix(\n prefix: string,\n path: string,\n skipPrefix: boolean,\n): string {\n if (skipPrefix) {\n return path;\n }\n if (prefix === \"\") {\n return path;\n }\n if (path.startsWith(\"/\")) {\n return `${prefix}${path}`;\n }\n return `${prefix}/${path}`;\n}\n\n/**\n * Fetch and parse a manifest from a URL (browser) or file path (node).\n * The runtime decides the fetch strategy via `globalThis.fetch`; this is\n * platform-agnostic. Callers that want custom loading should JSON.parse a\n * Manifest themselves and skip this helper.\n *\n * @throws if the fetch fails, the JSON is invalid, or the manifest fails\n * validation.\n */\nexport async function loadManifest(url: string): Promise<Manifest> {\n const fetchFn: typeof fetch | undefined = (\n globalThis as { fetch?: typeof fetch }\n ).fetch;\n if (typeof fetchFn !== \"function\") {\n throw new ManifestValidationError(\n \"loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch\",\n );\n }\n const res = await fetchFn(url);\n if (!res.ok) {\n throw new ManifestValidationError(\n `failed to fetch manifest from ${url}: HTTP ${res.status}`,\n );\n }\n const json = (await res.json()) as unknown;\n return validateManifest(json);\n}\n\n/**\n * Validate a manifest-shaped object and return it strongly-typed.\n * Throws {@link ManifestValidationError} on any hard failure.\n */\nexport function validateManifest(input: unknown): Manifest {\n if (input === null || typeof input !== \"object\") {\n throw new ManifestValidationError(\"manifest must be an object\");\n }\n const m = input as Manifest;\n if (\n m.manifest_version !== undefined &&\n m.manifest_version !== DEFAULT_MANIFEST_VERSION\n ) {\n throw new ManifestValidationError(\n `manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`,\n );\n }\n if (typeof m.app_id !== \"string\" || m.app_id.length === 0) {\n throw new ManifestValidationError(\n \"manifest.app_id is required and must be a non-empty string\",\n );\n }\n if (typeof m.name !== \"string\" || m.name.length === 0) {\n throw new ManifestValidationError(\n \"manifest.name is required and must be a non-empty string\",\n );\n }\n if (\n m.did !== undefined &&\n (typeof m.did !== \"string\" || m.did.length === 0)\n ) {\n throw new ManifestValidationError(\n \"manifest.did must be a non-empty DID string\",\n );\n }\n if (\n m.space !== undefined &&\n (typeof m.space !== \"string\" || m.space.length === 0)\n ) {\n throw new ManifestValidationError(\n \"manifest.space must be a non-empty string\",\n );\n }\n if (m.expiry !== undefined) {\n // Will throw with a clear error if invalid.\n parseExpiry(m.expiry);\n }\n if (m.knowledge !== undefined) {\n resolveManifestKnowledgeRoot(m.knowledge);\n }\n if (m.permissions !== undefined) {\n if (!Array.isArray(m.permissions)) {\n throw new ManifestValidationError(\n \"manifest.permissions must be an array\",\n );\n }\n m.permissions.forEach((p, i) =>\n validatePermissionEntry(p, `permissions[${i}]`),\n );\n }\n if (m.secrets !== undefined) {\n validateManifestSecrets(m.secrets);\n }\n return m;\n}\n\n/**\n * Resolve a manifest knowledge pointer to its root markdown file.\n *\n * This is metadata only; app knowledge does not create capabilities.\n */\nexport function resolveManifestKnowledgeRoot(\n knowledge: Manifest[\"knowledge\"] | undefined,\n): string | undefined {\n if (knowledge === undefined) {\n return undefined;\n }\n if (knowledge === true) {\n return DEFAULT_KNOWLEDGE_ROOT;\n }\n if (typeof knowledge !== \"string\" || knowledge.length === 0) {\n throw new ManifestValidationError(\n \"manifest.knowledge must be true or a knowledge/*.md root path\",\n );\n }\n if (!/^knowledge\\/.+\\.md$/.test(knowledge)) {\n throw new ManifestValidationError(\n \"manifest.knowledge must be true or a knowledge/*.md root path\",\n );\n }\n return knowledge;\n}\n\nfunction validateManifestSecrets(secrets: unknown): void {\n if (secrets === null || typeof secrets !== \"object\" || Array.isArray(secrets)) {\n throw new ManifestValidationError(\"manifest.secrets must be an object\");\n }\n\n for (const [name, spec] of Object.entries(secrets)) {\n if (!SECRET_NAME_RE.test(name)) {\n throw new ManifestValidationError(\n `manifest.secrets.${name} must match ${SECRET_NAME_RE.source}`,\n );\n }\n try {\n resolveSecretPath(\n secretNameFromSpec(name, spec as ManifestSecretActions),\n { scope: secretScopeFromSpec(spec as ManifestSecretActions) },\n );\n } catch (error) {\n throw new ManifestValidationError(\n `manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n const actions = secretActionsFromSpec(name, spec as ManifestSecretActions);\n if (actions.length === 0) {\n throw new ManifestValidationError(\n `manifest.secrets.${name} actions must be non-empty`,\n );\n }\n for (const action of actions) {\n if (typeof action !== \"string\" || action.length === 0) {\n throw new ManifestValidationError(\n `manifest.secrets.${name} actions must be non-empty strings`,\n );\n }\n }\n if (\n spec !== null &&\n typeof spec === \"object\" &&\n !Array.isArray(spec) &&\n (spec as { expiry?: unknown }).expiry !== undefined\n ) {\n parseExpiry((spec as { expiry: string }).expiry);\n }\n }\n}\n\nfunction validatePermissionEntry(p: unknown, path: string): void {\n if (p === null || typeof p !== \"object\") {\n throw new ManifestValidationError(`${path} must be an object`);\n }\n const entry = p as PermissionEntry;\n if (typeof entry.service !== \"string\" || entry.service.length === 0) {\n throw new ManifestValidationError(`${path}.service is required`);\n }\n if (\n entry.space !== undefined &&\n (typeof entry.space !== \"string\" || entry.space.length === 0)\n ) {\n throw new ManifestValidationError(\n `${path}.space must be a non-empty string`,\n );\n }\n if (typeof entry.path !== \"string\") {\n throw new ManifestValidationError(\n `${path}.path is required (use \"\" or \"/\" for root)`,\n );\n }\n if (!Array.isArray(entry.actions) || entry.actions.length === 0) {\n throw new ManifestValidationError(\n `${path}.actions must be a non-empty array`,\n );\n }\n for (const action of entry.actions) {\n if (typeof action !== \"string\" || action.length === 0) {\n throw new ManifestValidationError(\n `${path}.actions must contain non-empty strings`,\n );\n }\n if (entry.service === VAULT_PERMISSION_SERVICE) {\n vaultActionExpansion(action);\n }\n }\n if (entry.caveats !== undefined &&\n (!Array.isArray(entry.caveats) || entry.caveats.some((caveat) =>\n caveat === null || typeof caveat !== \"object\" || Array.isArray(caveat)\n ))\n ) {\n throw new ManifestValidationError(`${path}.caveats must be an array of objects`);\n }\n if (entry.expiry !== undefined) {\n parseExpiry(entry.expiry);\n }\n}\n\n/**\n * Normalize a `defaults` value: lowercase + trim, then match against known\n * tiers. Unknown string values silently fall back to `true` (standard).\n * Boolean values pass through.\n */\nexport function normalizeDefaults(\n value: Manifest[\"defaults\"] | undefined,\n): ManifestDefaults {\n if (value === undefined) {\n return DEFAULT_DEFAULTS;\n }\n if (typeof value === \"boolean\") {\n return value;\n }\n if (typeof value !== \"string\") {\n // Spec says unknown values silently fall back to `true`.\n return true;\n }\n const normalized = value.trim().toLowerCase();\n if (normalized === \"admin\" || normalized === \"all\") {\n return normalized;\n }\n // Anything else, including \"true\"/\"false\"/\"standard\"/garbage, falls back\n // to the standard tier per spec.\n return true;\n}\n\n/**\n * Return the default permission entries for the given tier. Entries are\n * deep-cloned so callers can mutate them without affecting the constants.\n */\nfunction defaultEntriesForTier(tier: ManifestDefaults): PermissionEntry[] {\n if (tier === false) {\n return [];\n }\n const source =\n tier === \"admin\"\n ? DEFAULT_ADMIN_ENTRIES\n : tier === \"all\"\n ? DEFAULT_ALL_ENTRIES\n : DEFAULT_STANDARD_ENTRIES;\n return source.map((e) => ({\n service: e.service,\n space: e.space,\n path: e.path,\n actions: [...e.actions],\n ...(e.skipPrefix !== undefined ? { skipPrefix: e.skipPrefix } : {}),\n }));\n}\n\n/**\n * Resolve a raw manifest into a {@link ResolvedCapabilities} object: expand\n * shortform actions, apply the prefix, merge defaults, and compute effective\n * expiries. Pure function — does no I/O.\n *\n * Resolution semantics (spec):\n * - `prefix` defaults to `app_id`; set to `\"\"` to disable prefix application entirely.\n * - `space` defaults to `applications`; per-permission `space` overrides it.\n * - `defaults` defaults to `true` (standard tier); unknown string values fall back to `true`.\n * - Per-entry expiry overrides per-delegation overrides manifest > `DEFAULT_EXPIRY`.\n * - Default entries use `skipPrefix: false` so they inherit the manifest prefix.\n */\nexport function resolveManifest(input: Manifest): ResolvedCapabilities {\n const manifest = validateManifest(input);\n\n const prefix =\n manifest.prefix !== undefined ? manifest.prefix : manifest.app_id;\n const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;\n const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);\n const includePublicSpace = manifest.includePublicSpace ?? true;\n const tier = normalizeDefaults(manifest.defaults);\n\n const defaultEntries = defaultEntriesForTier(tier);\n const explicitEntries = manifest.permissions ?? [];\n const secretEntries = secretEntriesForManifest(manifest.secrets);\n\n // Merge order: defaults first, then explicit entries, so explicit entries\n // for the same (service, space, path) tuple override defaults.\n const allEntries: PermissionEntry[] = [\n ...defaultEntries,\n ...explicitEntries,\n ...secretEntries,\n ];\n\n const resources: ResourceCapability[] = withCapabilitiesReadForSpaces(\n allEntries.flatMap((entry) => resolveEntry(entry, prefix, expiryMs, space)),\n );\n\n const additionalDelegates: ResolvedDelegate[] =\n manifest.did === undefined\n ? []\n : [\n {\n did: manifest.did,\n name: manifest.name,\n expiryMs,\n permissions: resources.map(cloneResourceCapability),\n },\n ];\n\n return {\n app_id: manifest.app_id,\n ...(manifest.did !== undefined ? { did: manifest.did } : {}),\n space,\n resources,\n expiryMs,\n includePublicSpace,\n additionalDelegates,\n };\n}\n\nfunction normalizeSecretActions(actions: readonly string[]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n const add = (action: string) => {\n if (!seen.has(action)) {\n out.push(action);\n seen.add(action);\n }\n };\n\n for (const action of actions) {\n if (action === \"read\") {\n add(\"get\");\n continue;\n }\n if (action === \"write\") {\n add(\"put\");\n continue;\n }\n if (action === \"delete\") {\n add(\"del\");\n continue;\n }\n if (\n action === \"get\" ||\n action === \"put\" ||\n action === \"del\" ||\n action === \"list\" ||\n action === \"metadata\"\n ) {\n add(action);\n continue;\n }\n if (\n action === \"tinycloud.kv/get\" ||\n action === \"tinycloud.kv/put\" ||\n action === \"tinycloud.kv/del\" ||\n action === \"tinycloud.kv/list\" ||\n action === \"tinycloud.kv/metadata\"\n ) {\n add(action);\n continue;\n }\n throw new ManifestValidationError(\n `unknown secret action ${JSON.stringify(action)}; expected read, write, delete, list, or metadata`,\n );\n }\n\n return out;\n}\n\nfunction secretNameFromSpec(\n fallbackName: string,\n spec: ManifestSecretActions,\n): string {\n if (spec !== null && typeof spec === \"object\" && !Array.isArray(spec)) {\n return spec.name ?? fallbackName;\n }\n return fallbackName;\n}\n\nfunction secretScopeFromSpec(\n spec: ManifestSecretActions,\n): string | undefined {\n if (spec !== null && typeof spec === \"object\" && !Array.isArray(spec)) {\n return spec.scope;\n }\n return undefined;\n}\n\nfunction secretActionsFromSpec(\n name: string,\n spec: ManifestSecretActions,\n): string[] {\n if (spec === true) {\n return [\"read\"];\n }\n if (typeof spec === \"string\") {\n return [spec];\n }\n if (Array.isArray(spec)) {\n return spec;\n }\n if (spec === null || typeof spec !== \"object\") {\n throw new ManifestValidationError(\n `manifest.secrets.${name} must be true, a string action, an actions array, or an object`,\n );\n }\n if (spec.actions === undefined) {\n return [\"read\"];\n }\n if (typeof spec.actions === \"string\") {\n return [spec.actions];\n }\n if (Array.isArray(spec.actions)) {\n return spec.actions;\n }\n throw new ManifestValidationError(\n `manifest.secrets.${name}.actions must be a string or array`,\n );\n}\n\nfunction secretEntriesForManifest(\n secrets: Manifest[\"secrets\"] | undefined,\n): PermissionEntry[] {\n if (secrets === undefined) {\n return [];\n }\n\n const entries: PermissionEntry[] = [];\n for (const [name, spec] of Object.entries(secrets)) {\n const actions = secretActionsFromSpec(name, spec);\n const secretPath = resolveSecretPath(\n secretNameFromSpec(name, spec),\n { scope: secretScopeFromSpec(spec) },\n );\n const extra: { expiry?: string; description?: string } =\n spec !== true && typeof spec === \"object\" && !Array.isArray(spec)\n ? spec\n : {};\n entries.push({\n service: VAULT_PERMISSION_SERVICE,\n space: SECRETS_SPACE,\n path: secretPath.vaultKey,\n actions: normalizeSecretActions(actions),\n skipPrefix: true,\n ...(extra.expiry !== undefined ? { expiry: extra.expiry } : {}),\n ...(extra.description !== undefined\n ? { description: extra.description }\n : {}),\n });\n }\n return entries;\n}\n\n/**\n * Expand a single permission entry into a {@link ResourceCapability}:\n * apply the prefix to the path and expand short actions into full URNs.\n */\nfunction resolveEntry(\n entry: PermissionEntry,\n prefix: string,\n _inheritedExpiryMs: number,\n inheritedSpace: string,\n): ResourceCapability[] {\n // Encryption permissions reference a networkId URN as their resource\n // — the manifest prefix is meaningless against a top-level\n // owner-scoped resource and would corrupt the URN. Always skip.\n const skipPrefixForEntry =\n entry.skipPrefix === true ||\n entry.service === ENCRYPTION_PERMISSION_SERVICE;\n const resolvedPath = applyPrefix(prefix, entry.path, skipPrefixForEntry);\n const entryExpiryMs =\n entry.expiry !== undefined ? parseExpiry(entry.expiry) : undefined;\n return expandPermissionEntry({\n ...entry,\n space: entry.space ?? inheritedSpace,\n path: resolvedPath,\n skipPrefix: true,\n }).map((expanded) => ({\n service: expanded.service,\n space: expanded.space ?? inheritedSpace,\n path: expanded.path,\n actions: expanded.actions,\n // Only populate `expiryMs` when the entry had its own expiry override.\n // When absent, callers use the parent (delegation or manifest) expiry\n // which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.\n ...(entryExpiryMs !== undefined ? { expiryMs: entryExpiryMs } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n }));\n}\n\nfunction expandVaultPermissionEntry(entry: PermissionEntry): PermissionEntry[] {\n const byBase = new Map<VaultKVBase, string[]>();\n\n for (const action of entry.actions) {\n const expansion = vaultActionExpansion(action);\n for (const base of expansion.bases) {\n const actions = byBase.get(base) ?? [];\n if (!actions.includes(expansion.action)) {\n actions.push(expansion.action);\n }\n byBase.set(base, actions);\n }\n }\n\n return [...byBase.entries()].map(([base, actions]) => ({\n ...entry,\n service: \"tinycloud.kv\",\n path: vaultKVPath(base, entry.path),\n actions,\n skipPrefix: true,\n }));\n}\n\nfunction vaultActionExpansion(action: string): VaultActionExpansion {\n const normalized = normalizeVaultAction(action);\n if (normalized === \"read\" || normalized === \"get\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/get\" };\n }\n if (normalized === \"write\" || normalized === \"put\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/put\" };\n }\n if (normalized === \"delete\" || normalized === \"del\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/del\" };\n }\n if (normalized === \"list\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/list\" };\n }\n if (normalized === \"head\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/get\" };\n }\n if (normalized === \"metadata\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/metadata\" };\n }\n\n throw new ManifestValidationError(\n `unknown vault action ${JSON.stringify(action)}; expected read, write, delete, get, put, del, list, head, or metadata`,\n );\n}\n\nfunction normalizeVaultAction(action: string): string {\n if (action.startsWith(`${VAULT_PERMISSION_SERVICE}/`)) {\n return action.slice(`${VAULT_PERMISSION_SERVICE}/`.length);\n }\n if (action.startsWith(\"tinycloud.kv/\")) {\n return action.slice(\"tinycloud.kv/\".length);\n }\n if (action.includes(\"/\")) {\n throw new ManifestValidationError(\n `unknown vault action ${JSON.stringify(action)}; expected a tinycloud.vault or tinycloud.kv action`,\n );\n }\n return action;\n}\n\nfunction vaultKVPath(base: VaultKVBase, path: string): string {\n const normalized = path.startsWith(\"/\") ? path.slice(1) : path;\n return `${base}/${normalized}`;\n}\n\nfunction cloneResourceCapability(\n entry: ResourceCapability,\n): ResourceCapability {\n return {\n service: entry.service,\n space: entry.space,\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.expiryMs !== undefined ? { expiryMs: entry.expiryMs } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n };\n}\n\nfunction clonePermissionEntry(entry: PermissionEntry): PermissionEntry {\n return {\n service: entry.service,\n ...(entry.space !== undefined ? { space: entry.space } : {}),\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.caveats !== undefined\n ? { caveats: JSON.parse(JSON.stringify(entry.caveats)) as Record<string, unknown>[] }\n : {}),\n ...(entry.skipPrefix !== undefined ? { skipPrefix: entry.skipPrefix } : {}),\n ...(entry.expiry !== undefined ? { expiry: entry.expiry } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n };\n}\n\nfunction dedupeResources(\n resources: readonly ResourceCapability[],\n): ResourceCapability[] {\n const byKey = new Map<string, ResourceCapability>();\n\n for (const resource of resources) {\n const key = `${resource.service}\\u0000${resource.space}\\u0000${resource.path}\\u0000${resource.expiryMs ?? \"\"}`;\n const existing = byKey.get(key);\n if (existing === undefined) {\n byKey.set(key, cloneResourceCapability(resource));\n continue;\n }\n\n const seen = new Set(existing.actions);\n for (const action of resource.actions) {\n if (!seen.has(action)) {\n existing.actions.push(action);\n seen.add(action);\n }\n }\n if (\n existing.description === undefined &&\n resource.description !== undefined\n ) {\n existing.description = resource.description;\n }\n }\n\n return [...byKey.values()];\n}\n\nfunction capabilitiesReadPermission(space: string): ResourceCapability {\n return {\n service: \"tinycloud.capabilities\",\n space,\n path: \"\",\n actions: [\"tinycloud.capabilities/read\"],\n };\n}\n\nfunction withCapabilitiesReadForSpaces(\n resources: readonly ResourceCapability[],\n): ResourceCapability[] {\n if (resources.length === 0) {\n return [];\n }\n\n const spaces = new Set(\n resources\n .filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE)\n .map((resource) => resource.space),\n );\n return dedupeResources([\n ...resources,\n ...[...spaces].map(capabilitiesReadPermission),\n ]);\n}\n\n/**\n * Compose one or more manifests into the single capability request that should\n * be signed. Fetching manifests is intentionally out of band; callers pass the\n * already-loaded manifest objects.\n */\nexport function composeManifestRequest(\n inputs: readonly Manifest[],\n options: ComposeManifestOptions = {},\n): ComposedManifestRequest {\n if (!Array.isArray(inputs) || inputs.length === 0) {\n throw new ManifestValidationError(\n \"composeManifestRequest requires at least one manifest\",\n );\n }\n\n const includeAccountRegistryPermissions =\n options.includeAccountRegistryPermissions ?? true;\n const manifests = inputs.map(validateManifest);\n const resolved = manifests.map(resolveManifest);\n const resources = resolved.flatMap((entry) => entry.resources);\n const delegationTargets = resolved.flatMap((entry) =>\n entry.additionalDelegates.map((delegate) => ({\n ...delegate,\n permissions: dedupeResources(delegate.permissions),\n })),\n );\n\n if (includeAccountRegistryPermissions) {\n resources.push(\n ...ACCOUNT_MANIFEST_PERMISSIONS.map((resource) => ({\n ...resource,\n actions: [...resource.actions],\n })),\n );\n }\n const resourcesWithImplicitCapabilities =\n withCapabilitiesReadForSpaces(resources);\n\n const manifestsByAppId = new Map<string, Manifest[]>();\n for (const manifest of manifests) {\n const current = manifestsByAppId.get(manifest.app_id);\n if (current === undefined) {\n manifestsByAppId.set(manifest.app_id, [manifest]);\n } else {\n current.push(manifest);\n }\n }\n\n const registryRecords: ManifestRegistryRecord[] =\n includeAccountRegistryPermissions\n ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({\n key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,\n app_id,\n manifests: appManifests.map((manifest) => ({\n ...manifest,\n permissions: manifest.permissions?.map(clonePermissionEntry),\n })),\n }))\n : [];\n\n return {\n manifests,\n resources: resourcesWithImplicitCapabilities,\n delegationTargets,\n registryRecords,\n expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),\n includePublicSpace: resolved.some((entry) => entry.includePublicSpace),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Abilities map construction (bridge to WASM prepareSession / createDelegation)\n// ---------------------------------------------------------------------------\n\n/**\n * The shape `prepareSession` and the multi-resource `createDelegation` WASM\n * export both accept:\n *\n * ```\n * { [shortService]: { [path]: [fullUrnAction, ...] } }\n * ```\n *\n * - `shortService` is the recap-level service segment (`\"kv\"`, `\"sql\"`,\n * `\"duckdb\"`, `\"capabilities\"`, `\"hooks\"`) — not the manifest long form.\n * - `path` is the fully-resolved path (prefix already applied). An empty\n * string means \"no path segment\" on the resource URI.\n * - Action strings are full URNs like `\"tinycloud.kv/get\"`.\n *\n * This is a single source of truth for both the session's own recap (at\n * sign-in) and the delegations it can derive (post sign-in). We re-use it\n * for both so one manifest drives both sides.\n */\nexport type AbilitiesMap = Record<string, Record<string, string[]>>;\n\n/**\n * Per-space abilities map accepted by the newer WASM session config:\n *\n * ```\n * { [spaceIdOrName]: { [shortService]: { [path]: [fullUrnAction, ...] } } }\n * ```\n */\nexport type SpaceAbilitiesMap = Record<string, AbilitiesMap>;\n\n/**\n * Convert a list of {@link ResourceCapability} entries (manifest\n * long-form service, full-URN actions) into the {@link AbilitiesMap}\n * shape the WASM layer expects.\n *\n * When multiple entries target the same `(service, path)` pair, their\n * action lists are merged and deduped. Entries whose service has no\n * short-form mapping in {@link SERVICE_LONG_TO_SHORT} are rejected with\n * a {@link ManifestValidationError} — the SDK does not silently drop\n * unknown services because the recap encoding would lose them.\n *\n * Paths are kept verbatim: this function does NOT collapse\n * `\"com.listen.app/\"` and `\"com.listen.app\"` or reinterpret empty /\n * slash strings. Callers that care about path canonicalization should\n * normalize before calling.\n */\nexport function resourceCapabilitiesToAbilitiesMap(\n resources: readonly ResourceCapability[],\n): AbilitiesMap {\n const out: AbilitiesMap = {};\n for (const r of resources) {\n const shortService = SERVICE_LONG_TO_SHORT[r.service];\n if (shortService === undefined) {\n throw new ManifestValidationError(\n `unknown service '${r.service}' — no short-form mapping. Known services: ${Object.keys(SERVICE_LONG_TO_SHORT).join(\", \")}`,\n );\n }\n if (out[shortService] === undefined) {\n out[shortService] = {};\n }\n const pathsMap = out[shortService];\n const existing = pathsMap[r.path];\n if (existing === undefined) {\n // Copy so downstream mutation can't leak back into the input.\n pathsMap[r.path] = [...r.actions];\n } else {\n // Merge + dedupe while preserving first-seen order.\n const seen = new Set(existing);\n for (const action of r.actions) {\n if (!seen.has(action)) {\n existing.push(action);\n seen.add(action);\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Group resolved capabilities by `space`, then convert each group into a WASM\n * abilities map. Short space names are left as-is here; platform layers that\n * know the wallet address and chain id turn them into full SpaceIds.\n */\nexport function resourceCapabilitiesToSpaceAbilitiesMap(\n resources: readonly ResourceCapability[],\n): SpaceAbilitiesMap {\n const grouped = new Map<string, ResourceCapability[]>();\n for (const resource of resources) {\n const entries = grouped.get(resource.space);\n if (entries === undefined) {\n grouped.set(resource.space, [resource]);\n } else {\n entries.push(resource);\n }\n }\n\n const out: SpaceAbilitiesMap = {};\n for (const [space, entries] of grouped.entries()) {\n out[space] = resourceCapabilitiesToAbilitiesMap(entries);\n }\n return out;\n}\n\n/**\n * Build the {@link AbilitiesMap} that a session should be signed with,\n * given a {@link ResolvedCapabilities} (i.e. the output of\n * {@link resolveManifest}).\n *\n * The resulting map is the **union** of:\n * 1. the app's own resources (`resolved.resources`), and\n * 2. every permission declared in every `additionalDelegates[*]` entry.\n *\n * The union is what makes the manifest's delegations ergonomic: at\n * sign-in, the session key acquires recap coverage for both the app's\n * runtime needs and every downstream delegation target. Post sign-in,\n * `delegateTo(backendDID, backendPermissions)` can then issue the\n * sub-delegation via the session key (no wallet prompt) because the\n * caps are already part of the granted set.\n *\n * Duplicate `(service, path, action)` triples across resources and\n * delegations are merged and deduped — the session SIWE doesn't need\n * them repeated.\n */\nexport function manifestAbilitiesUnion(\n resolved: ResolvedCapabilities,\n): AbilitiesMap {\n const all: ResourceCapability[] = [...resolved.resources];\n for (const delegate of resolved.additionalDelegates) {\n for (const perm of delegate.permissions) {\n all.push(perm);\n }\n }\n return resourceCapabilitiesToAbilitiesMap(all);\n}\n","import { getAddress, isAddress } from \"viem\";\nimport { KV, SQL, CAPABILITIES, SPACE, ENCRYPTION } from \"./capabilities.js\";\n\nexport * from \"./capabilities.js\";\n\nexport type ManifestDefaults = boolean | \"admin\" | \"all\";\n\nexport interface PermissionEntry {\n service: string;\n space?: string;\n path: string;\n actions: string[];\n skipPrefix?: boolean;\n expiry?: string;\n description?: string;\n}\n\nexport interface Manifest {\n manifest_version?: 1;\n app_id: string;\n name: string;\n description?: string;\n did?: string;\n icon?: string;\n appVersion?: string;\n expiry?: string;\n space?: string;\n prefix?: string;\n defaults?: ManifestDefaults | string;\n includePublicSpace?: boolean;\n knowledge?: true | string;\n permissions?: PermissionEntry[];\n}\n\nexport interface ResourceCapability {\n service: string;\n space: string;\n path: string;\n actions: string[];\n expiryMs?: number;\n description?: string;\n}\n\nexport interface ManifestRegistryRecord {\n key: string;\n app_id: string;\n manifests: Manifest[];\n}\n\nexport interface ComposedManifestRequest {\n manifests: Manifest[];\n resources: ResourceCapability[];\n delegationTargets: [];\n registryRecords: ManifestRegistryRecord[];\n expiryMs: number;\n includePublicSpace: boolean;\n}\n\nexport interface ComposeManifestOptions {\n includeAccountRegistryPermissions?: boolean;\n}\n\nexport const DEFAULT_MANIFEST_SPACE = \"applications\";\nexport const ACCOUNT_REGISTRY_SPACE = \"account\";\nexport const ACCOUNT_REGISTRY_PATH = \"applications/\";\nexport const SECRETS_SPACE = \"secrets\";\nexport const BOOTSTRAP_DEFAULT_SPACE = \"default\";\nexport const BOOTSTRAP_PUBLIC_SPACE = \"public\";\nexport const BOOTSTRAP_ENCRYPTION_NETWORK_NAME = \"default\";\nexport const BOOTSTRAP_ENCRYPTION_NETWORK_RESOURCE_TEMPLATE =\n `urn:tinycloud:encryption:{ownerDid}:${BOOTSTRAP_ENCRYPTION_NETWORK_NAME}` as const;\n\nconst DEFAULT_EXPIRY_MS = 30 * 24 * 60 * 60 * 1000;\nconst VAULT_PERMISSION_SERVICE = \"tinycloud.vault\";\nconst ENCRYPTION_PERMISSION_SERVICE = \"tinycloud.encryption\";\nconst ENCRYPTION_MANIFEST_SPACE = \"encryption\";\nconst NETWORK_CREATE_ACTION = ENCRYPTION.NETWORK_CREATE;\n\nexport const BOOTSTRAP_SPACE_NAMES = [\n BOOTSTRAP_DEFAULT_SPACE,\n DEFAULT_MANIFEST_SPACE,\n ACCOUNT_REGISTRY_SPACE,\n SECRETS_SPACE,\n BOOTSTRAP_PUBLIC_SPACE,\n] as const;\n\nexport type BootstrapSpaceName = (typeof BOOTSTRAP_SPACE_NAMES)[number];\n\nexport const TINYCLOUD_DEFAULT_SPACE_MANIFEST: Manifest = {\n app_id: \"xyz.tinycloud.default\",\n name: \"TinyCloud Default Space\",\n space: BOOTSTRAP_DEFAULT_SPACE,\n prefix: \"\",\n defaults: false,\n includePublicSpace: false,\n permissions: [\n {\n service: \"tinycloud.kv\",\n space: BOOTSTRAP_DEFAULT_SPACE,\n // Empty path = whole service on this space. Do NOT use \"/\": the recap\n // encoder joins it as `<space>/<service>//`, which the node's byte-prefix\n // resource matching can never extend (real paths start `<service>/x…`).\n path: \"\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: BOOTSTRAP_DEFAULT_SPACE,\n path: \"\",\n actions: [\"read\", \"write\"],\n },\n ],\n};\n\nexport const TINYCLOUD_APPLICATIONS_SPACE_MANIFEST: Manifest = {\n app_id: \"xyz.tinycloud.applications\",\n name: \"TinyCloud Applications Space\",\n space: DEFAULT_MANIFEST_SPACE,\n prefix: \"\",\n defaults: false,\n includePublicSpace: false,\n permissions: [\n {\n service: \"tinycloud.kv\",\n space: DEFAULT_MANIFEST_SPACE,\n // Empty path = whole service on this space. Do NOT use \"/\": the recap\n // encoder joins it as `<space>/<service>//`, which the node's byte-prefix\n // resource matching can never extend (real paths start `<service>/x…`).\n path: \"\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"\",\n actions: [\"read\", \"write\"],\n },\n ],\n};\n\nexport const TINYCLOUD_ACCOUNT_SPACE_MANIFEST: Manifest = {\n app_id: \"xyz.tinycloud.account\",\n name: \"TinyCloud Account Registry\",\n space: ACCOUNT_REGISTRY_SPACE,\n prefix: \"\",\n defaults: false,\n includePublicSpace: false,\n permissions: [\n {\n service: \"tinycloud.kv\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"applications/\",\n actions: [\"get\", \"put\", \"list\"],\n },\n {\n service: \"tinycloud.kv\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"spaces/\",\n actions: [\"get\", \"put\", \"list\"],\n },\n {\n service: \"tinycloud.sql\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"account\",\n actions: [\"read\", \"write\", \"schema\"],\n },\n ],\n};\n\nexport const TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST: Manifest = {\n app_id: \"xyz.tinycloud.secrets\",\n name: \"TinyCloud Secrets\",\n space: SECRETS_SPACE,\n prefix: \"\",\n defaults: false,\n includePublicSpace: false,\n permissions: [\n {\n service: \"tinycloud.vault\",\n space: SECRETS_SPACE,\n path: \"secrets/\",\n actions: [\"read\"],\n skipPrefix: true,\n },\n {\n service: \"tinycloud.kv\",\n space: SECRETS_SPACE,\n path: \"variables\",\n actions: [\"list\", \"metadata\"],\n skipPrefix: true,\n },\n {\n service: \"tinycloud.kv\",\n space: SECRETS_SPACE,\n path: \"variables/\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n skipPrefix: true,\n },\n {\n service: \"tinycloud.sql\",\n space: SECRETS_SPACE,\n path: \"default\",\n actions: [\"read\", \"write\", \"schema\"],\n skipPrefix: true,\n },\n {\n service: \"tinycloud.capabilities\",\n space: SECRETS_SPACE,\n path: \"\",\n actions: [\"read\"],\n skipPrefix: true,\n },\n ],\n};\n\nexport const TINYCLOUD_PUBLIC_SPACE_MANIFEST: Manifest = {\n app_id: \"xyz.tinycloud.public\",\n name: \"TinyCloud Public Space\",\n space: BOOTSTRAP_PUBLIC_SPACE,\n prefix: \"\",\n defaults: false,\n includePublicSpace: false,\n permissions: [\n {\n service: \"tinycloud.kv\",\n space: BOOTSTRAP_PUBLIC_SPACE,\n path: \"\",\n actions: [\"get\", \"list\", \"metadata\"],\n },\n ],\n};\n\nexport const BOOTSTRAP_SPACE_MANIFESTS: Readonly<Record<BootstrapSpaceName, Manifest>> = {\n [BOOTSTRAP_DEFAULT_SPACE]: TINYCLOUD_DEFAULT_SPACE_MANIFEST,\n [DEFAULT_MANIFEST_SPACE]: TINYCLOUD_APPLICATIONS_SPACE_MANIFEST,\n [ACCOUNT_REGISTRY_SPACE]: TINYCLOUD_ACCOUNT_SPACE_MANIFEST,\n [SECRETS_SPACE]: TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,\n [BOOTSTRAP_PUBLIC_SPACE]: TINYCLOUD_PUBLIC_SPACE_MANIFEST,\n};\n\nexport const BOOTSTRAP_PERSISTED_APPLICATION_MANIFESTS = [\n TINYCLOUD_SECRETS_BOOTSTRAP_MANIFEST,\n] as const;\n\nexport const ACCOUNT_INDEX_SCHEMA = [\n `CREATE TABLE IF NOT EXISTS applications (\n app_id TEXT PRIMARY KEY,\n name TEXT,\n description TEXT,\n updated_at TEXT,\n manifest_json TEXT NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS application_state (\n app_id TEXT PRIMARY KEY,\n manifest_hash TEXT NOT NULL,\n indexed_at TEXT NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS spaces (\n space_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n owner_did TEXT NOT NULL,\n type TEXT NOT NULL,\n permissions_json TEXT NOT NULL,\n status TEXT NOT NULL,\n registered_at TEXT,\n updated_at TEXT NOT NULL,\n expires_at TEXT\n )`,\n `CREATE TABLE IF NOT EXISTS delegations (\n cid TEXT PRIMARY KEY,\n direction TEXT NOT NULL,\n space_id TEXT NOT NULL,\n space_name TEXT,\n counterparty_did TEXT NOT NULL,\n delegate_did TEXT NOT NULL,\n delegator_did TEXT,\n path TEXT NOT NULL,\n actions_json TEXT NOT NULL,\n expiry TEXT NOT NULL,\n status TEXT NOT NULL,\n created_at TEXT,\n updated_at TEXT NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS sync_state (\n source TEXT PRIMARY KEY,\n synced_at TEXT NOT NULL,\n count INTEGER NOT NULL\n )`,\n \"CREATE INDEX IF NOT EXISTS idx_delegations_direction ON delegations(direction)\",\n \"CREATE INDEX IF NOT EXISTS idx_delegations_space ON delegations(space_id)\",\n \"CREATE INDEX IF NOT EXISTS idx_delegations_counterparty ON delegations(counterparty_did)\",\n \"CREATE INDEX IF NOT EXISTS idx_spaces_owner ON spaces(owner_did)\",\n \"CREATE INDEX IF NOT EXISTS idx_spaces_type ON spaces(type)\",\n] as const;\n\nexport const SECRET_RECORDS_SCHEMA = [\n `CREATE TABLE IF NOT EXISTS secret_records (\n scope TEXT NOT NULL,\n name TEXT NOT NULL,\n provider_id TEXT,\n custom_note TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n last_tested TEXT,\n test_status TEXT,\n test_message TEXT,\n PRIMARY KEY(scope, name)\n )`,\n] as const;\n\nexport interface BootstrapSpaceDescriptor {\n name: BootstrapSpaceName;\n manifest: Manifest;\n persistedAsApplication: boolean;\n}\n\nexport interface BootstrapManifest {\n spaces: readonly BootstrapSpaceDescriptor[];\n applications: readonly Manifest[];\n accountIndexSchema: readonly string[];\n secretRecordsSchema: readonly string[];\n encryptionNetwork: {\n name: typeof BOOTSTRAP_ENCRYPTION_NETWORK_NAME;\n };\n}\n\nexport const BOOTSTRAP_MANIFEST: BootstrapManifest = {\n spaces: BOOTSTRAP_SPACE_NAMES.map((name) => ({\n name,\n manifest: BOOTSTRAP_SPACE_MANIFESTS[name],\n persistedAsApplication: name === SECRETS_SPACE,\n })),\n applications: BOOTSTRAP_PERSISTED_APPLICATION_MANIFESTS,\n accountIndexSchema: ACCOUNT_INDEX_SCHEMA,\n secretRecordsSchema: SECRET_RECORDS_SCHEMA,\n encryptionNetwork: {\n name: BOOTSTRAP_ENCRYPTION_NETWORK_NAME,\n },\n};\n\nexport type BootstrapAllowlistKind = \"session\" | \"space/host\";\n\nexport interface BootstrapRawAbilityAllowlistEntry {\n service: \"tinycloud.encryption\";\n resource: typeof BOOTSTRAP_ENCRYPTION_NETWORK_RESOURCE_TEMPLATE;\n actions: readonly [typeof NETWORK_CREATE_ACTION];\n}\n\nexport interface BootstrapAllowlistEntry {\n kind: BootstrapAllowlistKind;\n space: BootstrapSpaceName;\n service: \"tinycloud.session\" | \"tinycloud.space\";\n actions: readonly string[];\n resources?: readonly ResourceCapability[];\n rawAbilities?: readonly BootstrapRawAbilityAllowlistEntry[];\n}\n\nfunction assertAddress(address: string): string {\n if (!isAddress(address, { strict: false })) {\n throw new Error(`Invalid EVM address: ${address}`);\n }\n return getAddress(address);\n}\n\nfunction assertChainId(chainId: number): void {\n if (!Number.isSafeInteger(chainId) || chainId <= 0) {\n throw new Error(`Invalid EIP-155 chain ID: ${chainId}`);\n }\n}\n\nexport function pkhDid(address: string, chainId: number = 1): string {\n assertChainId(chainId);\n return `did:pkh:eip155:${chainId}:${assertAddress(address)}`;\n}\n\nexport function makePkhSpaceId(\n address: string,\n chainId: number,\n name: string,\n): string {\n assertChainId(chainId);\n if (!name) {\n throw new Error(\"Space name cannot be empty\");\n }\n return `tinycloud:pkh:eip155:${chainId}:${assertAddress(address)}:${name}`;\n}\n\nfunction cloneManifest(manifest: Manifest): Manifest {\n return {\n ...manifest,\n permissions: manifest.permissions?.map((permission) => ({\n ...permission,\n actions: [...permission.actions],\n })),\n };\n}\n\nfunction actionUrn(service: string, action: string): string {\n return action.includes(\"/\") ? action : `${service}/${action}`;\n}\n\nfunction applyPrefix(prefix: string, path: string, skipPrefix?: boolean): string {\n if (skipPrefix === true || prefix === \"\" || path === \"\") {\n return path;\n }\n if (path === \"/\") {\n return `${prefix}/`;\n }\n const trimmedPrefix = prefix.endsWith(\"/\") ? prefix.slice(0, -1) : prefix;\n const trimmedPath = path.startsWith(\"/\") ? path.slice(1) : path;\n return `${trimmedPrefix}/${trimmedPath}`;\n}\n\nfunction expandVaultPermission(entry: PermissionEntry): PermissionEntry[] {\n return entry.actions.map((action) => {\n const normalized = action.startsWith(\"tinycloud.vault/\")\n ? action.slice(\"tinycloud.vault/\".length)\n : action.startsWith(\"tinycloud.kv/\")\n ? action.slice(\"tinycloud.kv/\".length)\n : action;\n const mapped =\n normalized === \"read\" || normalized === \"get\"\n ? KV.GET\n : normalized === \"write\" || normalized === \"put\"\n ? KV.PUT\n : normalized === \"delete\" || normalized === \"del\"\n ? KV.DEL\n : normalized === \"list\"\n ? KV.LIST\n : normalized === \"metadata\"\n ? KV.METADATA\n : undefined;\n if (mapped === undefined) {\n throw new Error(`unknown vault action ${JSON.stringify(action)}`);\n }\n const normalizedPath = entry.path.startsWith(\"/\")\n ? entry.path.slice(1)\n : entry.path;\n return {\n ...entry,\n service: \"tinycloud.kv\",\n path: `vault/${normalizedPath}`,\n actions: [mapped],\n skipPrefix: true,\n };\n });\n}\n\nfunction expandPermissionEntry(\n entry: PermissionEntry,\n prefix: string,\n inheritedSpace: string,\n): ResourceCapability[] {\n if (entry.service === VAULT_PERMISSION_SERVICE) {\n return expandVaultPermission(entry).flatMap((expanded) =>\n expandPermissionEntry(expanded, prefix, inheritedSpace),\n );\n }\n const skipPrefix =\n entry.skipPrefix === true || entry.service === ENCRYPTION_PERMISSION_SERVICE;\n return [\n {\n service: entry.service,\n space:\n entry.service === ENCRYPTION_PERMISSION_SERVICE\n ? ENCRYPTION_MANIFEST_SPACE\n : entry.space ?? inheritedSpace,\n path: applyPrefix(prefix, entry.path, skipPrefix),\n actions: entry.actions.map((action) => actionUrn(entry.service, action)),\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n },\n ];\n}\n\nfunction dedupeResources(resources: readonly ResourceCapability[]): ResourceCapability[] {\n const byKey = new Map<string, ResourceCapability>();\n for (const resource of resources) {\n const key = `${resource.service}\\u0000${resource.space}\\u0000${resource.path}`;\n const existing = byKey.get(key);\n if (!existing) {\n byKey.set(key, { ...resource, actions: [...resource.actions] });\n continue;\n }\n for (const action of resource.actions) {\n if (!existing.actions.includes(action)) {\n existing.actions.push(action);\n }\n }\n }\n return [...byKey.values()];\n}\n\nfunction withCapabilitiesReadForSpaces(\n resources: readonly ResourceCapability[],\n): ResourceCapability[] {\n const spaces = new Set(\n resources\n .filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE)\n .map((resource) => resource.space),\n );\n return dedupeResources([\n ...resources,\n ...[...spaces].map((space) => ({\n service: \"tinycloud.capabilities\",\n space,\n path: \"\",\n actions: [CAPABILITIES.READ],\n })),\n ]);\n}\n\nexport function composeManifestRequest(\n inputs: readonly Manifest[],\n options: ComposeManifestOptions = {},\n): ComposedManifestRequest {\n if (!Array.isArray(inputs) || inputs.length === 0) {\n throw new Error(\"composeManifestRequest requires at least one manifest\");\n }\n\n const includeAccountRegistryPermissions =\n options.includeAccountRegistryPermissions ?? true;\n const manifests = inputs.map(cloneManifest);\n const resources = manifests.flatMap((manifest) => {\n const prefix = manifest.prefix ?? manifest.app_id;\n const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;\n const explicit = manifest.permissions ?? [];\n return explicit.flatMap((entry) => expandPermissionEntry(entry, prefix, space));\n });\n\n if (includeAccountRegistryPermissions) {\n resources.push(\n ...ACCOUNT_MANIFEST_PERMISSIONS.map((resource) => ({\n ...resource,\n actions: [...resource.actions],\n })),\n );\n }\n\n const manifestsByAppId = new Map<string, Manifest[]>();\n for (const manifest of manifests) {\n const current = manifestsByAppId.get(manifest.app_id) ?? [];\n current.push(cloneManifest(manifest));\n manifestsByAppId.set(manifest.app_id, current);\n }\n\n return {\n manifests,\n resources: withCapabilitiesReadForSpaces(resources),\n delegationTargets: [],\n registryRecords: includeAccountRegistryPermissions\n ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({\n key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,\n app_id,\n manifests: appManifests,\n }))\n : [],\n expiryMs: DEFAULT_EXPIRY_MS,\n includePublicSpace: manifests.some(\n (manifest) => manifest.includePublicSpace ?? true,\n ),\n };\n}\n\nexport function composeBootstrapSpaceManifest(\n space: BootstrapSpaceName,\n): ComposedManifestRequest {\n return composeManifestRequest([BOOTSTRAP_SPACE_MANIFESTS[space]], {\n includeAccountRegistryPermissions: false,\n });\n}\n\n/**\n * The canonical account authority granted to ordinary sign-in sessions.\n *\n * Keep the completion marker as its own exact key: the registry prefixes do\n * not cover it. Consumers must clone entries and actions before composing a\n * request or converting this value to mutable WASM abilities.\n */\nexport const ACCOUNT_MANIFEST_PERMISSIONS: readonly ResourceCapability[] = Object.freeze([\n {\n service: \"tinycloud.kv\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: ACCOUNT_REGISTRY_PATH,\n actions: [KV.GET, KV.PUT, KV.LIST],\n },\n {\n service: \"tinycloud.kv\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"spaces/\",\n actions: [KV.GET, KV.PUT, KV.LIST],\n },\n {\n service: \"tinycloud.kv\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"system/bootstrap/complete\",\n actions: [KV.GET, KV.PUT],\n },\n {\n service: \"tinycloud.delegation\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"\",\n actions: [\"tinycloud.delegation/list\"],\n },\n {\n service: \"tinycloud.sql\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"account\",\n actions: [SQL.READ, SQL.WRITE, SQL.SCHEMA],\n },\n {\n service: \"tinycloud.capabilities\",\n space: ACCOUNT_REGISTRY_SPACE,\n path: \"\",\n actions: [CAPABILITIES.READ],\n },\n]);\n\nexport const BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST =\n composeBootstrapSpaceManifest(BOOTSTRAP_DEFAULT_SPACE);\n\nconst BOOTSTRAP_WIDENED_DEFAULT_SESSION_REQUEST: ComposedManifestRequest = {\n ...BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST,\n resources: [\n ...BOOTSTRAP_DEFAULT_ONLY_SESSION_REQUEST.resources,\n ...ACCOUNT_MANIFEST_PERMISSIONS.map((resource) => ({\n ...resource,\n actions: [...resource.actions],\n })),\n ],\n};\n\nexport const BOOTSTRAP_SESSION_REQUESTS: Readonly<\n Record<BootstrapSpaceName, ComposedManifestRequest>\n> = Object.freeze(\n Object.fromEntries(\n BOOTSTRAP_SPACE_NAMES.map((space) => [\n space,\n space === BOOTSTRAP_DEFAULT_SPACE\n ? BOOTSTRAP_WIDENED_DEFAULT_SESSION_REQUEST\n : composeBootstrapSpaceManifest(space),\n ]),\n ) as Record<BootstrapSpaceName, ComposedManifestRequest>,\n);\n\nconst ACCOUNT_SESSION_RAW_ALLOWLIST: readonly BootstrapRawAbilityAllowlistEntry[] =\n Object.freeze([\n {\n service: \"tinycloud.encryption\",\n resource: BOOTSTRAP_ENCRYPTION_NETWORK_RESOURCE_TEMPLATE,\n actions: [NETWORK_CREATE_ACTION],\n },\n ]);\n\nexport const BOOTSTRAP_ALLOWLIST: readonly BootstrapAllowlistEntry[] =\n Object.freeze(\n BOOTSTRAP_SPACE_NAMES.flatMap((space) => [\n {\n kind: \"session\" as const,\n service: \"tinycloud.session\" as const,\n space,\n actions: [\"siwe\"],\n resources: BOOTSTRAP_SESSION_REQUESTS[space].resources,\n ...(space === ACCOUNT_REGISTRY_SPACE\n ? { rawAbilities: ACCOUNT_SESSION_RAW_ALLOWLIST }\n : {}),\n },\n {\n kind: \"space/host\" as const,\n service: \"tinycloud.space\" as const,\n space,\n actions: [SPACE.HOST],\n },\n ]),\n );\n\nexport type BootstrapStepKind =\n | \"session\"\n | \"host\"\n | \"activate\"\n | \"account-index-schema\"\n | \"seed-spaces\"\n | \"seed-applications\"\n | \"encryption-network-create\"\n | \"secret-records-schema\";\n\nexport interface BootstrapStepBase {\n id: string;\n kind: BootstrapStepKind;\n}\n\nexport interface BootstrapSpaceStep extends BootstrapStepBase {\n kind: \"session\" | \"host\" | \"activate\";\n space: BootstrapSpaceName;\n spaceId: string;\n request?: ComposedManifestRequest;\n rawAbilities?: Record<string, string[]>;\n includeEncryptionNetworkCreate?: boolean;\n}\n\nexport interface BootstrapSchemaStep extends BootstrapStepBase {\n kind: \"account-index-schema\" | \"secret-records-schema\";\n space: typeof ACCOUNT_REGISTRY_SPACE | typeof SECRETS_SPACE;\n spaceId: string;\n database: string;\n schema: readonly string[];\n}\n\nexport interface BootstrapSeedSpacesStep extends BootstrapStepBase {\n kind: \"seed-spaces\";\n spaces: readonly {\n name: BootstrapSpaceName;\n spaceId: string;\n }[];\n}\n\nexport interface BootstrapSeedApplicationsStep extends BootstrapStepBase {\n kind: \"seed-applications\";\n manifests: readonly Manifest[];\n}\n\nexport interface BootstrapEncryptionNetworkStep extends BootstrapStepBase {\n kind: \"encryption-network-create\";\n networkId: string;\n}\n\nexport type BootstrapStep =\n | BootstrapSpaceStep\n | BootstrapSchemaStep\n | BootstrapSeedSpacesStep\n | BootstrapSeedApplicationsStep\n | BootstrapEncryptionNetworkStep;\n\nexport function bootstrapSpaceId(\n address: string,\n chainId: number,\n space: BootstrapSpaceName,\n): string {\n return makePkhSpaceId(address, chainId, space);\n}\n\nexport function bootstrapEncryptionNetworkId(\n address: string,\n chainId: number,\n): string {\n return `urn:tinycloud:encryption:${pkhDid(address, chainId)}:${BOOTSTRAP_ENCRYPTION_NETWORK_NAME}`;\n}\n\nexport function bootstrapSteps(\n address: string,\n chainId: number,\n): BootstrapStep[] {\n const spaces = BOOTSTRAP_SPACE_NAMES.map((name) => ({\n name,\n spaceId: bootstrapSpaceId(address, chainId, name),\n }));\n const account = spaces.find((space) => space.name === ACCOUNT_REGISTRY_SPACE)!;\n const secrets = spaces.find((space) => space.name === SECRETS_SPACE)!;\n const networkId = bootstrapEncryptionNetworkId(address, chainId);\n\n return [\n ...spaces.map((space) => ({\n id: `session:${space.name}`,\n kind: \"session\" as const,\n space: space.name,\n spaceId: space.spaceId,\n request: BOOTSTRAP_SESSION_REQUESTS[space.name],\n includeEncryptionNetworkCreate: space.name === ACCOUNT_REGISTRY_SPACE,\n ...(space.name === ACCOUNT_REGISTRY_SPACE\n ? { rawAbilities: { [networkId]: [NETWORK_CREATE_ACTION] } }\n : {}),\n })),\n ...spaces.map((space) => ({\n id: `host:${space.name}`,\n kind: \"host\" as const,\n space: space.name,\n spaceId: space.spaceId,\n })),\n ...spaces.map((space) => ({\n id: `activate:${space.name}`,\n kind: \"activate\" as const,\n space: space.name,\n spaceId: space.spaceId,\n })),\n {\n id: \"account:index-schema\",\n kind: \"account-index-schema\",\n space: ACCOUNT_REGISTRY_SPACE,\n spaceId: account.spaceId,\n database: \"account\",\n schema: ACCOUNT_INDEX_SCHEMA,\n },\n {\n id: \"account:seed-spaces\",\n kind: \"seed-spaces\",\n spaces,\n },\n {\n id: \"account:seed-applications\",\n kind: \"seed-applications\",\n manifests: BOOTSTRAP_PERSISTED_APPLICATION_MANIFESTS,\n },\n {\n id: \"encryption:network-create\",\n kind: \"encryption-network-create\",\n networkId,\n },\n {\n id: \"secrets:secret-records-schema\",\n kind: \"secret-records-schema\",\n space: SECRETS_SPACE,\n spaceId: secrets.spaceId,\n database: \"default\",\n schema: SECRET_RECORDS_SCHEMA,\n },\n ];\n}\n","// @generated by scripts/gen-capabilities.mjs — DO NOT EDIT.\n// Source: capabilities.json (registry version 1, sha256 daecd38d908d05d622684b580501dc38c5945b0499ce7eb3a18d77663b93c73a).\n//\n// Canonical single source of truth for TinyCloud capability action URNs (TC-112).\n// Regenerate with: node scripts/gen-capabilities.mjs\n\nexport const REGISTRY_VERSION = 1 as const;\nexport const REGISTRY_SOURCE_SHA256 = \"daecd38d908d05d622684b580501dc38c5945b0499ce7eb3a18d77663b93c73a\" as const;\n\n/** GitHub repository the registry lives in (TC-121; js-sdk sync anchor). */\nexport const REGISTRY_SOURCE_REPO = \"TinyCloudLabs/tinycloud-node\" as const;\n/**\n * Git commit the artifact was generated from. Authoritative when generated\n * in CI (GITHUB_SHA); approximate when generated locally, where it names\n * the parent of the commit that will contain this artifact.\n */\nexport const REGISTRY_SOURCE_GIT_SHA = \"390253aca30628f2ac2be28e64d8e3830da07aaa\" as const;\n\nexport type CapabilityStatus = \"active\" | \"deprecated-alias\" | \"reserved\";\n\nexport interface CapabilityEntry {\n urn: string;\n service: string;\n status: CapabilityStatus;\n aliasOf?: string;\n implies?: readonly string[];\n}\n\nexport const CAPABILITIES: readonly CapabilityEntry[] = [\n { urn: \"tinycloud.kv/get\", service: \"tinycloud.kv\", status: \"active\" },\n { urn: \"tinycloud.kv/list\", service: \"tinycloud.kv\", status: \"active\" },\n { urn: \"tinycloud.kv/metadata\", service: \"tinycloud.kv\", status: \"active\" },\n { urn: \"tinycloud.kv/put\", service: \"tinycloud.kv\", status: \"active\" },\n { urn: \"tinycloud.kv/del\", service: \"tinycloud.kv\", status: \"active\" },\n { urn: \"tinycloud.kv/delete\", service: \"tinycloud.kv\", status: \"deprecated-alias\", aliasOf: \"tinycloud.kv/del\" },\n { urn: \"tinycloud.sql/read\", service: \"tinycloud.sql\", status: \"active\" },\n { urn: \"tinycloud.sql/select\", service: \"tinycloud.sql\", status: \"deprecated-alias\", aliasOf: \"tinycloud.sql/read\" },\n { urn: \"tinycloud.sql/write\", service: \"tinycloud.sql\", status: \"active\" },\n { urn: \"tinycloud.sql/schema\", service: \"tinycloud.sql\", status: \"active\" },\n { urn: \"tinycloud.sql/admin\", service: \"tinycloud.sql\", status: \"active\", implies: [\"tinycloud.sql/schema\"] },\n { urn: \"tinycloud.sql/*\", service: \"tinycloud.sql\", status: \"active\", implies: [\"tinycloud.sql/read\", \"tinycloud.sql/write\", \"tinycloud.sql/schema\", \"tinycloud.sql/admin\"] },\n { urn: \"tinycloud.duckdb/read\", service: \"tinycloud.duckdb\", status: \"active\" },\n { urn: \"tinycloud.duckdb/write\", service: \"tinycloud.duckdb\", status: \"active\" },\n { urn: \"tinycloud.duckdb/admin\", service: \"tinycloud.duckdb\", status: \"active\" },\n { urn: \"tinycloud.duckdb/import\", service: \"tinycloud.duckdb\", status: \"active\" },\n { urn: \"tinycloud.duckdb/export\", service: \"tinycloud.duckdb\", status: \"active\" },\n { urn: \"tinycloud.duckdb/select\", service: \"tinycloud.duckdb\", status: \"deprecated-alias\", aliasOf: \"tinycloud.duckdb/read\" },\n { urn: \"tinycloud.duckdb/*\", service: \"tinycloud.duckdb\", status: \"active\", implies: [\"tinycloud.duckdb/read\", \"tinycloud.duckdb/write\", \"tinycloud.duckdb/admin\", \"tinycloud.duckdb/import\", \"tinycloud.duckdb/export\"] },\n { urn: \"tinycloud.capabilities/read\", service: \"tinycloud.capabilities\", status: \"active\" },\n { urn: \"tinycloud.delegation/status\", service: \"tinycloud.delegation\", status: \"active\" },\n { urn: \"tinycloud.delegation/list\", service: \"tinycloud.delegation\", status: \"active\" },\n { urn: \"tinycloud.hooks/subscribe\", service: \"tinycloud.hooks\", status: \"active\" },\n { urn: \"tinycloud.hooks/register\", service: \"tinycloud.hooks\", status: \"active\" },\n { urn: \"tinycloud.hooks/list\", service: \"tinycloud.hooks\", status: \"active\" },\n { urn: \"tinycloud.hooks/unregister\", service: \"tinycloud.hooks\", status: \"active\" },\n { urn: \"tinycloud.encryption/decrypt\", service: \"tinycloud.encryption\", status: \"active\" },\n { urn: \"tinycloud.encryption/network.create\", service: \"tinycloud.encryption\", status: \"active\" },\n { urn: \"tinycloud.encryption/network.revoke\", service: \"tinycloud.encryption\", status: \"active\" },\n { urn: \"tinycloud.space/host\", service: \"tinycloud.space\", status: \"active\" },\n { urn: \"tinycloud.space/create\", service: \"tinycloud.space\", status: \"active\" },\n { urn: \"tinycloud.space/list\", service: \"tinycloud.space\", status: \"active\" },\n { urn: \"tinycloud.space/info\", service: \"tinycloud.space\", status: \"active\" },\n { urn: \"tinycloud.vfs/get\", service: \"tinycloud.vfs\", status: \"reserved\" },\n { urn: \"tinycloud.vfs/list\", service: \"tinycloud.vfs\", status: \"reserved\" },\n { urn: \"tinycloud.vfs/metadata\", service: \"tinycloud.vfs\", status: \"reserved\" },\n { urn: \"tinycloud.vfs/put\", service: \"tinycloud.vfs\", status: \"reserved\" },\n { urn: \"tinycloud.vfs/delete\", service: \"tinycloud.vfs\", status: \"reserved\" },\n] as const;\n\n/// Every action URN accepted at the policy boundary for a service\n/// (active, deprecated-alias, and reserved), sorted.\nexport const ACCEPTED_ACTIONS: Readonly<Record<string, readonly string[]>> = {\n \"tinycloud.capabilities\": [\"tinycloud.capabilities/read\"],\n \"tinycloud.delegation\": [\"tinycloud.delegation/list\", \"tinycloud.delegation/status\"],\n \"tinycloud.duckdb\": [\"tinycloud.duckdb/*\", \"tinycloud.duckdb/admin\", \"tinycloud.duckdb/export\", \"tinycloud.duckdb/import\", \"tinycloud.duckdb/read\", \"tinycloud.duckdb/select\", \"tinycloud.duckdb/write\"],\n \"tinycloud.encryption\": [\"tinycloud.encryption/decrypt\", \"tinycloud.encryption/network.create\", \"tinycloud.encryption/network.revoke\"],\n \"tinycloud.hooks\": [\"tinycloud.hooks/list\", \"tinycloud.hooks/register\", \"tinycloud.hooks/subscribe\", \"tinycloud.hooks/unregister\"],\n \"tinycloud.kv\": [\"tinycloud.kv/del\", \"tinycloud.kv/delete\", \"tinycloud.kv/get\", \"tinycloud.kv/list\", \"tinycloud.kv/metadata\", \"tinycloud.kv/put\"],\n \"tinycloud.space\": [\"tinycloud.space/create\", \"tinycloud.space/host\", \"tinycloud.space/info\", \"tinycloud.space/list\"],\n \"tinycloud.sql\": [\"tinycloud.sql/*\", \"tinycloud.sql/admin\", \"tinycloud.sql/read\", \"tinycloud.sql/schema\", \"tinycloud.sql/select\", \"tinycloud.sql/write\"],\n \"tinycloud.vfs\": [\"tinycloud.vfs/delete\", \"tinycloud.vfs/get\", \"tinycloud.vfs/list\", \"tinycloud.vfs/metadata\", \"tinycloud.vfs/put\"],\n};\n\n/// aliasUrn -> canonical URN.\nexport const ALIASES: Readonly<Record<string, string>> = {\n \"tinycloud.duckdb/select\": \"tinycloud.duckdb/read\",\n \"tinycloud.kv/delete\": \"tinycloud.kv/del\",\n \"tinycloud.sql/select\": \"tinycloud.sql/read\",\n};\n\n/// urn -> directly implied URNs.\nexport const IMPLICATIONS: Readonly<Record<string, readonly string[]>> = {\n \"tinycloud.duckdb/*\": [\"tinycloud.duckdb/admin\", \"tinycloud.duckdb/export\", \"tinycloud.duckdb/import\", \"tinycloud.duckdb/read\", \"tinycloud.duckdb/write\"],\n \"tinycloud.sql/*\": [\"tinycloud.sql/admin\", \"tinycloud.sql/read\", \"tinycloud.sql/schema\", \"tinycloud.sql/write\"],\n \"tinycloud.sql/admin\": [\"tinycloud.sql/schema\"],\n};\n\n/// Resolve a deprecated alias to its canonical URN (single hop).\nexport function resolveAlias(action: string): string {\n return ALIASES[action] ?? action;\n}\n\n/// URNs directly implied by holding `action`.\nexport function impliedActions(action: string): readonly string[] {\n return IMPLICATIONS[action] ?? [];\n}\n","/**\n * Canonical TinyCloud capability URN registry (js-sdk side).\n *\n * TC-112 single source of truth: the registry is defined in tinycloud-node\n * (`capabilities.json` + codegen) and vendored here VERBATIM as\n * `./generated/capabilities.ts` — the ONE copy in this repo. A CI job\n * (`.github/workflows/capabilities-sync.yml`) fetches the generated file from\n * tinycloud-node at the rev pinned in `packages/sdk-rs/Cargo.toml` and fails on\n * any diff against the vendored copy, so this module can never silently drift\n * from the enforcer.\n *\n * Everything below is DERIVED from the vendored registry by mechanical string\n * transform — there is no second hand-maintained action list. The bootstrap\n * manifests, the node-sdk default abilities, the SQL/DuckDB service action\n * tables, and the web-sdk permission-modal labels all consume the constants\n * declared here.\n *\n * The only hand-authored URNs are the four request-kind artifacts\n * (`sql/execute`, `sql/export`, `duckdb/describe`, `duckdb/execute`): the node\n * routes these by request-body kind gated by read/write/admin — they are NOT\n * grantable capabilities and are intentionally absent from the vendored\n * registry. The SDK keeps them as exported constants because the service\n * clients send them as the invocation ability today (wire alignment tracked in\n * TC-114). They are excluded from `CAPABILITY_REGISTRY`.\n */\n\nimport {\n CAPABILITIES as GENERATED_CAPABILITIES,\n type CapabilityEntry,\n type CapabilityStatus,\n} from \"./generated/capabilities.js\";\n\nexport type { CapabilityStatus };\n\n/**\n * A single registry entry. Alias of the vendored `CapabilityEntry` so the\n * public type name stays stable while the shape tracks the node registry\n * (long-form `service`, optional `aliasOf`/`implies`).\n */\nexport type CapabilityRegistryEntry = CapabilityEntry;\n\n// ---------------------------------------------------------------------------\n// Derivation\n//\n// Each registry URN is `tinycloud.<service>/<action>`. The per-service constant\n// objects below are built by stripping the `tinycloud.<service>/` prefix and\n// upper-casing the action into a key (`.` -> `_`, `*` -> `ALL`). This is a pure\n// string transform over the vendored registry — no action is named twice.\n// ---------------------------------------------------------------------------\n\nfunction actionKey(action: string): string {\n if (action === \"*\") return \"ALL\";\n return action.toUpperCase().replace(/[^A-Z0-9]/g, \"_\");\n}\n\nfunction serviceOf(urn: string): string {\n // \"tinycloud.sql/read\" -> \"sql\"\n return urn.slice(\"tinycloud.\".length, urn.indexOf(\"/\"));\n}\n\nfunction actionOf(urn: string): string {\n return urn.slice(urn.indexOf(\"/\") + 1);\n}\n\nfunction deriveServiceConstants(service: string): Record<string, string> {\n const out: Record<string, string> = {};\n for (const entry of GENERATED_CAPABILITIES) {\n if (serviceOf(entry.urn) !== service) continue;\n out[actionKey(actionOf(entry.urn))] = entry.urn;\n }\n return out;\n}\n\n// ---------------------------------------------------------------------------\n// Per-service constants (long-form `tinycloud.<service>/<action>` URNs).\n// ---------------------------------------------------------------------------\n\nexport const KV = deriveServiceConstants(\"kv\") as {\n GET: \"tinycloud.kv/get\";\n LIST: \"tinycloud.kv/list\";\n METADATA: \"tinycloud.kv/metadata\";\n PUT: \"tinycloud.kv/put\";\n DEL: \"tinycloud.kv/del\";\n DELETE: \"tinycloud.kv/delete\";\n};\n\n// `execute`/`export` are request-kind artifacts, not registry capabilities —\n// hand-authored (see module header, TC-114).\nexport const SQL = {\n ...deriveServiceConstants(\"sql\"),\n EXECUTE: \"tinycloud.sql/execute\",\n EXPORT: \"tinycloud.sql/export\",\n} as {\n READ: \"tinycloud.sql/read\";\n SELECT: \"tinycloud.sql/select\";\n WRITE: \"tinycloud.sql/write\";\n SCHEMA: \"tinycloud.sql/schema\";\n ADMIN: \"tinycloud.sql/admin\";\n ALL: \"tinycloud.sql/*\";\n EXECUTE: \"tinycloud.sql/execute\";\n EXPORT: \"tinycloud.sql/export\";\n};\n\n// `describe`/`execute` are request-kind artifacts, not registry capabilities —\n// hand-authored (see module header, TC-114).\nexport const DUCKDB = {\n ...deriveServiceConstants(\"duckdb\"),\n DESCRIBE: \"tinycloud.duckdb/describe\",\n EXECUTE: \"tinycloud.duckdb/execute\",\n} as {\n READ: \"tinycloud.duckdb/read\";\n WRITE: \"tinycloud.duckdb/write\";\n ADMIN: \"tinycloud.duckdb/admin\";\n IMPORT: \"tinycloud.duckdb/import\";\n EXPORT: \"tinycloud.duckdb/export\";\n SELECT: \"tinycloud.duckdb/select\";\n ALL: \"tinycloud.duckdb/*\";\n DESCRIBE: \"tinycloud.duckdb/describe\";\n EXECUTE: \"tinycloud.duckdb/execute\";\n};\n\nexport const CAPABILITIES = deriveServiceConstants(\"capabilities\") as {\n READ: \"tinycloud.capabilities/read\";\n};\n\nexport const HOOKS = deriveServiceConstants(\"hooks\") as {\n SUBSCRIBE: \"tinycloud.hooks/subscribe\";\n REGISTER: \"tinycloud.hooks/register\";\n LIST: \"tinycloud.hooks/list\";\n UNREGISTER: \"tinycloud.hooks/unregister\";\n};\n\nexport const ENCRYPTION = deriveServiceConstants(\"encryption\") as {\n DECRYPT: \"tinycloud.encryption/decrypt\";\n NETWORK_CREATE: \"tinycloud.encryption/network.create\";\n NETWORK_REVOKE: \"tinycloud.encryption/network.revoke\";\n};\n\nexport const SPACE = deriveServiceConstants(\"space\") as {\n HOST: \"tinycloud.space/host\";\n CREATE: \"tinycloud.space/create\";\n LIST: \"tinycloud.space/list\";\n INFO: \"tinycloud.space/info\";\n};\n\n/**\n * The full registry with per-URN status/alias/implication metadata. Re-exported\n * verbatim from the vendored node artifact — used by the canonical-set test and\n * the CI cross-check. The four request-kind artifacts are intentionally NOT\n * present here (see module header).\n */\nexport const CAPABILITY_REGISTRY: readonly CapabilityRegistryEntry[] =\n GENERATED_CAPABILITIES;\n","/**\n * SDK Services - Core Types\n *\n * These types define the service architecture for TinyCloud SDK.\n * Services use dependency injection via IServiceContext for platform independence.\n */\n\n// =============================================================================\n// Result Type Pattern\n// =============================================================================\n\n/**\n * Result type for service operations.\n * Services return Result instead of throwing, making error handling explicit.\n *\n * @template T - The success data type\n * @template E - The error type (defaults to ServiceError)\n *\n * @example\n * ```typescript\n * const result = await kv.get('key');\n * if (result.ok) {\n * console.log(result.data);\n * } else {\n * console.error(result.error.code);\n * }\n * ```\n */\nexport type Result<T, E = ServiceError> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\n/**\n * Service error with structured information.\n */\nexport interface ServiceError {\n /** Error code for programmatic handling (e.g., 'KV_NOT_FOUND', 'AUTH_EXPIRED') */\n code: string;\n /** Human-readable error message */\n message: string;\n /** Service that produced the error (e.g., 'kv', 'sql') */\n service: string;\n /** Original error if this wraps another error */\n cause?: Error;\n /** Additional metadata about the error */\n meta?: Record<string, unknown>;\n}\n\n/**\n * A node-supplied permission hint after strict SDK validation. This is\n * deliberately narrower than a general capability: no wildcards, caveats,\n * descriptions, or transport metadata can cross the service boundary.\n */\nexport interface PermissionHint {\n readonly service: \"tinycloud.kv\" | \"tinycloud.encryption\";\n readonly space?: string;\n readonly path: string;\n readonly actions: readonly string[];\n}\n\n/**\n * Storage quota information returned with quota-related errors.\n */\nexport interface StorageQuotaInfo {\n usedBytes: number;\n limitBytes: number;\n service: string;\n}\n\n/**\n * Standard error codes used across services.\n */\nexport const ErrorCodes = {\n // Common errors\n NOT_FOUND: \"NOT_FOUND\",\n AUTH_EXPIRED: \"AUTH_EXPIRED\",\n AUTH_REQUIRED: \"AUTH_REQUIRED\",\n AUTH_UNAUTHORIZED: \"AUTH_UNAUTHORIZED\",\n NETWORK_ERROR: \"NETWORK_ERROR\",\n TIMEOUT: \"TIMEOUT\",\n ABORTED: \"ABORTED\",\n INVALID_INPUT: \"INVALID_INPUT\",\n PERMISSION_DENIED: \"PERMISSION_DENIED\",\n\n // KV-specific errors\n KV_NOT_FOUND: \"KV_NOT_FOUND\",\n KV_WRITE_FAILED: \"KV_WRITE_FAILED\",\n KV_PRECONDITION_FAILED: \"KV_PRECONDITION_FAILED\",\n KV_CONFLICT: \"KV_CONFLICT\",\n KV_RESPONSE_TOO_LARGE: \"KV_RESPONSE_TOO_LARGE\",\n\n // SQL-specific errors\n SQL_ERROR: \"SQL_ERROR\",\n SQL_PERMISSION_DENIED: \"SQL_PERMISSION_DENIED\",\n SQL_DATABASE_NOT_FOUND: \"SQL_DATABASE_NOT_FOUND\",\n SQL_RESPONSE_TOO_LARGE: \"SQL_RESPONSE_TOO_LARGE\",\n SQL_QUOTA_EXCEEDED: \"SQL_QUOTA_EXCEEDED\",\n SQL_INVALID_STATEMENT: \"SQL_INVALID_STATEMENT\",\n SQL_SCHEMA_ERROR: \"SQL_SCHEMA_ERROR\",\n SQL_READONLY_VIOLATION: \"SQL_READONLY_VIOLATION\",\n\n // Storage quota errors\n STORAGE_QUOTA_EXCEEDED: \"STORAGE_QUOTA_EXCEEDED\",\n STORAGE_LIMIT_REACHED: \"STORAGE_LIMIT_REACHED\",\n\n // DuckDB-specific errors\n DUCKDB_ERROR: \"DUCKDB_ERROR\",\n DUCKDB_PERMISSION_DENIED: \"DUCKDB_PERMISSION_DENIED\",\n DUCKDB_DATABASE_NOT_FOUND: \"DUCKDB_DATABASE_NOT_FOUND\",\n DUCKDB_RESPONSE_TOO_LARGE: \"DUCKDB_RESPONSE_TOO_LARGE\",\n DUCKDB_QUOTA_EXCEEDED: \"DUCKDB_QUOTA_EXCEEDED\",\n DUCKDB_INVALID_STATEMENT: \"DUCKDB_INVALID_STATEMENT\",\n DUCKDB_SCHEMA_ERROR: \"DUCKDB_SCHEMA_ERROR\",\n DUCKDB_READONLY_VIOLATION: \"DUCKDB_READONLY_VIOLATION\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n// =============================================================================\n// Service Session\n// =============================================================================\n\n/**\n * Session data required for authenticated service operations.\n * Both TinyCloudSession and web-sdk Session can be cast to this interface.\n */\nexport interface ServiceSession {\n /** The delegation header containing the UCAN */\n delegationHeader: { Authorization: string };\n /** The delegation CID */\n delegationCid: string;\n /** The space ID for this session */\n spaceId: string;\n /** The verification method DID */\n verificationMethod: string;\n /** The session key JWK (required for invoke) */\n jwk: object;\n}\n\n// =============================================================================\n// Platform Dependencies (Injected)\n// =============================================================================\n\n/**\n * Headers type - compatible with both browser and Node.js.\n */\nexport type ServiceHeaders = Record<string, string> | [string, string][];\n\n/**\n * A single fact object to include in the UCAN invocation.\n * Facts are key-value objects that the server reads from the UCAN facts field.\n */\nexport interface InvocationFact {\n [key: string]: unknown;\n}\n\n/**\n * Facts to include in the UCAN invocation.\n * This is an array of fact objects per the UCAN spec.\n * Used to pass additional parameters that the server reads from the UCAN facts field.\n */\nexport type InvocationFacts = InvocationFact[];\n\n/**\n * Invoke function signature - platform-specific implementation injected via DI.\n * Both node-sdk-wasm and web-sdk-wasm export this with identical signature.\n *\n * @param session - The service session with delegation data\n * @param service - Service name (e.g., \"kv\")\n * @param path - Resource path or key\n * @param action - Action to perform (e.g., \"tinycloud.kv/get\")\n * @param facts - Optional facts to include in the UCAN (e.g., for capabilities/read params)\n * @returns Headers to include in the request\n */\nexport type InvokeFunction = (\n session: ServiceSession,\n service: string,\n path: string,\n action: string,\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Multi-resource invocation entry.\n */\nexport interface InvokeAnyEntry {\n /**\n * Legacy space-scoped resource. Optional when `resource` is provided.\n */\n spaceId?: string;\n service: string;\n path: string;\n action: string;\n /** Optional raw resource URI. When set, WASM signs this URI directly. */\n resource?: string;\n /** Capability caveats that must remain on this invocation attenuation. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Invoke function for minting a single authorization header that covers\n * multiple capabilities across one effective invoker.\n */\nexport type InvokeAnyFunction = (\n session: ServiceSession,\n entries: InvokeAnyEntry[],\n facts?: InvocationFacts,\n) => ServiceHeaders;\n\n/**\n * Fetch request options - compatible with standard fetch API.\n */\nexport interface FetchRequestInit {\n method?: string;\n headers?: ServiceHeaders;\n body?: Blob | FormData | string;\n signal?: AbortSignal;\n redirect?: \"error\";\n}\n\n/**\n * Fetch response interface - compatible with standard Response.\n */\nexport interface FetchResponse {\n ok: boolean;\n status: number;\n statusText: string;\n body?: unknown;\n headers: {\n get(name: string): string | null;\n };\n json(): Promise<unknown>;\n text(): Promise<string>;\n arrayBuffer(): Promise<ArrayBuffer>;\n blob(): Promise<Blob>;\n}\n\n/**\n * Fetch function signature - allows for custom fetch implementations.\n * Compatible with both browser fetch and Node.js fetch.\n */\nexport type FetchFunction = (\n url: string,\n init?: FetchRequestInit,\n) => Promise<FetchResponse>;\n\n// =============================================================================\n// Retry Policy\n// =============================================================================\n\n/**\n * Configuration for automatic retry of failed requests.\n */\nexport interface RetryPolicy {\n /** Maximum number of attempts (including initial) */\n maxAttempts: number;\n /** Backoff strategy between retries */\n backoff: \"none\" | \"linear\" | \"exponential\";\n /** Base delay in milliseconds for backoff calculation */\n baseDelayMs: number;\n /** Maximum delay in milliseconds between retries */\n maxDelayMs: number;\n /** Error codes that should trigger a retry */\n retryableErrors: string[];\n}\n\n/**\n * Default retry policy.\n */\nexport const defaultRetryPolicy: RetryPolicy = {\n maxAttempts: 3,\n backoff: \"exponential\",\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n retryableErrors: [ErrorCodes.NETWORK_ERROR, ErrorCodes.TIMEOUT],\n};\n\n// =============================================================================\n// Service Context\n// =============================================================================\n\n/**\n * Event handler function type.\n */\nexport type EventHandler = (data: unknown) => void;\n\n/**\n * SDK telemetry event handler.\n */\nexport type TelemetryEventHandler = (event: string, data: unknown) => void;\n\n/**\n * Default-off telemetry configuration.\n */\nexport type TelemetryConfig =\n | boolean\n | {\n enabled?: boolean;\n onEvent?: TelemetryEventHandler;\n };\n\n/**\n * Service interface - base contract for all services.\n */\nexport interface IService {\n /** Initialize service with context */\n initialize(context: IServiceContext): void;\n\n /** Called when session changes (sign-in, sign-out, refresh) */\n onSessionChange(session: ServiceSession | null): void;\n\n /** Called when SDK signs out - should abort pending operations */\n onSignOut(): void;\n\n /** Service-specific configuration */\n readonly config: Record<string, unknown>;\n}\n\n/**\n * Context provided to services for accessing platform dependencies.\n * The SDK creates this context and passes it to services during initialization.\n */\nexport interface IServiceContext {\n // Session management\n /** Current active session, or null if not authenticated */\n readonly session: ServiceSession | null;\n /** Whether there is an active authenticated session */\n readonly isAuthenticated: boolean;\n\n // Platform dependencies (injected by SDK)\n /** Platform-specific invoke function from WASM binding */\n readonly invoke: InvokeFunction;\n /** Optional multi-resource invoke function */\n readonly invokeAny?: InvokeAnyFunction;\n /** Fetch function (defaults to globalThis.fetch) */\n readonly fetch: FetchFunction;\n /** Available TinyCloud host URLs */\n readonly hosts: string[];\n\n // Cross-service access\n /** Get another registered service by name */\n getService<T extends IService>(name: string): T | undefined;\n\n // Telemetry/Events\n /** Emit a telemetry event */\n emit(event: string, data: unknown): void;\n /** Subscribe to events */\n on(event: string, handler: EventHandler): () => void;\n\n // Lifecycle\n /** Abort signal that fires when SDK signs out */\n readonly abortSignal: AbortSignal;\n\n // Retry policy\n /** Retry policy for failed requests */\n readonly retryPolicy: RetryPolicy;\n}\n\n// =============================================================================\n// Telemetry Events\n// =============================================================================\n\n/**\n * Event emitted before a service request.\n */\nexport interface ServiceRequestEvent {\n service: string;\n action: string;\n span?: string;\n key?: string;\n timestamp: number;\n}\n\n/**\n * Event emitted after a service response.\n */\nexport interface ServiceResponseEvent {\n service: string;\n action: string;\n span?: string;\n ok: boolean;\n duration: number;\n durationMs?: number;\n status?: number;\n}\n\n/**\n * Event emitted on service error.\n */\nexport interface ServiceErrorEvent {\n service: string;\n span?: string;\n error: ServiceError;\n}\n\n/**\n * Event emitted on retry attempt.\n */\nexport interface ServiceRetryEvent {\n service: string;\n attempt: number;\n maxAttempts: number;\n error: ServiceError;\n}\n\n/**\n * Generic named span event for aggregating operation timings.\n */\nexport interface TelemetrySpanEvent {\n span: string;\n ok: boolean;\n durationMs: number;\n service?: string;\n action?: string;\n status?: number;\n error?: ServiceError;\n}\n\n/**\n * Telemetry event names.\n */\nexport const TelemetryEvents = {\n SPAN: \"telemetry.span\",\n SERVICE_REQUEST: \"service.request\",\n SERVICE_RESPONSE: \"service.response\",\n SERVICE_ERROR: \"service.error\",\n SERVICE_RETRY: \"service.retry\",\n SESSION_CHANGED: \"session.changed\",\n SESSION_EXPIRED: \"session.expired\",\n} as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Create a success result.\n */\nexport function ok<T>(data: T): Result<T> {\n return { ok: true, data };\n}\n\n/**\n * Create an error result.\n */\nexport function err<E = ServiceError>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n/**\n * Create a ServiceError.\n */\nexport function serviceError(\n code: string,\n message: string,\n service: string,\n options?: { cause?: Error; meta?: Record<string, unknown> },\n): ServiceError {\n return {\n code,\n message,\n service,\n cause: options?.cause,\n meta: options?.meta,\n };\n}\n","/** Safe projections for diagnostic sinks. */\n\nconst REDACTED = \"[REDACTED]\";\nconst SAFE_NUMBER_FIELDS = new Set([\n \"duration\",\n \"durationMs\",\n \"endedAt\",\n \"startedAt\",\n \"status\",\n \"timestamp\",\n]);\nconst SAFE_BOOLEAN_FIELDS = new Set([\"authenticated\", \"ok\", \"persisted\"]);\n\nfunction finiteNumber(value: unknown): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : undefined;\n}\n\nfunction isBinaryData(value: object): boolean {\n try {\n return (\n Array.isArray(value) ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n );\n } catch {\n // A revoked Proxy can throw while checking its shape. It is not diagnostic\n // data and must not prevent the SDK operation that emitted it.\n return true;\n }\n}\n\nfunction read(value: object, key: string): unknown {\n try {\n return (value as Record<string, unknown>)[key];\n } catch {\n return REDACTED;\n }\n}\n\n/** Return only stable, non-sensitive error diagnostics. */\nexport function projectDiagnosticError(error: unknown): Record<string, unknown> {\n if (typeof error !== \"object\" || error === null) return {};\n\n const status = finiteNumber(read(error, \"status\"));\n return status !== undefined && status >= 100 && status <= 599 ? { status } : {};\n}\n\n/**\n * Copy only fixed scalar metrics and URL origins. Diagnostic payloads can be\n * supplied by applications and nodes, so strings, nested values, and unknown\n * fields are never projected to debug or telemetry sinks.\n */\nexport function projectDiagnosticData(value: unknown): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value !== \"object\") return REDACTED;\n\n try {\n if (isBinaryData(value)) return REDACTED;\n\n const projected: Record<string, unknown> = {};\n for (const key of SAFE_NUMBER_FIELDS) {\n const number = finiteNumber(read(value, key));\n if (number !== undefined) projected[key] = number;\n }\n for (const key of SAFE_BOOLEAN_FIELDS) {\n const boolean = read(value, key);\n if (typeof boolean === \"boolean\") projected[key] = boolean;\n }\n\n // URLs, like every other string-bearing value, are never projected. Even\n // an origin can encode user-controlled data in a hostname.\n const url = read(value, \"url\");\n if (url !== undefined) projected.url = REDACTED;\n\n const error = read(value, \"error\");\n if (error !== undefined) projected.error = projectDiagnosticError(error);\n\n return projected;\n } catch {\n // Projection is best-effort only. Hostile Proxy traps and exotic objects\n // must never block telemetry, debug logging, or ordinary subscribers.\n return REDACTED;\n }\n}\n","import { projectDiagnosticData } from \"./diagnostics\";\n\nexport type TinyCloudDebugLevel = \"debug\";\n\nexport interface TinyCloudDebugEvent {\n sequence: number;\n timestamp: number;\n timestampIso: string;\n level: TinyCloudDebugLevel;\n event: string;\n message?: string;\n data?: unknown;\n durationMs?: number;\n startedAt?: number;\n endedAt?: number;\n}\n\nexport interface TinyCloudDebugEnableOptions {\n /**\n * Persist the debug flag in browser localStorage. Defaults to true.\n * Node runtimes intentionally do not persist this setting.\n */\n persist?: boolean;\n}\n\nexport interface TinyCloudDebugTimer {\n stop: (data?: unknown) => TinyCloudDebugEvent | undefined;\n}\n\ntype TinyCloudDebugGlobal = typeof globalThis & {\n TinyCloud_debug?: boolean | string;\n TinyCloudDebug?: TinyCloudDebugLogger;\n window?: unknown;\n enableTinyCloudDebug?: typeof enableTinyCloudDebug;\n disableTinyCloudDebug?: typeof disableTinyCloudDebug;\n getTinyCloudDebugLogs?: typeof getTinyCloudDebugLogs;\n clearTinyCloudDebugLogs?: typeof clearTinyCloudDebugLogs;\n};\n\ninterface TinyCloudDebugStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\nconst DEBUG_FLAG = \"TinyCloud_debug\";\nconst MAX_EVENTS = 1000;\n\nfunction getGlobal(): TinyCloudDebugGlobal {\n return globalThis as TinyCloudDebugGlobal;\n}\n\nfunction nowMs(): number {\n const performanceNow = globalThis.performance?.now?.bind(globalThis.performance);\n return typeof performanceNow === \"function\" ? performanceNow() : Date.now();\n}\n\nfunction isTrue(value: unknown): boolean {\n return value === true || value === \"true\" || value === \"1\";\n}\n\nfunction getProcessDebugFlag(): unknown {\n const processLike = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;\n return processLike?.env?.[DEBUG_FLAG];\n}\n\nfunction isBrowserWindow(): boolean {\n const global = getGlobal();\n return global.window === globalThis;\n}\n\nfunction getLocalStorage(): TinyCloudDebugStorage | undefined {\n if (!isBrowserWindow()) {\n return undefined;\n }\n\n try {\n return (globalThis as typeof globalThis & { localStorage?: TinyCloudDebugStorage }).localStorage;\n } catch {\n return undefined;\n }\n}\n\nfunction getStoredDebugFlag(): unknown {\n const storage = getLocalStorage();\n if (!storage) {\n return undefined;\n }\n\n try {\n return storage.getItem(DEBUG_FLAG);\n } catch {\n return undefined;\n }\n}\n\nfunction setStoredDebugFlag(enabled: boolean): void {\n const storage = getLocalStorage();\n if (!storage) {\n return;\n }\n\n try {\n if (enabled) {\n storage.setItem(DEBUG_FLAG, \"true\");\n } else {\n storage.removeItem(DEBUG_FLAG);\n }\n } catch {\n // localStorage can be unavailable in private browsing or restricted contexts.\n }\n}\n\nfunction shouldStartEnabled(): boolean {\n const global = getGlobal();\n return (\n isTrue(global[DEBUG_FLAG]) ||\n isTrue(getStoredDebugFlag()) ||\n isTrue(getProcessDebugFlag())\n );\n}\n\nexport class TinyCloudDebugLogger {\n private enabled: boolean = shouldStartEnabled();\n private sequence: number = 0;\n private events: TinyCloudDebugEvent[] = [];\n private maxEvents: number = MAX_EVENTS;\n\n isEnabled(): boolean {\n return (\n this.enabled ||\n isTrue(getGlobal()[DEBUG_FLAG]) ||\n isTrue(getProcessDebugFlag())\n );\n }\n\n enable(options: TinyCloudDebugEnableOptions = {}): void {\n this.enabled = true;\n getGlobal()[DEBUG_FLAG] = true;\n\n if (options.persist !== false) {\n setStoredDebugFlag(true);\n }\n\n this.log(\"debug.enabled\", { persisted: options.persist !== false });\n }\n\n disable(options: TinyCloudDebugEnableOptions = {}): void {\n this.log(\"debug.disabled\", { persisted: options.persist !== false });\n this.enabled = false;\n getGlobal()[DEBUG_FLAG] = false;\n\n if (options.persist !== false) {\n setStoredDebugFlag(false);\n }\n }\n\n clear(): void {\n this.events = [];\n }\n\n getLogs(): TinyCloudDebugEvent[] {\n return [...this.events];\n }\n\n log(event: string, data?: unknown, message?: string): TinyCloudDebugEvent | undefined {\n if (!this.isEnabled()) {\n return undefined;\n }\n\n return this.record({\n event,\n data,\n message,\n level: \"debug\",\n timestamp: Date.now(),\n timestampIso: new Date().toISOString(),\n });\n }\n\n startTimer(event: string, data?: unknown): TinyCloudDebugTimer {\n if (!this.isEnabled()) {\n return { stop: () => undefined };\n }\n\n const startedAt = nowMs();\n this.log(`${event}.start`, data);\n\n return {\n stop: (finishData?: unknown) => {\n const endedAt = nowMs();\n return this.record({\n event: `${event}.end`,\n data: finishData,\n durationMs: endedAt - startedAt,\n startedAt,\n endedAt,\n level: \"debug\",\n timestamp: Date.now(),\n timestampIso: new Date().toISOString(),\n });\n },\n };\n }\n\n async timeAsync<T>(\n event: string,\n operation: () => Promise<T>,\n data?: unknown,\n ): Promise<T> {\n if (!this.isEnabled()) {\n return operation();\n }\n\n const timer = this.startTimer(event, data);\n try {\n const result = await operation();\n timer.stop({ ok: true });\n return result;\n } catch (error) {\n timer.stop({ ok: false, error });\n throw error;\n }\n }\n\n time<T>(event: string, operation: () => T, data?: unknown): T {\n if (!this.isEnabled()) {\n return operation();\n }\n\n const timer = this.startTimer(event, data);\n try {\n const result = operation();\n timer.stop({ ok: true });\n return result;\n } catch (error) {\n timer.stop({ ok: false, error });\n throw error;\n }\n }\n\n private record(\n event: Omit<TinyCloudDebugEvent, \"sequence\">,\n ): TinyCloudDebugEvent {\n const debugEvent: TinyCloudDebugEvent = {\n ...event,\n ...(event.data === undefined\n ? {}\n : { data: projectDiagnosticData(event.data) }),\n ...(event.message === undefined ? {} : { message: \"[REDACTED]\" }),\n sequence: ++this.sequence,\n };\n\n this.events.push(debugEvent);\n if (this.events.length > this.maxEvents) {\n this.events.splice(0, this.events.length - this.maxEvents);\n }\n\n try {\n globalThis.console?.debug?.(\"[TinyCloud]\", debugEvent.event, debugEvent);\n } catch {\n // Debug logging should never affect SDK behavior.\n }\n\n return debugEvent;\n }\n}\n\nexport const tinyCloudDebugLogger = new TinyCloudDebugLogger();\n\nexport function enableTinyCloudDebug(\n options?: TinyCloudDebugEnableOptions,\n): TinyCloudDebugLogger {\n tinyCloudDebugLogger.enable(options);\n return tinyCloudDebugLogger;\n}\n\nexport function disableTinyCloudDebug(\n options?: TinyCloudDebugEnableOptions,\n): TinyCloudDebugLogger {\n tinyCloudDebugLogger.disable(options);\n return tinyCloudDebugLogger;\n}\n\nexport function getTinyCloudDebugLogs(): TinyCloudDebugEvent[] {\n return tinyCloudDebugLogger.getLogs();\n}\n\nexport function clearTinyCloudDebugLogs(): void {\n tinyCloudDebugLogger.clear();\n}\n\nexport function installTinyCloudDebugGlobals(): void {\n const global = getGlobal();\n global.TinyCloudDebug = tinyCloudDebugLogger;\n global.enableTinyCloudDebug = enableTinyCloudDebug;\n global.disableTinyCloudDebug = disableTinyCloudDebug;\n global.getTinyCloudDebugLogs = getTinyCloudDebugLogs;\n global.clearTinyCloudDebugLogs = clearTinyCloudDebugLogs;\n}\n\ninstallTinyCloudDebugGlobals();\n","/**\n * Zod schemas for SDK Services API response types.\n *\n * This is the source of truth for service response types. TypeScript types\n * are derived from these schemas using z.infer<>.\n *\n * @packageDocumentation\n */\n\nimport { z } from \"zod\";\n\n// =============================================================================\n// Validation Error Type\n// =============================================================================\n\n/**\n * Validation error type for schema validation failures.\n */\nexport interface ValidationError {\n code: \"VALIDATION_ERROR\";\n message: string;\n service: string;\n meta?: {\n issues: z.ZodIssue[];\n path?: string;\n };\n}\n\n// =============================================================================\n// Service Error Schema\n// =============================================================================\n\n/**\n * Schema for service error with structured information.\n */\nexport const ServiceErrorSchema = z.object({\n /** Error code for programmatic handling (e.g., 'KV_NOT_FOUND', 'AUTH_EXPIRED') */\n code: z.string(),\n /** Human-readable error message */\n message: z.string(),\n /** Service that produced the error (e.g., 'kv', 'sql') */\n service: z.string(),\n /** Original error if this wraps another error - not validated since Error is a class */\n cause: z.unknown().optional(),\n /** Additional metadata about the error - passthrough allows any object properties */\n meta: z.object({}).passthrough().optional(),\n});\n\nexport type ServiceErrorType = z.infer<typeof ServiceErrorSchema>;\n\n// =============================================================================\n// Result Schema Factory\n// =============================================================================\n\n/**\n * Creates a Result schema for a given data type.\n * Result is a discriminated union: { ok: true, data: T } | { ok: false, error: E }\n *\n * @param dataSchema - Zod schema for the success data type\n * @param errorSchema - Zod schema for the error type (defaults to ServiceErrorSchema)\n * @returns A Zod schema for Result<T, E>\n *\n * @example\n * ```typescript\n * const KVGetResultSchema = createResultSchema(z.string());\n * type KVGetResult = z.infer<typeof KVGetResultSchema>;\n * ```\n */\nexport function createResultSchema<T extends z.ZodTypeAny, E extends z.ZodTypeAny>(\n dataSchema: T,\n errorSchema: E = ServiceErrorSchema as unknown as E\n) {\n return z.discriminatedUnion(\"ok\", [\n z.object({\n ok: z.literal(true),\n data: dataSchema,\n }),\n z.object({\n ok: z.literal(false),\n error: errorSchema,\n }),\n ]);\n}\n\n/**\n * Pre-built Result schema with unknown data and ServiceError.\n * Useful for generic validation before type narrowing.\n */\nexport const GenericResultSchema = createResultSchema(z.unknown(), ServiceErrorSchema);\n\n// =============================================================================\n// KV Response Schemas\n// =============================================================================\n\n/**\n * Schema for KV response headers metadata.\n * Note: The `get` method is a function and cannot be validated with Zod.\n * This schema validates the data properties only.\n */\nexport const KVResponseHeadersSchema = z.object({\n /** ETag for conditional requests */\n etag: z.string().optional(),\n /** Content type of the stored value */\n contentType: z.string().optional(),\n /** Last modification timestamp */\n lastModified: z.string().optional(),\n /** Content length in bytes */\n contentLength: z.number().optional(),\n});\n\nexport type KVResponseHeadersType = z.infer<typeof KVResponseHeadersSchema>;\n\n/**\n * Creates a KVResponse schema for a given data type.\n *\n * @param dataSchema - Zod schema for the data payload type\n * @returns A Zod schema for KVResponse<T>\n *\n * @example\n * ```typescript\n * const UserResponseSchema = createKVResponseSchema(UserSchema);\n * type UserResponse = z.infer<typeof UserResponseSchema>;\n * ```\n */\nexport function createKVResponseSchema<T extends z.ZodTypeAny>(dataSchema: T) {\n return z.object({\n /** The data payload */\n data: dataSchema,\n /** Response headers with metadata */\n headers: KVResponseHeadersSchema,\n });\n}\n\n/**\n * Generic KVResponse schema with unknown data.\n * Useful for generic validation before type narrowing.\n */\nexport const GenericKVResponseSchema = createKVResponseSchema(z.unknown());\n\nexport type GenericKVResponseType = z.infer<typeof GenericKVResponseSchema>;\n\n/**\n * Schema for KV list response.\n */\nexport const KVListResponseSchema = z.object({\n /** Array of keys matching the list criteria */\n keys: z.array(z.string()),\n truncated: z.boolean().optional(),\n nextCursor: z.string().optional(),\n});\n\nexport type KVListResponseType = z.infer<typeof KVListResponseSchema>;\n\n/**\n * Result schema for KV list operations.\n */\nexport const KVListResultSchema = createResultSchema(KVListResponseSchema);\n\nexport type KVListResultType = z.infer<typeof KVListResultSchema>;\n\n// =============================================================================\n// Telemetry Event Schemas\n// =============================================================================\n\n/**\n * Schema for service request event.\n */\nexport const ServiceRequestEventSchema = z.object({\n service: z.string(),\n action: z.string(),\n span: z.string().optional(),\n key: z.string().optional(),\n timestamp: z.number(),\n});\n\nexport type ServiceRequestEventType = z.infer<typeof ServiceRequestEventSchema>;\n\n/**\n * Schema for service response event.\n */\nexport const ServiceResponseEventSchema = z.object({\n service: z.string(),\n action: z.string(),\n span: z.string().optional(),\n ok: z.boolean(),\n duration: z.number(),\n durationMs: z.number().optional(),\n status: z.number().optional(),\n});\n\nexport type ServiceResponseEventType = z.infer<typeof ServiceResponseEventSchema>;\n\n/**\n * Schema for service error event.\n */\nexport const ServiceErrorEventSchema = z.object({\n service: z.string(),\n span: z.string().optional(),\n error: ServiceErrorSchema,\n});\n\nexport type ServiceErrorEventType = z.infer<typeof ServiceErrorEventSchema>;\n\n/**\n * Schema for service retry event.\n */\nexport const ServiceRetryEventSchema = z.object({\n service: z.string(),\n attempt: z.number().int().positive(),\n maxAttempts: z.number().int().positive(),\n error: ServiceErrorSchema,\n});\n\nexport type ServiceRetryEventType = z.infer<typeof ServiceRetryEventSchema>;\n\n/**\n * Schema for generic named span event.\n */\nexport const TelemetrySpanEventSchema = z.object({\n span: z.string(),\n ok: z.boolean(),\n durationMs: z.number(),\n service: z.string().optional(),\n action: z.string().optional(),\n status: z.number().optional(),\n error: ServiceErrorSchema.optional(),\n});\n\nexport type TelemetrySpanEventType = z.infer<typeof TelemetrySpanEventSchema>;\n\n// =============================================================================\n// Retry Policy Schema\n// =============================================================================\n\n/**\n * Schema for retry policy configuration.\n */\nexport const RetryPolicySchema = z.object({\n /** Maximum number of attempts (including initial) */\n maxAttempts: z.number().int().positive(),\n /** Backoff strategy between retries */\n backoff: z.enum([\"none\", \"linear\", \"exponential\"]),\n /** Base delay in milliseconds for backoff calculation */\n baseDelayMs: z.number().nonnegative(),\n /** Maximum delay in milliseconds between retries */\n maxDelayMs: z.number().nonnegative(),\n /** Error codes that should trigger a retry */\n retryableErrors: z.array(z.string()),\n});\n\nexport type RetryPolicyType = z.infer<typeof RetryPolicySchema>;\n\n// =============================================================================\n// Service Session Schema\n// =============================================================================\n\n/**\n * Schema for service session data required for authenticated operations.\n */\nexport const ServiceSessionSchema = z.object({\n /** The delegation header containing the UCAN */\n delegationHeader: z.object({\n Authorization: z.string(),\n }),\n /** The delegation CID */\n delegationCid: z.string(),\n /** The space ID for this session */\n spaceId: z.string(),\n /** The verification method DID */\n verificationMethod: z.string(),\n /** The session key JWK (required for invoke) */\n jwk: z.object({}).passthrough(),\n});\n\nexport type ServiceSessionType = z.infer<typeof ServiceSessionSchema>;\n\n// =============================================================================\n// Validation Helpers\n// =============================================================================\n\n/**\n * Validate service error against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateServiceError(\n data: unknown\n): { ok: true; data: ServiceErrorType } | { ok: false; error: ValidationError } {\n const result = ServiceErrorSchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"validation\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n\n/**\n * Validate KV list response against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateKVListResponse(\n data: unknown\n): { ok: true; data: KVListResponseType } | { ok: false; error: ValidationError } {\n const result = KVListResponseSchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"kv\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n\n/**\n * Validate KV response headers against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateKVResponseHeaders(\n data: unknown\n): { ok: true; data: KVResponseHeadersType } | { ok: false; error: ValidationError } {\n const result = KVResponseHeadersSchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"kv\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n\n/**\n * Validate service session against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateServiceSession(\n data: unknown\n): { ok: true; data: ServiceSessionType } | { ok: false; error: ValidationError } {\n const result = ServiceSessionSchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"session\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n\n/**\n * Validate retry policy against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateRetryPolicy(\n data: unknown\n): { ok: true; data: RetryPolicyType } | { ok: false; error: ValidationError } {\n const result = RetryPolicySchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"config\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n\n/**\n * Validate service request event against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateServiceRequestEvent(\n data: unknown\n): { ok: true; data: ServiceRequestEventType } | { ok: false; error: ValidationError } {\n const result = ServiceRequestEventSchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"telemetry\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n\n/**\n * Validate service response event against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateServiceResponseEvent(\n data: unknown\n): { ok: true; data: ServiceResponseEventType } | { ok: false; error: ValidationError } {\n const result = ServiceResponseEventSchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"telemetry\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n\n/**\n * Validate named telemetry span event against the schema.\n *\n * @param data - Unknown data to validate\n * @returns Result with validated data or validation error\n */\nexport function validateTelemetrySpanEvent(\n data: unknown\n): { ok: true; data: TelemetrySpanEventType } | { ok: false; error: ValidationError } {\n const result = TelemetrySpanEventSchema.safeParse(data);\n if (!result.success) {\n return {\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: result.error.message,\n service: \"telemetry\",\n meta: { issues: result.error.issues },\n },\n };\n }\n return { ok: true, data: result.data };\n}\n","/**\n * ServiceContext implementation for TinyCloud SDK Services\n * @module @tinycloud/sdk-services\n */\n\nimport {\n IServiceContext,\n IService,\n ServiceSession,\n RetryPolicy,\n InvokeFunction,\n InvokeAnyFunction,\n FetchFunction,\n defaultRetryPolicy,\n EventHandler,\n TelemetryEventHandler,\n TelemetryConfig,\n} from \"./types\";\nimport { tinyCloudDebugLogger } from \"./debug\";\nimport { projectDiagnosticData } from \"./diagnostics\";\n\n/**\n * Configuration options for ServiceContext.\n */\nexport interface ServiceContextConfig {\n /** Function to invoke WASM operations */\n invoke: InvokeFunction;\n /** Optional function to mint a single authorization header for multiple capabilities */\n invokeAny?: InvokeAnyFunction;\n /** Function to make HTTP requests (defaults to globalThis.fetch) */\n fetch?: FetchFunction;\n /** List of TinyCloud host URLs */\n hosts: string[];\n /** Initial session (optional) */\n session?: ServiceSession | null;\n /** Retry policy configuration */\n retryPolicy?: Partial<RetryPolicy>;\n /** Default-off telemetry event delivery. */\n telemetry?: TelemetryConfig;\n}\n\n/**\n * ServiceContext provides platform dependencies and cross-service access to services.\n * This is the primary interface services use to interact with the SDK runtime.\n *\n * @example\n * ```typescript\n * const context = new ServiceContext({\n * invoke: wasmInvoke,\n * hosts: ['https://node.tinycloud.xyz'],\n * retryPolicy: { maxAttempts: 5 },\n * });\n *\n * // Register a service\n * const kvService = new KVService({});\n * context.registerService('kv', kvService);\n * kvService.initialize(context);\n *\n * // Update session when user signs in\n * context.setSession(userSession);\n * ```\n */\nexport class ServiceContext implements IServiceContext {\n private _session: ServiceSession | null = null;\n private _services: Map<string, IService> = new Map();\n private _eventHandlers: Map<string, Set<EventHandler>> = new Map();\n private _abortController: AbortController = new AbortController();\n private _retired = false;\n private readonly _invoke: InvokeFunction;\n private readonly _invokeAny?: InvokeAnyFunction;\n private readonly _fetch: FetchFunction;\n private readonly _hosts: string[];\n private readonly _retryPolicy: RetryPolicy;\n private readonly _telemetryEnabled: boolean;\n private readonly _telemetryHandler?: TelemetryEventHandler;\n\n constructor(config: ServiceContextConfig) {\n this._invoke = this.wrapInvoke(config.invoke);\n this._invokeAny = config.invokeAny\n ? this.wrapInvokeAny(config.invokeAny)\n : undefined;\n this._fetch = this.wrapFetch(config.fetch ?? globalThis.fetch.bind(globalThis));\n this._hosts = config.hosts;\n this._session = config.session ?? null;\n this._retryPolicy = {\n ...defaultRetryPolicy,\n ...config.retryPolicy,\n };\n this._telemetryEnabled =\n typeof config.telemetry === \"boolean\"\n ? config.telemetry\n : config.telemetry?.enabled === true;\n this._telemetryHandler =\n typeof config.telemetry === \"object\" ? config.telemetry.onEvent : undefined;\n }\n\n // ============================================================\n // Session Management\n // ============================================================\n\n /**\n * Get the current session.\n */\n get session(): ServiceSession | null {\n return this._retired ? null : this._session;\n }\n\n /**\n * Check if the context has an authenticated session.\n */\n get isAuthenticated(): boolean {\n return !this._retired && this._session !== null;\n }\n\n /**\n * Update the session and notify all registered services.\n *\n * @param session - New session or null to clear\n */\n setSession(session: ServiceSession | null): void {\n this.assertActive();\n this._session = session;\n this.emit('session.changed', { authenticated: session !== null });\n\n // Notify all services of session change\n for (const service of this._services.values()) {\n service.onSessionChange(session);\n }\n }\n\n // ============================================================\n // Platform Dependencies\n // ============================================================\n\n /**\n * Get the invoke function for WASM operations.\n */\n get invoke(): InvokeFunction {\n this.assertActive();\n return this._invoke;\n }\n\n /**\n * Get the multi-resource invoke function when available.\n */\n get invokeAny(): InvokeAnyFunction | undefined {\n this.assertActive();\n return this._invokeAny;\n }\n\n /**\n * Get the fetch function for HTTP requests.\n */\n get fetch(): FetchFunction {\n this.assertActive();\n return this._fetch;\n }\n\n /**\n * Get the list of TinyCloud host URLs.\n */\n get hosts(): string[] {\n this.assertActive();\n return this._hosts;\n }\n\n // ============================================================\n // Service Registry\n // ============================================================\n\n /**\n * Register a service with the context.\n *\n * @param name - Service name (e.g., 'kv')\n * @param service - Service instance\n */\n registerService(name: string, service: IService): void {\n this._services.set(name, service);\n }\n\n /**\n * Unregister a service from the context.\n *\n * @param name - Service name to remove\n */\n unregisterService(name: string): void {\n this._services.delete(name);\n }\n\n /**\n * Get a registered service by name.\n *\n * @param name - Service name\n * @returns The service instance or undefined if not registered\n */\n getService<T extends IService>(name: string): T | undefined {\n return this._services.get(name) as T | undefined;\n }\n\n // ============================================================\n // Event System (Telemetry)\n // ============================================================\n\n /**\n * Emit a telemetry event.\n *\n * @param event - Event name\n * @param data - Event data\n */\n emit(event: string, data: unknown): void {\n tinyCloudDebugLogger.log(event, data);\n\n if (this._telemetryEnabled && this._telemetryHandler) {\n try {\n this._telemetryHandler(event, projectDiagnosticData(data));\n } catch (error) {\n // Don't let telemetry handlers break SDK operations.\n console.error(`Error in telemetry handler for \"${event}\":`, error);\n }\n }\n\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(data);\n } catch (error) {\n // Don't let event handler errors break the flow\n console.error(`Error in event handler for \"${event}\":`, error);\n }\n }\n }\n }\n\n /**\n * Subscribe to telemetry events.\n *\n * @param event - Event name to subscribe to\n * @param handler - Handler function\n * @returns Unsubscribe function\n */\n on(event: string, handler: EventHandler): () => void {\n if (!this._eventHandlers.has(event)) {\n this._eventHandlers.set(event, new Set());\n }\n this._eventHandlers.get(event)!.add(handler);\n\n // Return unsubscribe function\n return () => {\n const handlers = this._eventHandlers.get(event);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n this._eventHandlers.delete(event);\n }\n }\n };\n }\n\n /**\n * Remove all event handlers for an event.\n *\n * @param event - Event name (if omitted, clears all events)\n */\n clearEventHandlers(event?: string): void {\n if (event) {\n this._eventHandlers.delete(event);\n } else {\n this._eventHandlers.clear();\n }\n }\n\n // ============================================================\n // Lifecycle\n // ============================================================\n\n /**\n * Get the abort signal for cancelling operations.\n */\n get abortSignal(): AbortSignal {\n return this._abortController.signal;\n }\n\n /**\n * Permanently retire this graph after its owner installs a replacement.\n * Unlike `abort()`, retirement never creates a fresh controller, so captured\n * services cannot resume calls under a superseded session.\n */\n retire(): void {\n if (this._retired) return;\n this._retired = true;\n this._session = null;\n this._abortController.abort();\n for (const service of this._services.values()) {\n // User-provided services are part of the retired graph, but their\n // cleanup must never make installing the replacement graph fail.\n try {\n service.onSignOut();\n } catch (error) {\n console.error(\"Error retiring service:\", error);\n }\n }\n }\n\n /**\n * Abort all pending operations and notify services.\n * Creates a new AbortController for future operations.\n */\n abort(): void {\n // A retired graph must never receive a fresh abort signal. Captured\n // services retain this context, so recreating its controller would make a\n // stale graph look live to custom cleanup code.\n if (this._retired) return;\n this._abortController.abort();\n this._abortController = new AbortController();\n\n // Notify all services\n for (const service of this._services.values()) {\n try {\n service.onSignOut();\n } catch (error) {\n console.error(\"Error signing out service:\", error);\n }\n }\n }\n\n /**\n * Sign out - abort operations and clear session.\n */\n signOut(): void {\n if (this._retired) return;\n this.abort();\n this.setSession(null);\n this.emit('session.expired', {});\n }\n\n // ============================================================\n // Retry Policy\n // ============================================================\n\n /**\n * Get the retry policy configuration.\n */\n get retryPolicy(): RetryPolicy {\n return this._retryPolicy;\n }\n\n private wrapInvoke(invoke: InvokeFunction): InvokeFunction {\n return (session, service, path, action, facts) => {\n this.assertActive();\n if (!tinyCloudDebugLogger.isEnabled()) {\n return invoke(session, service, path, action, facts);\n }\n\n const timer = tinyCloudDebugLogger.startTimer(\"sdk.invoke\", {\n service,\n path,\n action,\n facts,\n });\n\n try {\n const result = invoke(session, service, path, action, facts);\n timer.stop({ ok: true, service, path, action });\n return result;\n } catch (error) {\n timer.stop({ ok: false, service, path, action, error });\n throw error;\n }\n };\n }\n\n private assertActive(): void {\n if (this._retired) {\n throw new Error(\"Service graph has been retired by session replacement.\");\n }\n }\n\n private wrapInvokeAny(invokeAny: InvokeAnyFunction): InvokeAnyFunction {\n return (session, entries, facts) => {\n this.assertActive();\n if (!tinyCloudDebugLogger.isEnabled()) {\n return invokeAny(session, entries, facts);\n }\n\n const timer = tinyCloudDebugLogger.startTimer(\"sdk.invokeAny\", {\n entries,\n facts,\n });\n\n try {\n const result = invokeAny(session, entries, facts);\n timer.stop({ ok: true, entries });\n return result;\n } catch (error) {\n timer.stop({ ok: false, entries, error });\n throw error;\n }\n };\n }\n\n private wrapFetch(fetchFn: FetchFunction): FetchFunction {\n return async (url, init) => {\n this.assertActive();\n const request = {\n ...init,\n signal: this.combineSignals(init?.signal),\n };\n if (!tinyCloudDebugLogger.isEnabled()) {\n return fetchFn(url, request);\n }\n\n const timer = tinyCloudDebugLogger.startTimer(\"sdk.fetch\", {\n url,\n method: request.method ?? \"GET\",\n init: request,\n });\n\n try {\n const response = await fetchFn(url, request);\n timer.stop({\n ok: response.ok,\n url,\n method: request.method ?? \"GET\",\n status: response.status,\n statusText: response.statusText,\n });\n return response;\n } catch (error) {\n timer.stop({\n ok: false,\n url,\n method: request.method ?? \"GET\",\n error,\n });\n throw error;\n }\n };\n }\n\n private combineSignals(signal?: AbortSignal): AbortSignal {\n if (!signal) return this._abortController.signal;\n const combined = new AbortController();\n const abort = (source: AbortSignal) => combined.abort(source.reason);\n if (signal.aborted) {\n abort(signal);\n } else if (this._abortController.signal.aborted) {\n abort(this._abortController.signal);\n } else {\n signal.addEventListener(\"abort\", () => abort(signal), { once: true });\n this._abortController.signal.addEventListener(\n \"abort\",\n () => abort(this._abortController.signal),\n { once: true },\n );\n }\n return combined.signal;\n }\n}\n","/**\n * SDK Services - Error Utilities\n *\n * Utilities for creating and handling service errors.\n */\n\nimport {\n ServiceError,\n ErrorCodes,\n err,\n serviceError,\n type PermissionHint,\n} from \"./types\";\n\n/** Validate one structured node permission hint without accepting raw errors. */\nexport function parsePermissionHint(value: unknown): PermissionHint | undefined {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return undefined;\n const candidate = value as Record<string, unknown>;\n const keys = Object.keys(candidate).sort();\n if (keys.some((key) => ![\"actions\", \"path\", \"service\", \"space\"].includes(key))) return undefined;\n if (candidate.service !== \"tinycloud.kv\" && candidate.service !== \"tinycloud.encryption\") return undefined;\n if (typeof candidate.path !== \"string\" || candidate.path.length === 0 || candidate.path.includes(\"*\")) return undefined;\n if (candidate.service === \"tinycloud.kv\" &&\n (typeof candidate.space !== \"string\" || candidate.space.length === 0 || !candidate.path.startsWith(\"vault/\") || candidate.path.endsWith(\"/\"))) {\n return undefined;\n }\n if (candidate.service === \"tinycloud.encryption\" &&\n (candidate.space !== undefined || !candidate.path.startsWith(\"urn:tinycloud:encryption:\") || candidate.path.endsWith(\":\"))) {\n return undefined;\n }\n if (!Array.isArray(candidate.actions) || candidate.actions.length !== 1 ||\n typeof candidate.actions[0] !== \"string\" || candidate.actions[0].includes(\"*\")) return undefined;\n const expectedAction = candidate.service === \"tinycloud.kv\"\n ? \"tinycloud.kv/get\"\n : \"tinycloud.encryption/decrypt\";\n if (candidate.actions[0] !== expectedAction) return undefined;\n const space = typeof candidate.space === \"string\" ? candidate.space : undefined;\n return {\n service: candidate.service,\n ...(space === undefined ? {} : { space }),\n path: candidate.path,\n actions: [candidate.actions[0]],\n };\n}\n\n/** Extract only a validated structured hint from an SDK-owned error body. */\nexport function parsePermissionHintFromErrorText(text: string): PermissionHint | undefined {\n try {\n const parsed: unknown = JSON.parse(text);\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n const root = parsed as Record<string, unknown>;\n const nested = root.error;\n const nestedRecord = typeof nested === \"object\" && nested !== null\n ? nested as Record<string, unknown>\n : undefined;\n return parsePermissionHint(root.permissionHint) ??\n parsePermissionHint(nestedRecord?.permissionHint);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a service error for authentication required.\n */\nexport function authRequiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_REQUIRED,\n message: \"Authentication required. Please sign in first.\",\n service,\n };\n}\n\n/**\n * Create a service error for expired authentication.\n */\nexport function authExpiredError(service: string): ServiceError {\n return {\n code: ErrorCodes.AUTH_EXPIRED,\n message: \"Session has expired. Please sign in again.\",\n service,\n };\n}\n\n/**\n * Create a service error for network issues.\n */\nexport function networkError(\n service: string,\n message: string,\n cause?: Error\n): ServiceError {\n return {\n code: ErrorCodes.NETWORK_ERROR,\n message,\n service,\n cause,\n };\n}\n\n/**\n * Create a service error for timeouts.\n */\nexport function timeoutError(service: string): ServiceError {\n return {\n code: ErrorCodes.TIMEOUT,\n message: \"Request timed out.\",\n service,\n };\n}\n\n/**\n * Create a service error for aborted requests.\n */\nexport function abortedError(service: string): ServiceError {\n return {\n code: ErrorCodes.ABORTED,\n message: \"Request was aborted.\",\n service,\n };\n}\n\n/**\n * Create a service error for not found resources.\n */\nexport function notFoundError(\n service: string,\n resource: string\n): ServiceError {\n return {\n code: ErrorCodes.NOT_FOUND,\n message: `Resource not found: ${resource}`,\n service,\n };\n}\n\n/**\n * Create a service error for permission denied.\n */\nexport function permissionDeniedError(\n service: string,\n action: string\n): ServiceError {\n return {\n code: ErrorCodes.PERMISSION_DENIED,\n message: `Permission denied for action: ${action}`,\n service,\n };\n}\n\n/**\n * Parse the server's \"Unauthorized Action: {resource} / {ability}\" pattern.\n */\nexport function parseAuthError(responseText: string): { resource?: string; action?: string } {\n const match = responseText.match(/^Unauthorized Action:\\s*(.+?)\\s*\\/\\s*(tinycloud\\.\\S+)$/m);\n if (match) {\n return { resource: match[1].trim(), action: match[2].trim() };\n }\n return {};\n}\n\n/**\n * Create a service error for unauthorized action (missing capability).\n */\nexport function authUnauthorizedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });\n}\n\n/**\n * Authorization verdict read from an error's structure, never its message:\n * - `\"unauthenticated\"`: HTTP 401 — the session is missing or stale.\n * - `\"forbidden\"`: HTTP 403, or `AUTH_UNAUTHORIZED` without a status — the\n * session is valid but lacks the capability.\n * - `\"other\"`: a non-authorization HTTP error status (4xx/5xx).\n */\nexport type AuthorizationVerdict = \"unauthenticated\" | \"forbidden\" | \"other\";\n\nconst MAX_CAUSE_DEPTH = 8;\n\n/** The first 4xx/5xx among `status`, `statusCode`, `meta.status`. Success and\n * informational statuses (e.g. a 200 `Response` kept as `cause`) are not\n * failures, so they are skipped rather than ending the search. */\nfunction errorStatusOf(node: Record<string, unknown>): number | undefined {\n const meta = node.meta;\n const candidates = [\n node.status,\n node.statusCode,\n typeof meta === \"object\" && meta !== null\n ? (meta as Record<string, unknown>).status\n : undefined,\n ];\n for (const candidate of candidates) {\n if (\n typeof candidate === \"number\" &&\n Number.isInteger(candidate) &&\n candidate >= 400 &&\n candidate <= 599\n ) {\n return candidate;\n }\n }\n return undefined;\n}\n\n/**\n * Classify an error from its typed HTTP error status (`status`, `statusCode`,\n * or `meta.status`, 4xx/5xx only) and `AUTH_UNAUTHORIZED` code, following the\n * `cause` chain outer-first (at most {@link MAX_CAUSE_DEPTH} links, cycles\n * stop the walk) so wrappers that rethrow with `cause` keep the verdict. The\n * outermost error status decides, so an outer 5xx keeps its retry budget even\n * over an inner 401. Returns `undefined` for untyped errors; only then may\n * callers fall back to message heuristics.\n */\nexport function authorizationVerdictOf(\n error: unknown,\n): AuthorizationVerdict | undefined {\n const seen = new Set<unknown>();\n let sawUnauthorizedCode = false;\n let node: unknown = error;\n for (\n let depth = 0;\n depth < MAX_CAUSE_DEPTH && typeof node === \"object\" && node !== null && !seen.has(node);\n depth += 1\n ) {\n seen.add(node);\n const record = node as Record<string, unknown>;\n const status = errorStatusOf(record);\n if (status === 401) return \"unauthenticated\";\n if (status === 403) return \"forbidden\";\n if (status !== undefined) return \"other\";\n if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;\n node = record.cause;\n }\n return sawUnauthorizedCode ? \"forbidden\" : undefined;\n}\n\n/**\n * Create a service error for storage quota exceeded (402 Payment Required).\n */\nexport function storageQuotaExceededError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,\n message,\n service,\n meta,\n };\n}\n\n/**\n * Create a service error for storage limit reached (413 Payload Too Large).\n */\nexport function storageLimitReachedError(\n service: string,\n message: string,\n meta?: Record<string, unknown>\n): ServiceError {\n return {\n code: ErrorCodes.STORAGE_LIMIT_REACHED,\n message,\n service,\n meta,\n };\n}\n\n/** Shown when a write is rejected because the owner's storage is full. */\nexport const STORAGE_FULL_MESSAGE =\n \"TinyCloud storage is full, so this change was not saved. Reading still works. Free up space or upgrade your plan to save again.\";\n\n/** Shown when a write is larger than the storage the owner has left. */\nexport const STORAGE_WRITE_TOO_LARGE_MESSAGE =\n \"This change is larger than the TinyCloud storage you have left, so it was not saved. Reading still works. Free up space or upgrade your plan to save it.\";\n\n/**\n * Parse the byte counts from a node storage rejection\n * (\"... Used: 155744 bytes, Limit: 0 bytes\"). The limit is the share of the\n * owner's account-wide budget left for this space, so it is 0 when the\n * owner's other spaces already use the whole budget.\n */\nexport function parseStorageQuotaBytes(\n responseText: string\n): { usedBytes: number; limitBytes: number } | undefined {\n const match = responseText.match(/Used:\\s*(\\d+)\\s*bytes,\\s*Limit:\\s*(\\d+)\\s*bytes/i);\n if (!match) return undefined;\n return {\n usedBytes: parseInt(match[1], 10),\n limitBytes: parseInt(match[2], 10),\n };\n}\n\n/**\n * Build the error for a write the node rejected for storage: 402 when storage\n * is already full, 413 when this write is larger than what is left. KV, SQL\n * and DuckDB all report it with the same codes and message so an app can\n * detect \"storage full\" in one place with {@link isStorageFullError}.\n */\nexport function storageRejectionError(\n service: string,\n status: 402 | 413,\n meta: Record<string, unknown>,\n responseText: string\n): ServiceError {\n const quotaMeta = { ...meta, ...parseStorageQuotaBytes(responseText) };\n return status === 402\n ? storageQuotaExceededError(service, STORAGE_FULL_MESSAGE, quotaMeta)\n : storageLimitReachedError(service, STORAGE_WRITE_TOO_LARGE_MESSAGE, quotaMeta);\n}\n\n/**\n * True when a write failed for storage: the owner's TinyCloud storage is full\n * (`STORAGE_QUOTA_EXCEEDED`), or this write is larger than what is left\n * (`STORAGE_LIMIT_REACHED`). Reads keep working in both cases, so apps should\n * stay usable read-only and point the user at freeing up space or upgrading.\n */\nexport function isStorageFullError(error: { code?: string } | null | undefined): boolean {\n return (\n error?.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED ||\n error?.code === ErrorCodes.STORAGE_LIMIT_REACHED\n );\n}\n\n/**\n * Wrap an unknown error in a ServiceError.\n */\nexport function wrapError(\n service: string,\n error: unknown,\n defaultCode: string = ErrorCodes.NETWORK_ERROR\n): ServiceError {\n if (error instanceof Error) {\n // Check for abort errors\n if (error.name === \"AbortError\") {\n return abortedError(service);\n }\n\n // Check for timeout errors (varies by platform)\n if (\n error.name === \"TimeoutError\" ||\n error.message.toLowerCase().includes(\"timeout\")\n ) {\n return timeoutError(service);\n }\n\n return {\n code: defaultCode,\n message: error.message,\n service,\n cause: error,\n };\n }\n\n return {\n code: defaultCode,\n message: String(error),\n service,\n };\n}\n\n/**\n * Create an error Result from a ServiceError.\n */\nexport function errorResult(error: ServiceError) {\n return err(error);\n}\n","/**\n * BaseService - Abstract base class for all TinyCloud services.\n *\n * Provides common functionality:\n * - Context management\n * - Session lifecycle hooks\n * - Abort signal handling\n * - Telemetry emission\n */\n\nimport {\n IService,\n IServiceContext,\n ServiceSession,\n ServiceError,\n TelemetryEvents,\n err,\n Result,\n} from \"../types\";\nimport { authRequiredError, wrapError } from \"../errors\";\n\n/**\n * Abstract base class for TinyCloud services.\n *\n * Services extend this class to get common functionality like\n * context management, session lifecycle, and abort handling.\n *\n * @example\n * ```typescript\n * class MyService extends BaseService implements IMyService {\n * static readonly serviceName = 'myservice';\n *\n * constructor(config: MyServiceConfig = {}) {\n * super();\n * this._config = config;\n * }\n *\n * async doSomething(): Promise<Result<Data>> {\n * if (!this.requireAuth()) {\n * return err(authRequiredError('myservice'));\n * }\n * // ... implementation\n * }\n * }\n * ```\n */\nexport abstract class BaseService implements IService {\n /**\n * Service identifier used for registration.\n * Must be overridden by subclasses.\n */\n static readonly serviceName: string;\n\n /**\n * Service context providing access to platform dependencies.\n * Set during initialize().\n */\n protected context!: IServiceContext;\n\n /**\n * Abort controller for this service's operations.\n * Reset on sign-out.\n */\n protected abortController: AbortController = new AbortController();\n\n /**\n * Service-specific configuration.\n */\n protected _config: Record<string, unknown> = {};\n\n /**\n * Get the service configuration.\n */\n get config(): Record<string, unknown> {\n return this._config;\n }\n\n /**\n * Initialize the service with context.\n * Called by the SDK after instantiation.\n *\n * @param context - The service context\n */\n initialize(context: IServiceContext): void {\n this.context = context;\n }\n\n /**\n * Called when session changes (sign-in, sign-out, refresh).\n * Override in subclasses to handle session changes.\n *\n * @param session - The new session, or null if signed out\n */\n onSessionChange(session: ServiceSession | null): void {\n // Override in subclass if needed\n }\n\n /**\n * Called when SDK signs out.\n * Aborts all pending operations.\n */\n onSignOut(): void {\n this.abortController.abort();\n this.abortController = new AbortController();\n }\n\n /**\n * Get the abort signal for this service.\n * Combines the service-level abort with context-level abort.\n */\n protected get abortSignal(): AbortSignal {\n return this.combineSignals();\n }\n\n /**\n * Check if the service is authenticated.\n */\n protected get isAuthenticated(): boolean {\n return this.context?.isAuthenticated ?? false;\n }\n\n /**\n * Get the current session.\n * Throws if not authenticated.\n */\n protected get session(): ServiceSession {\n if (!this.context?.session) {\n throw new Error(\"Not authenticated\");\n }\n return this.context.session;\n }\n\n /**\n * Check authentication and return error result if not authenticated.\n * Use this at the start of methods that require authentication.\n *\n * @returns true if authenticated, false otherwise\n */\n protected requireAuth(): boolean {\n return this.isAuthenticated;\n }\n\n /**\n * Emit a telemetry event.\n *\n * @param event - Event name\n * @param data - Event data\n */\n protected emit(event: string, data: unknown): void {\n this.context?.emit(event, data);\n }\n\n /**\n * Emit a service request event.\n *\n * @param action - The action being performed\n * @param key - Optional key/path being accessed\n */\n protected emitRequest(action: string, key?: string): void {\n const service = this.getServiceName();\n this.emit(TelemetryEvents.SERVICE_REQUEST, {\n service,\n action,\n span: this.spanName(action),\n key,\n timestamp: Date.now(),\n });\n }\n\n /**\n * Emit a service response event.\n *\n * @param action - The action that was performed\n * @param ok - Whether the request was successful\n * @param startTime - Start time for duration calculation\n * @param status - Optional HTTP status code\n */\n protected emitResponse(\n action: string,\n ok: boolean,\n startTime: number,\n status?: number\n ): void {\n const service = this.getServiceName();\n const durationMs = Date.now() - startTime;\n const span = this.spanName(action);\n this.emit(TelemetryEvents.SERVICE_RESPONSE, {\n service,\n action,\n span,\n ok,\n duration: durationMs,\n durationMs,\n status,\n });\n this.emit(TelemetryEvents.SPAN, {\n span,\n service,\n action,\n ok,\n durationMs,\n status,\n });\n }\n\n /**\n * Emit a service error event.\n *\n * @param error - The service error\n */\n protected emitError(error: ServiceError, action?: string): void {\n const span = action ? this.spanName(action) : undefined;\n this.emit(TelemetryEvents.SERVICE_ERROR, {\n service: this.getServiceName(),\n ...(span ? { span } : {}),\n error,\n });\n }\n\n /**\n * Get the service name from the static property.\n * Subclasses must define static serviceName.\n */\n protected getServiceName(): string {\n return (this.constructor as typeof BaseService).serviceName;\n }\n\n /**\n * Stable span name used by SDK telemetry sinks.\n */\n protected spanName(action: string): string {\n return `sdk.${this.getServiceName()}.${action}`;\n }\n\n /**\n * Create a combined abort signal from multiple sources.\n *\n * @param signals - Additional abort signals to combine\n * @returns A combined abort signal\n */\n protected combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const controller = new AbortController();\n const allSignals = [\n this.abortController.signal,\n this.context?.abortSignal,\n ...signals.filter(Boolean),\n ].filter(Boolean) as AbortSignal[];\n\n for (const signal of allSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n\n return controller.signal;\n }\n\n /**\n * Wrap an operation with error handling and telemetry.\n *\n * @param action - The action name for telemetry\n * @param key - Optional key for telemetry\n * @param operation - The operation to execute\n * @returns Result of the operation\n */\n protected async withTelemetry<T>(\n action: string,\n key: string | undefined,\n operation: () => Promise<Result<T>>\n ): Promise<Result<T>> {\n const startTime = Date.now();\n this.emitRequest(action, key);\n\n try {\n const result = await operation();\n\n if (result.ok) {\n this.emitResponse(action, true, startTime);\n } else {\n this.emitResponse(action, false, startTime);\n this.emitError(result.error, action);\n }\n\n return result;\n } catch (error) {\n const serviceError = wrapError(this.getServiceName(), error);\n this.emitResponse(action, false, startTime);\n this.emitError(serviceError, action);\n return err(serviceError);\n }\n }\n}\n","/**\n * PrefixedKVService - A prefix-scoped view of KVService.\n *\n * Provides key-value operations scoped to a specific prefix.\n * All operations automatically prefix keys, enabling app data isolation\n * within a shared space.\n *\n * @example\n * ```typescript\n * const space = sdk.space('default');\n *\n * // Create prefix-scoped views\n * const myApp = space.kv.withPrefix('/app.myapp.com');\n * const sharedPhotos = space.kv.withPrefix('/photos');\n *\n * // Operations are automatically prefixed\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Actually writes to: /app.myapp.com/settings.json\n *\n * await myApp.get('settings.json');\n * // -> Actually reads from: /app.myapp.com/settings.json\n *\n * await sharedPhotos.list();\n * // -> Lists: /photos/*\n *\n * // Nested prefixes\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\n\nimport { Result, ok } from \"../types\";\nimport {\n KVGetOptions,\n KVPutOptions,\n KVBatchPutItem,\n KVBatchPutOptions,\n KVBatchPutResponse,\n KVBatchReadResponse,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVCreateSignedReadUrlOptions,\n KVResponse,\n KVListResponse,\n KVSignedReadUrlResponse,\n} from \"./types\";\n\n/**\n * Interface for prefixed KV operations.\n *\n * Provides the same operations as IKVService but scoped to a prefix.\n * Supports nested prefixes via withPrefix().\n */\nexport interface IPrefixedKVService {\n /**\n * The current prefix for this scoped view.\n */\n readonly prefix: string;\n\n /**\n * Get a value by key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to retrieve (will be prefixed)\n * @param options - Optional get configuration\n * @returns Result with the stored value and headers\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.get('settings.json');\n * // -> Reads from: /app.myapp.com/settings.json\n * ```\n */\n get<T = unknown>(\n key: string,\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVResponse<T>>>;\n\n batchGet<T = unknown>(\n keys: string[],\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<T>>>;\n\n /**\n * Store a value at a key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to store under (will be prefixed)\n * @param value - The value to store\n * @param options - Optional put configuration\n * @returns Result indicating success/failure\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Stores at: /app.myapp.com/settings.json\n * ```\n */\n put(\n key: string,\n value: unknown,\n options?: Omit<KVPutOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>>;\n\n /**\n * Store multiple values within this prefix in one TinyCloud KV invocation.\n */\n batchPut(\n items: KVBatchPutItem[],\n options?: Omit<KVBatchPutOptions, 'prefix'>\n ): Promise<Result<KVBatchPutResponse>>;\n\n /**\n * List keys within this prefix.\n *\n * Returns keys that match the prefix, with keys returned relative\n * to the prefix when removePrefix is true (default for prefixed service).\n *\n * @param options - Optional list configuration\n * @returns Result with array of matching keys\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.list();\n * // -> Lists keys under: /app.myapp.com/*\n * // Returns: ['settings.json', 'data/user.json', ...]\n * ```\n */\n list(options?: Omit<KVListOptions, 'prefix'>): Promise<Result<KVListResponse>>;\n\n /**\n * Delete a key.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to delete (will be prefixed)\n * @param options - Optional delete configuration\n * @returns Result indicating success/failure\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * await myApp.delete('old-settings.json');\n * // -> Deletes: /app.myapp.com/old-settings.json\n * ```\n */\n delete(\n key: string,\n options?: Omit<KVDeleteOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>>;\n\n /**\n * Get metadata for a key without retrieving the value.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to check (will be prefixed)\n * @param options - Optional head configuration\n * @returns Result with headers only\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const result = await myApp.head('large-file.bin');\n * // -> Gets metadata for: /app.myapp.com/large-file.bin\n * ```\n */\n head(key: string, options?: Omit<KVHeadOptions, 'prefix'>): Promise<Result<KVResponse<void>>>;\n\n batchHead(\n keys: string[],\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<void>>>;\n\n /**\n * Create a short-lived signed URL for reading a KV object.\n *\n * The key is automatically prefixed with this service's prefix.\n *\n * @param key - The key to expose via a signed read URL (will be prefixed)\n * @param options - Optional signed URL configuration\n * @returns Result with URL and expiry metadata\n */\n createSignedReadUrl(\n key: string,\n options?: Omit<KVCreateSignedReadUrlOptions, 'prefix'>\n ): Promise<Result<KVSignedReadUrlResponse>>;\n\n /**\n * Create a nested prefix-scoped view.\n *\n * The subPrefix is appended to the current prefix.\n *\n * @param subPrefix - The sub-prefix to append\n * @returns A new PrefixedKVService with the combined prefix\n *\n * @example\n * ```typescript\n * const myApp = kv.withPrefix('/app.myapp.com');\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json');\n * // -> Reads from: /app.myapp.com/settings/theme.json\n * ```\n */\n withPrefix(subPrefix: string): IPrefixedKVService;\n}\n\n/**\n * Interface for a KV service that supports prefix delegation.\n *\n * This is the subset of IKVService methods needed by PrefixedKVService.\n */\ninterface IKVServiceLike {\n get<T = unknown>(\n key: string,\n options?: KVGetOptions\n ): Promise<Result<KVResponse<T>>>;\n\n batchGet<T = unknown>(\n keys: string[],\n options?: KVGetOptions\n ): Promise<Result<KVBatchReadResponse<T>>>;\n\n put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>>;\n\n batchPut(\n items: KVBatchPutItem[],\n options?: KVBatchPutOptions\n ): Promise<Result<KVBatchPutResponse>>;\n\n list(options?: KVListOptions): Promise<Result<KVListResponse>>;\n\n delete(key: string, options?: KVDeleteOptions): Promise<Result<KVResponse<void>>>;\n\n head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;\n\n batchHead(\n keys: string[],\n options?: KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<void>>>;\n\n createSignedReadUrl(\n key: string,\n options?: KVCreateSignedReadUrlOptions\n ): Promise<Result<KVSignedReadUrlResponse>>;\n}\n\n/**\n * PrefixedKVService - Implementation of prefix-scoped KV operations.\n *\n * This class wraps a KVService (or another PrefixedKVService) and\n * automatically prefixes all key operations with the configured prefix.\n *\n * ## Prefix Convention\n *\n * | Pattern | Use Case | Example |\n * | -- | -- | -- |\n * | `/app.{domain}/` | App-private data | `/app.photos.xyz/settings.json` |\n * | `/{type}/` | Shared data type | `/photos/vacation.jpg` |\n * | `/.{name}/` | Hidden/system data | `/.cache/thumbnails/` |\n * | `/public/` | Explicitly shareable | `/public/profile.json` |\n *\n * @example\n * ```typescript\n * // Create from KVService\n * const prefixed = new PrefixedKVService(kvService, '/app.myapp.com');\n *\n * // Or use the withPrefix factory method on KVService\n * const prefixed = kvService.withPrefix('/app.myapp.com');\n *\n * // All operations are automatically prefixed\n * await prefixed.put('settings.json', { theme: 'dark' });\n * await prefixed.get('settings.json');\n *\n * // Nested prefixes\n * const nested = prefixed.withPrefix('/settings');\n * await nested.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\nexport class PrefixedKVService implements IPrefixedKVService {\n /**\n * The underlying KV service.\n */\n private readonly _kv: IKVServiceLike;\n\n /**\n * The prefix for this scoped view.\n */\n private readonly _prefix: string;\n\n /**\n * Create a new PrefixedKVService.\n *\n * @param kv - The underlying KV service to delegate to\n * @param prefix - The prefix to apply to all operations\n */\n constructor(kv: IKVServiceLike, prefix: string) {\n this._kv = kv;\n // Normalize prefix: ensure it doesn't end with slash\n this._prefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;\n }\n\n /**\n * The current prefix for this scoped view.\n */\n get prefix(): string {\n return this._prefix;\n }\n\n /**\n * Compute the full key path by combining prefix and key.\n *\n * @param key - The key to prefix\n * @returns The full path including prefix\n */\n private getFullKey(key: string): string {\n // Handle keys that start with slash\n const normalizedKey = key.startsWith('/') ? key : `/${key}`;\n return `${this._prefix}${normalizedKey}`;\n }\n\n /**\n * Get a value by key.\n */\n async get<T = unknown>(\n key: string,\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVResponse<T>>> {\n const fullKey = this.getFullKey(key);\n // Use empty prefix override to use the full key as-is\n return this._kv.get<T>(fullKey, { ...options, prefix: '' });\n }\n\n async batchGet<T = unknown>(\n keys: string[],\n options?: Omit<KVGetOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<T>>> {\n const response = await this._kv.batchGet<T>(\n keys.map((key) => this.getFullKey(key)),\n { ...options, prefix: '' }\n );\n if (!response.ok) return response;\n return ok({\n ...response.data,\n results: response.data.results.map((item, index) => ({\n ...item,\n key: keys[index]!,\n })),\n });\n }\n\n /**\n * Store a value at a key.\n */\n async put(\n key: string,\n value: unknown,\n options?: Omit<KVPutOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>> {\n const fullKey = this.getFullKey(key);\n return this._kv.put(fullKey, value, { ...options, prefix: '' });\n }\n\n /**\n * Store multiple values within this prefix in one TinyCloud KV invocation.\n */\n async batchPut(\n items: KVBatchPutItem[],\n options?: Omit<KVBatchPutOptions, 'prefix'>\n ): Promise<Result<KVBatchPutResponse>> {\n return this._kv.batchPut(\n items.map((item) => ({\n ...item,\n key: this.getFullKey(item.key),\n })),\n { ...options, prefix: '' }\n );\n }\n\n /**\n * List keys within this prefix.\n */\n async list(options?: Omit<KVListOptions, 'prefix'>): Promise<Result<KVListResponse>> {\n // List uses the prefix directly, and by default removes the prefix from results\n const removePrefix = options?.removePrefix ?? true;\n return this._kv.list({\n ...options,\n prefix: this._prefix,\n removePrefix,\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(\n key: string,\n options?: Omit<KVDeleteOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>> {\n const fullKey = this.getFullKey(key);\n return this._kv.delete(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Get metadata for a key without retrieving the value.\n */\n async head(\n key: string,\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVResponse<void>>> {\n const fullKey = this.getFullKey(key);\n return this._kv.head(fullKey, { ...options, prefix: '' });\n }\n\n async batchHead(\n keys: string[],\n options?: Omit<KVHeadOptions, 'prefix'>\n ): Promise<Result<KVBatchReadResponse<void>>> {\n const response = await this._kv.batchHead(\n keys.map((key) => this.getFullKey(key)),\n { ...options, prefix: '' }\n );\n if (!response.ok) return response;\n return ok({\n ...response.data,\n results: response.data.results.map((item, index) => ({\n ...item,\n key: keys[index]!,\n })),\n });\n }\n\n /**\n * Create a short-lived signed URL for reading a KV object.\n */\n async createSignedReadUrl(\n key: string,\n options?: Omit<KVCreateSignedReadUrlOptions, 'prefix'>\n ): Promise<Result<KVSignedReadUrlResponse>> {\n const fullKey = this.getFullKey(key);\n return this._kv.createSignedReadUrl(fullKey, { ...options, prefix: '' });\n }\n\n /**\n * Create a nested prefix-scoped view.\n */\n withPrefix(subPrefix: string): IPrefixedKVService {\n // Normalize subPrefix\n const normalizedSubPrefix = subPrefix.startsWith('/')\n ? subPrefix\n : `/${subPrefix}`;\n const combinedPrefix = `${this._prefix}${normalizedSubPrefix}`;\n return new PrefixedKVService(this._kv, combinedPrefix);\n }\n}\n","/**\n * KV Service Types\n *\n * Type definitions for the KV (Key-Value) service operations.\n */\n\nimport type { Result } from \"../types\";\n\n/**\n * Configuration for KVService.\n */\nexport interface KVServiceConfig {\n /**\n * Default prefix for all keys.\n * Useful for namespacing data within a space.\n *\n * @example\n * ```typescript\n * const kv = new KVService({ prefix: 'myapp/settings' });\n * await kv.put('theme', 'dark'); // Stores at 'myapp/settings/theme'\n * ```\n */\n prefix?: string;\n\n /**\n * Default timeout in milliseconds for KV operations.\n * Overrides the context-level timeout if set.\n */\n timeout?: number;\n\n /** Allow additional config properties */\n [key: string]: unknown;\n}\n\n/**\n * Options for KV get operations.\n */\nexport interface KVGetOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Return raw response instead of parsed JSON.\n * When true, data will be the raw response text.\n */\n raw?: boolean;\n\n /**\n * Return the raw response bytes as a Uint8Array instead of parsed/text data.\n * Use this to read back binary values (e.g. images) byte-identically. Takes\n * precedence over {@link raw}.\n */\n binary?: boolean;\n\n /**\n * Ask the node to reject the response when the stored value exceeds this\n * number of bytes. This bounds downloads before the response body is read.\n */\n maxResponseBytes?: number;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV put operations.\n */\nexport interface KVPutOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Content type for the value.\n * Defaults to 'application/json' for objects.\n */\n contentType?: string;\n\n /**\n * Custom metadata headers to store with the value.\n */\n metadata?: Record<string, string>;\n\n /** Only write when the current object has this strong ETag. */\n ifMatch?: string;\n\n /** Only write when the key does not exist. */\n ifNoneMatch?: \"*\";\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * One entry in a KV batch put request.\n */\nexport interface KVBatchPutItem {\n /**\n * The key to store under.\n */\n key: string;\n\n /**\n * The value to store.\n *\n * Objects are JSON stringified. Strings are stored as text. Binary values\n * should be supplied as Blob, ArrayBuffer, or Uint8Array.\n */\n value: unknown;\n\n /**\n * Content type for this item. Defaults to application/json for objects and\n * application/octet-stream for binary values.\n */\n contentType?: string;\n}\n\n/**\n * Options for KV batch put operations.\n */\nexport interface KVBatchPutOptions {\n /**\n * Override the default prefix for all entries in this batch.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Response from KV batch put operations.\n */\nexport interface KVBatchPutResponse {\n /**\n * Keys successfully written by the batch.\n */\n written: string[];\n\n /**\n * Number of written keys.\n */\n count: number;\n}\n\n/**\n * Response from a KV batch read. Transport or authorization failures are\n * returned by the outer Result; each requested key has its own Result so a\n * missing key does not discard successful siblings.\n */\nexport interface KVBatchReadResponse<T = unknown> {\n results: Array<{\n key: string;\n result: Result<KVResponse<T>>;\n }>;\n count: number;\n}\n\n/**\n * Options for KV list operations.\n */\nexport interface KVListOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Additional path to append to the prefix.\n */\n path?: string;\n\n /**\n * Whether to remove the prefix from returned keys.\n * When true, keys are returned relative to the prefix.\n */\n removePrefix?: boolean;\n\n /**\n * Return raw response instead of parsed JSON.\n */\n raw?: boolean;\n\n /** Maximum number of keys the node may return. */\n limit?: number;\n\n /** Opaque scope-bound cursor returned by a previous bounded list. */\n cursor?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV delete operations.\n */\nexport interface KVDeleteOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /** Only delete when the current object has this strong ETag. */\n ifMatch?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for KV head (metadata) operations.\n */\nexport interface KVHeadOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Default lifetime for signed KV read URLs when a caller omits expiresInSeconds.\n * SDK duration defaults are stored in milliseconds; createSignedReadUrl converts\n * this to the node endpoint's ttl_seconds field.\n *\n * Keep this in sync with EXPIRY.SIGNED_READ_URL_MS in @tinycloud/sdk-core.\n * sdk-services cannot import sdk-core because sdk-core depends on sdk-services.\n */\nexport const DEFAULT_SIGNED_READ_URL_EXPIRY_MS = 5 * 60 * 1000;\n\n/**\n * Options for creating a signed KV read URL.\n */\nexport interface KVCreateSignedReadUrlOptions {\n /**\n * Override the default prefix for this operation.\n */\n prefix?: string;\n\n /**\n * Requested URL lifetime in seconds.\n * Defaults to {@link DEFAULT_SIGNED_READ_URL_EXPIRY_MS} converted to seconds.\n * The node may cap this by its configured maximum, the invocation expiry,\n * or the parent delegation expiry.\n */\n expiresInSeconds?: number;\n\n /**\n * Optional blake3 content hash to bind the signed URL to a specific object.\n */\n contentHash?: string;\n\n /**\n * Optional ETag to bind the signed URL to a specific object version.\n */\n etag?: string;\n\n /**\n * Custom timeout for this operation in milliseconds.\n */\n timeout?: number;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Response headers from KV operations.\n */\nexport interface KVResponseHeaders {\n /**\n * ETag for conditional requests.\n */\n etag?: string;\n\n /**\n * Content type of the stored value.\n */\n contentType?: string;\n\n /**\n * Last modification timestamp.\n */\n lastModified?: string;\n\n /**\n * Content length in bytes.\n */\n contentLength?: number;\n\n /**\n * Get a header value by name.\n * @param name - Header name (case-insensitive)\n */\n get(name: string): string | null;\n}\n\n/**\n * Response from KV get/put operations.\n *\n * @template T - Type of the data payload\n */\nexport interface KVResponse<T = unknown> {\n /**\n * The data payload.\n * For get: the stored value.\n * For put: undefined.\n */\n data: T;\n\n /**\n * Response headers with metadata.\n */\n headers: KVResponseHeaders;\n}\n\n/**\n * Response from KV list operations.\n */\nexport interface KVListResponse {\n /**\n * Array of keys matching the list criteria.\n */\n keys: string[];\n\n /** True when more matching keys exist than the requested limit. */\n truncated?: boolean;\n\n /** Opaque cursor for the next page, when the node returned one. */\n nextCursor?: string;\n}\n\n/**\n * Response from signed KV read URL creation.\n */\nexport interface KVSignedReadUrlResponse {\n /**\n * Absolute URL suitable for passing to external readers.\n */\n url: string;\n\n /**\n * Opaque URL returned by tinycloud-node, usually relative to the node host.\n */\n relativeUrl: string;\n\n /**\n * Opaque signed KV ticket identifier.\n */\n ticketId: string;\n\n /**\n * Expiry timestamp as returned by tinycloud-node.\n */\n expiresAt: string;\n}\n\n/**\n * KV service action types.\n */\nexport const KVAction = {\n GET: \"tinycloud.kv/get\",\n PUT: \"tinycloud.kv/put\",\n LIST: \"tinycloud.kv/list\",\n DELETE: \"tinycloud.kv/del\",\n HEAD: \"tinycloud.kv/metadata\",\n} as const;\n\nexport type KVActionType = (typeof KVAction)[keyof typeof KVAction];\n","/**\n * KVService - Key-Value storage service implementation.\n *\n * Platform-agnostic KV service that works with both web-sdk and node-sdk.\n * Uses dependency injection via IServiceContext for platform dependencies.\n */\n\nimport { BaseService } from \"../base/BaseService\";\nimport {\n Result,\n ok,\n err,\n ErrorCodes,\n serviceError,\n FetchResponse,\n ServiceHeaders,\n} from \"../types\";\nimport {\n authRequiredError,\n wrapError,\n storageRejectionError,\n parseStorageQuotaBytes,\n parseAuthError,\n parsePermissionHintFromErrorText,\n authUnauthorizedError,\n} from \"../errors\";\nimport { IKVService } from \"./IKVService\";\nimport { PrefixedKVService, IPrefixedKVService } from \"./PrefixedKVService\";\nimport {\n DEFAULT_SIGNED_READ_URL_EXPIRY_MS,\n KVServiceConfig,\n KVGetOptions,\n KVPutOptions,\n KVBatchPutItem,\n KVBatchPutOptions,\n KVBatchPutResponse,\n KVBatchReadResponse,\n KVListOptions,\n KVDeleteOptions,\n KVHeadOptions,\n KVCreateSignedReadUrlOptions,\n KVResponse,\n KVListResponse,\n KVResponseHeaders,\n KVSignedReadUrlResponse,\n KVAction,\n} from \"./types\";\n\ninterface SignedKvUrlNodeResponse {\n url: string;\n ticketId: string;\n expiresAt: string;\n}\n\ninterface KvBatchNodeItem {\n key: string;\n ok: boolean;\n dataBase64?: string;\n headers?: Record<string, string>;\n error?: { code: string; message: string };\n}\n\ninterface KvBatchNodeResponse {\n results: KvBatchNodeItem[];\n}\n\nconst MAX_KV_BATCH_READ_ITEMS = 100;\n\nfunction encodeKvBatchPartName(path: string): string {\n return encodeURIComponent(path).replace(/[!'()*]/g, (char) =>\n `%${char.charCodeAt(0).toString(16).toUpperCase()}`\n );\n}\n\n/**\n * KV service implementation.\n *\n * Provides key-value storage operations using TinyCloud's KV API.\n * Uses the Result type pattern for explicit error handling.\n *\n * @example\n * ```typescript\n * // Register with SDK\n * const sdk = new TinyCloud({\n * services: { kv: KVService },\n * serviceConfigs: { kv: { prefix: 'myapp' } },\n * });\n *\n * // Use the service\n * const result = await sdk.kv.get('settings');\n * if (result.ok) {\n * console.log(result.data.data);\n * }\n * ```\n */\nexport class KVService extends BaseService implements IKVService {\n /**\n * Service identifier for registration.\n */\n static readonly serviceName = \"kv\";\n\n /**\n * Service configuration.\n */\n declare protected _config: KVServiceConfig;\n\n /**\n * Create a new KVService instance.\n *\n * @param config - Service configuration\n */\n constructor(config: KVServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n /**\n * Get the service configuration.\n */\n get config(): KVServiceConfig {\n return this._config;\n }\n\n private handleQuotaErrorResponse(\n response: FetchResponse,\n errorText: string,\n key: string\n ): Result<never> | undefined {\n if (response.status !== 402 && response.status !== 413) {\n return undefined;\n }\n // A 413 is about storage only when the node says so. A proxy's own\n // request-size limit (Cloudflare's HTML 413) is not.\n if (response.status === 413 && !parseStorageQuotaBytes(errorText)) {\n return undefined;\n }\n return err(\n storageRejectionError(\n \"kv\",\n response.status,\n { status: response.status, statusText: response.statusText, key },\n errorText\n )\n );\n }\n\n /**\n * Classify a KV 404 by reading the response body once.\n *\n * The server returns 404 both for a genuinely missing key AND for an\n * un-hosted space (body \"Space not found\"). Previously get/head/delete\n * collapsed every 404 to KV_NOT_FOUND before reading the body, so an\n * un-hosted-space read was indistinguishable from a missing key. We now\n * preserve status + the \"Space not found\" body for the un-hosted case (so the\n * CLI/SDK can normalize it to SPACE_NOT_HOSTED, matching put/list/sql), and\n * fall through to KV_NOT_FOUND for a real missing key.\n */\n private async classifyNotFound(\n response: FetchResponse,\n key: string\n ): Promise<Result<never>> {\n const errorText = await response.text();\n if (/space not found/i.test(errorText)) {\n return err(\n serviceError(\n ErrorCodes.KV_NOT_FOUND,\n `KV ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, \"kv\"));\n }\n\n /**\n * Get the full path with optional prefix.\n *\n * @param key - The key\n * @param prefixOverride - Optional prefix override\n * @returns The full path\n */\n private getFullPath(key: string, prefixOverride?: string): string {\n const prefix = prefixOverride ?? this._config.prefix ?? \"\";\n // A delegated share may scope this service to one exact key. In that\n // case `get(\"\")` means that key itself, not a synthetic child with a\n // trailing slash. This also keeps the service's prefix semantics useful\n // for exact (rather than prefix) capabilities.\n return prefix ? (key === \"\" ? prefix : `${prefix}/${key}`) : key;\n }\n\n /**\n * Get the host URL.\n */\n private get host(): string {\n return this.context.hosts[0];\n }\n\n private withJsonContentType(headers: ServiceHeaders): ServiceHeaders {\n if (Array.isArray(headers)) {\n return [...headers, [\"content-type\", \"application/json\"]];\n }\n\n return {\n ...headers,\n \"content-type\": \"application/json\",\n };\n }\n\n /**\n * Execute an invoke operation.\n *\n * @param path - Resource path\n * @param action - KV action\n * @param body - Optional request body\n * @param signal - Optional abort signal\n * @returns Fetch response\n */\n private async invokeOperation(\n path: string,\n action: string,\n body?: Blob | string,\n signal?: AbortSignal,\n extraHeaders?: Readonly<Record<string, string>>\n ): Promise<FetchResponse> {\n const session = this.context.session!;\n const headers = this.context.invoke(\n session,\n \"kv\",\n path,\n action\n );\n\n const requestHeaders: ServiceHeaders = Array.isArray(headers)\n ? [...headers, ...Object.entries(extraHeaders ?? {})]\n : { ...headers, ...extraHeaders };\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: requestHeaders,\n body,\n signal: this.combineSignals(signal),\n });\n }\n\n /**\n * Serialize a single put value into a fetch body.\n *\n * Binary values (Blob/ArrayBuffer/typed-array, incl. Node Buffer) are sent as\n * raw bytes (as a Blob) so they round-trip byte-identically — without this a\n * Buffer would be JSON.stringify'd into `{\"type\":\"Buffer\",\"data\":[...]}`.\n * Strings are returned unchanged (preserving prior behavior); other values are\n * JSON-encoded. `contentType` overrides the inferred type for binary values.\n */\n private serializePutValue(\n value: unknown,\n contentType?: string\n ): Blob | string {\n if (value instanceof Blob) {\n if (!contentType || value.type === contentType) {\n return value;\n }\n return new Blob([value], { type: contentType });\n }\n\n if (value instanceof ArrayBuffer) {\n return new Blob([value], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (ArrayBuffer.isView(value)) {\n // Pass a ranged view (honors byteOffset/byteLength so a Node Buffer backed\n // by a shared pool isn't over-read); Blob snapshots the bytes at\n // construction, so no defensive copy is needed.\n const view = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n return new Blob([view], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (typeof value === \"string\") {\n return contentType ? new Blob([value], { type: contentType }) : value;\n }\n\n return JSON.stringify(value);\n }\n\n private serializeBatchPutValue(item: KVBatchPutItem): Blob {\n const contentType = item.contentType;\n\n if (item.value instanceof Blob) {\n if (!contentType || item.value.type === contentType) {\n return item.value;\n }\n return new Blob([item.value], { type: contentType });\n }\n\n if (item.value instanceof ArrayBuffer) {\n return new Blob([item.value], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (ArrayBuffer.isView(item.value)) {\n const value = item.value;\n const bytes = new Uint8Array(value.byteLength);\n bytes.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n return new Blob([bytes], {\n type: contentType ?? \"application/octet-stream\",\n });\n }\n\n if (typeof item.value === \"string\") {\n return new Blob([item.value], {\n type: contentType ?? \"text/plain;charset=UTF-8\",\n });\n }\n\n const json = JSON.stringify(item.value);\n if (json === undefined) {\n throw new Error(`Cannot JSON serialize KV batch value for key \"${item.key}\"`);\n }\n\n return new Blob([json], {\n type: contentType ?? \"application/json\",\n });\n }\n\n private normalizeBatchPutResponse(data: unknown): KVBatchPutResponse | undefined {\n if (!data || typeof data !== \"object\") {\n return undefined;\n }\n\n const response = data as Partial<KVBatchPutResponse>;\n if (\n !Array.isArray(response.written) ||\n !response.written.every((key) => typeof key === \"string\") ||\n typeof response.count !== \"number\"\n ) {\n return undefined;\n }\n\n return {\n written: response.written,\n count: response.count,\n };\n }\n\n /**\n * Create KVResponseHeaders from fetch response headers.\n *\n * @param headers - Fetch response headers\n * @returns KVResponseHeaders object\n */\n private createResponseHeaders(headers: {\n get(name: string): string | null;\n }): KVResponseHeaders {\n return {\n etag: headers.get(\"etag\") ?? undefined,\n contentType: headers.get(\"content-type\") ?? undefined,\n lastModified: headers.get(\"last-modified\") ?? undefined,\n contentLength: headers.get(\"content-length\")\n ? parseInt(headers.get(\"content-length\")!, 10)\n : undefined,\n get: (name: string) => headers.get(name),\n };\n }\n\n private createBatchResponseHeaders(\n values: Record<string, string>\n ): KVResponseHeaders {\n const normalized = new Map(\n Object.entries(values).map(([name, value]) => [name.toLowerCase(), value])\n );\n return this.createResponseHeaders({\n get: (name) => normalized.get(name.toLowerCase()) ?? null,\n });\n }\n\n private parseBatchValue<T>(\n dataBase64: string,\n headers: Record<string, string>,\n raw: boolean = false,\n binary: boolean = false\n ): T {\n const encoded = globalThis.atob(dataBase64);\n const bytes = Uint8Array.from(encoded, (character) => character.charCodeAt(0));\n if (binary) return bytes as unknown as T;\n\n const text = new TextDecoder().decode(bytes);\n if (raw) return text as unknown as T;\n const contentType = Object.entries(headers).find(\n ([name]) => name.toLowerCase() === \"content-type\"\n )?.[1];\n return (contentType?.includes(\"application/json\")\n ? JSON.parse(text)\n : text) as T;\n }\n\n private normalizeBatchReadResponse(\n data: unknown,\n paths: string[],\n requireData: boolean\n ): KvBatchNodeResponse | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n const response = data as Partial<KvBatchNodeResponse>;\n if (!Array.isArray(response.results) || response.results.length !== paths.length) {\n return undefined;\n }\n\n for (let index = 0; index < response.results.length; index++) {\n const item = response.results[index];\n if (\n !item ||\n typeof item !== \"object\" ||\n item.key !== paths[index] ||\n typeof item.ok !== \"boolean\"\n ) {\n return undefined;\n }\n if (\n item.ok &&\n (!item.headers ||\n typeof item.headers !== \"object\" ||\n Object.values(item.headers).some((value) => typeof value !== \"string\") ||\n (requireData && typeof item.dataBase64 !== \"string\"))\n ) {\n return undefined;\n }\n if (\n !item.ok &&\n (!item.error ||\n typeof item.error.code !== \"string\" ||\n typeof item.error.message !== \"string\")\n ) {\n return undefined;\n }\n }\n return response as KvBatchNodeResponse;\n }\n\n private async batchRead<T>(\n keys: string[],\n action: typeof KVAction.GET | typeof KVAction.HEAD,\n options?: KVGetOptions | KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<T>>> {\n if (!this.requireAuth()) return err(authRequiredError(\"kv\"));\n if (keys.length === 0) return ok({ results: [], count: 0 });\n if (keys.length > MAX_KV_BATCH_READ_ITEMS) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n `KV batch reads accept at most ${MAX_KV_BATCH_READ_ITEMS} keys`,\n \"kv\"\n ));\n }\n if (keys.length === 1) {\n const key = keys[0]!;\n const result = action === KVAction.GET\n ? await this.get<T>(key, options as KVGetOptions)\n : await this.head(key, options as KVHeadOptions);\n return ok({\n results: [{ key, result: result as Result<KVResponse<T>> }],\n count: 1,\n });\n }\n if (!this.context.invokeAny) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batch reads require SDK runtime support for multi-resource invocations\",\n \"kv\"\n ));\n }\n\n const getOptions = action === KVAction.GET ? options as KVGetOptions : undefined;\n if (\n getOptions?.maxResponseBytes !== undefined &&\n (!Number.isSafeInteger(getOptions.maxResponseBytes) ||\n getOptions.maxResponseBytes <= 0)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV maxResponseBytes must be a positive safe integer\",\n \"kv\"\n ));\n }\n\n const paths = keys.map((key) => this.getFullPath(key, options?.prefix));\n if (new Set(paths).size !== paths.length) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batch read received duplicate keys after prefix resolution\",\n \"kv\"\n ));\n }\n\n try {\n const session = this.context.session!;\n const invocationHeaders = this.context.invokeAny(\n session,\n paths.map((path) => ({\n spaceId: session.spaceId,\n service: \"kv\",\n path,\n action,\n }))\n );\n const limitHeaders: Record<string, string> = {};\n if (getOptions?.maxResponseBytes !== undefined) {\n limitHeaders[\"x-tinycloud-max-response-bytes\"] = String(\n getOptions.maxResponseBytes\n );\n }\n const headers: ServiceHeaders = Array.isArray(invocationHeaders)\n ? [...invocationHeaders, ...Object.entries(limitHeaders)]\n : { ...invocationHeaders, ...limitHeaders };\n const response = await this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers,\n signal: this.combineSignals(options?.signal),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to batch read ${keys.length} key(s)`,\n response,\n errorText\n );\n }\n if (response.status === 413) {\n return err(serviceError(\n ErrorCodes.KV_RESPONSE_TOO_LARGE,\n \"A KV batch value exceeds the requested response limit\",\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to batch read ${keys.length} key(s): ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n\n const payload = this.normalizeBatchReadResponse(\n await response.json(),\n paths,\n action === KVAction.GET\n );\n if (!payload) {\n return err(serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV batch read response did not match the requested keys\",\n \"kv\"\n ));\n }\n\n const results = payload.results.map((item, index) => {\n if (!item.ok) {\n return {\n key: keys[index]!,\n result: err(serviceError(item.error!.code, item.error!.message, \"kv\")),\n };\n }\n const headers = item.headers!;\n const data = action === KVAction.HEAD\n ? undefined\n : this.parseBatchValue<T>(\n item.dataBase64!,\n headers,\n getOptions?.raw,\n getOptions?.binary\n );\n return {\n key: keys[index]!,\n result: ok({\n data: data as T,\n headers: this.createBatchResponseHeaders(headers),\n }),\n };\n });\n return ok({ results, count: results.length });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n }\n\n /**\n * Parse response body based on content type.\n *\n * @param response - Fetch response\n * @param raw - Whether to return raw text\n * @returns Parsed data\n */\n private async parseResponse<T>(\n response: FetchResponse,\n raw: boolean = false,\n binary: boolean = false\n ): Promise<T | undefined> {\n if (!response.ok) {\n return undefined;\n }\n\n if (binary) {\n return new Uint8Array(await response.arrayBuffer()) as unknown as T;\n }\n\n if (raw) {\n return (await response.text()) as unknown as T;\n }\n\n const contentType = response.headers.get(\"content-type\");\n if (contentType?.includes(\"application/json\")) {\n return (await response.json()) as T;\n } else if (contentType?.startsWith(\"text/\")) {\n return (await response.text()) as unknown as T;\n }\n\n // No content-type header - try to parse as JSON, fall back to text\n const text = await response.text();\n if (!text) {\n return undefined;\n }\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n }\n\n private async createSignedReadUrlError(\n response: FetchResponse,\n key: string\n ): Promise<Result<never>> {\n let errorText = response.statusText;\n try {\n const text = await response.text();\n if (text) {\n errorText = text;\n }\n } catch {\n // Ignore secondary body read failure.\n }\n\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to create signed read URL for key ${JSON.stringify(key)}`,\n response,\n errorText\n );\n }\n\n const code =\n response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;\n return err(\n serviceError(\n code,\n `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n /**\n * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP\n * status and the server text (`<context>: <status> - <text>`, the same\n * shape as every other KV failure) so callers that rethrow only the message\n * still see the status; `meta.status` carries it for typed callers.\n * Resource/ability hints are parsed from the raw body, never the message.\n */\n private authorizationFailure(\n context: string,\n response: FetchResponse,\n errorText: string,\n extraMeta: Record<string, unknown> = {}\n ): Result<never> {\n const { resource, action } = parseAuthError(errorText);\n const detail = errorText.trim().length > 0\n ? errorText\n : response.statusText || \"authorization failed\";\n return err(authUnauthorizedError(\"kv\", `${context}: ${response.status} - ${detail}`, {\n status: response.status,\n ...(action && { requiredAction: action }),\n ...(resource && { resource }),\n ...extraMeta,\n }));\n }\n\n private normalizeSignedReadUrlResponse(\n data: unknown\n ): KVSignedReadUrlResponse | undefined {\n if (!data || typeof data !== \"object\") {\n return undefined;\n }\n\n const response = data as Partial<SignedKvUrlNodeResponse>;\n if (\n typeof response.url !== \"string\" ||\n typeof response.ticketId !== \"string\" ||\n typeof response.expiresAt !== \"string\"\n ) {\n return undefined;\n }\n\n return {\n url: new URL(response.url, this.host).toString(),\n relativeUrl: response.url,\n ticketId: response.ticketId,\n expiresAt: response.expiresAt,\n };\n }\n\n /**\n * Get a value by key.\n */\n async get<T = unknown>(\n key: string,\n options?: KVGetOptions\n ): Promise<Result<KVResponse<T>>> {\n return this.withTelemetry(\"get\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (\n options?.maxResponseBytes !== undefined &&\n (!Number.isSafeInteger(options.maxResponseBytes) || options.maxResponseBytes <= 0)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV maxResponseBytes must be a positive safe integer\",\n \"kv\"\n ));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.GET,\n undefined,\n options?.signal,\n options?.maxResponseBytes === undefined\n ? undefined\n : { \"x-tinycloud-max-response-bytes\": String(options.maxResponseBytes) }\n );\n\n if (!response.ok) {\n if (response.status === 401 || response.status === 403) {\n const errorText = await response.text();\n const permissionHint = parsePermissionHintFromErrorText(errorText);\n return this.authorizationFailure(\n `Failed to get key ${JSON.stringify(key)}`,\n response,\n errorText,\n permissionHint === undefined ? {} : { permissionHint }\n );\n }\n\n if (response.status === 404) {\n return this.classifyNotFound(response, key);\n }\n\n const errorText = await response.text();\n if (response.status === 413) {\n return err(serviceError(\n ErrorCodes.KV_RESPONSE_TOO_LARGE,\n `KV value at key ${JSON.stringify(key)} exceeds the requested response limit`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n const data = await this.parseResponse<T>(\n response,\n options?.raw,\n options?.binary\n );\n return ok({\n data: data as T,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n async batchGet<T = unknown>(\n keys: string[],\n options?: KVGetOptions\n ): Promise<Result<KVBatchReadResponse<T>>> {\n return this.withTelemetry(\"batchGet\", String(keys.length), () =>\n this.batchRead<T>(keys, KVAction.GET, options)\n );\n }\n\n /**\n * Store a value at a key.\n */\n async put(\n key: string,\n value: unknown,\n options?: KVPutOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"put\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (options?.ifMatch !== undefined && options.ifNoneMatch !== undefined) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV put cannot combine ifMatch and ifNoneMatch\",\n \"kv\"\n ));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n // Serialize the value. Binary values (Blob/ArrayBuffer/typed-array/Buffer)\n // are sent as raw bytes so they round-trip byte-identically; strings are\n // sent as-is; everything else is JSON. Mirrors serializeBatchPutValue.\n const body = this.serializePutValue(value, options?.contentType);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.PUT,\n body,\n options?.signal,\n {\n ...(options?.ifMatch === undefined ? {} : { \"if-match\": options.ifMatch }),\n ...(options?.ifNoneMatch === undefined ? {} : { \"if-none-match\": options.ifNoneMatch }),\n }\n );\n\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to put key ${JSON.stringify(key)}`,\n response,\n await response.text()\n );\n }\n\n if (!response.ok) {\n const errorText = await response.text();\n\n if (response.status === 412) {\n return err(serviceError(\n ErrorCodes.KV_PRECONDITION_FAILED,\n `KV precondition failed for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n\n if (\n response.status === 503 &&\n (options?.ifMatch !== undefined || options?.ifNoneMatch !== undefined)\n ) {\n return err(serviceError(\n ErrorCodes.KV_CONFLICT,\n `Concurrent KV update conflicted for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n\n // Check for storage quota errors (402, 413)\n const quotaError = this.handleQuotaErrorResponse(\n response,\n errorText,\n key\n );\n if (quotaError) {\n return quotaError;\n }\n\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to put key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Store multiple values in one TinyCloud KV invocation.\n */\n async batchPut(\n items: KVBatchPutItem[],\n options?: KVBatchPutOptions\n ): Promise<Result<KVBatchPutResponse>> {\n return this.withTelemetry(\"batchPut\", String(items.length), async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (items.length === 0) {\n return ok({ written: [], count: 0 });\n }\n\n if (!this.context.invokeAny) {\n return err(\n serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV batchPut requires SDK runtime support for multi-resource invocations\",\n \"kv\"\n )\n );\n }\n\n const session = this.context.session!;\n const paths = items.map((item) => this.getFullPath(item.key, options?.prefix));\n const seen = new Set<string>();\n for (const path of paths) {\n if (seen.has(path)) {\n return err(\n serviceError(\n ErrorCodes.INVALID_INPUT,\n `KV batchPut received duplicate key after prefix resolution: ${path}`,\n \"kv\"\n )\n );\n }\n seen.add(path);\n }\n\n // Conservative: `true` means we handed a fully-constructed request to\n // fetch(). It does NOT prove bytes reached the node — DNS/connect\n // failures and synchronous fetch rejections are included. Proving\n // actual dispatch requires lower-level transport instrumentation (out\n // of scope). The over-approximation is intentional and bounded: it can\n // only cause a reconciliation of at most N idempotent, byte-identical\n // overwrites — never a corrupted or differently-timestamped record.\n let requestMayHaveDispatched = false;\n try {\n const body = new FormData();\n for (let index = 0; index < items.length; index++) {\n body.append(\n encodeKvBatchPartName(paths[index]!),\n this.serializeBatchPutValue(items[index]!)\n );\n }\n\n const headers = this.context.invokeAny(\n session,\n paths.map((path) => ({\n spaceId: session.spaceId,\n service: \"kv\",\n path,\n action: KVAction.PUT,\n }))\n );\n\n const url = `${this.host}/invoke`;\n const init = {\n method: \"POST\",\n headers,\n body,\n signal: this.combineSignals(options?.signal),\n };\n\n // Resolve the fetch function to a local BEFORE flipping the flag.\n // `this.context.fetch` is a getter (context.ts:154-156) that can\n // itself throw (assertActive()) — if the flag were set first, that\n // throw would be mislabeled as \"may have dispatched\" even though no\n // request was ever handed to a transport (Sol B2).\n const fetchFn = this.context.fetch;\n requestMayHaveDispatched = true;\n const response = await fetchFn(url, init);\n\n if (!response.ok) {\n let errorText: string;\n try {\n errorText = await response.text();\n } catch (textError) {\n // A response was received — the status is known and\n // authoritative — but its body could not be read. Falling\n // through to the generic catch below would report\n // NETWORK_ERROR + requestMayHaveDispatched: true, which the\n // allow-list treats as ambiguous regardless of status. That\n // would turn a deterministic 4xx (nothing written) into a false\n // ambiguity (Sol B4). Classify by status alone instead; whether\n // this is later reconciled is still governed solely by\n // AMBIGUOUS_WRITE_STATUSES, exactly like the body-readable path.\n //\n // Sol B2 (round 2): preserve the underlying rejection instead of\n // discarding it — a bare `catch {}` here would make a\n // body-read failure silently invisible, which is exactly the\n // debugging trap the no-swallowed-errors rule exists to\n // prevent.\n const cause = textError instanceof Error ? textError : new Error(String(textError));\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to batch put ${items.length} key(s): ${response.status} - <response body could not be read: ${cause.message}>`,\n \"kv\",\n { cause, meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n if (response.status === 401 || response.status === 403) {\n return this.authorizationFailure(\n `Failed to batch put ${items.length} key(s)`,\n response,\n errorText\n );\n }\n\n const quotaError = this.handleQuotaErrorResponse(\n response,\n errorText,\n \"batch\"\n );\n if (quotaError) {\n return quotaError;\n }\n\n return err(\n serviceError(\n ErrorCodes.KV_WRITE_FAILED,\n `Failed to batch put ${items.length} key(s): ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n let rawBody: unknown;\n try {\n rawBody = await response.json();\n } catch (jsonError) {\n // A 2xx response whose body isn't valid JSON is precisely the\n // unconfirmed-2xx case — the node answered success but the write\n // set can't be confirmed. Falling through to the generic catch\n // below would carry only `requestMayHaveDispatched`, omitting\n // `responseReceived`/`status`/`outcome`, so this must be classified\n // here with the identical metadata block used by the two sibling\n // unconfirmed branches (Sol B3).\n //\n // Sol B2 (round 2): preserve the underlying rejection instead of\n // discarding it — a bare `catch {}` here would make a\n // JSON-parse failure silently invisible, which is exactly the\n // debugging trap the no-swallowed-errors rule exists to\n // prevent.\n const cause = jsonError instanceof Error ? jsonError : new Error(String(jsonError));\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `KV batchPut response was not valid JSON: ${cause.message}`,\n \"kv\",\n {\n cause,\n meta: {\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n const batchResponse = this.normalizeBatchPutResponse(rawBody);\n if (!batchResponse) {\n // The code stays NETWORK_ERROR deliberately: adding a member to\n // ErrorCodes (types.ts:73-115) widens the exported ErrorCode union\n // (:117) and would break consumers with exhaustive switches. The\n // node answered 2xx (the write very likely landed) but the body\n // could not confirm it, so the ambiguity is carried in metadata\n // instead of a dedicated code.\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"KV batchPut response did not include matching written keys and count\",\n \"kv\",\n {\n meta: {\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n // Validate against the exact requested path set, not just internal\n // self-consistency (count === written.length would accept a\n // malformed response that reports the right count for the wrong\n // keys, e.g. a partial write padded to look complete).\n const requestedPaths = new Set(paths);\n const writtenPaths = new Set(batchResponse.written);\n const matchesRequest =\n batchResponse.count === paths.length &&\n batchResponse.written.length === paths.length &&\n writtenPaths.size === paths.length &&\n batchResponse.written.every((key) => requestedPaths.has(key));\n\n if (!matchesRequest) {\n // Same rationale as above: NETWORK_ERROR is kept and the\n // ambiguity is carried in meta rather than a new ErrorCodes member.\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `KV batchPut response did not confirm all ${paths.length} requested key(s) were written`,\n \"kv\",\n {\n meta: {\n requestMayHaveDispatched: true,\n responseReceived: true,\n status: response.status,\n outcome: \"batch-unconfirmed\",\n },\n }\n )\n );\n }\n\n return ok(batchResponse);\n } catch (error) {\n const wrapped = wrapError(\"kv\", error);\n return err({\n ...wrapped,\n meta: { ...wrapped.meta, requestMayHaveDispatched },\n });\n }\n });\n }\n\n /**\n * List keys with optional prefix filtering.\n */\n async list(options?: KVListOptions): Promise<Result<KVListResponse>> {\n return this.withTelemetry(\"list\", options?.prefix, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n if (\n options?.limit !== undefined &&\n (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 1000)\n ) {\n return err(serviceError(\n ErrorCodes.INVALID_INPUT,\n \"KV list limit must be an integer from 1 through 1000\",\n \"kv\"\n ));\n }\n\n // Build the path from prefix and optional path\n let listPath = options?.prefix ?? this._config.prefix ?? \"\";\n if (options?.path) {\n listPath = listPath ? `${listPath}/${options.path}` : options.path;\n }\n\n try {\n const response = await this.invokeOperation(\n listPath,\n KVAction.LIST,\n undefined,\n options?.signal,\n options?.limit === undefined && options?.cursor === undefined\n ? undefined\n : {\n ...(options?.limit === undefined ? {} : { \"x-tinycloud-limit\": String(options.limit) }),\n ...(options?.cursor === undefined ? {} : { \"x-tinycloud-cursor\": options.cursor }),\n }\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n return this.authorizationFailure(\n \"Failed to list keys\",\n response,\n await response.text()\n );\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to list keys: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n let keys = await this.parseResponse<string[]>(response, options?.raw);\n keys = keys ?? [];\n\n // Optionally remove prefix from keys\n if (options?.removePrefix && listPath) {\n const prefixWithSlash = listPath.endsWith(\"/\")\n ? listPath\n : `${listPath}/`;\n keys = keys.map((key) =>\n key.startsWith(prefixWithSlash)\n ? key.slice(prefixWithSlash.length)\n : key\n );\n }\n\n return ok({\n keys,\n ...(response.headers.get(\"x-tinycloud-truncated\") === null\n ? {}\n : { truncated: response.headers.get(\"x-tinycloud-truncated\") === \"true\" }),\n ...(response.headers.get(\"x-tinycloud-next-cursor\") === null\n ? {}\n : { nextCursor: response.headers.get(\"x-tinycloud-next-cursor\") ?? undefined }),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Delete a key.\n */\n async delete(\n key: string,\n options?: KVDeleteOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"delete\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.DELETE,\n undefined,\n options?.signal,\n options?.ifMatch === undefined\n ? undefined\n : { \"if-match\": options.ifMatch }\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n return this.authorizationFailure(\n `Failed to delete key ${JSON.stringify(key)}`,\n response,\n await response.text()\n );\n }\n\n if (response.status === 404) {\n return this.classifyNotFound(response, key);\n }\n\n const errorText = await response.text();\n if (response.status === 412) {\n return err(serviceError(\n ErrorCodes.KV_PRECONDITION_FAILED,\n `KV precondition failed for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n if (response.status === 503 && options?.ifMatch !== undefined) {\n return err(serviceError(\n ErrorCodes.KV_CONFLICT,\n `Concurrent KV delete conflicted for key ${JSON.stringify(key)}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n ));\n }\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to delete key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Get metadata for a key without retrieving the value.\n */\n async head(\n key: string,\n options?: KVHeadOptions\n ): Promise<Result<KVResponse<void>>> {\n return this.withTelemetry(\"head\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n\n try {\n const response = await this.invokeOperation(\n path,\n KVAction.HEAD,\n undefined,\n options?.signal\n );\n\n if (!response.ok) {\n if (response.status === 401) {\n return this.authorizationFailure(\n `Failed to get metadata for key ${JSON.stringify(key)}`,\n response,\n await response.text()\n );\n }\n\n if (response.status === 404) {\n return this.classifyNotFound(response, key);\n }\n\n const errorText = await response.text();\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n `Failed to get metadata for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,\n \"kv\",\n { meta: { status: response.status, statusText: response.statusText } }\n )\n );\n }\n\n return ok({\n data: undefined as void,\n headers: this.createResponseHeaders(response.headers),\n });\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n async batchHead(\n keys: string[],\n options?: KVHeadOptions\n ): Promise<Result<KVBatchReadResponse<void>>> {\n return this.withTelemetry(\"batchHead\", String(keys.length), () =>\n this.batchRead<void>(keys, KVAction.HEAD, options)\n );\n }\n\n /**\n * Create a short-lived signed URL for reading a KV object.\n */\n async createSignedReadUrl(\n key: string,\n options?: KVCreateSignedReadUrlOptions\n ): Promise<Result<KVSignedReadUrlResponse>> {\n return this.withTelemetry(\"createSignedReadUrl\", key, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"kv\"));\n }\n\n const path = this.getFullPath(key, options?.prefix);\n const session = this.context.session!;\n const headers = this.context.invoke(\n session,\n \"kv\",\n path,\n KVAction.GET\n );\n\n const body: {\n space: string;\n path: string;\n ttl_seconds: number;\n content_hash?: string;\n etag?: string;\n } = {\n space: session.spaceId,\n path,\n ttl_seconds:\n options?.expiresInSeconds ??\n Math.ceil(DEFAULT_SIGNED_READ_URL_EXPIRY_MS / 1000),\n };\n\n if (options?.contentHash !== undefined) {\n body.content_hash = options.contentHash;\n }\n if (options?.etag !== undefined) {\n body.etag = options.etag;\n }\n\n try {\n const response = await this.context.fetch(`${this.host}/signed/kv`, {\n method: \"POST\",\n headers: this.withJsonContentType(headers),\n body: JSON.stringify(body),\n signal: this.combineSignals(options?.signal),\n });\n\n if (!response.ok) {\n return this.createSignedReadUrlError(response, key);\n }\n\n const signedUrl = this.normalizeSignedReadUrlResponse(\n await response.json()\n );\n if (!signedUrl) {\n return err(\n serviceError(\n ErrorCodes.NETWORK_ERROR,\n \"Signed read URL response did not include url, ticketId, and expiresAt\",\n \"kv\"\n )\n );\n }\n\n return ok(signedUrl);\n } catch (error) {\n return err(wrapError(\"kv\", error));\n }\n });\n }\n\n /**\n * Create a prefix-scoped view of this KV service.\n *\n * Returns a PrefixedKVService that automatically prefixes all\n * key operations with the specified prefix. This enables apps\n * to isolate their data within a shared space.\n *\n * @param prefix - The prefix to apply to all operations\n * @returns A PrefixedKVService scoped to the prefix\n *\n * ## Prefix Conventions\n *\n * | Pattern | Use Case | Example |\n * | -- | -- | -- |\n * | `/app.{domain}/` | App-private data | `/app.photos.xyz/settings.json` |\n * | `/{type}/` | Shared data type | `/photos/vacation.jpg` |\n * | `/.{name}/` | Hidden/system data | `/.cache/thumbnails/` |\n * | `/public/` | Explicitly shareable | `/public/profile.json` |\n *\n * @example\n * ```typescript\n * const space = sdk.space('default');\n *\n * // Create prefix-scoped views\n * const myApp = space.kv.withPrefix('/app.myapp.com');\n * const sharedPhotos = space.kv.withPrefix('/photos');\n *\n * // Operations are automatically prefixed\n * await myApp.put('settings.json', { theme: 'dark' });\n * // -> Actually writes to: /app.myapp.com/settings.json\n *\n * await myApp.get('settings.json');\n * // -> Actually reads from: /app.myapp.com/settings.json\n *\n * await sharedPhotos.list();\n * // -> Lists: /photos/*\n *\n * // Nested prefixes\n * const settings = myApp.withPrefix('/settings');\n * await settings.get('theme.json'); // -> /app.myapp.com/settings/theme.json\n * ```\n */\n withPrefix(prefix: string): IPrefixedKVService {\n return new PrefixedKVService(this, prefix);\n }\n}\n","export interface ParsedServiceErrorBody {\n error?: string;\n message?: string;\n code?: string;\n}\n\nexport function parseServiceErrorBody(errorText: string): ParsedServiceErrorBody {\n try {\n return JSON.parse(errorText) as ParsedServiceErrorBody;\n } catch {\n return {};\n }\n}\n\nexport function formatServiceResponseError(\n serviceLabel: string,\n operation: string,\n status: number,\n errorText: string,\n parsed: ParsedServiceErrorBody,\n): string {\n if (parsed.message) {\n return compactMessage(parsed.message);\n }\n\n if (looksLikeHtml(errorText)) {\n if (status === 524 || /524\\s*[:-]/i.test(errorText) || /a timeout occurred/i.test(errorText)) {\n return `${serviceLabel} ${operation} failed: upstream request timed out. Please retry.`;\n }\n return `${serviceLabel} ${operation} failed: upstream service returned an HTML error page (${status}).`;\n }\n\n return `${serviceLabel} ${operation} failed: ${status} - ${compactMessage(errorText)}`;\n}\n\nexport function responseErrorMeta(\n status: number,\n statusText: string,\n errorText: string,\n): Record<string, unknown> {\n const meta: Record<string, unknown> = { status, statusText };\n const snippet = compactMessage(errorText);\n if (snippet) {\n meta.responseSnippet = snippet.slice(0, 300);\n }\n return meta;\n}\n\nfunction looksLikeHtml(text: string): boolean {\n return /<!doctype html/i.test(text) || /<html[\\s>]/i.test(text);\n}\n\nfunction compactMessage(message: string): string {\n const compact = message.replace(/\\s+/g, \" \").trim();\n return compact.length > 500 ? `${compact.slice(0, 500)}...` : compact;\n}\n","import type { Result } from \"../types\";\nimport type { SQLService } from \"./SQLService\";\nimport type { ISQLMigrations } from \"./ISQLService\";\nimport type {\n SqlMigrationApplyOptions,\n SqlMigrationApplyResponse,\n} from \"./types\";\n\nexport class SQLMigrations implements ISQLMigrations {\n constructor(\n private readonly service: SQLService,\n private readonly dbName: string,\n ) {}\n\n apply(options: SqlMigrationApplyOptions): Promise<Result<SqlMigrationApplyResponse>> {\n return this.service.applyMigrationsOnDb(this.dbName, options);\n }\n}\n","/**\n * DatabaseHandle - Handle for operations on a specific named database.\n *\n * Delegates all operations to the parent SQLService with the database name.\n */\n\nimport type { Result } from \"../types\";\nimport type { IDatabaseHandle, ISQLMigrations } from \"./ISQLService\";\nimport type { SQLService } from \"./SQLService\";\nimport { SQLMigrations } from \"./SQLMigrations\";\nimport type {\n SqlValue,\n SqlStatement,\n QueryResponse,\n ExecuteResponse,\n BatchResponse,\n QueryOptions,\n ExecuteOptions,\n BatchOptions,\n} from \"./types\";\n\nexport class DatabaseHandle implements IDatabaseHandle {\n private service: SQLService;\n public readonly name: string;\n public readonly migrations: ISQLMigrations;\n\n constructor(service: SQLService, name: string) {\n this.service = service;\n this.name = name;\n this.migrations = new SQLMigrations(service, name);\n }\n\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.service.queryOnDb<T>(this.name, sql, params, options);\n }\n\n async execute(\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.service.executeOnDb(this.name, sql, params, options);\n }\n\n async batch(\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.service.batchOnDb(this.name, statements, options);\n }\n\n async executeStatement(\n name: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.service.executeStatementOnDb(this.name, name, params, options);\n }\n\n async export(options?: QueryOptions): Promise<Result<Blob>> {\n return this.service.exportDb(this.name, options);\n }\n}\n","/**\n * SQL Service Types\n *\n * Type definitions for the SQL service operations.\n */\n\nimport { SQL } from \"@tinycloud/bootstrap\";\n\n/**\n * Configuration for SQLService.\n */\nexport interface SQLServiceConfig {\n /**\n * Default database name.\n * If not set, operations default to \"default\".\n */\n defaultDatabase?: string;\n\n /**\n * Default timeout in milliseconds for SQL operations.\n */\n timeout?: number;\n\n /** Allow additional config properties */\n [key: string]: unknown;\n}\n\n/**\n * Options for SQL query operations.\n */\nexport interface QueryOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n\n /** Maximum rows the node may return. Nodes without limit support ignore it. */\n maxRows?: number;\n\n /** Maximum serialized result bytes the node may return. */\n maxBytes?: number;\n}\n\n/**\n * Options for SQL execute operations.\n */\nexport interface ExecuteOptions {\n /**\n * Schema initialization statements (CREATE TABLE IF NOT EXISTS ...).\n * Executed before the main statement on first write.\n */\n schema?: string[];\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for SQL batch operations.\n */\nexport interface BatchOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A SQL value: null, number, string, or binary data.\n */\nexport type SqlValue = null | number | string | Uint8Array;\n\n/**\n * A SQL statement with optional parameters.\n */\nexport interface SqlStatement {\n sql: string;\n params?: SqlValue[];\n}\n\n/**\n * Response from SQL query operations.\n */\nexport interface QueryResponse<T = Record<string, unknown>> {\n columns: string[];\n rows: T[][];\n rowCount: number;\n}\n\n/**\n * Response from SQL execute operations.\n */\nexport interface ExecuteResponse {\n changes: number;\n lastInsertRowId: number | null;\n}\n\n/**\n * Response from SQL batch operations.\n */\nexport interface BatchResponse {\n results: ExecuteResponse[];\n}\n\n/**\n * A versioned SQL migration owned by an application or SDK subsystem.\n */\nexport interface SqlMigration {\n /**\n * Stable migration id within the namespace, e.g. \"001_initial\".\n */\n id: string;\n\n /**\n * SQL statements to apply when this migration has not been recorded.\n * Statements should be idempotent where SQLite supports it.\n */\n sql: Array<string | SqlStatement>;\n}\n\n/**\n * Options for applying SQL migrations.\n */\nexport interface SqlMigrationApplyOptions {\n /**\n * Namespace for this migration set, usually an app id or SDK subsystem.\n */\n namespace: string;\n\n /**\n * Ordered migration list.\n */\n migrations: SqlMigration[];\n\n /**\n * Custom abort signal for migration operations.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Result from applying SQL migrations.\n */\nexport interface SqlMigrationApplyResponse {\n database: string;\n namespace: string;\n status: \"already_current\" | \"applied\";\n applied: string[];\n skipped: string[];\n}\n\n/**\n * SQL service action types.\n *\n * URNs derive from the canonical capability registry in `@tinycloud/bootstrap`\n * (TC-112 single source of truth, vendored from tinycloud-node). `insert`,\n * `update`, and `delete` were dropped — zero client refs and never accepted\n * node-side. `select` is a deprecated alias of `read`. `execute`/`export` are\n * request-kind artifacts, not grantable capabilities: the node routes them by\n * request-body kind, so they are absent from the registry but kept here as the\n * invocation ability the client sends today (wire alignment tracked in TC-114).\n */\nexport const SQLAction = {\n READ: SQL.READ,\n WRITE: SQL.WRITE,\n SCHEMA: SQL.SCHEMA,\n ADMIN: SQL.ADMIN,\n SELECT: SQL.SELECT,\n EXECUTE: SQL.EXECUTE,\n EXPORT: SQL.EXPORT,\n ALL: SQL.ALL,\n} as const;\n\nexport type SQLActionType = (typeof SQLAction)[keyof typeof SQLAction];\n","/**\n * SQLService - SQL database service implementation.\n *\n * Platform-agnostic SQL service that works with both web-sdk and node-sdk.\n * Uses dependency injection via IServiceContext for platform dependencies.\n */\n\nimport { BaseService } from \"../base/BaseService\";\nimport {\n Result,\n ok,\n err,\n ErrorCodes,\n serviceError,\n type FetchResponse,\n type ServiceHeaders,\n type ServiceSession,\n} from \"../types\";\nimport {\n authRequiredError,\n wrapError,\n parseAuthError,\n storageRejectionError,\n} from \"../errors\";\nimport {\n formatServiceResponseError,\n parseServiceErrorBody,\n responseErrorMeta,\n} from \"../responseErrors\";\nimport type { ISQLService } from \"./ISQLService\";\nimport type { IDatabaseHandle } from \"./ISQLService\";\nimport { DatabaseHandle } from \"./DatabaseHandle\";\nimport {\n type SQLServiceConfig,\n type QueryOptions,\n type ExecuteOptions,\n type BatchOptions,\n type SqlValue,\n type SqlStatement,\n type QueryResponse,\n type ExecuteResponse,\n type BatchResponse,\n type SqlMigration,\n type SqlMigrationApplyOptions,\n type SqlMigrationApplyResponse,\n SQLAction,\n} from \"./types\";\n\nconst DDL_TOKENS = new Set([\"alter\", \"create\", \"drop\"]);\nconst MIGRATIONS_TABLE = \"__tinycloud_sql_migrations\";\nconst MIGRATIONS_SCHEMA = `CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (\n key TEXT PRIMARY KEY,\n namespace TEXT NOT NULL,\n id TEXT NOT NULL,\n applied_at TEXT NOT NULL,\n statement_count INTEGER NOT NULL\n)`;\nconst MIGRATIONS_META_NAMESPACE = \"tinycloud.sql.migrations\";\nconst MIGRATIONS_META_ID = \"000_metadata\";\n\nexport class SQLService extends BaseService implements ISQLService {\n static readonly serviceName = \"sql\";\n\n declare protected _config: SQLServiceConfig;\n private readonly migrationLocks = new Map<string, Promise<Result<SqlMigrationApplyResponse>>>();\n\n constructor(config: SQLServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n get config(): SQLServiceConfig {\n return this._config;\n }\n\n private get defaultDbName(): string {\n return this._config.defaultDatabase ?? \"default\";\n }\n\n private get host(): string {\n return this.context.hosts[0];\n }\n\n /**\n * Get a handle to a named database.\n */\n db(name?: string): IDatabaseHandle {\n return new DatabaseHandle(this, name ?? this.defaultDbName);\n }\n\n /**\n * Shortcut: query the default database.\n */\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.queryOnDb<T>(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: execute on the default database.\n */\n async execute(\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.executeOnDb(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: batch on the default database.\n */\n async batch(\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.batchOnDb(this.defaultDbName, statements, options);\n }\n\n async applyMigrationsOnDb(\n dbName: string,\n options: SqlMigrationApplyOptions,\n ): Promise<Result<SqlMigrationApplyResponse>> {\n const validationError = validateMigrationOptions(options);\n if (validationError) {\n return err(serviceError(ErrorCodes.INVALID_INPUT, validationError, \"sql\"));\n }\n\n const lockKey = `${dbName}\\0${options.namespace}`;\n const existing = this.migrationLocks.get(lockKey);\n if (existing) {\n return existing;\n }\n\n const promise = this.applyMigrationsOnDbUnlocked(dbName, options);\n this.migrationLocks.set(lockKey, promise);\n try {\n return await promise;\n } finally {\n this.migrationLocks.delete(lockKey);\n }\n }\n\n // === Internal methods called by DatabaseHandle ===\n\n async queryOnDb<T = Record<string, unknown>>(\n dbName: string,\n sql: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.withTelemetry(\"query\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n const body: Record<string, unknown> = {\n action: \"query\",\n sql,\n params: serializeSqlValues(params ?? []),\n };\n if (options?.maxRows !== undefined) body.maxRows = options.maxRows;\n if (options?.maxBytes !== undefined) body.maxBytes = options.maxBytes;\n\n const response = await this.invokeSQL(\n dbName,\n this.actionForSql(sql, SQLAction.READ),\n body,\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"query\");\n }\n\n const data = (await response.json()) as QueryResponse<T>;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async executeOnDb(\n dbName: string,\n sql: string,\n params?: SqlValue[],\n options?: ExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.withTelemetry(\"execute\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n const body: Record<string, unknown> = {\n action: \"execute\",\n sql,\n params: serializeSqlValues(params ?? []),\n };\n if (options?.schema) {\n body.schema = options.schema;\n }\n\n const actions = [\n this.actionForSql(sql, SQLAction.WRITE),\n ...(options?.schema ?? []).map((statement) =>\n this.actionForSql(statement, SQLAction.SCHEMA),\n ),\n ];\n const response = await this.invokeSQL(\n dbName,\n this.dedupeActions(actions),\n body,\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"execute\");\n }\n\n const data = (await response.json()) as ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async batchOnDb(\n dbName: string,\n statements: SqlStatement[],\n options?: BatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.withTelemetry(\"batch\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n const response = await this.invokeSQL(\n dbName,\n this.actionsForSqlBatch(statements),\n { action: \"batch\", statements },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"batch\");\n }\n\n const data = (await response.json()) as BatchResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async executeStatementOnDb(\n dbName: string,\n name: string,\n params?: SqlValue[],\n options?: QueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.withTelemetry(\"executeStatement\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.sql/execute` capability — it routes an\n // `execute_statement` request by body kind and classifies write-class\n // from the RESOLVED named statement's SQL\n // (tinycloud-node-server/src/routes/mod.rs:1168-1171). A named statement\n // may mutate, and the SQL parser rejects writes under a read/select\n // ability (tinycloud-core/src/sql/parser.rs:135) while permitting reads\n // under write (write ⊇ read), so `write` is the ability that works for\n // both read-only and mutating statements and is contained in a normal\n // read+write delegation.\n const response = await this.invokeSQL(\n dbName,\n SQLAction.WRITE,\n { action: \"execute_statement\", name, params: params ?? [] },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"executeStatement\");\n }\n\n const data = (await response.json()) as\n | QueryResponse\n | ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n async exportDb(\n dbName: string,\n options?: QueryOptions\n ): Promise<Result<Blob>> {\n return this.withTelemetry(\"export\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"sql\"));\n }\n\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.sql/export` capability — it routes an\n // `export` request by body kind and authorizes it as a READ\n // (`SqlRequest::Export` is non-write:\n // tinycloud-node-server/src/routes/mod.rs:1172, see comment :1155).\n const response = await this.invokeSQL(\n dbName,\n SQLAction.READ,\n { action: \"export\" },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"export\");\n }\n\n // FetchResponse doesn't expose blob(), so access it from the\n // underlying response which is a standard fetch Response at runtime\n const resp = response as any;\n if (typeof resp.blob === \"function\") {\n const blob = await resp.blob();\n return ok(blob as Blob);\n }\n // If blob() is not available, return the raw text as a Blob-like\n const text = await response.text();\n return ok(text as unknown as Blob);\n } catch (error) {\n return err(wrapError(\"sql\", error));\n }\n });\n }\n\n private async applyMigrationsOnDbUnlocked(\n dbName: string,\n options: SqlMigrationApplyOptions,\n ): Promise<Result<SqlMigrationApplyResponse>> {\n return this.withTelemetry(\"migrations.apply\", dbName, async () => {\n // Read before writing: an up-to-date database must open with a single\n // read, because writes fail once the owner's storage is full (402) and\n // read-only sessions cannot write at all.\n const listed = await this.queryOnDb<{ id: string }>(\n dbName,\n `SELECT id FROM ${MIGRATIONS_TABLE} WHERE namespace = ? ORDER BY applied_at, id`,\n [options.namespace],\n { signal: options.signal },\n );\n // A node may answer for a database that was never created with either\n // \"no such table\" or \"database not found\"; both mean nothing is applied.\n const tableMissing =\n !listed.ok &&\n (listed.error.code === ErrorCodes.SQL_DATABASE_NOT_FOUND ||\n isMissingTableError(listed.error.message));\n if (!listed.ok && !tableMissing) return listed;\n\n const appliedIds = new Set(\n (listed.ok ? listed.data.rows : [])\n .map((row) => rowValue(row, 0))\n .filter((id): id is string => typeof id === \"string\"),\n );\n const skipped = options.migrations\n .filter((migration) => appliedIds.has(migration.id))\n .map((migration) => migration.id);\n const pending = options.migrations.filter((migration) => !appliedIds.has(migration.id));\n const applied: string[] = [];\n\n if (pending.length > 0 && tableMissing) {\n const created = await this.ensureMigrationsTable(dbName, options.signal);\n if (!created.ok) return created;\n }\n\n for (const migration of pending) {\n const result = await this.applyOneMigration(dbName, options.namespace, migration, options.signal);\n if (!result.ok) return result;\n applied.push(migration.id);\n }\n\n return ok({\n database: dbName,\n namespace: options.namespace,\n status: applied.length > 0 ? \"applied\" : \"already_current\",\n applied,\n skipped,\n });\n });\n }\n\n private async applyOneMigration(\n dbName: string,\n namespace: string,\n migration: SqlMigration,\n signal?: AbortSignal,\n ): Promise<Result<void>> {\n const statements = [\n ...migration.sql.map((statement) =>\n typeof statement === \"string\" ? { sql: statement } : statement,\n ),\n {\n sql: `INSERT OR REPLACE INTO ${MIGRATIONS_TABLE} (key, namespace, id, applied_at, statement_count) VALUES (?, ?, ?, ?, ?)`,\n params: [\n migrationKey(namespace, migration.id),\n namespace,\n migration.id,\n new Date().toISOString(),\n migration.sql.length,\n ],\n },\n ];\n\n const result = await this.batchOnDb(dbName, statements, { signal });\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n private async ensureMigrationsTable(\n dbName: string,\n signal?: AbortSignal,\n ): Promise<Result<void>> {\n const result = await this.batchOnDb(\n dbName,\n [\n { sql: MIGRATIONS_SCHEMA },\n {\n sql: `INSERT OR REPLACE INTO ${MIGRATIONS_TABLE} (key, namespace, id, applied_at, statement_count) VALUES (?, ?, ?, ?, ?)`,\n params: [\n migrationKey(MIGRATIONS_META_NAMESPACE, MIGRATIONS_META_ID),\n MIGRATIONS_META_NAMESPACE,\n MIGRATIONS_META_ID,\n new Date().toISOString(),\n 1,\n ],\n },\n ],\n { signal },\n );\n if (!result.ok) return result;\n return ok(undefined);\n }\n\n // === Private helpers ===\n\n private async invokeSQL(\n dbName: string,\n actions: string | string[],\n body: Record<string, unknown>,\n signal?: AbortSignal\n ): Promise<FetchResponse> {\n const session = this.context.session!;\n const actionList = Array.isArray(actions) ? actions : [actions];\n const headers =\n actionList.length === 1\n ? this.context.invoke(session, \"sql\", dbName, actionList[0]!)\n : this.invokeSQLAny(session, dbName, actionList);\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: {\n ...(headers as Record<string, string>),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body) as any,\n signal: this.combineSignals(signal),\n });\n }\n\n private actionForSql(sql: string, fallback: string): string {\n const token = firstSqlToken(sql);\n if (token === \"pragma\") return SQLAction.ADMIN;\n if (token !== undefined && DDL_TOKENS.has(token)) return SQLAction.SCHEMA;\n return fallback;\n }\n\n private actionsForSqlBatch(statements: SqlStatement[]): string[] {\n return this.dedupeActions(\n statements.map((statement) => this.actionForSql(statement.sql, SQLAction.WRITE)),\n );\n }\n\n private dedupeActions(actions: string[]): string[] {\n return [...new Set(actions)];\n }\n\n private invokeSQLAny(\n session: ServiceSession,\n dbName: string,\n actions: string[],\n ): ServiceHeaders {\n if (!this.context.invokeAny) {\n throw new Error(\n `SQL operation requires multiple permissions (${actions.join(\", \")}) but this SDK runtime does not support multi-resource invocations`,\n );\n }\n\n return this.context.invokeAny(\n session,\n actions.map((action) => ({\n spaceId: session.spaceId,\n service: \"sql\",\n path: dbName,\n action,\n })),\n );\n }\n\n private async handleErrorResponse(\n response: FetchResponse,\n operation: string\n ): Promise<Result<never>> {\n const errorText = await response.text();\n const meta = responseErrorMeta(response.status, response.statusText, errorText);\n\n // The node answers a write-class request on a full space with 402.\n if (response.status === 402) {\n return err(storageRejectionError(\"sql\", 402, meta, errorText));\n }\n\n const errorBody = parseServiceErrorBody(errorText);\n\n const errorCode = this.mapHttpStatusToErrorCode(\n response.status,\n errorBody.error\n );\n const message = formatServiceResponseError(\n \"SQL\",\n operation,\n response.status,\n errorText,\n errorBody,\n );\n\n if (response.status === 401) {\n const { resource, action } = parseAuthError(errorText);\n if (action) meta.requiredAction = action;\n if (resource) meta.resource = resource;\n }\n\n return err(\n serviceError(errorCode, message, \"sql\", { meta })\n );\n }\n\n private mapHttpStatusToErrorCode(\n status: number,\n serverError?: string\n ): string {\n switch (status) {\n case 400:\n return ErrorCodes.SQL_ERROR;\n case 401:\n return ErrorCodes.AUTH_UNAUTHORIZED;\n case 403:\n if (serverError === \"sql_readonly_violation\") {\n return ErrorCodes.SQL_READONLY_VIOLATION;\n }\n return ErrorCodes.SQL_PERMISSION_DENIED;\n case 404:\n return ErrorCodes.SQL_DATABASE_NOT_FOUND;\n case 413:\n return ErrorCodes.SQL_RESPONSE_TOO_LARGE;\n case 429:\n return ErrorCodes.SQL_QUOTA_EXCEEDED;\n default:\n return ErrorCodes.NETWORK_ERROR;\n }\n }\n}\n\nfunction firstSqlToken(sql: string): string | undefined {\n let index = 0;\n\n while (index < sql.length) {\n while (index < sql.length && /\\s/.test(sql[index])) {\n index++;\n }\n\n if (sql.startsWith(\"--\", index)) {\n const newline = sql.indexOf(\"\\n\", index + 2);\n if (newline === -1) {\n return undefined;\n }\n index = newline + 1;\n continue;\n }\n\n if (sql.startsWith(\"/*\", index)) {\n const end = sql.indexOf(\"*/\", index + 2);\n if (end === -1) {\n return undefined;\n }\n index = end + 2;\n continue;\n }\n\n break;\n }\n\n const match = /^[A-Za-z_]+/.exec(sql.slice(index));\n return match?.[0].toLowerCase();\n}\n\nfunction validateMigrationOptions(options: SqlMigrationApplyOptions): string | null {\n if (!options || typeof options !== \"object\") {\n return \"SQL migrations require options\";\n }\n if (!options.namespace || typeof options.namespace !== \"string\") {\n return \"SQL migrations require a non-empty namespace\";\n }\n if (!Array.isArray(options.migrations)) {\n return \"SQL migrations require an ordered migrations array\";\n }\n\n const ids = new Set<string>();\n for (const migration of options.migrations) {\n if (!migration.id || typeof migration.id !== \"string\") {\n return \"SQL migrations require every migration to have a non-empty id\";\n }\n if (ids.has(migration.id)) {\n return `Duplicate SQL migration id: ${migration.id}`;\n }\n ids.add(migration.id);\n\n if (!Array.isArray(migration.sql)) {\n return `SQL migration ${migration.id} requires a sql array`;\n }\n for (const statement of migration.sql) {\n if (typeof statement === \"string\") {\n if (statement.trim() === \"\") {\n return `SQL migration ${migration.id} contains an empty statement`;\n }\n continue;\n }\n if (!statement || typeof statement.sql !== \"string\" || statement.sql.trim() === \"\") {\n return `SQL migration ${migration.id} contains an invalid statement`;\n }\n }\n }\n\n return null;\n}\n\nfunction serializeSqlValues(values: SqlValue[]): Array<null | number | string | number[]> {\n return values.map((value) =>\n value instanceof Uint8Array ? Array.from(value) : value,\n );\n}\n\nfunction isMissingTableError(message: string): boolean {\n return /no such table/i.test(message);\n}\n\nfunction migrationKey(namespace: string, id: string): string {\n return `${encodeURIComponent(namespace)}:${encodeURIComponent(id)}`;\n}\n\nfunction rowValue(row: unknown, index: number): unknown {\n if (Array.isArray(row)) return row[index];\n if (row && typeof row === \"object\") {\n const values = Object.values(row as Record<string, unknown>);\n return values[index];\n }\n return undefined;\n}\n","/**\n * DuckDbDatabaseHandle - Handle for operations on a specific named database.\n *\n * Delegates all operations to the parent DuckDbService with the database name.\n */\n\nimport type { Result } from \"../types\";\nimport type { IDuckDbDatabaseHandle } from \"./IDuckDbService\";\nimport type { DuckDbService } from \"./DuckDbService\";\nimport type {\n DuckDbValue,\n DuckDbStatement,\n QueryResponse,\n ExecuteResponse,\n BatchResponse,\n SchemaInfo,\n DuckDbQueryOptions,\n DuckDbExecuteOptions,\n DuckDbBatchOptions,\n DuckDbOptions,\n} from \"./types\";\n\nexport class DuckDbDatabaseHandle implements IDuckDbDatabaseHandle {\n private service: DuckDbService;\n public readonly name: string;\n\n constructor(service: DuckDbService, name: string) {\n this.service = service;\n this.name = name;\n }\n\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.service.queryOnDb<T>(this.name, sql, params, options);\n }\n\n async queryArrow(\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<ArrayBuffer>> {\n return this.service.queryArrowOnDb(this.name, sql, params, options);\n }\n\n async execute(\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.service.executeOnDb(this.name, sql, params, options);\n }\n\n async batch(\n statements: DuckDbStatement[],\n options?: DuckDbBatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.service.batchOnDb(this.name, statements, options);\n }\n\n async executeStatement(\n name: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.service.executeStatementOnDb(this.name, name, params, options);\n }\n\n async describe(options?: DuckDbOptions): Promise<Result<SchemaInfo>> {\n return this.service.describeDb(this.name, options);\n }\n\n async export(options?: DuckDbOptions): Promise<Result<Blob>> {\n return this.service.exportOnDb(this.name, options);\n }\n\n async import(data: Uint8Array, options?: DuckDbOptions): Promise<Result<void>> {\n return this.service.importOnDb(this.name, data, options);\n }\n}\n","/**\n * DuckDB Service Types\n *\n * Type definitions for the DuckDB service operations.\n */\n\nimport { DUCKDB } from \"@tinycloud/bootstrap\";\n\n/**\n * Configuration for DuckDbService.\n */\nexport interface DuckDbServiceConfig {\n /**\n * Default database name.\n * If not set, operations default to \"default\".\n */\n defaultDatabase?: string;\n\n /**\n * Default timeout in milliseconds for DuckDB operations.\n */\n timeout?: number;\n\n /** Allow additional config properties */\n [key: string]: unknown;\n}\n\n/**\n * Options for DuckDB query operations.\n */\nexport interface DuckDbQueryOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for DuckDB execute operations.\n */\nexport interface DuckDbExecuteOptions {\n /**\n * Schema initialization statements (CREATE TABLE IF NOT EXISTS ...).\n * Executed before the main statement on first write.\n */\n schema?: string[];\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for DuckDB batch operations.\n */\nexport interface DuckDbBatchOptions {\n /**\n * Whether to run statements in a transaction.\n */\n transactional?: boolean;\n\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * Options for DuckDB operations that only need an abort signal.\n */\nexport interface DuckDbOptions {\n /**\n * Custom abort signal for this operation.\n */\n signal?: AbortSignal;\n}\n\n/**\n * A DuckDB value: null, boolean, number, string, binary, array, or object.\n */\nexport type DuckDbValue =\n | null\n | boolean\n | number\n | string\n | Uint8Array\n | DuckDbValueArray\n | DuckDbValueRecord;\n\n/** Array of DuckDB values (workaround for circular type alias). */\nexport interface DuckDbValueArray extends Array<DuckDbValue> {}\n\n/** Record of DuckDB values (workaround for circular type alias). */\nexport interface DuckDbValueRecord {\n [key: string]: DuckDbValue;\n}\n\n/**\n * A DuckDB statement with optional parameters.\n */\nexport interface DuckDbStatement {\n sql: string;\n params?: DuckDbValue[];\n}\n\n/**\n * Response from DuckDB query operations.\n */\nexport interface QueryResponse<T = Record<string, unknown>> {\n columns: string[];\n rows: T[][];\n rowCount: number;\n}\n\n/**\n * Response from DuckDB execute operations.\n */\nexport interface ExecuteResponse {\n changes: number;\n}\n\n/**\n * Response from DuckDB batch operations.\n */\nexport interface BatchResponse {\n results: ExecuteResponse[];\n}\n\n/**\n * Schema information for a DuckDB database.\n */\nexport interface SchemaInfo {\n tables: TableInfo[];\n views: ViewInfo[];\n}\n\n/**\n * Information about a table.\n */\nexport interface TableInfo {\n name: string;\n columns: ColumnInfo[];\n}\n\n/**\n * Information about a column.\n */\nexport interface ColumnInfo {\n name: string;\n type: string;\n nullable: boolean;\n}\n\n/**\n * Information about a view.\n */\nexport interface ViewInfo {\n name: string;\n sql: string;\n}\n\n/**\n * DuckDB service action types.\n *\n * URNs derive from the canonical capability registry in `@tinycloud/bootstrap`\n * (TC-112 single source of truth).\n */\nexport const DuckDbAction = {\n READ: DUCKDB.READ,\n WRITE: DUCKDB.WRITE,\n ADMIN: DUCKDB.ADMIN,\n DESCRIBE: DUCKDB.DESCRIBE,\n EXPORT: DUCKDB.EXPORT,\n IMPORT: DUCKDB.IMPORT,\n EXECUTE: DUCKDB.EXECUTE,\n ALL: DUCKDB.ALL,\n} as const;\n\nexport type DuckDbActionType = (typeof DuckDbAction)[keyof typeof DuckDbAction];\n","/**\n * DuckDbService - DuckDB database service implementation.\n *\n * Platform-agnostic DuckDB service that works with both web-sdk and node-sdk.\n * Uses dependency injection via IServiceContext for platform dependencies.\n */\n\nimport { BaseService } from \"../base/BaseService\";\nimport {\n Result,\n ok,\n err,\n ErrorCodes,\n serviceError,\n type FetchResponse,\n} from \"../types\";\nimport {\n authRequiredError,\n wrapError,\n parseAuthError,\n storageRejectionError,\n} from \"../errors\";\nimport {\n formatServiceResponseError,\n parseServiceErrorBody,\n responseErrorMeta,\n} from \"../responseErrors\";\nimport type { IDuckDbService, IDuckDbDatabaseHandle } from \"./IDuckDbService\";\nimport { DuckDbDatabaseHandle } from \"./DuckDbDatabaseHandle\";\nimport {\n type DuckDbServiceConfig,\n type DuckDbQueryOptions,\n type DuckDbExecuteOptions,\n type DuckDbBatchOptions,\n type DuckDbOptions,\n type DuckDbValue,\n type DuckDbStatement,\n type QueryResponse,\n type ExecuteResponse,\n type BatchResponse,\n type SchemaInfo,\n DuckDbAction,\n} from \"./types\";\n\nexport class DuckDbService extends BaseService implements IDuckDbService {\n static readonly serviceName = \"duckdb\";\n\n declare protected _config: DuckDbServiceConfig;\n\n constructor(config: DuckDbServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n get config(): DuckDbServiceConfig {\n return this._config;\n }\n\n private get defaultDbName(): string {\n return this._config.defaultDatabase ?? \"default\";\n }\n\n private get host(): string {\n return this.context.hosts[0];\n }\n\n /**\n * Get a handle to a named database.\n */\n db(name?: string): IDuckDbDatabaseHandle {\n return new DuckDbDatabaseHandle(this, name ?? this.defaultDbName);\n }\n\n /**\n * Shortcut: query the default database (JSON format).\n */\n async query<T = Record<string, unknown>>(\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.queryOnDb<T>(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: query the default database (Arrow IPC format).\n */\n async queryArrow(\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<ArrayBuffer>> {\n return this.queryArrowOnDb(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: execute on the default database.\n */\n async execute(\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.executeOnDb(this.defaultDbName, sql, params, options);\n }\n\n /**\n * Shortcut: batch on the default database.\n */\n async batch(\n statements: DuckDbStatement[],\n options?: DuckDbBatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.batchOnDb(this.defaultDbName, statements, options);\n }\n\n // === Internal methods called by DuckDbDatabaseHandle ===\n\n async queryOnDb<T = Record<string, unknown>>(\n dbName: string,\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<QueryResponse<T>>> {\n return this.withTelemetry<QueryResponse<T>>(\"query\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n const response = await this.invokeDuckDb(\n dbName,\n DuckDbAction.READ,\n { action: \"query\", sql, params: params ?? [] },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"query\");\n }\n\n const data = (await response.json()) as QueryResponse<T>;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n async queryArrowOnDb(\n dbName: string,\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<ArrayBuffer>> {\n return this.withTelemetry<ArrayBuffer>(\"queryArrow\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n const response = await this.invokeDuckDb(\n dbName,\n DuckDbAction.READ,\n { action: \"query\", sql, params: params ?? [] },\n options?.signal,\n { Accept: \"application/vnd.apache.arrow.stream\" }\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"queryArrow\");\n }\n\n const buffer = await response.arrayBuffer();\n return ok(buffer);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n async executeOnDb(\n dbName: string,\n sql: string,\n params?: DuckDbValue[],\n options?: DuckDbExecuteOptions\n ): Promise<Result<ExecuteResponse>> {\n return this.withTelemetry(\"execute\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n const body: Record<string, unknown> = {\n action: \"execute\",\n sql,\n params: params ?? [],\n };\n if (options?.schema) {\n body.schema = options.schema;\n }\n\n const response = await this.invokeDuckDb(\n dbName,\n DuckDbAction.WRITE,\n body,\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"execute\");\n }\n\n const data = (await response.json()) as ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n async batchOnDb(\n dbName: string,\n statements: DuckDbStatement[],\n options?: DuckDbBatchOptions\n ): Promise<Result<BatchResponse>> {\n return this.withTelemetry(\"batch\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n const body: Record<string, unknown> = {\n action: \"batch\",\n statements,\n };\n if (options?.transactional !== undefined) {\n body.transactional = options.transactional;\n }\n\n const response = await this.invokeDuckDb(\n dbName,\n DuckDbAction.WRITE,\n body,\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"batch\");\n }\n\n const data = (await response.json()) as BatchResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n async executeStatementOnDb(\n dbName: string,\n name: string,\n params?: DuckDbValue[],\n options?: DuckDbQueryOptions\n ): Promise<Result<QueryResponse | ExecuteResponse>> {\n return this.withTelemetry(\"executeStatement\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.duckdb/execute` capability — it routes an\n // `executeStatement` request by body kind and classifies write-class\n // from the RESOLVED named statement's SQL\n // (tinycloud-node-server/src/routes/mod.rs:1698-1701). Mirroring the SQL\n // path, `write` is the ability that works for both read-only and\n // mutating named statements and is contained in a normal read+write\n // delegation.\n const response = await this.invokeDuckDb(\n dbName,\n DuckDbAction.WRITE,\n { action: \"executeStatement\", name, params: params ?? [] },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"executeStatement\");\n }\n\n const data = (await response.json()) as\n | QueryResponse\n | ExecuteResponse;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n async describeDb(\n dbName: string,\n options?: DuckDbOptions\n ): Promise<Result<SchemaInfo>> {\n return this.withTelemetry(\"describe\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n // TC-114: mint the dispatchable ability, not the literal method name.\n // The node has no `tinycloud.duckdb/describe` capability — it routes a\n // `describe` request by body kind and authorizes it as a READ\n // (`DuckDbRequest::Describe` is non-write:\n // tinycloud-node-server/src/routes/mod.rs:1705, see comment :1680).\n const response = await this.invokeDuckDb(\n dbName,\n DuckDbAction.READ,\n { action: \"describe\" },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"describe\");\n }\n\n const data = (await response.json()) as SchemaInfo;\n return ok(data);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n async exportOnDb(\n dbName: string,\n options?: DuckDbOptions\n ): Promise<Result<Blob>> {\n return this.withTelemetry(\"export\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n const response = await this.invokeDuckDb(\n dbName,\n DuckDbAction.EXPORT,\n { action: \"export\" },\n options?.signal\n );\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"export\");\n }\n\n const blob = await response.blob();\n return ok(blob);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n async importOnDb(\n dbName: string,\n data: Uint8Array,\n options?: DuckDbOptions\n ): Promise<Result<void>> {\n return this.withTelemetry(\"import\", dbName, async () => {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"duckdb\"));\n }\n\n try {\n const session = this.context.session!;\n const headers = this.context.invoke(\n session,\n \"duckdb\",\n dbName,\n DuckDbAction.IMPORT\n );\n\n const response = await this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: {\n ...(headers as Record<string, string>),\n \"Content-Type\": \"application/x-duckdb\",\n },\n body: new Blob([data]),\n signal: this.combineSignals(options?.signal),\n });\n\n if (!response.ok) {\n return this.handleErrorResponse(response, \"import\");\n }\n\n return ok(undefined);\n } catch (error) {\n return err(wrapError(\"duckdb\", error));\n }\n });\n }\n\n // === Private helpers ===\n\n private async invokeDuckDb(\n dbName: string,\n action: string,\n body: Record<string, unknown>,\n signal?: AbortSignal,\n extraHeaders?: Record<string, string>\n ): Promise<FetchResponse> {\n const session = this.context.session!;\n const headers = this.context.invoke(session, \"duckdb\", dbName, action);\n\n return this.context.fetch(`${this.host}/invoke`, {\n method: \"POST\",\n headers: {\n ...(headers as Record<string, string>),\n \"Content-Type\": \"application/json\",\n ...extraHeaders,\n },\n body: JSON.stringify(body),\n signal: this.combineSignals(signal),\n });\n }\n\n private async handleErrorResponse(\n response: FetchResponse,\n operation: string\n ): Promise<Result<never>> {\n const errorText = await response.text();\n const meta = responseErrorMeta(response.status, response.statusText, errorText);\n\n // The node answers a write-class request on a full space with 402.\n if (response.status === 402) {\n return err(storageRejectionError(\"duckdb\", 402, meta, errorText));\n }\n\n const errorBody = parseServiceErrorBody(errorText);\n\n const errorCode = this.mapHttpStatusToErrorCode(\n response.status,\n errorBody.error\n );\n const message = formatServiceResponseError(\n \"DuckDB\",\n operation,\n response.status,\n errorText,\n errorBody,\n );\n\n if (response.status === 401) {\n const { resource, action } = parseAuthError(errorText);\n if (action) meta.requiredAction = action;\n if (resource) meta.resource = resource;\n }\n\n return err(\n serviceError(errorCode, message, \"duckdb\", { meta })\n );\n }\n\n private mapHttpStatusToErrorCode(\n status: number,\n serverError?: string\n ): string {\n switch (status) {\n case 400:\n return ErrorCodes.DUCKDB_ERROR;\n case 401:\n return ErrorCodes.AUTH_UNAUTHORIZED;\n case 403:\n if (serverError === \"duckdb_readonly_violation\") {\n return ErrorCodes.DUCKDB_READONLY_VIOLATION;\n }\n return ErrorCodes.DUCKDB_PERMISSION_DENIED;\n case 404:\n return ErrorCodes.DUCKDB_DATABASE_NOT_FOUND;\n case 413:\n return ErrorCodes.DUCKDB_RESPONSE_TOO_LARGE;\n case 429:\n return ErrorCodes.DUCKDB_QUOTA_EXCEEDED;\n default:\n return ErrorCodes.NETWORK_ERROR;\n }\n }\n}\n","import { BaseService } from \"../base/BaseService\";\nimport type {\n FetchResponse,\n InvokeAnyEntry,\n ServiceHeaders,\n Result,\n} from \"../types\";\nimport { ErrorCodes, err, ok, serviceError } from \"../types\";\nimport { authRequiredError, wrapError } from \"../errors\";\nimport type { IHooksService } from \"./IHooksService\";\nimport type {\n HookEvent,\n HookStreamEvent,\n HookSubscription,\n HooksServiceConfig,\n SubscribeOptions,\n HookWebhookListOptions,\n HookWebhookRecord,\n HookWebhookRegistration,\n HookWebhookUnregisterOptions,\n} from \"./types\";\n\ninterface HookTicketResponse {\n ticket: string;\n expiresAt: string;\n}\n\ninterface HookSubscriber {\n requested: HookSubscription[];\n ttlSeconds?: number;\n queue: AsyncQueue<HookEvent>;\n}\n\nclass AsyncQueue<T> implements AsyncIterable<T>, AsyncIterator<T> {\n private readonly values: T[] = [];\n private readonly waiters: Array<(result: IteratorResult<T>) => void> = [];\n private closed = false;\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n return this;\n }\n\n push(value: T): void {\n if (this.closed) {\n return;\n }\n\n const waiter = this.waiters.shift();\n if (waiter) {\n waiter({ value, done: false });\n return;\n }\n\n this.values.push(value);\n }\n\n close(): void {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n while (this.waiters.length > 0) {\n const waiter = this.waiters.shift();\n waiter?.({ value: undefined as never, done: true });\n }\n }\n\n next(): Promise<IteratorResult<T>> {\n if (this.values.length > 0) {\n const value = this.values.shift()!;\n return Promise.resolve({ value, done: false });\n }\n\n if (this.closed) {\n return Promise.resolve({ value: undefined as never, done: true });\n }\n\n return new Promise<IteratorResult<T>>((resolve) => {\n this.waiters.push(resolve);\n });\n }\n}\n\nexport class HooksService extends BaseService implements IHooksService {\n static readonly serviceName = \"hooks\";\n\n declare protected _config: HooksServiceConfig;\n private readonly _subscribers: Set<HookSubscriber> = new Set();\n private _sharedStreamTask?: Promise<void>;\n private _sharedStreamAbort?: AbortController;\n private _refreshChain: Promise<void> = Promise.resolve();\n private _activeSignature = \"\";\n\n constructor(config: HooksServiceConfig = {}) {\n super();\n this._config = config;\n }\n\n get config(): HooksServiceConfig {\n return this._config;\n }\n\n private get host(): string {\n return this._config.host ?? this.context.hosts[0];\n }\n\n async *subscribe(\n subscriptions: HookSubscription[],\n options: SubscribeOptions = {},\n ): AsyncIterable<HookEvent> {\n if (!this.requireAuth()) {\n throw new Error(\"Authentication required for hooks subscription\");\n }\n if (subscriptions.length === 0) {\n throw new Error(\"At least one hook subscription is required\");\n }\n\n const normalized = subscriptions.map(normalizeSubscription);\n const subscriber: HookSubscriber = {\n requested: normalized,\n ttlSeconds: options.ttlSeconds,\n queue: new AsyncQueue<HookEvent>(),\n };\n\n this._subscribers.add(subscriber);\n const abortHandler = () => {\n this._subscribers.delete(subscriber);\n subscriber.queue.close();\n void this.scheduleSharedStreamRefresh();\n };\n\n if (options.signal) {\n if (options.signal.aborted) {\n abortHandler();\n } else {\n options.signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n }\n\n void this.scheduleSharedStreamRefresh();\n\n try {\n for await (const event of subscriber.queue) {\n yield event;\n }\n } finally {\n if (options.signal) {\n options.signal.removeEventListener(\"abort\", abortHandler);\n }\n abortHandler();\n }\n }\n\n async register(\n webhook: HookWebhookRegistration,\n ): Promise<Result<HookWebhookRecord>> {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"hooks\"));\n }\n\n if (typeof webhook.secret !== \"string\" || webhook.secret.trim().length === 0) {\n return err(\n serviceError(\n ErrorCodes.INVALID_INPUT,\n \"Webhook secret is required\",\n \"hooks\",\n { meta: { field: \"secret\" } },\n ),\n );\n }\n\n try {\n const response = await this.context.fetch(`${this.host}/hooks/webhooks`, {\n method: \"POST\",\n headers: {\n ...serviceHeadersToRecord(\n this.createHookHeaders(\n \"tinycloud.hooks/register\",\n buildScopePath(webhook.service, webhook.pathPrefix),\n ),\n ),\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n space: webhook.space,\n service: webhook.service,\n pathPrefix: normalizePathPrefix(webhook.pathPrefix),\n abilities: webhook.abilities ?? [],\n callbackUrl: webhook.callbackUrl,\n secret: webhook.secret,\n }),\n });\n\n if (!response.ok) {\n return err(\n await responseError(\"hooks\", \"failed to register webhook\", response),\n );\n }\n\n const data = normalizeWebhookRecord(await response.json());\n if (!data) {\n return err(\n wrapError(\n \"hooks\",\n new Error(\"Webhook registration response did not include a record\"),\n ),\n );\n }\n\n return ok(data);\n } catch (error) {\n return err(wrapError(\"hooks\", error));\n }\n }\n\n async list(\n options: HookWebhookListOptions = {},\n ): Promise<Result<HookWebhookRecord[]>> {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"hooks\"));\n }\n\n try {\n const query = new URLSearchParams();\n if (options.space) {\n query.set(\"space\", options.space);\n }\n if (options.service) {\n query.set(\"service\", options.service);\n }\n if (options.pathPrefix) {\n const normalizedPrefix = normalizePathPrefix(options.pathPrefix);\n if (normalizedPrefix) {\n query.set(\"prefix\", normalizedPrefix);\n }\n }\n\n const response = await this.context.fetch(\n `${this.host}/hooks/webhooks${query.size > 0 ? `?${query.toString()}` : \"\"}`,\n {\n method: \"GET\",\n headers: serviceHeadersToRecord(\n this.createHookHeaders(\n \"tinycloud.hooks/list\",\n options.service\n ? buildScopePath(options.service, options.pathPrefix)\n : \"webhooks\",\n ),\n ),\n },\n );\n\n if (!response.ok) {\n return err(\n await responseError(\"hooks\", \"failed to list webhooks\", response),\n );\n }\n\n const payload = await response.json();\n const records = normalizeWebhookRecordList(payload);\n if (!records) {\n return err(\n wrapError(\n \"hooks\",\n new Error(\"Webhook list response did not include records\"),\n ),\n );\n }\n\n return ok(records);\n } catch (error) {\n return err(wrapError(\"hooks\", error));\n }\n }\n\n async unregister(\n id: string,\n options: HookWebhookUnregisterOptions = {},\n ): Promise<Result<void>> {\n if (!this.requireAuth()) {\n return err(authRequiredError(\"hooks\"));\n }\n\n try {\n const response = await this.context.fetch(\n `${this.host}/hooks/webhooks/${encodeURIComponent(id)}`,\n {\n method: \"DELETE\",\n headers: serviceHeadersToRecord(\n this.createHookHeaders(\n \"tinycloud.hooks/unregister\",\n options.target\n ? buildScopePath(\n options.target.service,\n options.target.pathPrefix,\n )\n : `webhooks/${id}`,\n ),\n ),\n },\n );\n\n if (!response.ok) {\n return err(\n await responseError(\n \"hooks\",\n \"failed to unregister webhook\",\n response,\n ),\n );\n }\n\n return ok(undefined);\n } catch (error) {\n return err(wrapError(\"hooks\", error));\n }\n }\n\n private async scheduleSharedStreamRefresh(): Promise<void> {\n this._refreshChain = this._refreshChain\n .then(() => this.refreshSharedStream())\n .catch(() => undefined);\n await this._refreshChain;\n }\n\n private async refreshSharedStream(): Promise<void> {\n if (!this.requireAuth() || this._subscribers.size === 0) {\n this.abortSharedStream();\n this._activeSignature = \"\";\n return;\n }\n\n const state = this.collectSharedStreamState();\n if (state.signature !== this._activeSignature) {\n this._activeSignature = state.signature;\n this.abortSharedStream();\n }\n\n if (!this._sharedStreamTask) {\n this._sharedStreamTask = this.runSharedStream(state)\n .catch((error: unknown) => {\n if (!isAbortError(error)) {\n throw error;\n }\n })\n .finally(() => {\n this._sharedStreamTask = undefined;\n this._sharedStreamAbort = undefined;\n if (this._subscribers.size > 0) {\n void this.scheduleSharedStreamRefresh();\n }\n });\n }\n }\n\n private collectSharedStreamState(): {\n subscriptions: HookSubscription[];\n ttlSeconds?: number;\n signature: string;\n } {\n const merged = new Map<string, HookSubscription>();\n const ttlCandidates: number[] = [];\n\n for (const subscriber of this._subscribers) {\n if (typeof subscriber.ttlSeconds === \"number\") {\n ttlCandidates.push(subscriber.ttlSeconds);\n }\n for (const subscription of subscriber.requested) {\n merged.set(subscriptionSignature(subscription), subscription);\n }\n }\n\n const subscriptions = [...merged.values()].sort((left, right) =>\n subscriptionSignature(left).localeCompare(subscriptionSignature(right)),\n );\n const ttlSeconds =\n ttlCandidates.length > 0 ? Math.min(...ttlCandidates) : undefined;\n const signature = JSON.stringify({\n subscriptions: subscriptions.map(subscriptionSignature),\n ttlSeconds,\n });\n\n return {\n subscriptions,\n ttlSeconds,\n signature,\n };\n }\n\n private async runSharedStream(state: {\n subscriptions: HookSubscription[];\n ttlSeconds?: number;\n }): Promise<void> {\n const abortController = new AbortController();\n this._sharedStreamAbort = abortController;\n\n try {\n const host = this._config.host ?? this.context.hosts[0];\n const ticketResponse = await this.mintHookTicket(\n state.subscriptions,\n state.ttlSeconds,\n abortController.signal,\n );\n const streamResponse = await this.openHookStream(\n host,\n ticketResponse.ticket,\n abortController.signal,\n );\n\n for await (const message of parseSseStream(\n streamResponse.body,\n abortController.signal,\n )) {\n if (!message.data) {\n continue;\n }\n const event = parseHookEvent(message);\n for (const subscriber of this._subscribers) {\n if (matchesAnySubscription(event, subscriber.requested)) {\n subscriber.queue.push(event);\n }\n }\n }\n } finally {\n if (this._sharedStreamAbort === abortController) {\n this._sharedStreamAbort = undefined;\n }\n }\n }\n\n private abortSharedStream(): void {\n this._sharedStreamAbort?.abort();\n }\n\n private createHookHeaders(action: string, path: string): ServiceHeaders {\n return this.context.invoke(this.session, \"hooks\", path, action);\n }\n\n private async mintHookTicket(\n subscriptions: HookSubscription[],\n ttlSeconds: number | undefined,\n signal?: AbortSignal,\n ): Promise<HookTicketResponse> {\n const host = this._config.host ?? this.context.hosts[0];\n const headers = this.createInvokeHeaders(subscriptions);\n const ticketResponse = await this.context.fetch(`${host}/hooks/tickets`, {\n method: \"POST\",\n headers: {\n ...serviceHeadersToRecord(headers),\n \"content-type\": \"application/json\",\n },\n body: JSON.stringify({\n subscriptions,\n ttlSeconds,\n }),\n signal,\n });\n\n if (!ticketResponse.ok) {\n throw await responseError(\n \"hooks\",\n \"failed to mint hook ticket\",\n ticketResponse,\n );\n }\n\n const ticketJson = (await ticketResponse.json()) as HookTicketResponse;\n if (!ticketJson?.ticket) {\n throw new Error(\"Hook ticket response did not include a ticket\");\n }\n\n return ticketJson;\n }\n\n private async openHookStream(\n host: string,\n ticket: string,\n signal?: AbortSignal,\n ): Promise<FetchResponse> {\n const streamResponse = await this.context.fetch(\n `${host}/hooks/events?ticket=${encodeURIComponent(ticket)}`,\n {\n method: \"GET\",\n headers: { accept: \"text/event-stream\" },\n signal,\n },\n );\n\n if (!streamResponse.ok) {\n throw await responseError(\n \"hooks\",\n \"failed to open hook stream\",\n streamResponse,\n );\n }\n\n return streamResponse;\n }\n\n private createInvokeHeaders(\n subscriptions: HookSubscription[],\n ): ServiceHeaders {\n const entries: InvokeAnyEntry[] = subscriptions.map((subscription) => ({\n spaceId: subscription.space,\n service: \"hooks\",\n path: subscription.pathPrefix\n ? `${subscription.service}/${subscription.pathPrefix}`\n : subscription.service,\n action: \"tinycloud.hooks/subscribe\",\n }));\n\n if (this.context.invokeAny) {\n return this.context.invokeAny(this.session, entries);\n }\n\n if (entries.length === 1) {\n const entry = entries[0];\n return this.context.invoke(\n this.session,\n entry.service,\n entry.path,\n entry.action,\n );\n }\n\n throw new Error(\n \"This SDK runtime does not support multi-scope hook invocations\",\n );\n }\n}\n\nfunction buildScopePath(\n service: HookSubscription[\"service\"],\n pathPrefix?: string,\n): string {\n const normalized = normalizePathPrefix(pathPrefix);\n return normalized ? `${service}/${normalized}` : service;\n}\n\nfunction normalizeSubscription(\n subscription: HookSubscription,\n): HookSubscription {\n return {\n ...subscription,\n pathPrefix: normalizePathPrefix(subscription.pathPrefix),\n abilities: subscription.abilities ?? [],\n };\n}\n\nfunction subscriptionSignature(subscription: HookSubscription): string {\n return JSON.stringify({\n space: subscription.space,\n service: subscription.service,\n pathPrefix: subscription.pathPrefix ?? \"\",\n abilities: [...(subscription.abilities ?? [])].sort(),\n });\n}\n\nfunction matchesAnySubscription(\n event: HookEvent,\n subscriptions: HookSubscription[],\n): boolean {\n return subscriptions.some((subscription) =>\n matchesSubscription(event, subscription),\n );\n}\n\nfunction matchesSubscription(\n event: HookEvent,\n subscription: HookSubscription,\n): boolean {\n if (event.space !== subscription.space) {\n return false;\n }\n if (event.service !== subscription.service) {\n return false;\n }\n if (subscription.pathPrefix) {\n const prefix = subscription.pathPrefix.endsWith(\"/\")\n ? subscription.pathPrefix\n : `${subscription.pathPrefix}/`;\n if (\n event.path &&\n event.path !== subscription.pathPrefix &&\n !event.path.startsWith(prefix)\n ) {\n return false;\n }\n }\n const abilities = subscription.abilities ?? [];\n if (abilities.length > 0 && !abilities.includes(event.ability)) {\n return false;\n }\n return true;\n}\n\nfunction normalizePathPrefix(pathPrefix?: string): string | undefined {\n if (!pathPrefix) {\n return undefined;\n }\n const trimmed = pathPrefix.replace(/^\\/+|\\/+$/g, \"\");\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction serviceHeadersToRecord(\n headers: ServiceHeaders,\n): Record<string, string> {\n if (Array.isArray(headers)) {\n return Object.fromEntries(headers);\n }\n return { ...headers };\n}\n\nasync function responseError(\n service: string,\n message: string,\n response: FetchResponse,\n): Promise<ReturnType<typeof wrapError>> {\n let detail = response.statusText;\n try {\n const text = await response.text();\n if (text) {\n detail = text;\n }\n } catch {\n // Ignore secondary body read failure.\n }\n const error = wrapError(\n service,\n new Error(`${message}: ${response.status} ${detail}`),\n );\n // Typed status so callers never have to read it back out of the message.\n return {\n ...error,\n meta: { ...error.meta, status: response.status, statusText: response.statusText },\n };\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n\nasync function* parseSseStream(\n body: unknown,\n signal?: AbortSignal,\n): AsyncIterable<HookStreamEvent> {\n if (!body) {\n throw new Error(\"Hook stream response does not expose a readable body\");\n }\n\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n for await (const chunk of readBodyChunks(body, signal)) {\n buffer += decoder.decode(chunk, { stream: true }).replace(/\\r\\n/g, \"\\n\");\n\n let separatorIndex = buffer.indexOf(\"\\n\\n\");\n while (separatorIndex >= 0) {\n const rawEvent = buffer.slice(0, separatorIndex);\n buffer = buffer.slice(separatorIndex + 2);\n const parsed = parseSseEvent(rawEvent);\n if (parsed) {\n yield parsed;\n }\n separatorIndex = buffer.indexOf(\"\\n\\n\");\n }\n }\n\n buffer += decoder.decode();\n const trailing = parseSseEvent(buffer.trim());\n if (trailing) {\n yield trailing;\n }\n}\n\nasync function* readBodyChunks(\n body: unknown,\n signal?: AbortSignal,\n): AsyncIterable<Uint8Array> {\n const asyncIterable = body as AsyncIterable<Uint8Array>;\n if (typeof asyncIterable?.[Symbol.asyncIterator] === \"function\") {\n for await (const chunk of asyncIterable) {\n if (signal?.aborted) {\n break;\n }\n yield chunk;\n }\n return;\n }\n\n const stream = body as {\n getReader?: () => {\n read: () => Promise<{ done: boolean; value?: Uint8Array }>;\n releaseLock?: () => void;\n cancel?: () => Promise<void>;\n };\n };\n\n if (typeof stream.getReader !== \"function\") {\n throw new Error(\"Unsupported hook stream body type\");\n }\n\n const reader = stream.getReader();\n try {\n while (!signal?.aborted) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n if (value) {\n yield value;\n }\n }\n } finally {\n try {\n await reader.cancel?.();\n } catch {\n // Ignore cancellation failures.\n }\n reader.releaseLock?.();\n }\n}\n\nfunction parseSseEvent(rawEvent: string): HookStreamEvent | null {\n if (!rawEvent) {\n return null;\n }\n\n let event = \"message\";\n let id: string | undefined;\n const dataLines: string[] = [];\n\n for (const line of rawEvent.split(\"\\n\")) {\n if (!line || line.startsWith(\":\")) {\n continue;\n }\n const [field, ...rest] = line.split(\":\");\n const value = rest.join(\":\").replace(/^ /, \"\");\n switch (field) {\n case \"event\":\n event = value;\n break;\n case \"id\":\n id = value;\n break;\n case \"data\":\n dataLines.push(value);\n break;\n default:\n break;\n }\n }\n\n if (dataLines.length === 0) {\n return null;\n }\n\n return {\n event,\n id,\n data: dataLines.join(\"\\n\"),\n };\n}\n\nfunction parseHookEvent(message: HookStreamEvent): HookEvent {\n const parsed = JSON.parse(message.data) as Partial<HookEvent>;\n return {\n type: \"write\",\n id: parsed.id ?? message.id ?? \"\",\n space: parsed.space ?? \"\",\n service: parsed.service ?? \"\",\n ability: parsed.ability ?? \"\",\n path: parsed.path,\n actor: parsed.actor ?? \"\",\n epoch: parsed.epoch ?? \"\",\n eventIndex: parsed.eventIndex ?? 0,\n timestamp: parsed.timestamp ?? \"\",\n };\n}\n\nfunction normalizeWebhookRecord(data: unknown): HookWebhookRecord | null {\n if (!data || typeof data !== \"object\") {\n return null;\n }\n\n const record = isRecordContainer(data);\n const candidate =\n pickWebhookRecord(record) ??\n normalizeWebhookRecord(record.webhook) ??\n normalizeWebhookRecord(record.hook) ??\n normalizeWebhookRecord(record.subscription) ??\n normalizeWebhookRecord(record.data);\n return candidate ?? null;\n}\n\nfunction normalizeWebhookRecordList(data: unknown): HookWebhookRecord[] | null {\n if (Array.isArray(data)) {\n const records = data\n .map((entry) => normalizeWebhookRecord(entry))\n .filter((entry): entry is HookWebhookRecord => entry !== null);\n return records;\n }\n\n if (!data || typeof data !== \"object\") {\n return null;\n }\n\n const record = isRecordContainer(data);\n const nested =\n maybeRecordArray(record.webhooks) ??\n maybeRecordArray(record.subscriptions) ??\n maybeRecordArray(record.hooks) ??\n maybeRecordArray(record.data);\n if (nested) {\n return nested;\n }\n\n const single = pickWebhookRecord(record);\n return single ? [single] : null;\n}\n\nfunction maybeRecordArray(value: unknown): HookWebhookRecord[] | null {\n if (!Array.isArray(value)) {\n return null;\n }\n\n const records = value\n .map((entry) => normalizeWebhookRecord(entry))\n .filter((entry): entry is HookWebhookRecord => entry !== null);\n return records;\n}\n\nfunction pickWebhookRecord(\n value: Record<string, unknown>,\n): HookWebhookRecord | null {\n const id = stringField(value, \"id\");\n const space = stringField(value, \"space\") ?? stringField(value, \"spaceId\");\n const service = stringField(value, \"service\");\n const callbackUrl =\n stringField(value, \"callbackUrl\") ?? stringField(value, \"callback_url\");\n if (!id || !space || !service || !callbackUrl) {\n return null;\n }\n\n return {\n id,\n space,\n service: service as HookWebhookRecord[\"service\"],\n pathPrefix:\n optionalStringField(value, \"pathPrefix\") ??\n optionalStringField(value, \"path_prefix\"),\n abilities:\n stringArrayField(value, \"abilities\") ??\n parsedStringArrayField(value, \"abilitiesJson\") ??\n parsedStringArrayField(value, \"abilities_json\"),\n callbackUrl,\n active: booleanField(value, \"active\") ?? true,\n createdAt:\n stringField(value, \"createdAt\") ??\n stringField(value, \"created_at\") ??\n new Date().toISOString(),\n subscriberDid:\n optionalStringField(value, \"subscriberDid\") ??\n optionalStringField(value, \"subscriber_did\"),\n };\n}\n\nfunction isRecordContainer(value: object): Record<string, unknown> {\n return value as Record<string, unknown>;\n}\n\nfunction stringField(\n value: Record<string, unknown>,\n key: string,\n): string | undefined {\n const field = value[key];\n return typeof field === \"string\" ? field : undefined;\n}\n\nfunction optionalStringField(\n value: Record<string, unknown>,\n key: string,\n): string | undefined {\n return stringField(value, key);\n}\n\nfunction booleanField(\n value: Record<string, unknown>,\n key: string,\n): boolean | undefined {\n const field = value[key];\n return typeof field === \"boolean\" ? field : undefined;\n}\n\nfunction stringArrayField(\n value: Record<string, unknown>,\n key: string,\n): string[] | undefined {\n const field = value[key];\n if (!Array.isArray(field)) {\n return undefined;\n }\n const strings = field.filter(\n (item): item is string => typeof item === \"string\",\n );\n return strings.length === field.length ? strings : undefined;\n}\n\nfunction parsedStringArrayField(\n value: Record<string, unknown>,\n key: string,\n): string[] | undefined {\n const field = value[key];\n if (typeof field !== \"string\") {\n return undefined;\n }\n\n try {\n const parsed = JSON.parse(field) as unknown;\n if (!Array.isArray(parsed)) {\n return undefined;\n }\n const strings = parsed.filter(\n (item): item is string => typeof item === \"string\",\n );\n return strings.length === parsed.length ? strings : undefined;\n } catch {\n return undefined;\n }\n}\n","import type { StorageQuotaInfo } from \"../types\";\n\nexport interface QuotaConfig {\n /** Called when a storage quota error is detected (402/413) */\n onUpgradeRequired?: (info: StorageQuotaInfo) => void;\n}\n\nexport interface QuotaStatus {\n /** Storage limit in bytes for this space */\n limitBytes: number;\n /** Storage used in bytes for this space */\n usedBytes?: number;\n /** Remaining storage in bytes */\n remainingBytes?: number;\n}\n\nexport class TinyCloudQuota {\n private config: QuotaConfig;\n private quotaUrl: string | null = null;\n\n constructor(config: QuotaConfig = {}) {\n this.config = config;\n }\n\n /** Set the quota URL discovered from the /info endpoint */\n setQuotaUrl(url: string | null): void {\n this.quotaUrl = url;\n }\n\n /** Whether a quota service is available */\n get available(): boolean {\n return this.quotaUrl !== null;\n }\n\n /** Query quota status for a space from the quota URL */\n async getQuota(spaceId: string): Promise<QuotaStatus | null> {\n if (!this.quotaUrl) return null;\n\n const resp = await fetch(\n `${this.quotaUrl}/api/quota/${encodeURIComponent(spaceId)}`\n );\n if (!resp.ok) return null;\n\n const data = (await resp.json()) as { storage_limit_bytes?: number };\n return {\n limitBytes: data.storage_limit_bytes ?? 0,\n };\n }\n\n /** Trigger the upgrade callback when a quota error is encountered */\n handleQuotaError(info: StorageQuotaInfo): void {\n this.config.onUpgradeRequired?.(info);\n }\n}\n","/**\n * Data Vault Service Types\n *\n * Type definitions for the Data Vault (encrypted KV) service operations.\n */\n\nimport type {\n DecryptCapabilityProof,\n IEncryptionService,\n} from \"../encryption\";\nimport type { PermissionHint } from \"../types\";\n\nexport interface VaultNetworkEncryptionConfig {\n /** Default encryption network used for inline vault envelopes. */\n networkId: string;\n /** TinyCloud encryption module used for local encrypt and node-mediated decrypt. */\n service: IEncryptionService;\n /** Proof material presented to the encryption module for decrypt requests. */\n decryptCapabilityProof?:\n | DecryptCapabilityProof\n | (() => DecryptCapabilityProof | Promise<DecryptCapabilityProof>);\n}\n\n/**\n * Configuration for DataVaultService.\n */\nexport interface DataVaultConfig {\n /** Space ID for encrypted data storage */\n spaceId: string;\n /** Key rotation policy */\n keyRotation?: \"per-write\" | \"per-key\"; // default: \"per-write\"\n /** Network-envelope encryption mode. When set, vault.unlock/key grants are not used. */\n encryption?: VaultNetworkEncryptionConfig;\n}\n\n/**\n * Options for vault put operations.\n */\nexport interface VaultPutOptions {\n /** Custom metadata tags appended to the envelope */\n metadata?: Record<string, string>;\n /** Content type hint for deserialization (default: auto-detect) */\n contentType?: string;\n /** Custom serializer (default: JSON.stringify for objects) */\n serialize?: (value: unknown) => Uint8Array;\n}\n\n/**\n * Options for vault get operations.\n */\nexport interface VaultGetOptions<T = unknown> {\n /** Custom deserializer (default: JSON.parse if content-type is JSON) */\n deserialize?: (data: Uint8Array) => T;\n /** Return raw decrypted bytes without deserialization */\n raw?: boolean;\n /** Delegated KV service for reading from the grantor's space (used by getShared) */\n kv?: { get<V>(key: string, options?: { raw?: boolean }): Promise<{ ok: boolean; data?: { data: V }; error?: { message: string } }> };\n}\n\n/**\n * Options for vault list operations.\n */\nexport interface VaultListOptions {\n /** Prefix filter for key names */\n prefix?: string;\n /** Remove prefix from returned keys */\n removePrefix?: boolean;\n /** Maximum number of keys to return. */\n limit?: number;\n /** Opaque cursor returned by a prior bounded page. */\n cursor?: string;\n}\n\nexport interface VaultListPage {\n keys: string[];\n truncated: boolean;\n nextCursor?: string;\n}\n\n/**\n * Options for vault grant (sharing) operations.\n */\nexport interface VaultGrantOptions {\n /** Additional metadata on the grant */\n metadata?: Record<string, string>;\n}\n\n/**\n * A decrypted vault entry returned by get operations.\n *\n * @template T - Type of the decrypted value\n */\nexport interface VaultEntry<T> {\n /** Decrypted value */\n value: T;\n /** Envelope metadata */\n metadata: Record<string, string>;\n /** Key ID used for encryption */\n keyId: string;\n}\n\n/**\n * Safe classification for a network-encrypted vault read.\n *\n * Failure variants intentionally contain no node response, encrypted envelope,\n * plaintext, or key material. Consumers that need to distinguish an authorized\n * absence from a failed read should use this instead of {@link IDataVaultService.get}.\n */\nexport type VaultNetworkReadResult<T> =\n | { status: \"ok\"; entry: VaultEntry<T> }\n | { status: \"not_found\" }\n | { status: \"node_unreachable\" }\n | { status: \"permission_required\"; hint: PermissionHint }\n | { status: \"read_failed\" }\n | { status: \"corrupt_envelope\" }\n | { status: \"decrypt_failed\" }\n | { status: \"invalid_payload\" };\n\n/**\n * Structured error codes for vault operations.\n */\n/**\n * Input types for creating vault errors (service field added automatically).\n */\nexport type VaultErrorInput =\n | { code: \"DECRYPTION_FAILED\"; message?: string; cause?: Error }\n | { code: \"KEY_NOT_FOUND\"; key: string; message?: string }\n | { code: \"INTEGRITY_ERROR\"; message?: string; cause?: Error }\n | { code: \"GRANT_NOT_FOUND\"; grantor: string; key: string; message?: string }\n | { code: \"VAULT_LOCKED\"; message?: string }\n | { code: \"PUBLIC_KEY_NOT_FOUND\"; did: string; message?: string }\n | { code: \"STORAGE_ERROR\"; cause: Error; message?: string }\n | {\n code: \"STORAGE_QUOTA_EXCEEDED\" | \"STORAGE_LIMIT_REACHED\";\n message: string;\n meta?: Record<string, unknown>;\n };\n\n/**\n * Vault error with service field (compatible with ServiceError).\n */\nexport type VaultError = VaultErrorInput & { service: \"vault\"; message: string };\n\n\n/** KV actions the vault needs on the public space for key publishing. */\nexport const VaultPublicSpaceKVActions = [\n \"tinycloud.kv/get\",\n \"tinycloud.kv/put\",\n \"tinycloud.kv/metadata\",\n] as const;\n\n/** Version-keyed signing message configuration for vault key derivation. */\nexport const VaultVersionConfig = {\n \"1\": {\n masterMessage: (spaceId: string) => `tinycloud-vault-master-v1:${spaceId}`,\n identityMessage: \"tinycloud-encryption-identity-v1\",\n },\n} as const;\n\nexport const CURRENT_VAULT_VERSION = \"1\" as const;\n\n/** Metadata header keys used in vault envelopes */\nexport const VaultHeaders = {\n VERSION: \"x-vault-version\",\n CIPHER: \"x-vault-cipher\",\n KEY_ID: \"x-vault-key-id\",\n CONTENT_TYPE: \"x-vault-content-type\",\n KDF: \"x-vault-kdf\",\n KEY_ROTATION: \"x-vault-key-rotation\",\n GRANT_VERSION: \"x-vault-grant-version\",\n GRANTOR: \"x-vault-grantor\",\n} as const;\n","/**\n * Caches vault signatures in IndexedDB, encrypted with a\n * non-extractable CryptoKey. Browser-only — no-ops in Node.\n */\n\n// Minimal type declarations for browser APIs used below.\n// The sdk-services tsconfig targets ES2020 without DOM lib;\n// these declarations let the file compile while the runtime\n// guards (isBrowser()) ensure we never call them in Node.\ndeclare const indexedDB: {\n open(name: string, version?: number): IDBOpenDBRequest;\n};\ninterface IDBOpenDBRequest {\n result: IDBDatabase;\n error: DOMException | null;\n onupgradeneeded: ((this: IDBOpenDBRequest) => void) | null;\n onsuccess: ((this: IDBOpenDBRequest) => void) | null;\n onerror: ((this: IDBOpenDBRequest) => void) | null;\n}\ninterface IDBDatabase {\n objectStoreNames: { contains(name: string): boolean };\n createObjectStore(name: string): void;\n transaction(storeNames: string | string[], mode?: string): IDBTransaction;\n}\ninterface IDBTransaction {\n objectStore(name: string): IDBObjectStore;\n}\ninterface IDBObjectStore {\n get(key: string): IDBRequest;\n put(value: unknown, key: string): IDBRequest;\n delete(key: string): IDBRequest;\n getAllKeys(): IDBRequest;\n}\ninterface IDBRequest<T = unknown> {\n result: T;\n error: DOMException | null;\n onsuccess: ((this: IDBRequest<T>) => void) | null;\n onerror: ((this: IDBRequest<T>) => void) | null;\n}\ntype IDBValidKey = string | number | Date | ArrayBuffer | Uint8Array | IDBValidKey[];\n\nconst DB_NAME = \"tinycloud-vault-cache\";\nconst DB_VERSION = 1;\nconst STORE_NAME = \"signatures\";\nconst WRAP_KEY_ID = \"__wrap_key__\";\n\n/** Check whether we're in a browser with IndexedDB + SubtleCrypto support. */\nfunction isBrowser(): boolean {\n try {\n return (\n typeof indexedDB !== \"undefined\" &&\n typeof crypto !== \"undefined\" &&\n typeof crypto.subtle !== \"undefined\"\n );\n } catch {\n return false;\n }\n}\n\n/** Open (or create) the IndexedDB database. */\nfunction openDB(): Promise<IDBDatabase> {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n db.createObjectStore(STORE_NAME);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n });\n}\n\n/** Generic IDB get helper. */\nfunction idbGet<T>(db: IDBDatabase, key: string): Promise<T | undefined> {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readonly\");\n const store = tx.objectStore(STORE_NAME);\n const req = store.get(key);\n req.onsuccess = () => resolve(req.result as T | undefined);\n req.onerror = () => reject(req.error);\n });\n}\n\n/** Generic IDB put helper. */\nfunction idbPut(db: IDBDatabase, key: string, value: unknown): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n const store = tx.objectStore(STORE_NAME);\n const req = store.put(value, key);\n req.onsuccess = () => resolve();\n req.onerror = () => reject(req.error);\n });\n}\n\n/** Generic IDB delete helper. */\nfunction idbDelete(db: IDBDatabase, key: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readwrite\");\n const store = tx.objectStore(STORE_NAME);\n const req = store.delete(key);\n req.onsuccess = () => resolve();\n req.onerror = () => reject(req.error);\n });\n}\n\n/** List all keys in the store. */\nfunction idbKeys(db: IDBDatabase): Promise<string[]> {\n return new Promise((resolve, reject) => {\n const tx = db.transaction(STORE_NAME, \"readonly\");\n const store = tx.objectStore(STORE_NAME);\n const req = store.getAllKeys();\n req.onsuccess = () =>\n resolve((req.result as IDBValidKey[]).filter((k) => typeof k === \"string\") as string[]);\n req.onerror = () => reject(req.error);\n });\n}\n\ninterface EncryptedEntry {\n iv: Uint8Array;\n ciphertext: Uint8Array;\n}\n\n/**\n * Get or create the non-extractable AES-GCM wrapping key.\n * The key is stored directly in IndexedDB (structured-clone preserves CryptoKey).\n */\nasync function getWrapKey(db: IDBDatabase): Promise<CryptoKey> {\n const existing = await idbGet<CryptoKey>(db, WRAP_KEY_ID);\n if (existing) return existing;\n\n const key = await crypto.subtle.generateKey(\n { name: \"AES-GCM\", length: 256 },\n false, // non-extractable\n [\"encrypt\", \"decrypt\"]\n );\n await idbPut(db, WRAP_KEY_ID, key);\n return key;\n}\n\n/** Encrypt signature bytes with the wrap key. */\nasync function encryptSig(\n wrapKey: CryptoKey,\n sigBytes: Uint8Array\n): Promise<EncryptedEntry> {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const ciphertext = new Uint8Array(\n await crypto.subtle.encrypt({ name: \"AES-GCM\", iv }, wrapKey, sigBytes)\n );\n return { iv, ciphertext };\n}\n\n/** Decrypt an encrypted entry back to signature bytes. */\nasync function decryptSig(\n wrapKey: CryptoKey,\n entry: EncryptedEntry\n): Promise<Uint8Array> {\n const plaintext = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv: entry.iv },\n wrapKey,\n entry.ciphertext\n );\n return new Uint8Array(plaintext);\n}\n\n/** Cache key for a given spaceId. */\nfunction cacheKey(spaceId: string): string {\n return `sig:${spaceId}`;\n}\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Load a cached signature for the given spaceId.\n * Returns null on cache miss or in non-browser environments.\n */\nexport async function loadCachedSignature(\n spaceId: string\n): Promise<Uint8Array | null> {\n if (!isBrowser()) return null;\n try {\n const db = await openDB();\n const entry = await idbGet<EncryptedEntry>(db, cacheKey(spaceId));\n if (!entry) return null;\n const wrapKey = await getWrapKey(db);\n return await decryptSig(wrapKey, entry);\n } catch {\n return null;\n }\n}\n\n/**\n * Cache a signature for the given spaceId.\n * No-ops in non-browser environments.\n */\nexport async function cacheSignature(\n spaceId: string,\n sigBytes: Uint8Array\n): Promise<void> {\n if (!isBrowser()) return;\n try {\n const db = await openDB();\n const wrapKey = await getWrapKey(db);\n const encrypted = await encryptSig(wrapKey, sigBytes);\n await idbPut(db, cacheKey(spaceId), encrypted);\n } catch {\n // Best-effort — swallow errors\n }\n}\n\n/**\n * Clear cached signature(s).\n * If spaceId is provided, clears only that entry; otherwise clears all.\n */\nexport async function clearSignatureCache(\n spaceId?: string\n): Promise<void> {\n if (!isBrowser()) return;\n try {\n const db = await openDB();\n if (spaceId) {\n await idbDelete(db, cacheKey(spaceId));\n } else {\n // Clear all sig entries but keep the wrap key\n const keys = await idbKeys(db);\n for (const k of keys) {\n if (k.startsWith(\"sig:\")) {\n await idbDelete(db, k);\n }\n }\n }\n } catch {\n // Best-effort\n }\n}\n","/**\n * DataVaultService - Encrypted key-value storage service implementation.\n *\n * Platform-agnostic encrypted KV service that wraps KVService internally.\n * Uses dependency injection via VaultCrypto for WASM crypto operations\n * and DataVaultServiceConfig for platform dependencies.\n *\n * Architecture:\n * - Extends BaseService (not KVService)\n * - Wraps two KV instances: dataKV (prefix \"vault/\") and keyKV (prefix \"keys/\")\n * - Master key and encryption identity live in memory only (cleared on lock)\n */\n\nimport { BaseService } from \"../base/BaseService\";\nimport {\n Result,\n ok,\n err,\n ErrorCodes,\n serviceError,\n IServiceContext,\n ServiceSession,\n} from \"../types\";\nimport { parsePermissionHint, wrapError } from \"../errors\";\nimport type { IKVService } from \"../kv/IKVService\";\nimport type { KVService } from \"../kv/KVService\";\nimport type {\n DecryptCapabilityProof,\n InlineEncryptedEnvelope,\n} from \"../encryption\";\nimport type { IDataVaultService } from \"./IDataVaultService\";\nimport {\n DataVaultConfig,\n VaultPutOptions,\n VaultGetOptions,\n VaultListOptions,\n VaultGrantOptions,\n VaultEntry,\n VaultNetworkReadResult,\n VaultError,\n VaultErrorInput,\n VaultHeaders,\n VaultVersionConfig,\n CURRENT_VAULT_VERSION,\n VaultListPage,\n} from \"./types\";\nimport {\n loadCachedSignature,\n cacheSignature,\n clearSignatureCache,\n} from \"./SignatureCache\";\n\n// =============================================================================\n// Crypto Interface\n// =============================================================================\n\n/**\n * Crypto operations interface - implementations provided by WASM bindings.\n * Passed via DataVaultServiceConfig to keep the service platform-agnostic.\n */\nexport interface VaultCrypto {\n encrypt(key: Uint8Array, plaintext: Uint8Array): Uint8Array;\n decrypt(key: Uint8Array, blob: Uint8Array): Uint8Array;\n deriveKey(\n signature: Uint8Array,\n salt: Uint8Array,\n info: Uint8Array\n ): Uint8Array;\n x25519FromSeed(\n seed: Uint8Array\n ): { publicKey: Uint8Array; privateKey: Uint8Array };\n x25519Dh(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;\n randomBytes(length: number): Uint8Array;\n sha256(data: Uint8Array): Uint8Array;\n}\n\n// =============================================================================\n// Extended Config\n// =============================================================================\n\n/**\n * Extended config used internally by DataVaultService.\n * Includes crypto operations and TinyCloud instance references.\n */\ninterface DataVaultServiceConfig extends DataVaultConfig {\n [key: string]: unknown;\n /** Crypto operations (WASM bindings) */\n crypto: VaultCrypto;\n /** TinyCloud instance for space/kv/delegation operations */\n tc: {\n kv: IKVService;\n ensurePublicSpace(): Promise<Result<void>>;\n publicKV: IKVService;\n readPublicSpace<T>(\n host: string,\n spaceId: string,\n key: string\n ): Promise<Result<T>>;\n makePublicSpaceId(address: string, chainId: number): string;\n did: string;\n address: string;\n chainId: number;\n hosts: string[];\n };\n}\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/** Convert a caught value to an Error. WASM throws plain objects, not Error instances. */\nfunction toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === \"object\" && error !== null) {\n return new Error(JSON.stringify(error));\n }\n return new Error(String(error));\n}\n\nfunction toBytes(str: string): Uint8Array {\n return new TextEncoder().encode(str);\n}\n\nfunction fromBytes(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes);\n}\n\nfunction hexEncode(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\nfunction concatBytes(...arrays: Uint8Array[]): Uint8Array {\n const total = arrays.reduce((acc, arr) => acc + arr.length, 0);\n const result = new Uint8Array(total);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\nfunction base64Encode(bytes: Uint8Array): string {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\nfunction base64Decode(str: string): Uint8Array {\n const binary = atob(str);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n}\n\nfunction unwrapKVData<T = unknown>(value: unknown): T {\n if (value !== null && typeof value === \"object\" && \"data\" in value) {\n return (value as { data: T }).data;\n }\n return value as T;\n}\n\nfunction hasHttpResponse(error: { meta?: Record<string, unknown> }): boolean {\n return typeof error.meta?.status === \"number\";\n}\n\nfunction isUnlockSigner(\n signer: unknown\n): signer is { signMessage(message: string): Promise<string> } {\n return (\n typeof signer === \"object\" &&\n signer !== null &&\n typeof (signer as { signMessage?: unknown }).signMessage === \"function\"\n );\n}\n\nfunction defaultVaultMessage(input: VaultErrorInput): string {\n switch (input.code) {\n case \"DECRYPTION_FAILED\": return input.message ?? \"Decryption failed\";\n case \"KEY_NOT_FOUND\": return input.message ?? `Key not found: ${input.key}`;\n case \"INTEGRITY_ERROR\": return input.message ?? \"Integrity check failed\";\n case \"GRANT_NOT_FOUND\": return input.message ?? `Grant not found: ${input.grantor} / ${input.key}`;\n case \"VAULT_LOCKED\": return input.message ?? \"Vault is locked\";\n case \"PUBLIC_KEY_NOT_FOUND\": return input.message ?? `Public key not found for ${input.did}`;\n case \"STORAGE_ERROR\": return input.message ?? input.cause.message;\n case \"STORAGE_QUOTA_EXCEEDED\":\n case \"STORAGE_LIMIT_REACHED\":\n return input.message;\n }\n}\n\nfunction vaultError(input: VaultErrorInput): Result<never, VaultError> {\n const error: VaultError = {\n ...input,\n service: \"vault\",\n message: defaultVaultMessage(input),\n };\n return { ok: false, error };\n}\n\n/**\n * Wrap a failed KV write. A storage rejection keeps its code, message and\n * byte counts so callers can tell \"storage full\" from other failures.\n */\nfunction kvWriteError(\n context: string,\n error: { code: string; message: string; meta?: Record<string, unknown> }\n): Result<never, VaultError> {\n if (\n error.code === ErrorCodes.STORAGE_QUOTA_EXCEEDED ||\n error.code === ErrorCodes.STORAGE_LIMIT_REACHED\n ) {\n return vaultError({ code: error.code, message: error.message, meta: error.meta });\n }\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(`${context}: ${error.message}`),\n });\n}\n\n// =============================================================================\n// DataVaultService\n// =============================================================================\n\n/**\n * Data Vault service implementation.\n *\n * Provides encrypted key-value storage with client-side encryption,\n * key management, and sharing via X25519 grants.\n *\n * @example\n * ```typescript\n * // Unlock the vault\n * await vault.unlock(signer);\n *\n * // Store encrypted data\n * await vault.put('secret/notes', { content: 'Hello' });\n *\n * // Retrieve and decrypt\n * const entry = await vault.get<{ content: string }>('secret/notes');\n * if (entry.ok) {\n * console.log(entry.data.value.content); // 'Hello'\n * }\n *\n * // Share with another user\n * await vault.grant('secret/notes', recipientDID);\n * ```\n */\nexport class DataVaultService extends BaseService implements IDataVaultService {\n /**\n * Service identifier for registration.\n */\n static readonly serviceName = \"vault\";\n\n /**\n * Service configuration.\n */\n declare protected _config: DataVaultServiceConfig;\n\n private masterKey: Uint8Array | null = null;\n private encryptionIdentity: {\n publicKey: Uint8Array;\n privateKey: Uint8Array;\n } | null = null;\n private _isUnlocked = false;\n private vaultConfig: DataVaultServiceConfig;\n private unlockInFlight: Promise<Result<void, VaultError>> | null = null;\n\n /**\n * Create a new DataVaultService instance.\n *\n * @param config - Service configuration including crypto and tc references\n */\n constructor(config: DataVaultServiceConfig) {\n super();\n this.vaultConfig = config;\n this._config = config;\n }\n\n /**\n * Get the service configuration.\n */\n get config(): DataVaultServiceConfig {\n return this._config;\n }\n\n /**\n * Whether the vault is currently unlocked.\n */\n get isUnlocked(): boolean {\n return this.usesNetworkEncryption || this._isUnlocked;\n }\n\n /**\n * The vault's public encryption key (X25519).\n * Throws if vault is locked.\n */\n get publicKey(): Uint8Array {\n if (this.usesNetworkEncryption) {\n throw new Error(\"Network-encrypted vaults do not expose a local public key\");\n }\n if (!this.encryptionIdentity) {\n throw new Error(\"Vault is locked\");\n }\n return this.encryptionIdentity.publicKey;\n }\n\n /**\n * Convenience accessor for crypto operations.\n */\n private get crypto(): VaultCrypto {\n return this.vaultConfig.crypto;\n }\n\n /**\n * Convenience accessor for TinyCloud instance.\n */\n private get tc() {\n return this.vaultConfig.tc;\n }\n\n private get networkEncryption() {\n return this.vaultConfig.encryption;\n }\n\n private get usesNetworkEncryption(): boolean {\n return this.networkEncryption !== undefined;\n }\n\n /**\n * Get the host URL.\n */\n private get host(): string {\n return this.tc.hosts[0];\n }\n\n private async decryptCapabilityProof(): Promise<DecryptCapabilityProof> {\n const proof = this.networkEncryption?.decryptCapabilityProof;\n if (typeof proof === \"function\") {\n return await proof();\n }\n return proof ?? { proofs: [] };\n }\n\n private serializeValue(\n value: unknown,\n options?: VaultPutOptions,\n ): { plaintext: Uint8Array; contentType: string } {\n let plaintext: Uint8Array;\n if (value instanceof Uint8Array) {\n plaintext = value;\n } else if (options?.serialize) {\n plaintext = options.serialize(value);\n } else if (typeof value === \"string\") {\n plaintext = toBytes(value);\n } else {\n plaintext = toBytes(JSON.stringify(value));\n }\n\n const contentType =\n options?.contentType ??\n (value instanceof Uint8Array ? \"application/octet-stream\" : \"application/json\");\n return { plaintext, contentType };\n }\n\n private deserializeValue<T>(\n plaintext: Uint8Array,\n contentType: string,\n options?: VaultGetOptions<T>,\n ): T {\n if (options?.raw) {\n return plaintext as unknown as T;\n }\n if (options?.deserialize) {\n return options.deserialize(plaintext);\n }\n if (contentType === \"application/json\") {\n return JSON.parse(fromBytes(plaintext)) as T;\n }\n return plaintext as unknown as T;\n }\n\n // =========================================================================\n // Phase 1: Core Operations\n // =========================================================================\n\n /**\n * Unlock the vault. Derives keys from two wallet signatures:\n * 1. Master signature (per-space) — used to derive the master encryption key\n * 2. Identity signature (per-address) — used to derive X25519 encryption identity\n *\n * If the identity public key already exists in the public space, the identity\n * signature is skipped entirely (no wallet popup). The identity private key is\n * only needed for sharing operations.\n *\n * @param signer - Object with signMessage method. Optional when cached\n * signatures exist (browser only).\n */\n async unlock(\n signer?: { signMessage(message: string): Promise<string> } | unknown\n ): Promise<Result<void, VaultError>> {\n if (this.usesNetworkEncryption) {\n this._isUnlocked = true;\n return { ok: true, data: undefined };\n }\n\n const unlockSigner = isUnlockSigner(signer) ? signer : undefined;\n if (\n this._isUnlocked &&\n this.masterKey &&\n (this.encryptionIdentity || !unlockSigner)\n ) {\n return { ok: true, data: undefined };\n }\n\n if (this.unlockInFlight) {\n return this.unlockInFlight;\n }\n\n this.unlockInFlight = this.withTelemetry(\"unlock\", undefined, async () => {\n const spaceId = this.vaultConfig.spaceId;\n const versionConfig = VaultVersionConfig[CURRENT_VAULT_VERSION];\n const masterCacheKey = `vault-master:${spaceId}`;\n const identityCacheKey = `vault-identity:${this.tc.address}`;\n\n try {\n // -----------------------------------------------------------------\n // Step 1: Master signature → master key\n // -----------------------------------------------------------------\n if (!this.masterKey) {\n let masterSigBytes = await loadCachedSignature(masterCacheKey);\n\n if (!masterSigBytes) {\n if (!unlockSigner) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Signer is required when no cached master signature exists\",\n });\n }\n const sig = await unlockSigner.signMessage(\n versionConfig.masterMessage(spaceId)\n );\n masterSigBytes = toBytes(sig);\n await cacheSignature(masterCacheKey, masterSigBytes);\n }\n\n // Derive master key: deriveKey(sigBytes, sha256(spaceId), \"vault-master\")\n this.masterKey = this.crypto.deriveKey(\n masterSigBytes,\n this.crypto.sha256(toBytes(spaceId)),\n toBytes(\"vault-master\")\n );\n }\n\n // -----------------------------------------------------------------\n // Step 2: Identity — check public space first, then sign if needed\n // -----------------------------------------------------------------\n const publicSpaceId = this.tc.makePublicSpaceId(this.tc.address, this.tc.chainId);\n\n // Check public space for existing vault pubkey\n let existingPubKey: string | null = null;\n try {\n const existing = await this.tc.readPublicSpace<string>(\n this.host, publicSpaceId, \".well-known/vault-pubkey\"\n );\n if (existing.ok && existing.data) {\n existingPubKey = existing.data as string;\n }\n } catch {\n // Read failed — treat as missing\n }\n\n if (existingPubKey) {\n // Public key exists — trust it, skip identity signing entirely\n this.encryptionIdentity = {\n publicKey: base64Decode(existingPubKey),\n privateKey: new Uint8Array(0), // private key not available without signing\n };\n } else {\n // Public key missing — need identity signature to derive keypair\n let identitySigBytes = await loadCachedSignature(identityCacheKey);\n\n if (!identitySigBytes) {\n if (!unlockSigner) {\n // No signer available — skip identity derivation.\n // Vault still works for get/put (only needs master key).\n this.encryptionIdentity = null;\n this._isUnlocked = true;\n return ok(undefined);\n }\n const sig = await unlockSigner.signMessage(\n versionConfig.identityMessage\n );\n identitySigBytes = toBytes(sig);\n await cacheSignature(identityCacheKey, identitySigBytes);\n }\n\n // Derive X25519 keypair from identity signature\n const seed = this.crypto.deriveKey(\n identitySigBytes,\n toBytes(\"tinycloud-x25519\"),\n toBytes(\"encryption-identity\")\n );\n this.encryptionIdentity = this.crypto.x25519FromSeed(seed);\n\n // Publish public key to public space\n try {\n const pubKeyB64 = base64Encode(this.encryptionIdentity.publicKey);\n await this.tc.ensurePublicSpace();\n await this.tc.publicKV.put(\".well-known/vault-pubkey\", pubKeyB64);\n await this.tc.publicKV.put(\".well-known/vault-version\", CURRENT_VAULT_VERSION);\n await this.tc.publicKV.put(\".well-known/vault-space\", this.vaultConfig.spaceId);\n } catch {\n // Publishing failed — vault still usable\n }\n }\n\n this._isUnlocked = true;\n return ok(undefined);\n } catch (error) {\n // Clear key material on failure\n this.masterKey = null;\n this.encryptionIdentity = null;\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<void, VaultError>>;\n\n try {\n return await this.unlockInFlight;\n } finally {\n this.unlockInFlight = null;\n }\n }\n\n /**\n * Clear the cached vault signatures.\n *\n * @param spaceId - Clear only this space's master cache. If omitted, clears all.\n */\n async clearCache(spaceId?: string): Promise<void> {\n if (spaceId) {\n await clearSignatureCache(`vault-master:${spaceId}`);\n } else {\n await clearSignatureCache();\n }\n }\n\n /**\n * Lock the vault, clearing all key material from memory.\n */\n lock(): void {\n this.masterKey = null;\n this.encryptionIdentity = null;\n this._isUnlocked = false;\n }\n\n /**\n * Called when SDK signs out. Locks the vault and aborts operations.\n */\n onSignOut(): void {\n this.lock();\n super.onSignOut();\n }\n\n private async putNetworkEncrypted(\n key: string,\n value: unknown,\n options?: VaultPutOptions,\n ): Promise<Result<void, VaultError>> {\n const config = this.networkEncryption;\n if (!config) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Network encryption is not configured\",\n });\n }\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n const { plaintext, contentType } = this.serializeValue(value, options);\n const metadata: Record<string, string> = {\n [VaultHeaders.VERSION]: \"2\",\n [VaultHeaders.CIPHER]: \"tinycloud-network-envelope\",\n [VaultHeaders.CONTENT_TYPE]: contentType,\n ...(options?.metadata ?? {}),\n };\n const aad = toBytes(`tinycloud.vault:${this.vaultConfig.spaceId}:${key}`);\n const envelopeResult = await config.service.encryptToNetwork(\n config.networkId,\n plaintext,\n { aad, metadata },\n );\n if (!envelopeResult.ok) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(envelopeResult.error.message),\n });\n }\n\n const valuePutResult = await this.tc.kv.put(\n `vault/${key}`,\n JSON.stringify(envelopeResult.data),\n );\n if (!valuePutResult.ok) {\n return kvWriteError(\"Failed to store encrypted value\", valuePutResult.error);\n }\n\n return { ok: true, data: undefined };\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: toError(error),\n });\n }\n }\n\n private async getNetworkEncrypted<T = unknown>(\n key: string,\n options?: VaultGetOptions<T>,\n ): Promise<Result<VaultEntry<T>, VaultError>> {\n const config = this.networkEncryption;\n if (!config) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Network encryption is not configured\",\n });\n }\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n const valueResult = await this.tc.kv.get<string>(`vault/${key}`, {\n raw: true,\n });\n if (!valueResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n\n const rawEnvelope = unwrapKVData<string>(valueResult.data);\n const envelope = (\n typeof rawEnvelope === \"string\" ? JSON.parse(rawEnvelope) : rawEnvelope\n ) as InlineEncryptedEnvelope;\n const proof = await this.decryptCapabilityProof();\n const plaintextResult = await config.service.decryptEnvelope(envelope, proof);\n if (!plaintextResult.ok) {\n return vaultError({\n code: \"DECRYPTION_FAILED\",\n message: plaintextResult.error.message,\n });\n }\n\n const metadata: Record<string, string> = envelope.metadata ?? {};\n const contentType =\n metadata[VaultHeaders.CONTENT_TYPE] ?? \"application/json\";\n const keyId =\n metadata[VaultHeaders.KEY_ID] ??\n envelope.encryptedSymmetricKeyHash.slice(0, 16);\n const value = this.deserializeValue<T>(\n plaintextResult.data,\n contentType,\n options,\n );\n\n return { ok: true, data: { value, metadata, keyId } };\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: toError(error),\n });\n }\n }\n\n /**\n * Read a network-encrypted entry while preserving safe failure phases.\n *\n * Unlike {@link getNetworkEncrypted}, this is intentionally message-free so\n * an operation layer can distinguish an authorized absence from a failed\n * read without receiving node, envelope, plaintext, or key material.\n */\n async readNetworkEncrypted<T = unknown>(\n key: string,\n options?: VaultGetOptions<T>,\n ): Promise<VaultNetworkReadResult<T>> {\n const config = this.networkEncryption;\n if (!config || !this.requireAuth()) {\n return { status: \"read_failed\" };\n }\n\n let valueResult: Awaited<ReturnType<IKVService[\"get\"]>>;\n try {\n valueResult = await this.tc.kv.get<string>(`vault/${key}`, { raw: true });\n } catch {\n return { status: \"node_unreachable\" };\n }\n\n if (!valueResult.ok) {\n const permissionHint = parsePermissionHint(valueResult.error.meta?.permissionHint);\n if (valueResult.error.code === \"AUTH_UNAUTHORIZED\" && permissionHint !== undefined) {\n return { status: \"permission_required\", hint: permissionHint };\n }\n if (\n (valueResult.error.code === \"NOT_FOUND\" ||\n valueResult.error.code === \"KV_NOT_FOUND\") &&\n !hasHttpResponse(valueResult.error)\n ) {\n return { status: \"not_found\" };\n }\n if (\n valueResult.error.code === \"NETWORK_ERROR\" ||\n valueResult.error.code === \"TIMEOUT\" ||\n valueResult.error.code === \"ABORTED\"\n ) {\n return hasHttpResponse(valueResult.error)\n ? { status: \"read_failed\" }\n : { status: \"node_unreachable\" };\n }\n return { status: \"read_failed\" };\n }\n\n let envelope: InlineEncryptedEnvelope;\n try {\n const rawEnvelope = unwrapKVData<string>(valueResult.data);\n envelope = (\n typeof rawEnvelope === \"string\" ? JSON.parse(rawEnvelope) : rawEnvelope\n ) as InlineEncryptedEnvelope;\n } catch {\n return { status: \"corrupt_envelope\" };\n }\n\n let plaintextResult: Awaited<ReturnType<typeof config.service.decryptEnvelope>>;\n try {\n const proof = await this.decryptCapabilityProof();\n plaintextResult = await config.service.decryptEnvelope(envelope, proof);\n } catch {\n return { status: \"decrypt_failed\" };\n }\n\n if (!plaintextResult.ok) {\n const permissionHint = parsePermissionHint(\n (plaintextResult.error as { permissionHint?: unknown }).permissionHint,\n );\n if (plaintextResult.error.code === \"DECRYPT_DENIED\" && permissionHint !== undefined) {\n return { status: \"permission_required\", hint: permissionHint };\n }\n if (plaintextResult.error.code === \"INVALID_ENVELOPE\") {\n return { status: \"corrupt_envelope\" };\n }\n if (plaintextResult.error.code === \"TRANSPORT_ERROR\") {\n return { status: \"node_unreachable\" };\n }\n return { status: \"decrypt_failed\" };\n }\n\n try {\n const metadata: Record<string, string> = envelope.metadata ?? {};\n const contentType =\n metadata[VaultHeaders.CONTENT_TYPE] ?? \"application/json\";\n const keyId =\n metadata[VaultHeaders.KEY_ID] ??\n envelope.encryptedSymmetricKeyHash.slice(0, 16);\n const value = this.deserializeValue<T>(\n plaintextResult.data,\n contentType,\n options,\n );\n return { status: \"ok\", entry: { value, metadata, keyId } };\n } catch {\n return { status: \"invalid_payload\" };\n }\n }\n\n private async headNetworkEncrypted(\n key: string,\n ): Promise<Result<Record<string, string>, VaultError>> {\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n const valueResult = await this.tc.kv.get<string>(`vault/${key}`, {\n raw: true,\n });\n if (!valueResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n const rawEnvelope = unwrapKVData<string>(valueResult.data);\n const envelope = (\n typeof rawEnvelope === \"string\" ? JSON.parse(rawEnvelope) : rawEnvelope\n ) as InlineEncryptedEnvelope;\n return { ok: true, data: envelope.metadata ?? {} };\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: toError(error),\n });\n }\n }\n\n /**\n * Encrypt and store a value at the given key.\n *\n * @param key - The key to store under\n * @param value - The value to encrypt and store\n * @param options - Optional put configuration\n */\n async put(\n key: string,\n value: unknown,\n options?: VaultPutOptions\n ): Promise<Result<void, VaultError>> {\n return this.withTelemetry(\"put\", key, async () => {\n if (this.usesNetworkEncryption) {\n return this.putNetworkEncrypted(key, value, options);\n }\n\n if (!this._isUnlocked || !this.masterKey) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before storing data\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n // Serialize value\n let plaintext: Uint8Array;\n if (value instanceof Uint8Array) {\n plaintext = value;\n } else if (options?.serialize) {\n plaintext = options.serialize(value);\n } else if (typeof value === \"string\") {\n plaintext = toBytes(value);\n } else {\n plaintext = toBytes(JSON.stringify(value));\n }\n\n const contentType =\n options?.contentType ??\n (value instanceof Uint8Array\n ? \"application/octet-stream\"\n : \"application/json\");\n\n // Generate per-entry key\n const entryKey = this.crypto.randomBytes(32);\n const keyId = hexEncode(this.crypto.sha256(entryKey)).slice(0, 16);\n\n // Encrypt value with entry key\n const encrypted = this.crypto.encrypt(entryKey, plaintext);\n\n // Encrypt entry key with master key\n const keyBlob = this.crypto.encrypt(this.masterKey, entryKey);\n\n // Build metadata\n const metadata: Record<string, string> = {\n [VaultHeaders.VERSION]: \"1\",\n [VaultHeaders.CIPHER]: \"aes-256-gcm\",\n [VaultHeaders.KEY_ID]: keyId,\n [VaultHeaders.CONTENT_TYPE]: contentType,\n [VaultHeaders.KDF]: \"hkdf-sha256\",\n [VaultHeaders.KEY_ROTATION]:\n this.vaultConfig.keyRotation ?? \"per-write\",\n ...(options?.metadata ?? {}),\n };\n\n // Store encrypted entry key in key space\n const keyMetadata = JSON.stringify({\n keyId,\n contentType,\n ...metadata,\n });\n const keyPayload = JSON.stringify({\n key: base64Encode(keyBlob),\n metadata: keyMetadata,\n });\n const keyPutResult = await this.tc.kv.put(\n `keys/${key}`,\n keyPayload\n );\n if (!keyPutResult.ok) {\n return kvWriteError(\"Failed to store key blob\", keyPutResult.error);\n }\n\n // Store encrypted value in data space\n const valuePayload = JSON.stringify({\n data: base64Encode(encrypted),\n metadata,\n });\n const valuePutResult = await this.tc.kv.put(\n `vault/${key}`,\n valuePayload\n );\n if (!valuePutResult.ok) {\n return kvWriteError(\"Failed to store encrypted value\", valuePutResult.error);\n }\n\n return ok(undefined);\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<void, VaultError>>;\n }\n\n /**\n * Retrieve and decrypt a value by key.\n *\n * @param key - The key to retrieve\n * @param options - Optional get configuration\n * @returns Result with the decrypted entry\n */\n async get<T = unknown>(\n key: string,\n options?: VaultGetOptions<T>\n ): Promise<Result<VaultEntry<T>, VaultError>> {\n return this.withTelemetry(\"get\", key, async () => {\n if (this.usesNetworkEncryption) {\n return this.getNetworkEncrypted<T>(key, options);\n }\n\n if (!this._isUnlocked || !this.masterKey) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before reading data\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n // Fetch encrypted entry key from key space\n const keyResult = await this.tc.kv.get<string>(`keys/${key}`, {\n raw: true,\n });\n if (!keyResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n\n const keyEnvelope = JSON.parse(keyResult.data.data as string);\n const keyBlobBytes = base64Decode(keyEnvelope.key);\n const entryKey = this.crypto.decrypt(this.masterKey, keyBlobBytes);\n\n // Fetch encrypted value from data space\n const valueResult = await this.tc.kv.get<string>(`vault/${key}`, {\n raw: true,\n });\n if (!valueResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n\n const valueEnvelope = JSON.parse(valueResult.data.data as string);\n const encryptedBytes = base64Decode(valueEnvelope.data);\n const plaintext = this.crypto.decrypt(entryKey, encryptedBytes);\n\n // Read metadata\n const metadata: Record<string, string> = valueEnvelope.metadata ?? {};\n const contentType =\n metadata[VaultHeaders.CONTENT_TYPE] ?? \"application/json\";\n const keyId = metadata[VaultHeaders.KEY_ID] ?? \"\";\n\n // Deserialize\n let value: T;\n if (options?.raw) {\n value = plaintext as unknown as T;\n } else if (options?.deserialize) {\n value = options.deserialize(plaintext);\n } else if (contentType === \"application/json\") {\n value = JSON.parse(fromBytes(plaintext)) as T;\n } else {\n value = plaintext as unknown as T;\n }\n\n return ok({ value, metadata, keyId });\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"decryption\")\n ) {\n return vaultError({\n code: \"DECRYPTION_FAILED\",\n message: error.message,\n });\n }\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<VaultEntry<T>, VaultError>>;\n }\n\n /**\n * Delete an encrypted key.\n * Removes both the encrypted value and the key blob.\n *\n * @param key - The key to delete\n */\n async delete(key: string): Promise<Result<void, VaultError>> {\n return this.withTelemetry(\"delete\", key, async () => {\n if (!this.isUnlocked) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before deleting data\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n if (this.usesNetworkEncryption) {\n const valueDelResult = await this.tc.kv.delete(`vault/${key}`);\n if (!valueDelResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n return ok(undefined);\n }\n\n // Delete from both key space and data space\n const [keyDelResult, valueDelResult] = await Promise.all([\n this.tc.kv.delete(`keys/${key}`),\n this.tc.kv.delete(`vault/${key}`),\n ]);\n\n if (!keyDelResult.ok && !valueDelResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n\n return ok(undefined);\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<void, VaultError>>;\n }\n\n /**\n * List vault keys with optional prefix filtering.\n *\n * @param options - Optional list configuration\n * @returns Result with array of key names (vault/ prefix stripped)\n */\n async list(\n options?: VaultListOptions\n ): Promise<Result<string[], VaultError>> {\n const page = await this.listPage(options);\n return page.ok ? { ok: true, data: page.data.keys } : page;\n }\n\n async listPage(\n options?: VaultListOptions\n ): Promise<Result<VaultListPage, VaultError>> {\n return this.withTelemetry(\"list\", options?.prefix, async () => {\n if (!this.isUnlocked) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before listing data\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n const listPrefix = options?.prefix\n ? `vault/${options.prefix}`\n : \"vault/\";\n\n const listResult = await this.tc.kv.list({\n prefix: listPrefix,\n removePrefix: true,\n ...(options?.limit === undefined ? {} : { limit: options.limit }),\n ...(options?.cursor === undefined ? {} : { cursor: options.cursor }),\n });\n\n if (!listResult.ok) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(\n `Failed to list vault keys: ${listResult.error.message}`\n ),\n });\n }\n\n // Keys are already stripped of the \"vault/\" prefix by removePrefix\n let keys = listResult.data.keys;\n\n // If a user prefix was provided, strip it too if requested\n if (options?.removePrefix && options.prefix) {\n const userPrefix = options.prefix.endsWith(\"/\")\n ? options.prefix\n : `${options.prefix}/`;\n keys = keys.map((k) =>\n k.startsWith(userPrefix) ? k.slice(userPrefix.length) : k\n );\n }\n\n return ok({\n keys,\n truncated: listResult.data.truncated === true,\n ...(listResult.data.nextCursor === undefined ? {} : { nextCursor: listResult.data.nextCursor }),\n });\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<VaultListPage, VaultError>>;\n }\n\n /**\n * Get envelope metadata for a key without decrypting the value.\n *\n * @param key - The key to inspect\n * @returns Result with metadata headers\n */\n async head(\n key: string\n ): Promise<Result<Record<string, string>, VaultError>> {\n return this.withTelemetry(\"head\", key, async () => {\n if (this.usesNetworkEncryption) {\n return this.headNetworkEncrypted(key);\n }\n\n if (!this._isUnlocked) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before reading metadata\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n // Fetch envelope without decrypting\n const valueResult = await this.tc.kv.get<string>(`vault/${key}`, {\n raw: true,\n });\n if (!valueResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n\n const valueEnvelope = JSON.parse(valueResult.data.data as string);\n const metadata: Record<string, string> = valueEnvelope.metadata ?? {};\n return ok(metadata);\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<Record<string, string>, VaultError>>;\n }\n\n // =========================================================================\n // Batch Operations\n // =========================================================================\n\n /**\n * Encrypt and store multiple entries.\n *\n * @param entries - Array of key/value pairs with optional per-entry options\n * @returns Array of results, one per entry\n */\n async putMany(\n entries: Array<{ key: string; value: unknown; options?: VaultPutOptions }>\n ): Promise<Result<void, VaultError>[]> {\n return Promise.all(\n entries.map((entry) => this.put(entry.key, entry.value, entry.options))\n );\n }\n\n /**\n * Retrieve and decrypt multiple keys.\n *\n * @param keys - Array of keys to retrieve\n * @param options - Optional get configuration applied to all entries\n * @returns Array of results, one per key\n */\n async getMany<T = unknown>(\n keys: string[],\n options?: VaultGetOptions<T>\n ): Promise<Result<VaultEntry<T>, VaultError>[]> {\n return Promise.all(keys.map((key) => this.get<T>(key, options)));\n }\n\n // =========================================================================\n // Phase 2: Sharing\n // =========================================================================\n\n /**\n * Re-encrypt a vault key for another user (renamed from grant).\n * Re-encrypts the data key to the recipient's public key via X25519 DH.\n *\n * @param key - The key to share\n * @param recipientDID - The recipient's primary DID (did:pkh:...)\n * @param options - Optional grant configuration\n */\n async reencrypt(\n key: string,\n recipientDID: string,\n options?: VaultGrantOptions\n ): Promise<Result<void, VaultError>> {\n return this.withTelemetry(\"reencrypt\", key, async () => {\n if (this.usesNetworkEncryption) {\n void recipientDID;\n void options;\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(\n \"Vault key grants are deprecated for network-encrypted vaults; grant tinycloud.encryption/decrypt on the network plus KV access to vault data.\",\n ),\n });\n }\n\n if (!this._isUnlocked || !this.masterKey) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before granting access\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n // Step 1: Resolve recipient's public key\n const pubKeyResult = await this.resolvePublicKey(recipientDID);\n if (!pubKeyResult.ok) {\n return pubKeyResult;\n }\n const bobPubKey = pubKeyResult.data;\n\n // Step 2: Fetch and decrypt entry key from key space\n const keyResult = await this.tc.kv.get<string>(`keys/${key}`, {\n raw: true,\n });\n if (!keyResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n\n const keyEnvelope = JSON.parse(keyResult.data.data as string);\n const keyBlobBytes = base64Decode(keyEnvelope.key);\n const entryKey = this.crypto.decrypt(this.masterKey, keyBlobBytes);\n\n // Step 3: Create ephemeral X25519 key pair\n const ephemeralSeed = this.crypto.randomBytes(32);\n const ephemeralKeyPair = this.crypto.x25519FromSeed(ephemeralSeed);\n\n // Step 4: Compute shared secret via DH\n const sharedSecret = this.crypto.x25519Dh(\n ephemeralKeyPair.privateKey,\n bobPubKey\n );\n\n // Step 5: Derive encryption key from shared secret\n const encryptionKey = this.crypto.deriveKey(\n sharedSecret,\n toBytes(\"tinycloud-x25519\"),\n toBytes(\"vault-grant\")\n );\n\n // Step 6: Encrypt entry key with derived key\n const encryptedGrant = this.crypto.encrypt(encryptionKey, entryKey);\n\n // Step 7: Concatenate ephemeral public key + encrypted grant\n const grantBlob = concatBytes(\n ephemeralKeyPair.publicKey,\n encryptedGrant\n );\n\n // Step 8: Store grant in key space\n const grantPayload = JSON.stringify({\n grant: base64Encode(grantBlob),\n spaceId: this.vaultConfig.spaceId,\n metadata: {\n [VaultHeaders.GRANT_VERSION]: \"1\",\n [VaultHeaders.GRANTOR]: this.tc.did,\n ...(options?.metadata ?? {}),\n },\n });\n // Store grant in the vault's space (main space)\n const grantPutResult = await this.tc.kv.put(\n `grants/${recipientDID}/${key}`,\n grantPayload\n );\n if (!grantPutResult.ok) {\n return kvWriteError(\"Failed to store grant\", grantPutResult.error);\n }\n\n return ok(undefined);\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<void, VaultError>>;\n }\n\n /**\n * @deprecated Use reencrypt() instead.\n */\n async grant(\n key: string,\n recipientDID: string,\n options?: VaultGrantOptions\n ): Promise<Result<void, VaultError>> {\n return this.reencrypt(key, recipientDID, options);\n }\n\n /**\n * Retrieve and decrypt a value shared by another user.\n *\n * @param grantorDID - The DID of the user who shared the data\n * @param key - The key that was shared\n * @param options - Optional get configuration\n * @returns Result with the decrypted entry\n */\n async getShared<T = unknown>(\n grantorDID: string,\n key: string,\n options?: VaultGetOptions<T>\n ): Promise<Result<VaultEntry<T>, VaultError>> {\n return this.withTelemetry(\"getShared\", key, async () => {\n if (this.usesNetworkEncryption) {\n const grantorKV = options?.kv;\n if (!grantorKV) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(\n \"getShared requires a delegated KV service via options.kv.\",\n ),\n });\n }\n\n const config = this.networkEncryption;\n if (!config) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Network encryption is not configured\",\n });\n }\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n const valueResult = await grantorKV.get<string>(`vault/${key}`, {\n raw: true,\n });\n if (!valueResult.ok) {\n return vaultError({ code: \"KEY_NOT_FOUND\", key });\n }\n const rawEnvelope = unwrapKVData<string>(valueResult.data);\n const envelope = (\n typeof rawEnvelope === \"string\" ? JSON.parse(rawEnvelope) : rawEnvelope\n ) as InlineEncryptedEnvelope;\n const proof = await this.decryptCapabilityProof();\n const plaintextResult = await config.service.decryptEnvelope(\n envelope,\n proof,\n );\n if (!plaintextResult.ok) {\n return vaultError({\n code: \"DECRYPTION_FAILED\",\n message: plaintextResult.error.message,\n });\n }\n\n const metadata: Record<string, string> = envelope.metadata ?? {};\n const contentType =\n metadata[VaultHeaders.CONTENT_TYPE] ?? \"application/json\";\n const keyId =\n metadata[VaultHeaders.KEY_ID] ??\n envelope.encryptedSymmetricKeyHash.slice(0, 16);\n const value = this.deserializeValue<T>(\n plaintextResult.data,\n contentType,\n options,\n );\n void grantorDID;\n return { ok: true, data: { value, metadata, keyId } };\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: toError(error),\n });\n }\n }\n\n if (\n !this._isUnlocked ||\n !this.masterKey ||\n !this.encryptionIdentity\n ) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before reading shared data\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n const myDID = this.tc.did;\n const grantorKV = options?.kv;\n\n if (!grantorKV) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(\n \"getShared requires a delegated KV service via options.kv. \" +\n \"Use useDelegation() to get delegated access, then pass { kv: access.kv }.\"\n ),\n });\n }\n\n // Step 1: Fetch grant from grantor's space via delegated KV\n const grantResult = await grantorKV.get<string>(`grants/${myDID}/${key}`, {\n raw: true,\n });\n if (!grantResult.ok) {\n return vaultError({\n code: \"GRANT_NOT_FOUND\",\n grantor: grantorDID,\n key,\n });\n }\n\n const grantEnvelope = typeof grantResult.data?.data === \"string\"\n ? JSON.parse(grantResult.data.data as string)\n : grantResult.data?.data;\n const grantBlobBytes = base64Decode((grantEnvelope as any).grant);\n\n // Step 2: Extract ephemeral public key and encrypted grant\n const ephemeralPubKey = grantBlobBytes.slice(0, 32);\n const encryptedGrant = grantBlobBytes.slice(32);\n\n // Step 3: Compute shared secret using our private key\n const sharedSecret = this.crypto.x25519Dh(\n this.encryptionIdentity.privateKey,\n ephemeralPubKey\n );\n\n // Step 4: Derive decryption key\n const encryptionKey = this.crypto.deriveKey(\n sharedSecret,\n toBytes(\"tinycloud-x25519\"),\n toBytes(\"vault-grant\")\n );\n\n // Step 5: Decrypt entry key\n const entryKey = this.crypto.decrypt(encryptionKey, encryptedGrant);\n\n // Step 6: Fetch encrypted value from grantor's space via delegated KV\n const valueResult = await grantorKV.get<string>(`vault/${key}`, {\n raw: true,\n });\n if (!valueResult.ok) {\n return vaultError({\n code: \"KEY_NOT_FOUND\",\n key,\n });\n }\n\n const valueEnvelope = typeof valueResult.data?.data === \"string\"\n ? JSON.parse(valueResult.data.data as string)\n : valueResult.data?.data;\n const encryptedBytes = base64Decode((valueEnvelope as any).data);\n const plaintext = this.crypto.decrypt(entryKey, encryptedBytes);\n\n // Read metadata\n const metadata: Record<string, string> = (valueEnvelope as any).metadata ?? {};\n const contentType =\n metadata[VaultHeaders.CONTENT_TYPE] ?? \"application/json\";\n const keyId = metadata[VaultHeaders.KEY_ID] ?? \"\";\n\n // Deserialize\n let value: T;\n if (options?.raw) {\n value = plaintext as unknown as T;\n } else if (options?.deserialize) {\n value = options.deserialize(plaintext);\n } else if (contentType === \"application/json\") {\n value = JSON.parse(fromBytes(plaintext)) as T;\n } else {\n value = plaintext as unknown as T;\n }\n\n return ok({ value, metadata, keyId });\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"decryption\")\n ) {\n return vaultError({\n code: \"DECRYPTION_FAILED\",\n message: error.message,\n });\n }\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<VaultEntry<T>, VaultError>>;\n }\n\n /**\n * Resolve another user's public encryption key from their DID.\n *\n * @param did - The DID to resolve (did:pkh:eip155:{chainId}:{address})\n * @returns Result with the public key bytes\n */\n async resolvePublicKey(\n did: string\n ): Promise<Result<Uint8Array, VaultError>> {\n try {\n const parts = this.parseDID(did);\n if (!parts) {\n return vaultError({ code: \"PUBLIC_KEY_NOT_FOUND\", did });\n }\n\n const spaceId = this.tc.makePublicSpaceId(\n parts.address,\n parts.chainId\n );\n\n const result = await this.tc.readPublicSpace<string>(\n this.host,\n spaceId,\n \".well-known/vault-pubkey\"\n );\n\n if (!result.ok) {\n return vaultError({ code: \"PUBLIC_KEY_NOT_FOUND\", did });\n }\n\n const pubKeyBytes = base64Decode(result.data as string);\n return { ok: true, data: pubKeyBytes } as Result<Uint8Array, VaultError>;\n } catch (error) {\n return vaultError({ code: \"PUBLIC_KEY_NOT_FOUND\", did });\n }\n }\n\n /**\n * List DIDs that have been granted access to a key.\n *\n * @param key - The key to list grants for\n * @returns Result with array of recipient DIDs\n */\n async listGrants(\n key: string\n ): Promise<Result<string[], VaultError>> {\n return this.withTelemetry(\"listGrants\", key, async () => {\n if (this.usesNetworkEncryption) {\n void key;\n return { ok: true, data: [] };\n }\n\n if (!this._isUnlocked) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before listing grants\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n const listResult = await this.tc.kv.list({\n prefix: \"grants/\",\n removePrefix: true,\n });\n\n if (!listResult.ok) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(\n `Failed to list grants: ${listResult.error.message}`\n ),\n });\n }\n\n // Grant paths are: {recipientDID}/{key}\n // Filter for the specific key and extract DIDs\n const dids: string[] = [];\n for (const grantPath of listResult.data.keys) {\n // Path format: {recipientDID}/{key}\n // The key may contain slashes, so we need to match the suffix\n if (grantPath.endsWith(`/${key}`)) {\n const did = grantPath.slice(\n 0,\n grantPath.length - key.length - 1\n );\n if (did) {\n dids.push(did);\n }\n }\n }\n\n return ok(dids);\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<string[], VaultError>>;\n }\n\n // =========================================================================\n // Phase 3: Key Rotation / Revocation\n // =========================================================================\n\n /**\n * Revoke a previously issued grant.\n *\n * This performs a full key rotation:\n * 1. Lists current grantees\n * 2. Removes the revoked recipient\n * 3. Re-encrypts the value with a new entry key\n * 4. Re-issues grants to remaining recipients\n *\n * @param key - The key to revoke access to\n * @param recipientDID - The recipient whose access to revoke\n */\n async revoke(\n key: string,\n recipientDID: string\n ): Promise<Result<void, VaultError>> {\n return this.withTelemetry(\"revoke\", key, async () => {\n if (this.usesNetworkEncryption) {\n void recipientDID;\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause: new Error(\n \"Vault key grants are deprecated for network-encrypted vaults; revoke KV and tinycloud.encryption/decrypt grants instead.\",\n ),\n });\n }\n\n if (!this._isUnlocked || !this.masterKey) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Vault must be unlocked before revoking access\",\n });\n }\n\n if (!this.requireAuth()) {\n return vaultError({\n code: \"VAULT_LOCKED\",\n message: \"Authentication required\",\n });\n }\n\n try {\n // Step 1: List all current grantees\n const granteesResult = await this.listGrants(key);\n if (!granteesResult.ok) {\n return granteesResult;\n }\n\n const remainingGrantees = granteesResult.data.filter(\n (did) => did !== recipientDID\n );\n\n // Step 2: Delete the grant for the revoked recipient\n const deleteGrantResult = await this.tc.kv.delete(\n `grants/${recipientDID}/${key}`\n );\n // Grant may already be deleted, that's fine\n\n // Step 3: Fetch and decrypt current value\n const getResult = await this.get(key);\n if (!getResult.ok) {\n return getResult as Result<never, VaultError>;\n }\n\n const currentEntry = getResult.data;\n\n // Step 4: Generate new entry key\n const newEntryKey = this.crypto.randomBytes(32);\n const newKeyId = hexEncode(this.crypto.sha256(newEntryKey)).slice(\n 0,\n 16\n );\n\n // Step 5: Re-serialize and re-encrypt value with new key\n let plaintext: Uint8Array;\n if (currentEntry.value instanceof Uint8Array) {\n plaintext = currentEntry.value;\n } else {\n plaintext = toBytes(JSON.stringify(currentEntry.value));\n }\n\n const encrypted = this.crypto.encrypt(newEntryKey, plaintext);\n\n // Step 6: Encrypt new entry key with master key\n const newKeyBlob = this.crypto.encrypt(this.masterKey, newEntryKey);\n\n // Step 7: Update metadata with new key ID\n const metadata: Record<string, string> = {\n ...currentEntry.metadata,\n [VaultHeaders.KEY_ID]: newKeyId,\n };\n\n // Step 8: Store updated key blob\n const keyPayload = JSON.stringify({\n key: base64Encode(newKeyBlob),\n metadata: JSON.stringify({\n keyId: newKeyId,\n ...metadata,\n }),\n });\n const keyPutResult = await this.tc.kv.put(\n `keys/${key}`,\n keyPayload\n );\n if (!keyPutResult.ok) {\n return kvWriteError(\"Failed to store rotated key blob\", keyPutResult.error);\n }\n\n // Step 9: Store re-encrypted value\n const valuePayload = JSON.stringify({\n data: base64Encode(encrypted),\n metadata,\n });\n const valuePutResult = await this.tc.kv.put(\n `vault/${key}`,\n valuePayload\n );\n if (!valuePutResult.ok) {\n return kvWriteError(\"Failed to store re-encrypted value\", valuePutResult.error);\n }\n\n // Step 10: Re-issue grants to remaining recipients\n for (const did of remainingGrantees) {\n const grantResult = await this.reencrypt(key, did);\n if (!grantResult.ok) {\n // Continue re-issuing to other recipients even if one fails\n // The failed grant can be re-issued manually\n }\n }\n\n return ok(undefined);\n } catch (error) {\n return vaultError({\n code: \"STORAGE_ERROR\",\n cause:\n toError(error),\n });\n }\n }) as Promise<Result<void, VaultError>>;\n }\n\n // =========================================================================\n // Internal Helpers\n // =========================================================================\n\n /**\n * Parse a DID string to extract address and chainId.\n * Expected format: did:pkh:eip155:{chainId}:{address}\n *\n * @param did - The DID to parse\n * @returns Parsed address and chainId, or null if invalid\n */\n private parseDID(\n did: string\n ): { address: string; chainId: number } | null {\n // did:pkh:eip155:{chainId}:{address}\n const parts = did.split(\":\");\n if (\n parts.length !== 5 ||\n parts[0] !== \"did\" ||\n parts[1] !== \"pkh\" ||\n parts[2] !== \"eip155\"\n ) {\n return null;\n }\n\n const chainId = parseInt(parts[3], 10);\n const address = parts[4];\n if (isNaN(chainId) || !address) {\n return null;\n }\n\n return { address, chainId };\n }\n}\n","import type { VaultCrypto } from \"./DataVaultService\";\n\nexport interface WasmVaultFunctions {\n vault_encrypt(key: Uint8Array, plaintext: Uint8Array): Uint8Array;\n vault_decrypt(key: Uint8Array, blob: Uint8Array): Uint8Array;\n /** WASM order: (salt, signature, info) — NOT (signature, salt, info) */\n vault_derive_key(salt: Uint8Array, signature: Uint8Array, info: Uint8Array): Uint8Array;\n vault_x25519_from_seed(seed: Uint8Array): { publicKey: Uint8Array; privateKey: Uint8Array };\n vault_x25519_dh(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;\n vault_random_bytes(length: number): Uint8Array;\n vault_sha256(data: Uint8Array): Uint8Array;\n}\n\nexport function createVaultCrypto(wasm: WasmVaultFunctions): VaultCrypto {\n return {\n encrypt: (key, plaintext) => wasm.vault_encrypt(key, plaintext),\n decrypt: (key, blob) => wasm.vault_decrypt(key, blob),\n deriveKey: (signature, salt, info) => wasm.vault_derive_key(salt, signature, info),\n x25519FromSeed: (seed) => wasm.vault_x25519_from_seed(seed),\n x25519Dh: (privateKey, publicKey) => wasm.vault_x25519_dh(privateKey, publicKey),\n randomBytes: (length) => wasm.vault_random_bytes(length),\n sha256: (data) => wasm.vault_sha256(data),\n };\n}\n","export const SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;\n\nconst SECRET_PREFIX = \"secrets/\";\nconst SCOPED_SECRET_PREFIX = \"secrets/scoped/\";\nconst RESERVED_SECRET_SCOPES = new Set([\"default\", \"global\"]);\n\nexport interface SecretScopeOptions {\n /** Optional logical scope. Omit for the global secret namespace. */\n scope?: string;\n}\n\nexport interface ResolvedSecretPath {\n /** Canonical env-style secret name. */\n name: string;\n /** Canonical scope. Undefined means global. */\n scope?: string;\n /** Key passed to the data vault service. */\n vaultKey: string;\n /** KV permission path that backs the encrypted vault entry. */\n permissionPaths: {\n vault: string;\n };\n}\n\nexport interface ParsedSecretCatalogKey {\n name: string;\n scope?: string;\n}\n\nexport function canonicalizeSecretScope(\n scope: string | undefined,\n): string | undefined {\n if (scope === undefined) {\n return undefined;\n }\n\n const trimmed = scope.trim();\n if (trimmed === \"\") {\n throw new Error(\n \"Secret scope must be non-empty; omit scope for global secrets.\",\n );\n }\n\n const canonical = trimmed\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n\n if (canonical === \"\") {\n throw new Error(\"Secret scope must contain at least one letter or number.\");\n }\n if (RESERVED_SECRET_SCOPES.has(canonical)) {\n throw new Error(\n `Secret scope ${JSON.stringify(scope)} is reserved; omit scope for global secrets.`,\n );\n }\n\n return canonical;\n}\n\nexport function resolveSecretPath(\n name: string,\n options: SecretScopeOptions = {},\n): ResolvedSecretPath {\n const normalizedName = name.trim();\n if (!SECRET_NAME_RE.test(normalizedName)) {\n throw new Error(\n `Invalid secret name ${JSON.stringify(name)}. Secret names must match ${SECRET_NAME_RE.source}.`,\n );\n }\n\n const scope = canonicalizeSecretScope(options.scope);\n const vaultKey =\n scope === undefined\n ? `${SECRET_PREFIX}${normalizedName}`\n : `${SCOPED_SECRET_PREFIX}${scope}/${normalizedName}`;\n\n return {\n name: normalizedName,\n ...(scope !== undefined ? { scope } : {}),\n vaultKey,\n permissionPaths: {\n vault: `vault/${vaultKey}`,\n },\n };\n}\n\nexport function resolveSecretListPrefix(\n options: SecretScopeOptions = {},\n): string {\n const scope = canonicalizeSecretScope(options.scope);\n return scope === undefined\n ? \"vault/secrets/\"\n : `vault/secrets/scoped/${scope}/`;\n}\n\n/** Parse a key relative to `secrets/` into its canonical catalog identity. */\nexport function parseSecretCatalogKey(\n key: string,\n): ParsedSecretCatalogKey | undefined {\n if (SECRET_NAME_RE.test(key)) {\n return { name: key };\n }\n\n const parts = key.split(\"/\");\n if (parts.length !== 3 || parts[0] !== \"scoped\") {\n return undefined;\n }\n\n const [, rawScope, name] = parts;\n if (!name || !SECRET_NAME_RE.test(name)) {\n return undefined;\n }\n\n try {\n const scope = canonicalizeSecretScope(rawScope);\n if (scope === undefined || scope !== rawScope) {\n return undefined;\n }\n return { name, scope };\n } catch {\n return undefined;\n }\n}\n","import { ErrorCodes, err, type Result, type ServiceError } from \"../types\";\nimport type { IDataVaultService } from \"../vault/IDataVaultService\";\nimport type { VaultError } from \"../vault/types\";\nimport type {\n ISecretsService,\n SecretCatalogEntry,\n SecretPayload,\n SecretsError,\n} from \"./ISecretsService\";\nimport {\n canonicalizeSecretScope,\n parseSecretCatalogKey,\n resolveSecretPath,\n SECRET_NAME_RE,\n type ResolvedSecretPath,\n type SecretScopeOptions,\n} from \"./paths\";\n\nfunction invalidSecretInput(message: string): Result<never, ServiceError> {\n return err({\n code: ErrorCodes.INVALID_INPUT,\n service: \"secrets\",\n message,\n });\n}\n\nfunction resolveSecretPathResult(\n name: string,\n options?: SecretScopeOptions,\n): ResolvedSecretPath | Result<never, ServiceError> {\n try {\n return resolveSecretPath(name, options);\n } catch (error) {\n return invalidSecretInput(\n error instanceof Error ? error.message : String(error),\n );\n }\n}\n\nexport class SecretsService implements ISecretsService {\n private readonly getVault: () => IDataVaultService;\n\n constructor(vault: IDataVaultService | (() => IDataVaultService)) {\n this.getVault = typeof vault === \"function\" ? vault : () => vault;\n }\n\n get vault(): IDataVaultService {\n return this.getVault();\n }\n\n get isUnlocked(): boolean {\n return this.vault.isUnlocked;\n }\n\n unlock(signer?: unknown): Promise<Result<void, VaultError>> {\n return this.vault.unlock(signer);\n }\n\n lock(): void {\n this.vault.lock();\n }\n\n async get(\n name: string,\n options?: SecretScopeOptions,\n ): Promise<Result<string, SecretsError>> {\n const secretPath = resolveSecretPathResult(name, options);\n if (\"ok\" in secretPath) return secretPath;\n\n const result = await this.vault.get<SecretPayload>(secretPath.vaultKey);\n if (!result.ok) {\n return result;\n }\n return { ok: true, data: result.data.value.value };\n }\n\n async put(\n name: string,\n value: string,\n options?: SecretScopeOptions,\n ): Promise<Result<void, SecretsError>> {\n const secretPath = resolveSecretPathResult(name, options);\n if (\"ok\" in secretPath) return secretPath;\n\n const now = new Date().toISOString();\n return this.vault.put(secretPath.vaultKey, {\n value,\n createdAt: now,\n updatedAt: now,\n } satisfies SecretPayload);\n }\n\n async delete(\n name: string,\n options?: SecretScopeOptions,\n ): Promise<Result<void, SecretsError>> {\n const secretPath = resolveSecretPathResult(name, options);\n if (\"ok\" in secretPath) return secretPath;\n\n return this.vault.delete(secretPath.vaultKey);\n }\n\n async list(\n options?: SecretScopeOptions,\n ): Promise<Result<string[], SecretsError>> {\n let prefix: string;\n try {\n const scope = canonicalizeSecretScope(options?.scope);\n prefix = scope === undefined ? \"secrets/\" : `secrets/scoped/${scope}/`;\n } catch (error) {\n return invalidSecretInput(\n error instanceof Error ? error.message : String(error),\n );\n }\n\n const result = await this.vault.list({\n prefix,\n removePrefix: true,\n });\n if (!result.ok) {\n return result;\n }\n return {\n ok: true,\n data: result.data.filter((name) => SECRET_NAME_RE.test(name)),\n };\n }\n\n async listAll(): Promise<Result<SecretCatalogEntry[], SecretsError>> {\n const entries = new Map<string, SecretCatalogEntry>();\n let cursor: string | undefined;\n\n do {\n const result = await this.vault.listPage({\n prefix: \"secrets/\",\n removePrefix: true,\n limit: 1000,\n ...(cursor === undefined ? {} : { cursor }),\n });\n if (!result.ok) {\n return result;\n }\n\n for (const key of result.data.keys) {\n const entry = parseSecretCatalogKey(key);\n if (!entry) continue;\n entries.set(`${entry.scope ?? \"\"}\\u0000${entry.name}`, entry);\n }\n\n cursor = result.data.truncated ? result.data.nextCursor : undefined;\n if (result.data.truncated && cursor === undefined) {\n return invalidSecretInput(\n \"Secret catalog listing was truncated without a continuation cursor.\",\n );\n }\n } while (cursor !== undefined);\n\n return {\n ok: true,\n data: [...entries.values()].sort(\n (a, b) =>\n (a.scope ?? \"\").localeCompare(b.scope ?? \"\") ||\n a.name.localeCompare(b.name),\n ),\n };\n }\n}\n","/**\n * Canonical JSON serialization and content hashing for TinyCloud\n * encryption requests/responses.\n *\n * The node and SDK must agree byte-for-byte on the canonical form so\n * that body-hash bindings (`bodyHash`, `encryptedSymmetricKeyHash`,\n * `receiverPublicKeyHash`) verify on both sides.\n *\n * Canonical rules:\n * - Object keys are sorted lexicographically by code point.\n * - Strings are encoded with the JSON.stringify default (RFC 8259).\n * - Numbers are emitted via JSON.stringify; callers should restrict to\n * integers or use string fields for high-precision values.\n * - `undefined` properties are dropped. `null` is preserved.\n * - Arrays preserve element order.\n *\n * Hashing uses SHA-256 supplied by the caller (via an `EncryptionCrypto`\n * binding) so this module stays platform-agnostic.\n */\n\nexport type Json =\n | null\n | boolean\n | number\n | string\n | Json[]\n | { [key: string]: Json | undefined };\n\n/**\n * Produce the canonical JSON string for {@link value}. Object keys are\n * sorted, `undefined` properties are dropped, and primitive types are\n * encoded by `JSON.stringify`.\n */\nexport function canonicalize(value: Json | undefined): string {\n if (value === undefined) {\n return \"\";\n }\n return stringify(value);\n}\n\nfunction stringify(value: Json): string {\n if (value === null) return \"null\";\n switch (typeof value) {\n case \"boolean\":\n case \"number\":\n return JSON.stringify(value);\n case \"string\":\n return JSON.stringify(value);\n case \"object\": {\n if (Array.isArray(value)) {\n return `[${value.map(stringify).join(\",\")}]`;\n }\n const keys = Object.keys(value).sort();\n const parts: string[] = [];\n for (const k of keys) {\n const v = value[k];\n if (v === undefined) continue;\n parts.push(`${JSON.stringify(k)}:${stringify(v)}`);\n }\n return `{${parts.join(\",\")}}`;\n }\n default:\n throw new TypeError(\n `canonicalize: unsupported value type ${typeof value}`,\n );\n }\n}\n\nconst HEX = \"0123456789abcdef\";\n\nexport function hexEncode(bytes: Uint8Array): string {\n let out = \"\";\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i];\n out += HEX[(b >> 4) & 0xf] + HEX[b & 0xf];\n }\n return out;\n}\n\nexport function hexDecode(hex: string): Uint8Array {\n if (hex.length % 2 !== 0) {\n throw new Error(\"hex string must have even length\");\n }\n const out = new Uint8Array(hex.length / 2);\n for (let i = 0; i < out.length; i++) {\n const hi = parseInt(hex[i * 2], 16);\n const lo = parseInt(hex[i * 2 + 1], 16);\n if (Number.isNaN(hi) || Number.isNaN(lo)) {\n throw new Error(\"invalid hex character\");\n }\n out[i] = (hi << 4) | lo;\n }\n return out;\n}\n\nexport function base64Encode(bytes: Uint8Array): string {\n // base64 (standard, not url-safe) so envelopes pass through JSON cleanly.\n // We avoid `btoa` to stay node-friendly without polyfills.\n const chars =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n let out = \"\";\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i];\n const b1 = i + 1 < bytes.length ? bytes[i + 1] : 0;\n const b2 = i + 2 < bytes.length ? bytes[i + 2] : 0;\n out += chars[(b0 >> 2) & 0x3f];\n out += chars[((b0 << 4) | (b1 >> 4)) & 0x3f];\n out += i + 1 < bytes.length ? chars[((b1 << 2) | (b2 >> 6)) & 0x3f] : \"=\";\n out += i + 2 < bytes.length ? chars[b2 & 0x3f] : \"=\";\n }\n return out;\n}\n\nexport function base64Decode(s: string): Uint8Array {\n const clean = s.replace(/[^A-Za-z0-9+/=]/g, \"\");\n const len = clean.length;\n if (len % 4 !== 0) {\n throw new Error(\"invalid base64 input\");\n }\n const padding =\n clean.endsWith(\"==\") ? 2 : clean.endsWith(\"=\") ? 1 : 0;\n const outLen = (len / 4) * 3 - padding;\n const out = new Uint8Array(outLen);\n const lookup: Record<string, number> = {};\n const chars =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n for (let i = 0; i < chars.length; i++) lookup[chars[i]] = i;\n\n let outIdx = 0;\n for (let i = 0; i < len; i += 4) {\n const v0 = lookup[clean[i]] ?? 0;\n const v1 = lookup[clean[i + 1]] ?? 0;\n const v2 = clean[i + 2] === \"=\" ? 0 : (lookup[clean[i + 2]] ?? 0);\n const v3 = clean[i + 3] === \"=\" ? 0 : (lookup[clean[i + 3]] ?? 0);\n const b0 = (v0 << 2) | (v1 >> 4);\n const b1 = ((v1 & 0x0f) << 4) | (v2 >> 2);\n const b2 = ((v2 & 0x03) << 6) | v3;\n if (outIdx < outLen) out[outIdx++] = b0;\n if (outIdx < outLen) out[outIdx++] = b1;\n if (outIdx < outLen) out[outIdx++] = b2;\n }\n return out;\n}\n\nexport function utf8Encode(s: string): Uint8Array {\n return new TextEncoder().encode(s);\n}\n\nexport function utf8Decode(b: Uint8Array): string {\n return new TextDecoder().decode(b);\n}\n\n/**\n * Compute `hexEncode(sha256(canonicalize(value)))`. The SHA-256 binding\n * is injected so the module remains usable in both the WASM and pure-JS\n * paths.\n */\nexport function canonicalHashHex(\n sha256: (bytes: Uint8Array) => Uint8Array,\n value: Json,\n): string {\n const canonical = canonicalize(value);\n return hexEncode(sha256(utf8Encode(canonical)));\n}\n","/**\n * TinyCloud encryption network identifiers.\n *\n * A network id is `urn:tinycloud:encryption:<ownerDid>:<network>` where\n * `ownerDid` is the owner's DID and `network` is a\n * non-empty label drawn from `[a-z0-9][a-z0-9-]*`.\n *\n * The embedded owner DID is the root authority for the network: any\n * delegation chain ending in a `tinycloud.encryption/decrypt` grant on\n * the network must root at this owner DID.\n */\n\nconst URN_PREFIX = \"urn:tinycloud:encryption:\";\nconst NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;\nconst PKH_EIP155_DID_RE = /^did:pkh:eip155:(\\d+):(0x[a-fA-F0-9]{40})$/;\n\nexport interface ParsedNetworkId {\n /** The full URN string. */\n networkId: string;\n /** Owner DID embedded in the URN (the network's root authority). */\n ownerDid: string;\n /** Network label (the suffix after the owner DID). */\n name: string;\n}\n\nexport class NetworkIdError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NetworkIdError\";\n }\n}\n\n/**\n * Parse a network id string into its owner DID and name components.\n *\n * Throws {@link NetworkIdError} when the input does not match\n * `urn:tinycloud:encryption:<did>:<name>`, when the embedded DID is\n * malformed, or when the network name fails {@link NETWORK_NAME_RE}.\n */\nexport function parseNetworkId(networkId: string): ParsedNetworkId {\n if (typeof networkId !== \"string\" || networkId.length === 0) {\n throw new NetworkIdError(\"networkId must be a non-empty string\");\n }\n if (!networkId.startsWith(URN_PREFIX)) {\n throw new NetworkIdError(\n `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`,\n );\n }\n const body = networkId.slice(URN_PREFIX.length);\n // `body` = \"<ownerDid>:<network>\"\n // ownerDid contains ':' (e.g. did:key:z6Mk...), so we split on the LAST colon\n // and treat the suffix as the network name.\n const lastColon = body.lastIndexOf(\":\");\n if (lastColon <= 0 || lastColon === body.length - 1) {\n throw new NetworkIdError(\n `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`,\n );\n }\n const ownerDid = body.slice(0, lastColon);\n const name = body.slice(lastColon + 1);\n\n if (!ownerDid.startsWith(\"did:\")) {\n throw new NetworkIdError(\n `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`,\n );\n }\n // Minimal DID shape: did:<method>:<id> — three colon-separated segments,\n // each non-empty.\n const didParts = ownerDid.split(\":\");\n if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {\n throw new NetworkIdError(\n `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`,\n );\n }\n if (!NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n return { networkId, ownerDid, name };\n}\n\n/**\n * Construct a network id URN from an owner DID and a network name.\n * Validates inputs and throws {@link NetworkIdError} on bad shape.\n */\nexport function buildNetworkId(ownerDid: string, name: string): string {\n if (typeof ownerDid !== \"string\" || !ownerDid.startsWith(\"did:\")) {\n throw new NetworkIdError(\"ownerDid must be a DID\");\n }\n if (typeof name !== \"string\" || !NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n const networkId = `${URN_PREFIX}${ownerDid}:${name}`;\n // Re-validate the composed result so the same error path triggers\n // for caller inputs that compose into a malformed URN.\n parseNetworkId(networkId);\n return networkId;\n}\n\n/**\n * Returns true when {@link networkId} is a syntactically valid network URN.\n */\nexport function isNetworkId(networkId: unknown): networkId is string {\n if (typeof networkId !== \"string\") {\n return false;\n }\n try {\n parseNetworkId(networkId);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction parsePkhOwnerDid(ownerDid: string):\n | { chainId: string; address: string }\n | null {\n const match = ownerDid.match(PKH_EIP155_DID_RE);\n if (!match) return null;\n return {\n chainId: match[1],\n address: match[2].toLowerCase(),\n };\n}\n\n/**\n * Compare owner DIDs as network principals. For `did:pkh:eip155`, EVM\n * address casing is not part of principal identity; other DID methods\n * remain exact string matches.\n */\nexport function ownerDidMatches(a: string, b: string): boolean {\n const aPkh = parsePkhOwnerDid(a);\n const bPkh = parsePkhOwnerDid(b);\n if (aPkh && bPkh) {\n return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;\n }\n return a === b;\n}\n\n/**\n * Resolve the discovery key used to look up a network's descriptor under\n * an owner's public account-space.\n *\n * Format: `.well-known/encryption/network/<name>`.\n */\nexport function networkDiscoveryKey(name: string): string {\n if (!NETWORK_NAME_RE.test(name)) {\n throw new NetworkIdError(\n `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`,\n );\n }\n return `.well-known/encryption/network/${name}`;\n}\n\nexport const ENCRYPTION_NETWORK_URN_PREFIX = URN_PREFIX;\nexport const NETWORK_NAME_PATTERN = NETWORK_NAME_RE;\n","/**\n * Type definitions for TinyCloud one-of-one encryption.\n *\n * Wire shapes mirror the protocol described in the encryption\n * architecture: inline envelopes carry the encrypted symmetric key\n * alongside the ciphertext; decrypt requests are short-lived UCAN\n * invocations against a target node plus networkId.\n */\n\nimport type { Json } from \"./canonical\";\nimport type { PermissionHint } from \"../types\";\n\n/** Default ciphersuite identifier for v1 envelopes. */\nexport const DEFAULT_ENCRYPTION_ALG = \"x25519-aes256gcm/v1\" as const;\n\n/** Inline-envelope schema version. */\nexport const ENVELOPE_VERSION = 1 as const;\n\n/** Default key version on freshly-created networks. */\nexport const DEFAULT_KEY_VERSION = 1 as const;\n\n/** Decrypt-invocation fact type. */\nexport const DECRYPT_FACT_TYPE = \"tinycloud.encryption.decrypt/v1\" as const;\n\n/** Decrypt response type. */\nexport const DECRYPT_RESULT_TYPE =\n \"tinycloud.encryption.decrypt-result/v1\" as const;\n\n/** Encryption service identifier (manifest long form). */\nexport const ENCRYPTION_SERVICE = \"tinycloud.encryption\" as const;\n\n/** Short form used in recap/abilities maps. */\nexport const ENCRYPTION_SERVICE_SHORT = \"encryption\" as const;\n\n/** Decrypt ability URN. */\nexport const DECRYPT_ACTION = \"tinycloud.encryption/decrypt\" as const;\n\n/**\n * Inline encrypted envelope persisted in KV/SQL records.\n *\n * - `encryptedSymmetricKey` is opaque to the SDK; only the network can\n * unwrap it.\n * - `encryptedSymmetricKeyHash` is the canonical hash of the wrapped key\n * bytes (hex-encoded sha-256 of the base64 string's canonical JSON\n * form). The node recomputes this on every decrypt request.\n * - `ciphertext` and `aad` are payload bytes only; the node never sees\n * them.\n */\nexport interface InlineEncryptedEnvelope {\n /** Schema version. */\n v: typeof ENVELOPE_VERSION;\n /** Network id URN. */\n networkId: string;\n /** Ciphersuite identifier. */\n alg: string;\n /** Network key version that was used to wrap the symmetric key. */\n keyVersion: number;\n /** Base64-encoded wrapped symmetric key / capsule. */\n encryptedSymmetricKey: string;\n /** Hex sha-256 of the canonical encryptedSymmetricKey string. */\n encryptedSymmetricKeyHash: string;\n /** Base64-encoded payload ciphertext. */\n ciphertext: string;\n /** Base64-encoded associated data, if any. */\n aad?: string;\n /** Caller-supplied metadata. Not authenticated against the node. */\n metadata?: Record<string, string>;\n}\n\n/**\n * Node-published network descriptor. The node DB is authoritative; a\n * cached copy may also live under\n * `.well-known/encryption/network/<name>` in the owner's account\n * space (a discovery record only).\n */\nexport interface NetworkDescriptor {\n networkId: string;\n ownerDid: string;\n name: string;\n members: ReadonlyArray<{ nodeId: string; role: \"primary\" | \"share\" }>;\n threshold: { n: number; t: number };\n state: \"pending\" | \"generating\" | \"active\" | \"rotating\" | \"revoked\" | \"failed\";\n /** Base64-encoded network public key. */\n publicEncryptionKey: string;\n alg: string;\n keyVersion: number;\n keyBackend: \"local-one-of-one\" | \"dstack\" | \"threshold\";\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Decrypt-request body sent over the wire. Hashed (canonically) and\n * bound to the UCAN invocation via `facts.bodyHash`.\n */\nexport interface DecryptRequestBody {\n type: typeof DECRYPT_FACT_TYPE;\n targetNode: string;\n networkId: string;\n alg: string;\n keyVersion: number;\n /** Base64-encoded wrapped symmetric key from the envelope. */\n encryptedSymmetricKey: string;\n /** Recomputed hash of the wrapped key. */\n encryptedSymmetricKeyHash: string;\n /** Base64-encoded per-request receiver public key. */\n receiverPublicKey: string;\n /** Hash of the receiver public key. */\n receiverPublicKeyHash: string;\n}\n\n/**\n * Decrypt-response body returned by the node. The SDK verifies the\n * signature, recomputes hashes, then unwraps `wrappedKey` with the\n * per-request receiver private key before decrypting the payload.\n */\nexport interface DecryptResponseBody {\n type: typeof DECRYPT_RESULT_TYPE;\n targetNode: string;\n networkId: string;\n invocationCid: string;\n encryptedSymmetricKeyHash: string;\n receiverPublicKeyHash: string;\n /** Base64-encoded symmetric key re-encrypted to receiverPublicKey. */\n wrappedKey: string;\n alg: string;\n keyVersion: number;\n requestHash: string;\n nodeId: string;\n /** Base64-encoded ed25519 signature over canonical(response - signature field). */\n nodeSignature: string;\n}\n\n/**\n * Decrypt-invocation facts attached to the UCAN. Verifiers recompute\n * `bodyHash`, `encryptedSymmetricKeyHash`, and `receiverPublicKeyHash`\n * from the request body and reject any mismatch.\n */\nexport interface DecryptInvocationFact {\n type: typeof DECRYPT_FACT_TYPE;\n targetNode: string;\n networkId: string;\n bodyHash: string;\n encryptedSymmetricKeyHash: string;\n receiverPublicKeyHash: string;\n alg: string;\n keyVersion: number;\n}\n\n/**\n * Per-request receiver key pair (x25519). The private key never\n * leaves the SDK; the public key is sent to the node so the node can\n * rewrap the symmetric key.\n */\nexport interface ReceiverKeyPair {\n publicKey: Uint8Array;\n privateKey: Uint8Array;\n}\n\n/**\n * Crypto primitives injected into the encryption module. The SDK\n * provides these via WASM bindings; tests provide simple in-memory\n * implementations.\n */\nexport interface EncryptionCrypto {\n /** SHA-256 → 32-byte digest. */\n sha256(data: Uint8Array): Uint8Array;\n /** Cryptographically secure random bytes. */\n randomBytes(length: number): Uint8Array;\n /** Derive an x25519 key pair from a 32-byte seed. */\n x25519FromSeed(seed: Uint8Array): ReceiverKeyPair;\n /** Compute the x25519 ECDH shared secret. */\n x25519Dh(privateKey: Uint8Array, publicKey: Uint8Array): Uint8Array;\n /** Authenticated symmetric encryption (the node's symmetric scheme). */\n authEncrypt(\n key: Uint8Array,\n plaintext: Uint8Array,\n aad?: Uint8Array,\n ): Uint8Array;\n /** Authenticated symmetric decryption (matched to authEncrypt). */\n authDecrypt(\n key: Uint8Array,\n ciphertext: Uint8Array,\n aad?: Uint8Array,\n ): Uint8Array;\n /**\n * Wrap a symmetric key for the network's public encryption key using\n * a sealed-box / hpke / x25519+symmetric construction. Implementation\n * is opaque; only the node can unwrap.\n */\n sealToNetworkKey(\n networkPublicKey: Uint8Array,\n symmetricKey: Uint8Array,\n ): Uint8Array;\n /**\n * Open a wrapped symmetric key that was re-encrypted to the\n * per-request receiver public key. The matching private key must be\n * supplied here; the SDK never sends it to the node.\n */\n openWithReceiverKey(\n receiverPrivateKey: Uint8Array,\n wrappedKey: Uint8Array,\n ): Uint8Array;\n /**\n * Verify an ed25519 signature over `message` produced by the node\n * identified by `nodeId` (the public-key DID).\n */\n verifyNodeSignature(\n nodeId: string,\n message: Uint8Array,\n signature: Uint8Array,\n ): boolean;\n}\n\n/**\n * Signer interface used to derive a receiver key pair from a wallet or\n * session signer. The signature is HKDF-extracted into the receiver\n * seed so the public key is reproducible given the same context.\n */\nexport interface ReceiverKeySigner {\n signMessage(message: string): Promise<string>;\n}\n\n/** Capability proof material accompanying a decrypt invocation. */\nexport interface DecryptCapabilityProof {\n /** Delegation chain CIDs rooted at the network owner DID. */\n proofs: ReadonlyArray<string>;\n /** Optional Authorization header value to use instead of building one. */\n authorization?: string;\n}\n\n/**\n * Inputs to the decrypt invocation builder.\n */\nexport interface BuildDecryptInvocationInput {\n /** Target node DID — also the UCAN audience. */\n targetNode: string;\n /** Network id URN — also the recap resource. */\n networkId: string;\n /** Canonical body that will be POSTed. */\n body: DecryptRequestBody;\n /** Facts include hashes bound to the canonical body. */\n facts: DecryptInvocationFact;\n /** Capability proof chain. */\n proof: DecryptCapabilityProof;\n /** Optional `nbf` UCAN field as an ISO date string. */\n notBefore?: string;\n /** Optional `exp` UCAN field as an ISO date string. */\n expiration?: string;\n}\n\n/**\n * The output of {@link buildDecryptInvocation}.\n */\nexport interface BuiltDecryptInvocation {\n /** HTTP `Authorization` header value. */\n authorization: string;\n /** CID of the invocation (used by the node response binding). */\n invocationCid: string;\n /** Canonical body string the node will hash. */\n canonicalBody: string;\n}\n\n/**\n * Signer interface for producing the decrypt invocation. WASM bindings\n * implement this with the same session signer used for KV/SQL\n * invocations; tests can stub it.\n */\nexport interface DecryptInvocationSigner {\n signDecryptInvocation(input: BuildDecryptInvocationInput): Promise<BuiltDecryptInvocation>;\n}\n\n/**\n * Errors thrown / returned from the encryption module.\n */\nexport type EncryptionErrorInput =\n | { code: \"NETWORK_NOT_FOUND\"; networkId?: string; name?: string; message?: string }\n | { code: \"NETWORK_NOT_ACTIVE\"; state: string; message?: string }\n | { code: \"INVALID_NETWORK_ID\"; message: string }\n | { code: \"INVALID_ENVELOPE\"; message: string }\n | { code: \"DECRYPT_DENIED\"; message: string; permissionHint?: PermissionHint }\n | { code: \"INVALID_RESPONSE\"; message: string }\n | { code: \"RESPONSE_SIGNATURE_INVALID\"; message?: string }\n | { code: \"RESPONSE_BINDING_MISMATCH\"; field: string; message?: string }\n | { code: \"TRANSPORT_ERROR\"; cause: Error; message?: string }\n | { code: \"INVALID_INPUT\"; message: string };\n\nexport type EncryptionError = EncryptionErrorInput & {\n service: \"encryption\";\n message: string;\n};\n\nfunction defaultEncryptionMessage(input: EncryptionErrorInput): string {\n switch (input.code) {\n case \"NETWORK_NOT_FOUND\":\n return (\n input.message ??\n `Network not found: ${input.networkId ?? input.name ?? \"<unknown>\"}`\n );\n case \"NETWORK_NOT_ACTIVE\":\n return input.message ?? `Network not active (state=${input.state})`;\n case \"INVALID_NETWORK_ID\":\n return input.message;\n case \"INVALID_ENVELOPE\":\n return input.message;\n case \"DECRYPT_DENIED\":\n return input.message;\n case \"INVALID_RESPONSE\":\n return input.message;\n case \"RESPONSE_SIGNATURE_INVALID\":\n return input.message ?? \"Node response signature failed to verify\";\n case \"RESPONSE_BINDING_MISMATCH\":\n return (\n input.message ??\n `Node response binding mismatch on field ${JSON.stringify(input.field)}`\n );\n case \"TRANSPORT_ERROR\":\n return input.message ?? input.cause.message;\n case \"INVALID_INPUT\":\n return input.message;\n }\n}\n\nexport function encryptionError(input: EncryptionErrorInput): EncryptionError {\n return {\n ...input,\n service: \"encryption\",\n message: defaultEncryptionMessage(input),\n };\n}\n\n/** Helper for the test/runtime layers to coerce arbitrary throwables. */\nexport function toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (typeof error === \"object\" && error !== null) {\n return new Error(JSON.stringify(error));\n }\n return new Error(String(error));\n}\n\n/** Re-export for ergonomic typing of canonical payloads. */\nexport type CanonicalJson = Json;\n","/**\n * Network-descriptor discovery.\n *\n * Resolution order (per architecture):\n *\n * 1. The node's authoritative endpoint\n * `GET /encryption/networks/<networkId>` returns the current\n * descriptor (`state`, `publicEncryptionKey`, `keyVersion`, ...).\n * 2. If the node is unreachable, fall back to the cached discovery\n * record at `.well-known/encryption/network/<name>` inside the\n * owner's public space.\n *\n * The node DB is authoritative on conflict; cached records are\n * advisory only.\n */\n\nimport {\n NetworkIdError,\n networkDiscoveryKey,\n ownerDidMatches,\n parseNetworkId,\n} from \"./networkId\";\nimport {\n encryptionError,\n type EncryptionError,\n type NetworkDescriptor,\n} from \"./types\";\n\nexport type DiscoverySource = \"node\" | \"well-known\";\n\nexport interface DiscoveredNetwork {\n descriptor: NetworkDescriptor;\n source: DiscoverySource;\n}\n\nexport interface NodeDescriptorFetcher {\n /** Fetch the descriptor by full networkId URN. */\n fetchByNetworkId(networkId: string): Promise<NetworkDescriptor | null>;\n}\n\nexport interface WellKnownDescriptorFetcher {\n /**\n * Read the cached well-known descriptor by owner DID + network name.\n * Returns null if no record exists or the record is unreadable.\n */\n fetchWellKnown(\n ownerDid: string,\n discoveryKey: string,\n ): Promise<NetworkDescriptor | null>;\n}\n\nexport interface DiscoverNetworkInput {\n /** Either a networkId URN or a bare network name (paired with `ownerDid`). */\n identifier: string;\n /** Required when identifier is a bare name. */\n ownerDid?: string;\n node?: NodeDescriptorFetcher;\n wellKnown?: WellKnownDescriptorFetcher;\n}\n\n/**\n * Resolve a network descriptor. The node fetcher is preferred; the\n * well-known fallback is used only on transport failure.\n *\n * The returned descriptor is sanity-checked: `networkId`, `ownerDid`,\n * and `name` must agree with the URN, and the public key field must\n * be non-empty.\n */\nexport async function discoverNetwork(\n input: DiscoverNetworkInput,\n):\n | Promise<{ ok: true; data: DiscoveredNetwork } | { ok: false; error: EncryptionError }> {\n let networkId: string;\n let ownerDid: string;\n let name: string;\n try {\n if (input.identifier.startsWith(\"urn:tinycloud:encryption:\")) {\n const parsed = parseNetworkId(input.identifier);\n networkId = parsed.networkId;\n ownerDid = parsed.ownerDid;\n name = parsed.name;\n } else {\n if (input.ownerDid === undefined) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message:\n \"discoverNetwork requires `ownerDid` when identifier is a bare network name\",\n }),\n };\n }\n networkId = `urn:tinycloud:encryption:${input.ownerDid}:${input.identifier}`;\n const parsed = parseNetworkId(networkId);\n ownerDid = parsed.ownerDid;\n name = parsed.name;\n }\n } catch (err) {\n if (err instanceof NetworkIdError) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_NETWORK_ID\",\n message: err.message,\n }),\n };\n }\n throw err;\n }\n\n // 1) Try the node first.\n if (input.node !== undefined) {\n try {\n const descriptor = await input.node.fetchByNetworkId(networkId);\n if (descriptor !== null) {\n const validated = validateDescriptor(descriptor, networkId, ownerDid, name);\n if (!validated.ok) return validated;\n return { ok: true, data: { descriptor: validated.data, source: \"node\" } };\n }\n } catch (err) {\n // Fall through to well-known\n }\n }\n\n // 2) Fallback to well-known cache.\n if (input.wellKnown !== undefined) {\n try {\n const descriptor = await input.wellKnown.fetchWellKnown(\n ownerDid,\n networkDiscoveryKey(name),\n );\n if (descriptor !== null) {\n const validated = validateDescriptor(descriptor, networkId, ownerDid, name);\n if (!validated.ok) return validated;\n return {\n ok: true,\n data: { descriptor: validated.data, source: \"well-known\" },\n };\n }\n } catch (err) {\n // Fall through to NOT_FOUND\n }\n }\n\n return {\n ok: false,\n error: encryptionError({\n code: \"NETWORK_NOT_FOUND\",\n networkId,\n name,\n }),\n };\n}\n\nfunction validateDescriptor(\n descriptor: NetworkDescriptor,\n networkId: string,\n ownerDid: string,\n name: string,\n):\n | { ok: true; data: NetworkDescriptor }\n | { ok: false; error: EncryptionError } {\n let descriptorNetwork;\n try {\n descriptorNetwork = parseNetworkId(descriptor.networkId);\n } catch (err) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_NETWORK_ID\",\n message: `descriptor networkId is malformed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n }),\n };\n }\n\n if (\n descriptorNetwork.name !== name ||\n !ownerDidMatches(descriptorNetwork.ownerDid, ownerDid)\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_NETWORK_ID\",\n message: `descriptor networkId ${JSON.stringify(descriptor.networkId)} does not match expected ${JSON.stringify(networkId)}`,\n }),\n };\n }\n\n const descriptorOwnerDid = descriptorOwner(descriptor);\n if (\n descriptorOwnerDid === undefined ||\n !ownerDidMatches(descriptorOwnerDid, ownerDid) ||\n !ownerDidMatches(descriptorOwnerDid, descriptorNetwork.ownerDid)\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_NETWORK_ID\",\n message: \"descriptor ownerDid does not match networkId ownerDid\",\n }),\n };\n }\n if (descriptor.name !== name) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_NETWORK_ID\",\n message: \"descriptor name does not match networkId name\",\n }),\n };\n }\n if (\n typeof descriptor.publicEncryptionKey !== \"string\" ||\n descriptor.publicEncryptionKey.length === 0\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_NETWORK_ID\",\n message: \"descriptor publicEncryptionKey must be a non-empty string\",\n }),\n };\n }\n return {\n ok: true,\n data: {\n ...descriptor,\n ownerDid: descriptorOwnerDid,\n },\n };\n}\n\nfunction descriptorOwner(descriptor: NetworkDescriptor): string | undefined {\n if (typeof descriptor.ownerDid === \"string\" && descriptor.ownerDid.length > 0) {\n return descriptor.ownerDid;\n }\n\n const legacyDescriptor = descriptor as NetworkDescriptor & {\n principal?: unknown;\n };\n return typeof legacyDescriptor.principal === \"string\" &&\n legacyDescriptor.principal.length > 0\n ? legacyDescriptor.principal\n : undefined;\n}\n\n/**\n * Reject a descriptor that is not in a state that accepts decrypt\n * requests. Only `active` and `rotating` networks may decrypt; revoked\n * or pending networks reject.\n */\nexport function ensureNetworkUsableForDecrypt(\n descriptor: NetworkDescriptor,\n):\n | { ok: true; data: NetworkDescriptor }\n | { ok: false; error: EncryptionError } {\n if (descriptor.state === \"active\" || descriptor.state === \"rotating\") {\n return { ok: true, data: descriptor };\n }\n return {\n ok: false,\n error: encryptionError({\n code: \"NETWORK_NOT_ACTIVE\",\n state: descriptor.state,\n }),\n };\n}\n","/**\n * Inline-envelope encrypt / decrypt helpers.\n *\n * Encryption is fully local: the SDK generates a per-record symmetric\n * key, encrypts the payload, then wraps the symmetric key against the\n * network's public encryption key. The wrapped key (and key hash)\n * travels alongside the ciphertext inside an\n * {@link InlineEncryptedEnvelope}.\n *\n * Decryption is split: the node unwraps the symmetric key to the\n * per-request receiver public key (see {@link buildDecryptInvocation}\n * and the decrypt route); this module is responsible for the local\n * payload decryption once the symmetric key is available.\n */\n\nimport {\n base64Decode,\n base64Encode,\n canonicalHashHex,\n} from \"./canonical\";\nimport {\n DEFAULT_ENCRYPTION_ALG,\n DEFAULT_KEY_VERSION,\n ENVELOPE_VERSION,\n encryptionError,\n type EncryptionCrypto,\n type EncryptionError,\n type InlineEncryptedEnvelope,\n} from \"./types\";\nimport { parseNetworkId } from \"./networkId\";\n\nexport interface EncryptToNetworkInput {\n /** Target network id URN. */\n networkId: string;\n /** Network public key bytes (already discovered). */\n networkPublicKey: Uint8Array;\n /** Payload bytes to encrypt. Callers serialize objects to bytes themselves. */\n plaintext: Uint8Array;\n /** Optional associated authenticated data. */\n aad?: Uint8Array;\n /** Ciphersuite identifier. Defaults to {@link DEFAULT_ENCRYPTION_ALG}. */\n alg?: string;\n /** Key version. Defaults to {@link DEFAULT_KEY_VERSION}. */\n keyVersion?: number;\n /** Caller-supplied envelope metadata. */\n metadata?: Record<string, string>;\n}\n\nexport interface EncryptToNetworkResult {\n envelope: InlineEncryptedEnvelope;\n /** Symmetric key returned for caller bookkeeping; do NOT persist. */\n symmetricKey: Uint8Array;\n}\n\n/**\n * Local-only encrypt: generates a symmetric key, encrypts the payload,\n * wraps the key against the network public key, and returns the\n * inline envelope.\n */\nexport function encryptToNetwork(\n crypto: EncryptionCrypto,\n input: EncryptToNetworkInput,\n): EncryptToNetworkResult {\n parseNetworkId(input.networkId);\n const alg = input.alg ?? DEFAULT_ENCRYPTION_ALG;\n const keyVersion = input.keyVersion ?? DEFAULT_KEY_VERSION;\n\n const symmetricKey = crypto.randomBytes(32);\n const ciphertext = crypto.authEncrypt(symmetricKey, input.plaintext, input.aad);\n const wrapped = crypto.sealToNetworkKey(input.networkPublicKey, symmetricKey);\n const encryptedSymmetricKey = base64Encode(wrapped);\n const encryptedSymmetricKeyHash = canonicalHashHex(\n crypto.sha256,\n encryptedSymmetricKey,\n );\n\n const envelope: InlineEncryptedEnvelope = {\n v: ENVELOPE_VERSION,\n networkId: input.networkId,\n alg,\n keyVersion,\n encryptedSymmetricKey,\n encryptedSymmetricKeyHash,\n ciphertext: base64Encode(ciphertext),\n ...(input.aad !== undefined ? { aad: base64Encode(input.aad) } : {}),\n ...(input.metadata !== undefined ? { metadata: input.metadata } : {}),\n };\n\n return { envelope, symmetricKey };\n}\n\n/**\n * Validate an inline envelope shape. Returns an error if the envelope\n * is missing required fields or fails internal hash recomputation.\n */\nexport function validateEnvelope(\n crypto: EncryptionCrypto,\n envelope: unknown,\n): { ok: true; data: InlineEncryptedEnvelope } | { ok: false; error: EncryptionError } {\n if (envelope === null || typeof envelope !== \"object\") {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_ENVELOPE\",\n message: \"envelope must be an object\",\n }),\n };\n }\n const e = envelope as InlineEncryptedEnvelope;\n if (e.v !== ENVELOPE_VERSION) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_ENVELOPE\",\n message: `envelope.v must be ${ENVELOPE_VERSION} (got ${e.v as unknown as string})`,\n }),\n };\n }\n try {\n parseNetworkId(e.networkId);\n } catch (err) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_ENVELOPE\",\n message: `envelope.networkId is malformed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n }),\n };\n }\n for (const field of [\n \"alg\",\n \"encryptedSymmetricKey\",\n \"encryptedSymmetricKeyHash\",\n \"ciphertext\",\n ] as const) {\n if (typeof e[field] !== \"string\" || (e[field] as string).length === 0) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_ENVELOPE\",\n message: `envelope.${field} must be a non-empty string`,\n }),\n };\n }\n }\n if (typeof e.keyVersion !== \"number\" || !Number.isInteger(e.keyVersion)) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_ENVELOPE\",\n message: \"envelope.keyVersion must be an integer\",\n }),\n };\n }\n const expectedHash = canonicalHashHex(crypto.sha256, e.encryptedSymmetricKey);\n if (expectedHash !== e.encryptedSymmetricKeyHash) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_ENVELOPE\",\n message:\n \"envelope.encryptedSymmetricKeyHash does not match canonical hash of encryptedSymmetricKey\",\n }),\n };\n }\n return { ok: true, data: e };\n}\n\n/**\n * Decrypt an inline envelope given the unwrapped symmetric key.\n *\n * Callers typically obtain the symmetric key via the node decrypt\n * endpoint plus the per-request receiver key pair (see\n * `EncryptionService.decryptEnvelope`).\n */\nexport function decryptEnvelopeWithKey(\n crypto: EncryptionCrypto,\n envelope: InlineEncryptedEnvelope,\n symmetricKey: Uint8Array,\n): Uint8Array {\n const ciphertext = base64Decode(envelope.ciphertext);\n const aad = envelope.aad !== undefined ? base64Decode(envelope.aad) : undefined;\n return crypto.authDecrypt(symmetricKey, ciphertext, aad);\n}\n","/**\n * TinyCloud encryption decrypt-invocation builder.\n *\n * The decrypt invocation is a UCAN-style TinyCloud invocation against\n * a node plus a network id. It is structurally distinct from the\n * existing space-shaped invocations:\n *\n * - `aud` (audience) is the **target node DID** (not a space owner).\n * - `att` (attenuation) uses the **networkId URN** as the resource\n * key (not a `tinycloud:pkh:...:<space>` URI).\n * - `fct` (facts) binds `bodyHash`, `encryptedSymmetricKeyHash`, and\n * `receiverPublicKeyHash` so the node can recompute them from the\n * POST body.\n *\n * Production callers inject a `DecryptInvocationSigner` backed by the\n * WASM UCAN/session signer. Tests pass a deterministic stub.\n */\n\nimport { canonicalize, canonicalHashHex, hexEncode, utf8Encode } from \"./canonical\";\nimport { parseNetworkId } from \"./networkId\";\nimport {\n DECRYPT_ACTION,\n DECRYPT_FACT_TYPE,\n ENCRYPTION_SERVICE,\n encryptionError,\n type BuildDecryptInvocationInput,\n type BuiltDecryptInvocation,\n type CanonicalJson,\n type DecryptCapabilityProof,\n type DecryptInvocationFact,\n type DecryptInvocationSigner,\n type DecryptRequestBody,\n type EncryptionCrypto,\n type EncryptionError,\n} from \"./types\";\n\nexport interface CanonicalDecryptRequest {\n /** The canonical body that the node will hash. */\n canonicalBody: string;\n /** Hex sha-256 of the canonical body bytes. */\n bodyHash: string;\n /** Hex sha-256 of the receiver public key bytes. */\n receiverPublicKeyHash: string;\n}\n\nexport interface BuildCanonicalDecryptRequestInput {\n crypto: EncryptionCrypto;\n body: DecryptRequestBody;\n receiverPublicKey: Uint8Array;\n}\n\n/**\n * Build the canonical body string and its bound hashes for a decrypt\n * request. The output is what gets POSTed to the node, and what the\n * node will hash to verify `facts.bodyHash`.\n */\nexport function buildCanonicalDecryptRequest(\n input: BuildCanonicalDecryptRequestInput,\n): CanonicalDecryptRequest {\n const canonicalBody = canonicalize(input.body as unknown as CanonicalJson);\n const bodyHash = canonicalHashHex(\n input.crypto.sha256,\n input.body as unknown as CanonicalJson,\n );\n const receiverPublicKeyHash = canonicalHashHex(\n input.crypto.sha256,\n input.body.receiverPublicKey,\n );\n return { canonicalBody, bodyHash, receiverPublicKeyHash };\n}\n\nexport interface BuildDecryptFactsInput {\n crypto: EncryptionCrypto;\n body: DecryptRequestBody;\n /** Encrypted symmetric key hash from the envelope (already canonical). */\n encryptedSymmetricKeyHash: string;\n /** Receiver public key bytes. */\n receiverPublicKey: Uint8Array;\n /** Canonical body string used to derive bodyHash. */\n canonicalBody?: string;\n}\n\n/**\n * Build the {@link DecryptInvocationFact} that will be embedded in the\n * UCAN `fct` field. Hashes are recomputed here so callers cannot drift\n * from the canonical body without the node noticing.\n */\nexport function buildDecryptFacts(\n input: BuildDecryptFactsInput,\n): DecryptInvocationFact {\n // When a precomputed canonicalBody string is supplied, hash its bytes\n // directly. Don't route it through canonicalHashHex, which would\n // re-canonicalize the string itself (escaping it as a JSON value).\n const bodyHash =\n input.canonicalBody !== undefined\n ? hexEncode(input.crypto.sha256(utf8Encode(input.canonicalBody)))\n : canonicalHashHex(\n input.crypto.sha256,\n input.body as unknown as CanonicalJson,\n );\n const receiverPublicKeyHash = canonicalHashHex(\n input.crypto.sha256,\n input.body.receiverPublicKey,\n );\n return {\n type: DECRYPT_FACT_TYPE,\n targetNode: input.body.targetNode,\n networkId: input.body.networkId,\n bodyHash,\n encryptedSymmetricKeyHash: input.encryptedSymmetricKeyHash,\n receiverPublicKeyHash,\n alg: input.body.alg,\n keyVersion: input.body.keyVersion,\n };\n}\n\n/**\n * Recap-shaped attenuation for the decrypt invocation. The resource\n * key is the networkId URN; the ability is the long-form\n * `tinycloud.encryption/decrypt`. This is intentionally distinct from\n * the existing space-shaped invocation map so callers cannot\n * accidentally fake a space prefix.\n */\nexport function buildDecryptAttenuation(\n networkId: string,\n): Record<string, Record<string, Record<string, never>>> {\n parseNetworkId(networkId);\n return {\n [networkId]: {\n [DECRYPT_ACTION]: {},\n },\n };\n}\n\n/**\n * Validate a {@link BuildDecryptInvocationInput} payload — the body\n * shape, the facts bindings, and the audience contract — without\n * actually signing. Returns either the input (typed) or a structured\n * error so callers can short-circuit before calling into WASM.\n */\nexport function checkDecryptInvocationInput(\n crypto: EncryptionCrypto,\n input: BuildDecryptInvocationInput,\n):\n | { ok: true; data: BuildDecryptInvocationInput; canonicalBody: string }\n | { ok: false; error: EncryptionError } {\n if (input.body.type !== DECRYPT_FACT_TYPE) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: `body.type must be ${DECRYPT_FACT_TYPE}`,\n }),\n };\n }\n if (input.facts.type !== DECRYPT_FACT_TYPE) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: `facts.type must be ${DECRYPT_FACT_TYPE}`,\n }),\n };\n }\n if (input.facts.targetNode !== input.targetNode) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message:\n \"facts.targetNode must equal targetNode — the UCAN audience binds the request to a single node\",\n }),\n };\n }\n if (input.body.targetNode !== input.targetNode) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: \"body.targetNode must equal targetNode\",\n }),\n };\n }\n if (input.facts.networkId !== input.networkId) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: \"facts.networkId must equal networkId\",\n }),\n };\n }\n if (input.body.networkId !== input.networkId) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: \"body.networkId must equal networkId\",\n }),\n };\n }\n if (input.facts.alg !== input.body.alg) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: \"facts.alg must equal body.alg\",\n }),\n };\n }\n if (input.facts.keyVersion !== input.body.keyVersion) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: \"facts.keyVersion must equal body.keyVersion\",\n }),\n };\n }\n if (\n input.facts.encryptedSymmetricKeyHash !==\n input.body.encryptedSymmetricKeyHash\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message:\n \"facts.encryptedSymmetricKeyHash must equal body.encryptedSymmetricKeyHash\",\n }),\n };\n }\n if (\n input.facts.receiverPublicKeyHash !== input.body.receiverPublicKeyHash\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message:\n \"facts.receiverPublicKeyHash must equal body.receiverPublicKeyHash\",\n }),\n };\n }\n try {\n parseNetworkId(input.networkId);\n } catch (err) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_NETWORK_ID\",\n message: err instanceof Error ? err.message : String(err),\n }),\n };\n }\n const canonicalBody = canonicalize(\n input.body as unknown as CanonicalJson,\n );\n const expectedBodyHash = canonicalHashHex(crypto.sha256, input.body as unknown as CanonicalJson);\n if (expectedBodyHash !== input.facts.bodyHash) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message: \"facts.bodyHash does not match the canonical body hash\",\n }),\n };\n }\n return { ok: true, data: input, canonicalBody };\n}\n\n/**\n * Compose the high-level decrypt invocation and hand it to the signer\n * for UCAN minting. The signer returns the Authorization header value\n * plus the invocation CID; this function only validates and\n * orchestrates — it does not perform crypto signing itself.\n */\nexport async function buildDecryptInvocation(\n crypto: EncryptionCrypto,\n signer: DecryptInvocationSigner,\n input: BuildDecryptInvocationInput,\n): Promise<{ ok: true; data: BuiltDecryptInvocation } | { ok: false; error: EncryptionError }> {\n const checked = checkDecryptInvocationInput(crypto, input);\n if (!checked.ok) {\n return checked;\n }\n try {\n const built = await signer.signDecryptInvocation(checked.data);\n if (!built.authorization || !built.invocationCid) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message:\n \"decrypt-invocation signer returned an empty authorization or invocationCid\",\n }),\n };\n }\n if (built.canonicalBody !== checked.canonicalBody) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_INPUT\",\n message:\n \"decrypt-invocation signer returned a canonicalBody that does not match the SDK's canonicalization — signer must use the SDK-provided body\",\n }),\n };\n }\n return { ok: true, data: built };\n } catch (err) {\n return {\n ok: false,\n error: encryptionError({\n code: \"TRANSPORT_ERROR\",\n cause: err instanceof Error ? err : new Error(String(err)),\n message: `failed to sign decrypt invocation: ${\n err instanceof Error ? err.message : String(err)\n }`,\n }),\n };\n }\n}\n\n/** Re-export so callers can introspect the service+action constants. */\nexport { DECRYPT_ACTION, ENCRYPTION_SERVICE };\n","/**\n * Per-request receiver key generation.\n *\n * The receiver key pair is generated for a single decrypt request: the\n * SDK sends `receiverPublicKey` to the node so the node can rewrap the\n * symmetric key; the matching private key never leaves the SDK.\n *\n * Two derivation modes are supported:\n *\n * 1. **Random** — `crypto.randomBytes(32)` seeds a fresh x25519 pair.\n * This is the default for one-of-one flows.\n *\n * 2. **Signed-context** — the caller provides a signer that signs a\n * deterministic context message (network id + nonce + invocation\n * intent). The signature bytes are SHA-256'd into the seed. This is\n * useful when callers want the receiver key to be reproducible by\n * the same session signer (e.g. for delegated reads where the\n * signer is the only stable secret).\n */\n\nimport { utf8Encode } from \"./canonical\";\nimport type {\n EncryptionCrypto,\n ReceiverKeyPair,\n ReceiverKeySigner,\n} from \"./types\";\n\nexport interface RandomReceiverKeyInput {\n crypto: EncryptionCrypto;\n}\n\nexport function generateRandomReceiverKey(\n input: RandomReceiverKeyInput,\n): ReceiverKeyPair {\n const seed = input.crypto.randomBytes(32);\n return input.crypto.x25519FromSeed(seed);\n}\n\nexport interface SignedReceiverKeyInput {\n crypto: EncryptionCrypto;\n signer: ReceiverKeySigner;\n networkId: string;\n /** Optional extra context (e.g. invocation nonce) folded into the message. */\n context?: string;\n}\n\n/**\n * Deterministic receiver-key derivation: signs a context string with\n * the supplied signer, then SHA-256s the signature bytes into the\n * x25519 seed.\n *\n * The context message is:\n * `tinycloud.encryption.receiver-key/v1:<networkId>:<context>`\n *\n * Callers MUST include unique context (e.g. a fresh nonce) on every\n * request unless they explicitly want reproducibility.\n */\nexport async function deriveSignedReceiverKey(\n input: SignedReceiverKeyInput,\n): Promise<ReceiverKeyPair> {\n const message = `tinycloud.encryption.receiver-key/v1:${input.networkId}:${input.context ?? \"\"}`;\n const sig = await input.signer.signMessage(message);\n // Treat the signature as opaque bytes; sha256 collapses to a 32-byte seed.\n const sigBytes = utf8Encode(sig);\n const seed = input.crypto.sha256(sigBytes);\n return input.crypto.x25519FromSeed(seed);\n}\n","/**\n * Decrypt-response verification.\n *\n * The node returns the unwrapped symmetric key re-encrypted to the\n * per-request receiver public key. Before the SDK uses the wrapped\n * key, it must:\n *\n * 1. Verify the node signature over the canonical response (excluding\n * the signature field).\n * 2. Recompute every binding hash and reject any mismatch.\n * 3. Confirm `targetNode`, `networkId`, `alg`, and `keyVersion` echo\n * what the SDK sent.\n *\n * After verification, the SDK opens `wrappedKey` with the per-request\n * receiver private key and uses the resulting symmetric key to\n * decrypt the inline envelope payload.\n */\n\nimport { base64Decode, canonicalize, hexEncode, utf8Encode } from \"./canonical\";\nimport {\n DECRYPT_RESULT_TYPE,\n encryptionError,\n type CanonicalJson,\n type DecryptInvocationFact,\n type DecryptRequestBody,\n type DecryptResponseBody,\n type EncryptionCrypto,\n type EncryptionError,\n} from \"./types\";\n\n/**\n * Construct the canonical bytes that the node signed.\n *\n * The signature covers the response with `nodeSignature` removed. We\n * canonicalize and hash the same way the node does so the binding is\n * stable.\n */\nexport function canonicalSignedResponse(\n response: DecryptResponseBody,\n): string {\n // Strip the signature; canonicalize sorts keys for us.\n const { nodeSignature: _drop, ...rest } = response;\n return canonicalize(rest as unknown as CanonicalJson);\n}\n\nexport interface VerifyDecryptResponseInput {\n crypto: EncryptionCrypto;\n request: DecryptRequestBody;\n facts: DecryptInvocationFact;\n /** CID of the signed invocation that produced this response. */\n invocationCid: string;\n /** Hex bodyHash that was bound in `facts.bodyHash`. */\n requestBodyHash: string;\n response: DecryptResponseBody;\n}\n\n/**\n * Verify the node's decrypt response. Returns the response on success;\n * a structured error on signature, binding, or shape failure.\n */\nexport function verifyDecryptResponse(\n input: VerifyDecryptResponseInput,\n):\n | { ok: true; data: DecryptResponseBody }\n | { ok: false; error: EncryptionError } {\n const { crypto, request, facts, invocationCid, requestBodyHash, response } =\n input;\n\n if (response.type !== DECRYPT_RESULT_TYPE) {\n return {\n ok: false,\n error: encryptionError({\n code: \"INVALID_RESPONSE\",\n message: `response.type must be ${DECRYPT_RESULT_TYPE}`,\n }),\n };\n }\n if (response.targetNode !== request.targetNode) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"targetNode\",\n }),\n };\n }\n if (response.networkId !== request.networkId) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"networkId\",\n }),\n };\n }\n if (response.nodeId !== request.targetNode) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"nodeId\",\n }),\n };\n }\n if (response.alg !== request.alg) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"alg\",\n }),\n };\n }\n if (response.keyVersion !== request.keyVersion) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"keyVersion\",\n }),\n };\n }\n if (\n response.encryptedSymmetricKeyHash !==\n request.encryptedSymmetricKeyHash\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"encryptedSymmetricKeyHash\",\n }),\n };\n }\n if (response.receiverPublicKeyHash !== request.receiverPublicKeyHash) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"receiverPublicKeyHash\",\n }),\n };\n }\n if (response.invocationCid !== invocationCid) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"invocationCid\",\n }),\n };\n }\n // requestHash binds invocationCid || requestBodyHash per spec. The `||`\n // notation is concatenation, not a literal delimiter.\n const expectedRequestHash = hexEncode(\n crypto.sha256(utf8Encode(`${invocationCid}${requestBodyHash}`)),\n );\n if (response.requestHash !== expectedRequestHash) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"requestHash\",\n }),\n };\n }\n // The facts must agree with the response (defensive: catches a node\n // that returns a fresh body bound to a different invocation).\n if (\n facts.encryptedSymmetricKeyHash !== response.encryptedSymmetricKeyHash ||\n facts.receiverPublicKeyHash !== response.receiverPublicKeyHash ||\n facts.networkId !== response.networkId ||\n facts.targetNode !== response.targetNode ||\n facts.alg !== response.alg ||\n facts.keyVersion !== response.keyVersion\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_BINDING_MISMATCH\",\n field: \"facts\",\n }),\n };\n }\n\n const signedBytes = new TextEncoder().encode(\n canonicalSignedResponse(response),\n );\n const signatureBytes = base64Decode(response.nodeSignature);\n if (\n !crypto.verifyNodeSignature(response.nodeId, signedBytes, signatureBytes)\n ) {\n return {\n ok: false,\n error: encryptionError({\n code: \"RESPONSE_SIGNATURE_INVALID\",\n }),\n };\n }\n return { ok: true, data: response };\n}\n\n/**\n * After verification, open the response's `wrappedKey` with the\n * per-request receiver private key and return the symmetric key.\n */\nexport function openWrappedKey(\n crypto: EncryptionCrypto,\n receiverPrivateKey: Uint8Array,\n response: DecryptResponseBody,\n): Uint8Array {\n const wrapped = base64Decode(response.wrappedKey);\n return crypto.openWithReceiverKey(receiverPrivateKey, wrapped);\n}\n","/**\n * EncryptionService — TinyCloud one-of-one encryption service.\n *\n * Responsibilities:\n * - Network discovery (`discoverNetwork`).\n * - Local envelope encryption (`encryptToNetwork`).\n * - Node-mediated decrypt with full request/response binding\n * verification (`decryptEnvelope`).\n *\n * Non-responsibilities:\n * - KV/SQL access. Callers fetch encrypted envelopes from their data\n * service of choice and pass them in.\n * - Network onboarding/ceremony. Those routes are node-only.\n */\n\nimport { BaseService } from \"../base/BaseService\";\nimport { type Result } from \"../types\";\nimport {\n base64Decode,\n base64Encode,\n canonicalHashHex,\n} from \"./canonical\";\nimport {\n discoverNetwork as discoverNetworkFn,\n ensureNetworkUsableForDecrypt,\n type NodeDescriptorFetcher,\n type WellKnownDescriptorFetcher,\n} from \"./discovery\";\nimport {\n decryptEnvelopeWithKey,\n encryptToNetwork as encryptToNetworkFn,\n validateEnvelope,\n} from \"./envelope\";\nimport {\n buildCanonicalDecryptRequest,\n buildDecryptFacts,\n buildDecryptInvocation,\n} from \"./invocation\";\nimport { generateRandomReceiverKey } from \"./receiverKey\";\nimport {\n openWrappedKey,\n verifyDecryptResponse,\n} from \"./response\";\nimport { DecryptTransportResponseError } from \"@tinycloud/sdk-services/internal/decrypt-transport-response-error\";\nimport {\n DECRYPT_FACT_TYPE,\n encryptionError,\n type DecryptCapabilityProof,\n type DecryptInvocationSigner,\n type DecryptRequestBody,\n type DecryptResponseBody,\n type EncryptionCrypto,\n type EncryptionError,\n type InlineEncryptedEnvelope,\n type NetworkDescriptor,\n toError,\n} from \"./types\";\nimport type {\n EncryptToNetworkOptions,\n DecryptEnvelopeOptions,\n IEncryptionService,\n} from \"./IEncryptionService\";\n\n// Local Result helpers typed to EncryptionError. The shared `ok` / `err`\n// from `../types` default the error generic to `ServiceError`, which\n// widens our return-type inference here.\nfunction encOk<T>(data: T): Result<T, EncryptionError> {\n return { ok: true, data };\n}\nfunction encErr<T = never>(error: EncryptionError): Result<T, EncryptionError> {\n return { ok: false, error };\n}\n\n/**\n * Transport for posting decrypt requests to a TinyCloud node.\n *\n * Implementations supply the `Authorization` header (built by the\n * decrypt-invocation signer) and POST the canonical body. The\n * response body is JSON-decoded into a {@link DecryptResponseBody}.\n */\nexport interface DecryptTransport {\n postDecrypt(input: {\n targetNode: string;\n networkId: string;\n authorization: string;\n canonicalBody: string;\n signal?: AbortSignal;\n }): Promise<DecryptResponseBody>;\n}\n\nexport { DecryptTransportResponseError } from \"@tinycloud/sdk-services/internal/decrypt-transport-response-error\";\n\nexport interface EncryptionServiceConfig {\n crypto: EncryptionCrypto;\n signer: DecryptInvocationSigner;\n transport: DecryptTransport;\n node?: NodeDescriptorFetcher;\n wellKnown?: WellKnownDescriptorFetcher;\n /** Bound by an owning service graph to prevent cross-session authority use. */\n assertActive?: () => void;\n [key: string]: unknown;\n}\n\nexport class EncryptionService\n extends BaseService\n implements IEncryptionService\n{\n static readonly serviceName = \"encryption\";\n\n declare protected _config: EncryptionServiceConfig;\n\n constructor(config: EncryptionServiceConfig) {\n super();\n this._config = config;\n }\n\n get config(): EncryptionServiceConfig {\n return this._config;\n }\n\n private get crypto(): EncryptionCrypto {\n return this._config.crypto;\n }\n\n async discoverNetwork(\n identifier: string,\n ownerDid?: string,\n ): Promise<Result<NetworkDescriptor, EncryptionError>> {\n this.assertActive();\n const result = await discoverNetworkFn({\n identifier,\n ...(ownerDid !== undefined ? { ownerDid } : {}),\n ...(this._config.node !== undefined ? { node: this._config.node } : {}),\n ...(this._config.wellKnown !== undefined\n ? { wellKnown: this._config.wellKnown }\n : {}),\n });\n this.assertActive();\n if (!result.ok) return result;\n return encOk(result.data.descriptor);\n }\n\n async encryptToNetwork(\n networkId: string,\n plaintext: Uint8Array,\n options?: EncryptToNetworkOptions,\n ): Promise<Result<InlineEncryptedEnvelope, EncryptionError>> {\n try {\n this.assertActive();\n const discovered = await this.discoverNetwork(networkId);\n if (!discovered.ok) return discovered;\n const usable = ensureNetworkUsableForDecrypt(discovered.data);\n // For encryption we tolerate \"rotating\" but reject revoked/failed/pending —\n // ensureNetworkUsableForDecrypt enforces exactly that constraint.\n if (!usable.ok) return usable;\n\n const descriptor = usable.data;\n const networkPublicKey = base64Decode(descriptor.publicEncryptionKey);\n const result = encryptToNetworkFn(this.crypto, {\n // Discovery returns the node's canonical network identifier. Persist\n // that spelling in the envelope so a later response can be compared\n // byte-for-byte (notably for checksummed did:pkh principals).\n networkId: descriptor.networkId,\n networkPublicKey,\n plaintext,\n ...(options?.aad !== undefined ? { aad: options.aad } : {}),\n alg: options?.alg ?? descriptor.alg,\n keyVersion: options?.keyVersion ?? descriptor.keyVersion,\n ...(options?.metadata !== undefined ? { metadata: options.metadata } : {}),\n });\n return encOk(result.envelope);\n } catch (error) {\n return encErr(\n encryptionError({\n code: \"TRANSPORT_ERROR\",\n cause: toError(error),\n }),\n );\n }\n }\n\n async decryptEnvelope(\n envelope: InlineEncryptedEnvelope,\n capabilityProof: DecryptCapabilityProof,\n options?: DecryptEnvelopeOptions,\n ): Promise<Result<Uint8Array, EncryptionError>> {\n try {\n this.assertActive();\n const validated = validateEnvelope(this.crypto, envelope);\n if (!validated.ok) return validated;\n if (\n options?.aad !== undefined &&\n validated.data.aad !== base64Encode(options.aad)\n ) {\n return encErr(\n encryptionError({\n code: \"INVALID_INPUT\",\n message: \"decryptEnvelope aad does not match the envelope\",\n }),\n );\n }\n\n let descriptor: NetworkDescriptor;\n if (options?.descriptor !== undefined) {\n descriptor = options.descriptor;\n } else {\n const discovered = await this.discoverNetwork(envelope.networkId);\n if (!discovered.ok) return discovered;\n descriptor = discovered.data;\n }\n const usable = ensureNetworkUsableForDecrypt(descriptor);\n if (!usable.ok) return usable;\n\n const targetNode =\n options?.targetNode ?? descriptor.members[0]?.nodeId;\n if (targetNode === undefined) {\n return encErr(\n encryptionError({\n code: \"INVALID_INPUT\",\n message: \"no target node available from descriptor\",\n }),\n );\n }\n\n // Generate per-request receiver key. We use the random mode by\n // default; callers wanting deterministic derivation must wrap\n // this service directly.\n const receiverKey = generateRandomReceiverKey({ crypto: this.crypto });\n const receiverPublicKey = base64Encode(receiverKey.publicKey);\n const receiverPublicKeyHash = canonicalHashHex(\n this.crypto.sha256,\n receiverPublicKey,\n );\n\n const body: DecryptRequestBody = {\n type: DECRYPT_FACT_TYPE,\n targetNode,\n networkId: envelope.networkId,\n alg: envelope.alg,\n keyVersion: envelope.keyVersion,\n encryptedSymmetricKey: envelope.encryptedSymmetricKey,\n encryptedSymmetricKeyHash: envelope.encryptedSymmetricKeyHash,\n receiverPublicKey,\n receiverPublicKeyHash,\n };\n const canonicalRequest = buildCanonicalDecryptRequest({\n crypto: this.crypto,\n body,\n receiverPublicKey: receiverKey.publicKey,\n });\n const facts = buildDecryptFacts({\n crypto: this.crypto,\n body,\n encryptedSymmetricKeyHash: envelope.encryptedSymmetricKeyHash,\n receiverPublicKey: receiverKey.publicKey,\n canonicalBody: canonicalRequest.canonicalBody,\n });\n\n const built = await buildDecryptInvocation(this.crypto, this._config.signer, {\n targetNode,\n networkId: envelope.networkId,\n body,\n facts,\n proof: capabilityProof,\n });\n this.assertActive();\n if (!built.ok) {\n if (built.error.code !== \"TRANSPORT_ERROR\") return built;\n return encErr(\n encryptionError({\n code: \"INVALID_INPUT\",\n message: \"Unable to build decrypt request\",\n }),\n );\n }\n\n let response: DecryptResponseBody;\n try {\n response = await this._config.transport.postDecrypt({\n targetNode,\n networkId: envelope.networkId,\n authorization: built.data.authorization,\n canonicalBody: built.data.canonicalBody,\n signal: this.abortSignal,\n });\n this.assertActive();\n } catch (error) {\n if (error instanceof DecryptTransportResponseError) {\n return encErr(\n encryptionError(\n error.status === 401 || error.status === 403\n ? {\n code: \"DECRYPT_DENIED\",\n message: \"Node denied decrypt request\",\n ...(error.permissionHint === undefined\n ? {}\n : { permissionHint: error.permissionHint }),\n }\n : { code: \"INVALID_RESPONSE\", message: \"Node decrypt request failed\" },\n ),\n );\n }\n return encErr(\n encryptionError({\n code: \"TRANSPORT_ERROR\",\n cause: toError(error),\n }),\n );\n }\n\n const verified = verifyDecryptResponse({\n crypto: this.crypto,\n request: body,\n facts,\n invocationCid: built.data.invocationCid,\n requestBodyHash: facts.bodyHash,\n response,\n });\n if (!verified.ok) return verified;\n\n const symmetricKey = openWrappedKey(\n this.crypto,\n receiverKey.privateKey,\n verified.data,\n );\n const plaintext = decryptEnvelopeWithKey(\n this.crypto,\n envelope,\n symmetricKey,\n );\n return encOk(plaintext);\n } catch (error) {\n return encErr(\n encryptionError({\n code: \"INVALID_RESPONSE\",\n message: \"Local decryption failed\",\n }),\n );\n }\n }\n\n private assertActive(): void {\n this._config.assertActive?.();\n }\n}\n","/**\n * TinyCloud encryption service exports.\n *\n * Reference architecture:\n * - Network ids: `urn:tinycloud:encryption:<ownerDid>:<network>`\n * - Inline envelopes: `{ v, networkId, alg, keyVersion,\n * encryptedSymmetricKey, encryptedSymmetricKeyHash, ciphertext, aad,\n * metadata }`\n * - Decrypt requests: UCAN-style invocations against a node + networkId.\n */\n\nexport {\n EncryptionService,\n} from \"./EncryptionService\";\n// The root and `/encryption` entrypoints both load this canonical CJS module.\n// That gives this public error normal class identity in CJS and ESM without a\n// mutable global registry or a forgeable structural marker.\nexport { DecryptTransportResponseError } from \"@tinycloud/sdk-services/internal/decrypt-transport-response-error\";\nexport type {\n DecryptTransport,\n EncryptionServiceConfig,\n} from \"./EncryptionService\";\nexport type {\n IEncryptionService,\n EncryptToNetworkOptions,\n DecryptEnvelopeOptions,\n} from \"./IEncryptionService\";\n\nexport {\n parseNetworkId,\n buildNetworkId,\n isNetworkId,\n networkDiscoveryKey,\n NetworkIdError,\n ENCRYPTION_NETWORK_URN_PREFIX,\n NETWORK_NAME_PATTERN,\n type ParsedNetworkId,\n} from \"./networkId\";\n\nexport {\n canonicalize,\n canonicalHashHex,\n hexEncode,\n hexDecode,\n base64Encode,\n base64Decode,\n utf8Encode,\n utf8Decode,\n type Json,\n} from \"./canonical\";\n\nexport {\n encryptToNetwork,\n decryptEnvelopeWithKey,\n validateEnvelope,\n type EncryptToNetworkInput,\n type EncryptToNetworkResult,\n} from \"./envelope\";\n\nexport {\n generateRandomReceiverKey,\n deriveSignedReceiverKey,\n type RandomReceiverKeyInput,\n type SignedReceiverKeyInput,\n} from \"./receiverKey\";\n\nexport {\n buildCanonicalDecryptRequest,\n buildDecryptFacts,\n buildDecryptAttenuation,\n buildDecryptInvocation,\n checkDecryptInvocationInput,\n DECRYPT_ACTION,\n ENCRYPTION_SERVICE,\n type CanonicalDecryptRequest,\n type BuildCanonicalDecryptRequestInput,\n type BuildDecryptFactsInput,\n} from \"./invocation\";\n\nexport {\n verifyDecryptResponse,\n canonicalSignedResponse,\n openWrappedKey,\n type VerifyDecryptResponseInput,\n} from \"./response\";\n\nexport {\n discoverNetwork,\n ensureNetworkUsableForDecrypt,\n type DiscoverNetworkInput,\n type DiscoveredNetwork,\n type DiscoverySource,\n type NodeDescriptorFetcher,\n type WellKnownDescriptorFetcher,\n} from \"./discovery\";\n\nexport {\n DEFAULT_ENCRYPTION_ALG,\n ENVELOPE_VERSION,\n DEFAULT_KEY_VERSION,\n DECRYPT_FACT_TYPE,\n DECRYPT_RESULT_TYPE,\n ENCRYPTION_SERVICE_SHORT,\n encryptionError,\n type BuildDecryptInvocationInput,\n type BuiltDecryptInvocation,\n type CanonicalJson,\n type DecryptCapabilityProof,\n type DecryptInvocationFact,\n type DecryptInvocationSigner,\n type DecryptRequestBody,\n type DecryptResponseBody,\n type EncryptionCrypto,\n type EncryptionError,\n type EncryptionErrorInput,\n type InlineEncryptedEnvelope,\n type NetworkDescriptor,\n type ReceiverKeyPair,\n type ReceiverKeySigner,\n} from \"./types\";\n","/**\n * Capability subset checking and recap parsing.\n *\n * This module powers the capability-chain delegation flow. The key decision\n * a `delegateTo` call has to make is: \"are the requested capabilities a\n * subset of what the current session already grants?\"\n *\n * - If yes → issue the delegation via the session-key UCAN path (no wallet prompt).\n * - If no → raise {@link PermissionNotInManifestError} so the caller can\n * trigger an escalation flow via `requestPermissions`.\n *\n * Canonical spec: `.claude/specs/capability-chain.md`.\n *\n * @packageDocumentation\n */\n\nimport {\n DEFAULT_MANIFEST_SPACE,\n type PermissionEntry,\n SERVICE_SHORT_TO_LONG,\n expandActionShortNames,\n} from \"./manifest\";\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a `delegateTo` call requests capabilities that the current\n * session does not already grant. The caller can catch this and trigger\n * `requestPermissions(missing)` to show an escalation modal.\n */\nexport class PermissionNotInManifestError extends Error {\n public readonly missing: PermissionEntry[];\n public readonly granted: PermissionEntry[];\n\n constructor(missing: PermissionEntry[], granted: PermissionEntry[]) {\n super(\n `Requested capabilities exceed current session. Missing ${missing.length} entries.`\n );\n this.name = \"PermissionNotInManifestError\";\n this.missing = missing;\n this.granted = granted;\n }\n}\n\n/**\n * Thrown when the current session has expired (or will expire within the\n * safety margin). The caller should trigger a fresh sign-in.\n */\nexport class SessionExpiredError extends Error {\n public readonly expiredAt: Date;\n\n constructor(expiredAt: Date) {\n super(`Session expired at ${expiredAt.toISOString()}`);\n this.name = \"SessionExpiredError\";\n this.expiredAt = expiredAt;\n }\n}\n\n/**\n * Thrown when the installed WASM delegation boundary cannot encode every\n * constrained ReCap branch in a requested child delegation.\n */\nexport class CaveatedDelegationUnsupportedError extends Error {\n /** Stable machine-readable error code for fail-closed caveated delegation. */\n code = \"CAVEATED_DELEGATION_UNSUPPORTED\" as const;\n /** Requested entries whose constrained ReCap branches cannot be preserved. */\n entries: PermissionEntry[];\n\n constructor(entries: PermissionEntry[]) {\n super(\n \"Cannot create a caveated delegation because this WASM delegation boundary cannot preserve every ReCap caveat branch exactly.\"\n );\n this.name = \"CaveatedDelegationUnsupportedError\";\n this.entries = entries.map(cloneEntry);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Space normalization\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a space identifier to its short-name form.\n *\n * Recap resource URIs in a signed SIWE encode the space as a full URI of the\n * form `tinycloud:pkh:eip155:{chainId}:{address}:{name}` (e.g.\n * `tinycloud:pkh:eip155:1:0xd559...:default`). Manifest permissions and\n * backend-advertised permissions use the short `{name}` form (e.g.\n * `\"default\"`).\n *\n * Strict string comparison between these two forms would always fail, so we\n * normalize both sides to the short name before comparing. The trailing\n * segment after the last `:` in a `tinycloud:` URI is the space name.\n *\n * Short names (`\"default\"`, `\"work-space\"`) are returned unchanged.\n * Any non-`tinycloud:` string is returned unchanged. A malformed URI with a\n * trailing colon is returned unchanged (so the check degrades to a strict\n * mismatch rather than collapsing to an empty string).\n *\n * @internal\n */\nexport function normalizeSpace(space: string): string {\n if (!space.startsWith(\"tinycloud:\")) {\n return space;\n }\n const lastColon = space.lastIndexOf(\":\");\n if (lastColon === -1 || lastColon === space.length - 1) {\n return space;\n }\n return space.slice(lastColon + 1);\n}\n\n// ---------------------------------------------------------------------------\n// Subset check\n// ---------------------------------------------------------------------------\n\nexport interface SubsetCheckResult {\n /** True when every requested entry is covered by a granted entry. */\n subset: boolean;\n /** Entries the granted set does not cover (empty when `subset` is true). */\n missing: PermissionEntry[];\n}\n\n/**\n * Check whether `requested` is a strict subset of `granted`.\n *\n * Matching rules for each `requested[i]`:\n * - `service` matches exactly.\n * - `space` matches exactly.\n * - Path containment:\n * - If `granted.path` ends with `/`, it covers any `requested.path` that\n * starts with `granted.path`.\n * - Otherwise, the paths must match exactly.\n * - Action containment: every requested URN must be matched exactly or by a\n * service-scoped wildcard such as `tinycloud.kv/*`.\n *\n * Grant entries with the same service, space, path coverage, and caveats are\n * considered together. This matters because ReCap parsers may return one\n * entry per action even when the original manifest grouped those actions.\n * Any `requested` entry whose actions are not collectively covered is added\n * to `missing` and the overall result is non-subset.\n *\n * Both sides are expected to be in the canonical long-form shape (service\n * starts with `tinycloud.`, actions are full URNs). Use {@link parseRecapCapabilities}\n * or `expandActionShortNames` to normalize inputs first.\n */\nexport function isCapabilitySubset(\n requested: readonly PermissionEntry[],\n granted: readonly PermissionEntry[]\n): SubsetCheckResult {\n const missing: PermissionEntry[] = [];\n\n for (const req of requested) {\n const matchingScope = granted.filter((entry) =>\n canonicalizeEntryScopeMatches(req, entry)\n );\n const requestedActions = expandActionShortNames(\n req.service,\n req.actions\n );\n const grantedActions = matchingScope.flatMap((entry) =>\n expandActionShortNames(entry.service, entry.actions)\n );\n const covered = requestedActions.every((requestedAction) =>\n grantedActions.some((grantedAction) =>\n actionContains(grantedAction, requestedAction)\n )\n );\n if (!covered) {\n missing.push(cloneEntry(req));\n }\n }\n\n return { subset: missing.length === 0, missing };\n}\n\n/**\n * Returns true when `granted` fully covers `requested` — same service, same\n * space, path containment per spec, and action set containment.\n */\nfunction canonicalizeEntryScopeMatches(\n requested: PermissionEntry,\n granted: PermissionEntry\n): boolean {\n if (requested.service !== granted.service) {\n return false;\n }\n // Normalize both sides so callers passing short names (`\"default\"`) match\n // recap-parsed full URIs (`\"tinycloud:pkh:eip155:1:0xd559...:default\"`) and\n // vice versa. Idempotent for short names.\n if (\n normalizeSpace(requested.space ?? DEFAULT_MANIFEST_SPACE) !==\n normalizeSpace(granted.space ?? DEFAULT_MANIFEST_SPACE)\n ) {\n return false;\n }\n if (!pathContains(granted.path, requested.path)) {\n return false;\n }\n // There is no safe general partial ordering for arbitrary ReCap caveat\n // objects. A caveated authority therefore only covers the same caveat set;\n // treating it as uncaveated would authorize a broader child delegation.\n if (!recapCaveatsEqual(requested.caveats, granted.caveats)) {\n return false;\n }\n return true;\n}\n\n/**\n * Compare ReCap caveat branch sets without losing signed branch structure.\n *\n * ReCap represents an unconstrained ability as `[{}]`, while SDK callers can\n * represent that same authority as `undefined` or `[]`. Those three forms are\n * equivalent. Every other branch, including an unconstrained branch mixed\n * with a constrained one, remains part of the canonical comparison.\n */\nexport function recapCaveatsEqual(\n left: readonly Record<string, unknown>[] | undefined,\n right: readonly Record<string, unknown>[] | undefined,\n): boolean {\n return canonicalizeRecapCaveats(left) === canonicalizeRecapCaveats(right);\n}\n\n/**\n * Canonicalize a ReCap caveat branch set for exact equality checks.\n *\n * Object keys and branch order do not affect equality. A lone unconstrained\n * branch is normalized to the SDK's empty-caveat representation, but an\n * unconstrained branch in a mixed set is deliberately retained.\n */\nexport function canonicalizeRecapCaveats(\n caveats: readonly Record<string, unknown>[] | undefined,\n): string {\n const canonicalize = (value: unknown): string => {\n if (value === null || typeof value === \"boolean\" || typeof value === \"string\") {\n return JSON.stringify(value);\n }\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) return JSON.stringify(value);\n throw new Error(\"ReCap caveats must contain only JSON values.\");\n }\n if (Array.isArray(value)) return `[${value.map(canonicalize).join(\",\")}]`;\n if (typeof value === \"object\") {\n const object = value as Record<string, unknown>;\n const prototype = Object.getPrototypeOf(object);\n if (prototype !== Object.prototype && prototype !== null) {\n throw new Error(\"ReCap caveats must contain only JSON values.\");\n }\n return `{${Object.keys(object).sort().map((key) =>\n `${JSON.stringify(key)}:${canonicalize(object[key])}`,\n ).join(\",\")}}`;\n }\n throw new Error(\"ReCap caveats must contain only JSON values.\");\n };\n const branches = (caveats ?? []).map(canonicalize).sort();\n if (branches.length === 0 || (branches.length === 1 && branches[0] === \"{}\")) {\n return \"[]\";\n }\n return `[${branches.join(\",\")}]`;\n}\n\n/** Return whether a granted action pattern covers one requested action. */\nexport function actionContains(grantedAction: string, requestedAction: string): boolean {\n if (grantedAction === \"*\" || grantedAction === requestedAction) return true;\n if (!grantedAction.endsWith(\"/*\")) return false;\n const service = grantedAction.slice(0, -2);\n return requestedAction === service || requestedAction.startsWith(`${service}/`);\n}\n\n/**\n * Path containment per spec:\n * - `granted.path` ends with `/` → prefix match (requested starts with granted)\n * - otherwise → exact string match\n *\n * The empty string is treated as \"no path constraint\" and matches anything.\n * The single-character `\"/\"` also matches anything (it is a trailing-slash\n * prefix of zero-length).\n */\nfunction pathContains(grantedPath: string, requestedPath: string): boolean {\n if (grantedPath === \"\" || grantedPath === \"/\") {\n return true;\n }\n if (grantedPath.endsWith(\"/\")) {\n return requestedPath.startsWith(grantedPath);\n }\n return requestedPath === grantedPath;\n}\n\nfunction cloneEntry(entry: PermissionEntry): PermissionEntry {\n return {\n service: entry.service,\n ...(entry.space !== undefined ? { space: entry.space } : {}),\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.caveats !== undefined\n ? { caveats: JSON.parse(JSON.stringify(entry.caveats)) as Record<string, unknown>[] }\n : {}),\n ...(entry.skipPrefix !== undefined ? { skipPrefix: entry.skipPrefix } : {}),\n ...(entry.expiry !== undefined ? { expiry: entry.expiry } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Recap parsing (WASM wrapper)\n// ---------------------------------------------------------------------------\n\n/**\n * The raw shape returned from the WASM `parseRecapFromSiwe` export. The\n * Rust layer encodes the service in the short form (e.g. `\"kv\"`) because\n * that is what the SIWE recap resource URI actually contains. We normalize\n * to the manifest long form (`\"tinycloud.kv\"`) in {@link parseRecapCapabilities}.\n *\n * @internal\n */\nexport interface WasmRecapEntry {\n service: string;\n space: string;\n path: string;\n actions: string[];\n /** Exact ReCap caveats shared by every action in this entry. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Signature of the WASM `parseRecapFromSiwe` export. Accepts the signed\n * SIWE message string and returns an array of raw recap entries. Throws if\n * the SIWE is malformed or the recap statement has been tampered.\n *\n * Exposed as an interface so the SDK can inject the web or node binding\n * without `capabilities.ts` needing to know which.\n */\nexport type ParseRecapFromSiwe = (siweString: string) => WasmRecapEntry[];\n\n/**\n * Parse a signed SIWE message into an array of {@link PermissionEntry}\n * objects in the canonical long-form manifest shape.\n *\n * This is a thin wrapper around the WASM `parseRecapFromSiwe` export that:\n * 1. Normalizes short-form services (`\"kv\"`) to long-form (`\"tinycloud.kv\"`).\n * 2. Returns entries in a deterministic order (sorted by space, then service,\n * then path) so downstream equality checks are stable.\n *\n * Returns an empty array when the SIWE has no recap resource (plain auth\n * SIWE); this matches the WASM function's behavior and the spec.\n *\n * @param parseWasm The WASM `parseRecapFromSiwe` binding.\n * @param siwe The signed SIWE message string (exactly what `session.siwe` stores).\n */\nexport function parseRecapCapabilities(\n parseWasm: ParseRecapFromSiwe,\n siwe: string\n): PermissionEntry[] {\n const raw = parseWasm(siwe);\n if (!Array.isArray(raw)) {\n throw new Error(\n \"parseRecapFromSiwe returned a non-array value; wasm binding may be out of sync\"\n );\n }\n const normalized: PermissionEntry[] = raw.map((entry) => {\n const longService =\n SERVICE_SHORT_TO_LONG[entry.service] ??\n // Unknown short names pass through. If the recap already contained a\n // long-form service (e.g. a future tinycloud-node version emits long\n // form directly), don't double-prefix.\n (entry.service.startsWith(\"tinycloud.\")\n ? entry.service\n : `tinycloud.${entry.service}`);\n return {\n service: longService,\n // The Rust layer emits the space as a full `tinycloud:pkh:...:name`\n // URI (the recap target URI). Normalize to the short name so the\n // returned entries match the shape manifests use.\n space: normalizeSpace(entry.space),\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.caveats === undefined\n ? {}\n : { caveats: JSON.parse(JSON.stringify(entry.caveats)) as Record<string, unknown>[] }),\n };\n });\n\n // Sort for determinism (callers do equality checks on arrays of entries\n // in tests; deterministic ordering keeps those stable).\n normalized.sort((a, b) => {\n const aSpace = a.space ?? DEFAULT_MANIFEST_SPACE;\n const bSpace = b.space ?? DEFAULT_MANIFEST_SPACE;\n if (aSpace !== bSpace) return aSpace < bSpace ? -1 : 1;\n if (a.service !== b.service) return a.service < b.service ? -1 : 1;\n if (a.path !== b.path) return a.path < b.path ? -1 : 1;\n return 0;\n });\n\n return normalized;\n}\n","import {\n canonicalizeRecapCaveats,\n isCapabilitySubset,\n type PermissionEntry,\n} from \"@tinycloud/sdk-core\";\n\n/**\n * A capability in the canonical form persisted in a v1 request artifact.\n *\n * The SDK remains the semantic authority for containment. This module only\n * establishes deterministic exact identity for persistence and display.\n */\nexport type CanonicalPermissionEntry = Readonly<PermissionEntry>;\n\nexport interface AuthorityEvaluation {\n readonly satisfied: boolean;\n readonly missing: readonly CanonicalPermissionEntry[];\n}\n\n/**\n * Operation-owned space resolution. The SDK's general capability evaluator\n * intentionally preserves its historical short/full-space compatibility; the\n * operation kernel supplies this narrower resolver when a space identifies a\n * remote owner's resource.\n */\nexport type OperationSpaceResolver = (space: string) => string;\n\nconst EXACT_PERMISSION_KEYS = new Set([\n \"service\",\n \"space\",\n \"path\",\n \"actions\",\n \"skipPrefix\",\n \"expiry\",\n \"description\",\n]);\n\n/**\n * Validate requirements that can be represented without semantic broadening in\n * a v1 artifact. Caveated authority is intentionally rejected: the v1 request\n * schema and grant path cannot preserve those signed branches end-to-end.\n */\nexport function validateExactCapabilities(value: unknown): PermissionEntry[] | undefined {\n if (!Array.isArray(value)) return undefined;\n\n const permissions: PermissionEntry[] = [];\n for (const candidate of value) {\n if (!isExactPermission(candidate)) return undefined;\n permissions.push(candidate);\n }\n return canonicalizeCapabilities(permissions);\n}\n\n/**\n * Evaluates required authority with the SDK's public subset primitive.\n *\n * In particular, this intentionally does not implement matching rules or\n * manufacture wildcards. A wildcard can cover a requirement only when it was\n * already present in the supplied granted authority and the SDK says so.\n */\nexport function evaluateAuthority(\n granted: readonly PermissionEntry[],\n required: readonly PermissionEntry[],\n): AuthorityEvaluation {\n const canonicalGranted = canonicalizeCapabilities(granted);\n const canonicalRequired = canonicalizeCapabilities(required);\n const result = isCapabilitySubset(canonicalRequired, canonicalGranted);\n\n return {\n satisfied: result.subset,\n missing: canonicalizeCapabilities(result.missing),\n };\n}\n\n/**\n * Evaluate operation capabilities without allowing two owners' same-named\n * spaces to compare equal. Path and action containment still come from the\n * SDK primitive; only the operation space identity is made owner-exact.\n */\nexport function evaluateOperationAuthority(\n granted: readonly PermissionEntry[],\n required: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): AuthorityEvaluation {\n const canonicalGranted = canonicalizeOperationCapabilities(granted, resolveSpace);\n const canonicalRequired = canonicalizeOperationCapabilities(required, resolveSpace);\n const missing: CanonicalPermissionEntry[] = [];\n\n for (const requested of canonicalRequired) {\n const covered = canonicalGranted.some((candidate) =>\n candidate.service === requested.service &&\n operationSpacesEqual(candidate.space, requested.space) &&\n isCapabilitySubset([requested], [candidate]).subset,\n );\n if (!covered) missing.push(requested);\n }\n\n return {\n satisfied: missing.length === 0,\n missing: canonicalizeCapabilities(missing),\n };\n}\n\n/**\n * Whether a delegation's signed capabilities stay inside the stored request\n * it answers. `tinycloud.auth.import` and runtime replay share this check, so\n * a delegation import accepts is never one replay discards.\n */\nexport function delegationWithinRequest(\n requested: readonly PermissionEntry[],\n effective: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): boolean {\n return requested.length > 0 &&\n evaluateOperationAuthority(requested, canonicalizeCapabilities(effective), resolveSpace).satisfied;\n}\n\n/**\n * Validate a runtime hint as an exact member of the registered plan. Runtime\n * nodes may report the one phase that failed, but they may not introduce a\n * broader, differently-owned, caveated, or otherwise new capability.\n */\nexport function isExactCapabilityMemberSubset(\n hinted: readonly PermissionEntry[],\n planned: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): boolean {\n const canonicalHinted = canonicalizeOperationCapabilities(hinted, resolveSpace);\n const canonicalPlanned = canonicalizeOperationCapabilities(planned, resolveSpace);\n return canonicalHinted.length > 0 && canonicalHinted.every((hint) => canonicalPlanned.some((plan) =>\n JSON.stringify(hint) === JSON.stringify(plan),\n ));\n}\n\n/** Canonical identity used by operation preflight, hints, and artifacts. */\nexport function canonicalizeOperationCapabilities(\n permissions: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): CanonicalPermissionEntry[] {\n return canonicalizeCapabilities(permissions.map((permission) => {\n const canonicalPath = canonicalizeOperationPath(permission);\n if (permission.space === undefined || resolveSpace === undefined) {\n return canonicalPath;\n }\n try {\n return { ...canonicalPath, space: resolveSpace(permission.space) };\n } catch {\n // An unresolvable identity remains a non-matching literal. It must never\n // be widened to a wildcard or to another authenticated owner.\n return canonicalPath;\n }\n }));\n}\n\nfunction canonicalizeOperationPath(permission: PermissionEntry): PermissionEntry {\n if (permission.service !== \"tinycloud.encryption\") return permission;\n const match = permission.path.match(\n /^(urn:tinycloud:encryption:did:pkh:eip155:\\d+:)(0x[0-9a-fA-F]{40})(:.*)$/,\n );\n if (!match) return permission;\n return { ...permission, path: `${match[1]}${match[2]!.toLowerCase()}${match[3]}` };\n}\n\nfunction operationSpacesEqual(\n granted: string | undefined,\n requested: string | undefined,\n): boolean {\n return granted === requested;\n}\n\n/** Returns a stable exact identity for one capability without containment. */\nexport function permissionIdentity(permission: PermissionEntry): string {\n return JSON.stringify(canonicalizeCapability(permission));\n}\n\n/**\n * Sorts actions and entries and removes only exactly-identical capabilities.\n * It does not coalesce paths, services, spaces, or actions into a broader\n * capability.\n */\nexport function canonicalizeCapabilities(\n permissions: readonly PermissionEntry[],\n): CanonicalPermissionEntry[] {\n const byIdentity = new Map<string, CanonicalPermissionEntry>();\n for (const permission of permissions) {\n const canonical = canonicalizeCapability(permission);\n byIdentity.set(permissionIdentityFromCanonical(canonical), canonical);\n }\n\n return [...byIdentity.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([, permission]) => permission);\n}\n\nfunction canonicalizeCapability(permission: PermissionEntry): CanonicalPermissionEntry {\n const actions = [...new Set(permission.actions)].sort((left, right) => left.localeCompare(right));\n return {\n service: permission.service,\n ...(permission.space !== undefined ? { space: permission.space } : {}),\n path: permission.path,\n actions,\n ...(permission.skipPrefix !== undefined ? { skipPrefix: permission.skipPrefix } : {}),\n ...(permission.expiry !== undefined ? { expiry: permission.expiry } : {}),\n ...(permission.description !== undefined ? { description: permission.description } : {}),\n ...(permission.caveats === undefined\n ? {}\n : { caveats: canonicalizeCaveats(permission.caveats) }),\n };\n}\n\nfunction permissionIdentityFromCanonical(permission: CanonicalPermissionEntry): string {\n return JSON.stringify({\n service: permission.service,\n ...(permission.space !== undefined ? { space: permission.space } : {}),\n path: permission.path,\n actions: permission.actions,\n ...(permission.skipPrefix !== undefined ? { skipPrefix: permission.skipPrefix } : {}),\n ...(permission.expiry !== undefined ? { expiry: permission.expiry } : {}),\n ...(permission.description !== undefined ? { description: permission.description } : {}),\n ...(permission.caveats === undefined ? {} : { caveats: permission.caveats }),\n });\n}\n\nfunction isExactPermission(value: unknown): value is PermissionEntry {\n if (\n !isExactRecord(value) ||\n Object.getOwnPropertyNames(value).some((key) => !EXACT_PERMISSION_KEYS.has(key)) ||\n Object.getOwnPropertySymbols(value).length > 0\n ) {\n return false;\n }\n if (\n !isExactText(value.service) ||\n !isExactPath(value.path, value.actions) ||\n (value.space !== undefined && !isExactText(value.space)) ||\n !Array.isArray(value.actions) ||\n value.actions.length === 0 ||\n !value.actions.every(isExactText) ||\n new Set(value.actions).size !== value.actions.length ||\n value.skipPrefix === true ||\n (value.skipPrefix !== undefined && value.skipPrefix !== false) ||\n (value.expiry !== undefined && !isExactText(value.expiry)) ||\n (value.description !== undefined && !isExactText(value.description))\n ) {\n return false;\n }\n return !(\"caveats\" in value);\n}\n\nfunction isExactRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isExactText(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0 && !value.includes(\"*\");\n}\n\nfunction isExactPath(value: unknown, actions: unknown): value is string {\n if (typeof value !== \"string\" || value === \"/\" || value.includes(\"*\")) return false;\n if (value === \"\" || value.endsWith(\"/\")) {\n return Array.isArray(actions) && actions.includes(\"tinycloud.kv/list\");\n }\n return true;\n}\n\nfunction canonicalizeCaveats(\n caveats: readonly Record<string, unknown>[],\n): Record<string, unknown>[] {\n return [...caveats]\n .map((caveat) => ({\n identity: canonicalizeRecapCaveats([caveat]),\n value: JSON.parse(JSON.stringify(caveat)) as Record<string, unknown>,\n }))\n .sort((left, right) => left.identity.localeCompare(right.identity))\n .map(({ value }) => value);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport {\n chmod,\n link,\n mkdir,\n readFile,\n readdir,\n rename,\n rmdir,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, relative, resolve } from \"node:path\";\n\nconst DEFAULT_PROFILE = \"default\";\nconst DEFAULT_LOCK_TIMEOUT_MS = 2_000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\nconst DEFAULT_STALE_LOCK_MS = 30_000;\nconst TEST_LOCK_CONTENTION_SIGNAL_PATH = \"TC_TEST_PROFILE_LOCK_CONTENTION_SIGNAL_PATH\";\n// Profile state holds keys, sessions and delegations: owner-only access.\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIR_MODE = 0o700;\nconst TEST_LOCK_RECOVERY_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RECOVERY_BARRIER_DIR\";\nconst TEST_LOCK_OWNERLESS_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_OWNERLESS_BARRIER_DIR\";\nconst TEST_LOCK_PUBLISH_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_PUBLISH_BARRIER_DIR\";\nconst TEST_LOCK_RELEASE_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RELEASE_BARRIER_DIR\";\nconst TEST_LOCK_CLAIM_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIM_BARRIER_DIR\";\nconst TEST_LOCK_CLAIMED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIMED_BARRIER_DIR\";\nconst TEST_LOCK_FENCED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_FENCED_BARRIER_DIR\";\nconst TEST_LOCK_VERIFIED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_VERIFIED_BARRIER_DIR\";\n/** One acquisition of a profile lock; `active` is cleared before release. */\ninterface HeldProfileLock {\n readonly lockPath: string;\n active: boolean;\n}\n/**\n * Async context shared by every copy of this module in the process. Each\n * bundled operations entry point (`state`, `delegation-binding`, the root)\n * carries its own copy, so module-level context would make lock reentrancy and\n * the invocation state root stop at an entry-point boundary.\n *\n * The `.v1` in each key is the format version shared by every copy that reads\n * it. Bump it whenever the `HeldProfileLock` entry shape or the on-disk lock\n * protocol changes, so copies with different formats (two operations versions\n * in one process) stop trusting each other's held-lock entries and simply\n * wait for the lock instead. TC-633 changes the held-lock shape and adds a\n * per-process `turnsInProgress` set: whichever of TC-602 and TC-633 lands\n * second bumps these keys to `.v2` and makes that set process-wide under the\n * same version.\n */\nfunction processWideContext<T>(key: string): AsyncLocalStorage<T> {\n const registry = globalThis as unknown as Record<symbol, AsyncLocalStorage<T> | undefined>;\n return registry[Symbol.for(key)] ??= new AsyncLocalStorage<T>();\n}\nconst invocationStateRoot = processWideContext<string>(\"tinycloud.operations.invocationStateRoot.v1\");\n/** Profile lock acquisitions held by the current async call chain (see withProfileLock). */\nconst heldProfileLocks = processWideContext<readonly HeldProfileLock[]>(\"tinycloud.operations.heldProfileLocks.v1\");\n\nexport type ProfileStoreName =\n | \"session\"\n | \"additional-delegations\"\n | \"auth-requests\";\n\nexport interface StoreMetadata {\n formatVersion: number;\n}\n\nexport interface ProfileStoreContents<T> {\n formatVersion: number;\n records: T[];\n}\n\nexport interface ProfileLockOptions {\n timeoutMs?: number;\n retryMs?: number;\n staleAfterMs?: number;\n}\n\nexport class ProfileLockTimeoutError extends Error {\n readonly code = \"PROFILE_LOCK_TIMEOUT\";\n\n constructor(profile: string, timeoutMs: number) {\n super(`Timed out waiting for the profile lock for \"${profile}\" after ${timeoutMs}ms.`);\n this.name = \"ProfileLockTimeoutError\";\n }\n}\n\n/**\n * The CLI's delegated-secret path treats TC_HOME as a home directory, rather\n * than a direct TinyCloud directory. Keep the shared store on that convention.\n */\nexport function tinycloudHomePath(): string {\n const home = invocationStateRoot.getStore() ??\n process.env.TC_HOME ??\n process.env.HOME ??\n process.env.USERPROFILE ??\n homedir();\n return join(home, \".tinycloud\");\n}\n\n/** Run one operation against an isolated TinyCloud home directory. */\nexport function withTinyCloudStateRoot<T>(\n stateRoot: string | undefined,\n action: () => Promise<T>,\n): Promise<T> {\n if (stateRoot === undefined) return action();\n if (!isAbsolute(stateRoot) || stateRoot.includes(\"\\0\")) {\n throw new TypeError(\"The TinyCloud state root must be an absolute path.\");\n }\n return invocationStateRoot.run(resolve(stateRoot), action);\n}\n\nexport function tinycloudConfigPath(): string {\n return join(tinycloudHomePath(), \"config.json\");\n}\n\nexport function profilesPath(): string {\n return join(tinycloudHomePath(), \"profiles\");\n}\n\nexport function profilePath(profile: string): string {\n return join(profilesPath(), validateProfileName(profile));\n}\n\nexport function profileConfigPath(profile: string): string {\n return join(profilePath(profile), \"profile.json\");\n}\n\nexport function sessionPath(profile: string): string {\n return profileStorePath(profile, \"session\");\n}\n\nexport function additionalDelegationsPath(profile: string): string {\n return profileStorePath(profile, \"additional-delegations\");\n}\n\nexport function authRequestsPath(profile: string): string {\n return profileStorePath(profile, \"auth-requests\");\n}\n\nexport function profileStorePath(profile: string, store: ProfileStoreName): string {\n return join(profilePath(profile), `${store}.json`);\n}\n\n/** The format record is deliberately separate from each legacy JSON payload. */\nexport function profileStoreMetadataPath(profile: string, store: ProfileStoreName): string {\n return `${profileStorePath(profile, store)}.metadata.json`;\n}\n\nexport function profileLockPath(profile: string): string {\n return join(profilePath(profile), \".lock\");\n}\n\nexport function profileLockMetadataPath(profile: string): string {\n return join(profileLockPath(profile), \"owner.json\");\n}\n\n/**\n * Reads JSON without hiding malformed or inaccessible files. A missing file is\n * the only condition represented as null.\n */\nexport async function readJson<T>(filePath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(filePath, \"utf8\")) as T;\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return null;\n throw error;\n }\n}\n\n/**\n * Writes the same pretty-printed, trailing-newline JSON shape used by the CLI,\n * but publishes it with an atomic rename from the same directory.\n */\nexport async function writeJsonAtomic(filePath: string, value: unknown): Promise<void> {\n const directory = dirname(filePath);\n const temporaryPath = join(\n directory,\n `.${basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,\n );\n const contents = `${JSON.stringify(value, null, 2)}\\n`;\n\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n try {\n await writeFile(temporaryPath, contents, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE });\n await rename(temporaryPath, filePath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n throw error;\n }\n}\n\nexport async function readProfile<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n return readJson<T>(profileConfigPath(profile));\n}\n\nexport async function readSession<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n await readStoreMetadata(profile, \"session\");\n return readJson<T>(sessionPath(profile));\n}\n\nexport async function readAdditionalDelegations<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"additional-delegations\")).records;\n}\n\nexport async function readAuthRequests<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"auth-requests\")).records;\n}\n\nexport async function readStoreMetadata(\n profile: string,\n store: ProfileStoreName,\n): Promise<StoreMetadata> {\n const metadata = await readJson<StoreMetadata>(profileStoreMetadataPath(profile, store));\n if (metadata === null) return { formatVersion: 1 };\n if (\n typeof metadata !== \"object\" ||\n metadata.formatVersion !== 1\n ) {\n throw new TypeError(`Unsupported store format for \"${store}\".`);\n }\n return metadata;\n}\n\nexport async function readProfileStore<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n): Promise<ProfileStoreContents<T>> {\n const [metadata, raw] = await Promise.all([\n readStoreMetadata(profile, store),\n readJson<unknown>(profileStorePath(profile, store)),\n ]);\n return {\n formatVersion: metadata.formatVersion,\n records: Array.isArray(raw) ? raw as T[] : [],\n };\n}\n\n/**\n * Runs a small critical section under the one advisory lock shared by all\n * profile stores. Lock release verifies its ownership token before removing\n * the exact metadata instance it acquired.\n *\n * Reentrant within one async call chain: a critical section that already\n * holds a profile's lock (for example a login's compare-and-commit) can call\n * store writers that take the same lock without deadlocking. Ownership is\n * keyed by the resolved lock path (so switching state roots inside a lock\n * still takes the other root's lock) and lasts exactly as long as the\n * acquisition: it is revoked before release, so work deferred past the\n * critical section waits for the lock like any other caller.\n */\nexport async function withProfileLock<T>(\n profile: string,\n action: () => Promise<T>,\n options: ProfileLockOptions = {},\n): Promise<T> {\n const normalizedProfile = validateProfileName(profile);\n const lockPath = profileLockPath(normalizedProfile);\n const held = (heldProfileLocks.getStore() ?? []).filter((ownership) => ownership.active);\n if (held.some((ownership) => ownership.lockPath === lockPath)) return action();\n const release = await acquireProfileLock(normalizedProfile, options);\n const ownership: HeldProfileLock = { lockPath, active: true };\n try {\n return await heldProfileLocks.run([...held, ownership], action);\n } finally {\n ownership.active = false;\n await release();\n }\n}\n\n/**\n * Appends a record or replaces the existing record with the supplied explicit\n * key. The extractor is supplied by the owning store so state.ts never needs\n * to know permission-request or delegation record shapes.\n */\nexport async function upsertProfileRecord<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n key: string,\n record: T,\n getKey: (candidate: T) => string | undefined,\n options: ProfileLockOptions = {},\n): Promise<T[]> {\n if (!key) throw new TypeError(\"A non-empty record key is required.\");\n\n return withProfileLock(profile, async () => {\n const current = (await readProfileStore<T>(profile, store)).records;\n const next = current.filter((candidate) => getKey(candidate) !== key);\n next.push(record);\n await writeJsonAtomic(profileStorePath(profile, store), next);\n await writeFormatOneMetadata(profile, store);\n return next;\n }, options);\n}\n\n/**\n * Performs a typed read-modify-write of a legacy array store under the one\n * per-profile lock. The payload stays an array and the sibling metadata stays\n * format 1, so existing CLI readers keep their byte/layout contract.\n */\nexport async function updateProfileStore<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n options: ProfileLockOptions = {},\n): Promise<Result> {\n return withProfileLock(\n profile,\n () => updateProfileStoreWhileLocked(profile, store, update),\n options,\n );\n}\n\n/** The caller must already own this profile's lock. */\nasync function updateProfileStoreWhileLocked<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n): Promise<Result> {\n const current = await readProfileStore<T>(profile, store);\n const next = await update(current.records);\n await writeJsonAtomic(profileStorePath(profile, store), next.records);\n await writeFormatOneMetadata(profile, store);\n return next.result;\n}\n\nexport async function writeSession<T extends object>(\n profile: string,\n session: T,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await readStoreMetadata(profile, \"session\");\n await writeJsonAtomic(sessionPath(profile), session);\n await writeFormatOneMetadata(profile, \"session\");\n }, options);\n}\n\nexport async function removeSession(\n profile: string,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await readStoreMetadata(profile, \"session\");\n await rm(sessionPath(profile), { force: true });\n }, options);\n}\n\nasync function writeFormatOneMetadata(profile: string, store: ProfileStoreName): Promise<void> {\n await writeJsonAtomic(profileStoreMetadataPath(profile, store), { formatVersion: 1 });\n}\n\nasync function acquireProfileLock(\n profile: string,\n options: ProfileLockOptions,\n): Promise<() => Promise<void>> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;\n const retryMs = options.retryMs ?? DEFAULT_LOCK_RETRY_MS;\n const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;\n const startedAt = Date.now();\n const lockPath = profileLockPath(profile);\n\n const directory = profilePath(profile);\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n // Every store write takes this lock: tighten the tree older releases\n // created 0775 before writing sessions or delegations into it.\n for (const path of [tinycloudHomePath(), profilesPath(), directory]) {\n await chmod(path, PRIVATE_DIR_MODE).catch((error: unknown) => {\n // A profile deletion can remove the profile directory right after the\n // mkdir above; the lock attempt below recreates it 0700.\n if (path !== directory || !isErrno(error, \"ENOENT\")) throw error;\n });\n }\n\n while (true) {\n const token = randomUUID();\n if (await publishProfileLock(profile, lockPath, token)) {\n return async () => {\n await releaseProfileLock(profile, lockPath, token);\n };\n }\n await signalTestLockContention(profile);\n\n if (await recoverStaleLock(profile, lockPath, staleAfterMs)) continue;\n\n const elapsedMs = Date.now() - startedAt;\n if (elapsedMs >= timeoutMs) {\n throw new ProfileLockTimeoutError(profile, timeoutMs);\n }\n await sleep(Math.min(retryMs, timeoutMs - elapsedMs));\n }\n}\n\n/**\n * One attempt to take the lock. Nothing here replaces an existing path:\n *\n * 1. `mkdir(.lock)` (never recursive) is exclusive, and is the primitive\n * older releases use too, so they exclude each other.\n * 2. Ownership is published by `link`ing a fully written owner file (staged\n * beside `.lock`, on the same filesystem) to `.lock/owner.json`. `link`\n * fails with EEXIST if an owner exists, and with ENOENT if the directory\n * was removed meanwhile (an aged ownerless-lock reclaim). Only a\n * successful link holds the lock; on either failure nothing is released,\n * since nothing was acquired, and the caller retries.\n *\n * A profile deletion removes the profile directory once it has released its\n * lock, so `mkdir(.lock)` can see ENOENT; the directory is recreated (0700)\n * and the caller retries within its deadline, as for a first write to a new\n * profile.\n *\n * Only one owner.json can exist at the lock path, and it is removed only by\n * its holder's release or by recovery of a dead holder, so at most one\n * process holds the lock. Returns false when the lock is not acquired.\n */\nasync function publishProfileLock(profile: string, lockPath: string, token: string): Promise<boolean> {\n try {\n await mkdir(lockPath, { mode: PRIVATE_DIR_MODE });\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) return false;\n if (!isErrno(error, \"ENOENT\")) throw error;\n await mkdir(profilePath(profile), { recursive: true, mode: PRIVATE_DIR_MODE });\n return false;\n }\n await waitForTestBarrier(TEST_LOCK_PUBLISH_BARRIER_DIR, profile);\n const staged = join(dirname(lockPath), `.lock-owner-${token}.tmp`);\n const owner = { pid: process.pid, createdAt: new Date().toISOString(), token };\n try {\n await writeFile(staged, `${JSON.stringify(owner, null, 2)}\\n`, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n await link(staged, join(lockPath, \"owner.json\"));\n return true;\n } catch (error) {\n if (isErrno(error, \"EEXIST\") || isErrno(error, \"ENOENT\")) return false;\n // Do not leave an ownerless lock behind for others to wait out. rmdir\n // removes only an empty directory; anyone who created it after ours went\n // away sees ENOENT on link and retries.\n await rmdir(lockPath).catch(() => undefined);\n throw error;\n } finally {\n await rm(staged, { force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Releases the lock if `.lock/owner.json` is still this acquisition's. No\n * one else can put an owner record there while it exists (owners are only\n * linked into a directory their own `mkdir` created, and recovery removes\n * only a dead holder's record), so the record read is the one unlinked.\n */\nasync function releaseProfileLock(profile: string, lockPath: string, token: string): Promise<void> {\n const ownerPath = join(lockPath, \"owner.json\");\n const owner = await readJson<{ token?: unknown }>(ownerPath).catch(() => null);\n if (owner?.token !== token) return;\n await rm(ownerPath, { force: true });\n // `.lock` is empty from here until the rmdir; a contender that removes it\n // and creates its own meanwhile is not harmed: rmdir fails on its owner\n // file, or the contender's link sees ENOENT and retries. If a recoverer's\n // claim file is still inside, rmdir fails and that recoverer removes the\n // directory when it drops its claim.\n await waitForTestBarrier(TEST_LOCK_RELEASE_BARRIER_DIR, profile);\n await rmdir(lockPath).catch(() => undefined);\n}\n\n/**\n * Gives process-level contention tests an event-driven witness that a second\n * writer has reached an already-held lock. This deliberately has no effect\n * outside an explicit test environment, and only creates a new signal file\n * beneath TC_HOME; it never changes lock acquisition or release semantics.\n */\nasync function signalTestLockContention(profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n\n const configuredPath = process.env[TEST_LOCK_CONTENTION_SIGNAL_PATH];\n const configuredHome = process.env.TC_HOME;\n if (!configuredPath || !configuredHome) return;\n\n const home = resolve(configuredHome);\n const signalPath = resolve(configuredPath);\n const pathFromHome = relative(home, signalPath);\n if (\n pathFromHome === \"\" ||\n pathFromHome === \"..\" ||\n pathFromHome.startsWith(`..${process.platform === \"win32\" ? \"\\\\\" : \"/\"}`) ||\n pathFromHome.startsWith(\"../\") ||\n pathFromHome.startsWith(\"..\\\\\")\n ) {\n return;\n }\n\n await writeFile(signalPath, `${profile}\\n`, { encoding: \"utf8\", flag: \"wx\" })\n .catch(() => undefined);\n}\n\n/**\n * Reclaims a lock directory with no owner record that is older than the\n * stale threshold: one left by a crash, or by an older release. A process\n * killed while recovering (or releasing, in older releases) can leave its\n * `.stale-*` / `.release-*` claim file behind; such claim files are removed\n * first. owner.json is never touched, and the directory itself is only\n * `rmdir`ed, which succeeds only while it is empty, so a published lock is\n * never removed. If the directory was a contender's lock not yet published,\n * that contender's `link` fails with ENOENT and it retries without holding\n * anything.\n */\nasync function recoverOwnerlessLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n try {\n const { mtimeMs } = await stat(lockPath);\n if (Date.now() - mtimeMs < staleAfterMs) return false;\n } catch {\n return false;\n }\n const checkedAt = performance.now();\n await waitForTestBarrier(TEST_LOCK_OWNERLESS_BARRIER_DIR, profile);\n try {\n const entries = await readdir(lockPath);\n if (entries.includes(\"owner.json\")) return false;\n // The age check above is only good for a while; see recoveryFenced.\n if (recoveryFenced(checkedAt, staleAfterMs)) return false;\n for (const name of entries.filter((entry) => ABANDONED_CLAIM.test(entry))) {\n await rm(join(lockPath, name), { force: true });\n }\n await rmdir(lockPath);\n } catch {\n return false;\n }\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/**\n * Whether a recovery step decided at `since` (a monotonic timestamp) is too\n * old to act on. Claim-only and ownerless cleanup act only on a `.lock`\n * unchanged for `staleAfterMs`, and a recoverer's claim (or the change that\n * made a directory look abandoned) dates the directory no earlier than\n * `since`. Acting within half that window means no such cleanup can have\n * removed the directory and let a new holder in meanwhile; past it, the\n * recoverer drops only its own claim and starts the acquisition over.\n */\nfunction recoveryFenced(since: number, staleAfterMs: number): boolean {\n return performance.now() - since >= staleAfterMs / 2;\n}\n\n/** Claim files of stale recovery (and of release in older releases). */\nconst ABANDONED_CLAIM = /^\\.(?:release|stale)-[0-9a-f-]+\\.json$/;\n/** Owner files staged by publishProfileLock beside `.lock`. */\nconst STAGED_OWNER = /^\\.lock-owner-[0-9a-f-]+\\.tmp$/;\n\n/**\n * Removes owner files a crashed acquirer staged beside `.lock`. A live\n * acquirer's staged file exists only for its write and link, so only files\n * older than the stale threshold are removed. Runs after a recovery, never\n * on the uncontended path.\n */\nasync function removeAgedOwnerFiles(lockPath: string, staleAfterMs: number): Promise<void> {\n const directory = dirname(lockPath);\n const names = await readdir(directory).catch(() => []);\n for (const name of names.filter((entry) => STAGED_OWNER.test(entry))) {\n const path = join(directory, name);\n const aged = await stat(path).then(({ mtimeMs }) => Date.now() - mtimeMs >= staleAfterMs, () => false);\n if (aged) await rm(path, { force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Removes a dead holder's lock. The claim is a hard link to owner.json, so\n * the owner record is never moved away: a recoverer whose observation is\n * outdated (the dead holder's lock was already recovered and a live holder\n * now owns `.lock`) sees another token, drops its link and leaves the live\n * owner record in place. Only after the claimed record is confirmed to be\n * the observed dead holder's is owner.json unlinked. Nothing can link a new\n * owner.json meanwhile: the claim keeps `.lock` non-empty, so it cannot be\n * removed and recreated.\n */\nasync function recoverStaleLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n const ownerPath = join(lockPath, \"owner.json\");\n const owner = await readJson<{ pid?: unknown; createdAt?: unknown; token?: unknown }>(ownerPath)\n .catch(() => null);\n if (owner === null) return recoverOwnerlessLock(profile, lockPath, staleAfterMs);\n if (!isStaleOwner(owner, staleAfterMs)) return false;\n\n const observedToken = typeof owner?.token === \"string\" && owner.token.length > 0\n ? owner.token\n : \"legacy\";\n await waitForTestBarrier(TEST_LOCK_RECOVERY_BARRIER_DIR, profile);\n const claimPath = join(lockPath, `.stale-${randomUUID()}.json`);\n try {\n await link(ownerPath, claimPath);\n } catch {\n return false;\n }\n const claimedAt = performance.now();\n await waitForTestBarrier(TEST_LOCK_CLAIM_BARRIER_DIR, profile);\n\n const claimed = await readJson<{ pid?: unknown; createdAt?: unknown; token?: unknown }>(claimPath)\n .catch(() => null);\n const sameInstance = claimed !== null && (\n observedToken === \"legacy\"\n ? claimed.token === undefined\n : claimed.token === observedToken\n );\n await waitForTestBarrier(TEST_LOCK_VERIFIED_BARRIER_DIR, profile);\n if (sameInstance && recoveryFenced(claimedAt, staleAfterMs)) {\n // Paused too long: another recoverer may have finished this recovery and\n // a cleanup may have removed this claim, so owner.json may now be a live\n // holder's. Touch nothing but this claim.\n await rm(claimPath, { force: true });\n await waitForTestBarrier(TEST_LOCK_FENCED_BARRIER_DIR, profile);\n return false;\n }\n if (sameInstance) {\n await rm(ownerPath, { force: true });\n await waitForTestBarrier(TEST_LOCK_CLAIMED_BARRIER_DIR, profile);\n }\n await rm(claimPath, { force: true });\n // Empty now if the claimed holder is gone or a live holder released while\n // this claim kept `.lock` from being removed: remove it rather than leave\n // an ownerless lock to age out. rmdir fails while an owner record exists.\n await rmdir(lockPath).catch(() => undefined);\n if (!sameInstance) return false;\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/** Test-only rendezvous (NODE_ENV=test and the named barrier directory set). */\nasync function waitForTestBarrier(environmentName: string, profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n const barrierDirectory = process.env[environmentName];\n if (!barrierDirectory) return;\n await mkdir(barrierDirectory, { recursive: true });\n const readyPath = join(barrierDirectory, `ready-${process.pid}-${profile}`);\n await writeFile(readyPath, \"ready\\n\", { encoding: \"utf8\", flag: \"wx\" }).catch(() => undefined);\n const releasePath = join(barrierDirectory, \"release\");\n while (true) {\n try {\n await readFile(releasePath, \"utf8\");\n return;\n } catch {\n await sleep(1);\n }\n }\n}\n\nfunction isStaleOwner(\n owner: { pid?: unknown; createdAt?: unknown } | null,\n staleAfterMs: number,\n): boolean {\n const now = Date.now();\n const createdAt = typeof owner?.createdAt === \"string\"\n ? Date.parse(owner.createdAt)\n : Number.NaN;\n\n // Reclaim only a fully published lock whose owner PID is confirmed dead.\n // A lock without an owner record is handled by recoverOwnerlessLock: a\n // process holds the lock only once its owner.json is linked in.\n if (!Number.isFinite(createdAt) || now - createdAt < staleAfterMs) return false;\n if (typeof owner?.pid !== \"number\" || !Number.isInteger(owner.pid) || owner.pid <= 0) {\n return false;\n }\n return !isProcessAlive(owner.pid);\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return !isErrno(error, \"ESRCH\");\n }\n}\n\nfunction validateProfileName(profile: string): string {\n if (\n !profile ||\n profile === \".\" ||\n profile === \"..\" ||\n profile.includes(\"/\") ||\n profile.includes(\"\\\\\") ||\n profile.includes(\"\\0\")\n ) {\n throw new TypeError(\"Profile names must be non-empty path segments.\");\n }\n return profile;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && \"code\" in error &&\n (error as { code?: unknown }).code === code;\n}\n\nfunction sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport { DEFAULT_PROFILE };\n"],"mappings":"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","names":["ms","DecryptTransportResponseError","randomUUID","z","CAPABILITIES","CAPABILITIES","ok","serviceError","headers","errorText","resolve","resolve","valueDelResult","hexEncode","base64Encode","base64Decode","hexEncode","toError","err","crypto","base64Encode","base64Decode","hexEncode","crypto","err","crypto","hexEncode","base64Decode","toError","base64Encode","DEFAULT_MANIFEST_SPACE","DEFAULT_MANIFEST_SPACE","canonicalize","ownership","resolve","z","randomUUID"]}
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/ms/index.js","../src/artifacts.ts","../../sdk-core/src/manifest.ts","../../sdk-core/src/capabilities.ts","../src/authority.ts","../src/state.ts"],"sourcesContent":["/**\n * Helpers.\n */\n\nvar s = 1000;\nvar m = s * 60;\nvar h = m * 60;\nvar d = h * 24;\nvar w = d * 7;\nvar y = d * 365.25;\n\n/**\n * Parse or format the given `val`.\n *\n * Options:\n *\n * - `long` verbose formatting [false]\n *\n * @param {String|Number} val\n * @param {Object} [options]\n * @throws {Error} throw an error if val is not a non-empty string or a number\n * @return {String|Number}\n * @api public\n */\n\nmodule.exports = function (val, options) {\n options = options || {};\n var type = typeof val;\n if (type === 'string' && val.length > 0) {\n return parse(val);\n } else if (type === 'number' && isFinite(val)) {\n return options.long ? fmtLong(val) : fmtShort(val);\n }\n throw new Error(\n 'val is not a non-empty string or a valid number. val=' +\n JSON.stringify(val)\n );\n};\n\n/**\n * Parse the given `str` and return milliseconds.\n *\n * @param {String} str\n * @return {Number}\n * @api private\n */\n\nfunction parse(str) {\n str = String(str);\n if (str.length > 100) {\n return;\n }\n var match = /^(-?(?:\\d+)?\\.?\\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(\n str\n );\n if (!match) {\n return;\n }\n var n = parseFloat(match[1]);\n var type = (match[2] || 'ms').toLowerCase();\n switch (type) {\n case 'years':\n case 'year':\n case 'yrs':\n case 'yr':\n case 'y':\n return n * y;\n case 'weeks':\n case 'week':\n case 'w':\n return n * w;\n case 'days':\n case 'day':\n case 'd':\n return n * d;\n case 'hours':\n case 'hour':\n case 'hrs':\n case 'hr':\n case 'h':\n return n * h;\n case 'minutes':\n case 'minute':\n case 'mins':\n case 'min':\n case 'm':\n return n * m;\n case 'seconds':\n case 'second':\n case 'secs':\n case 'sec':\n case 's':\n return n * s;\n case 'milliseconds':\n case 'millisecond':\n case 'msecs':\n case 'msec':\n case 'ms':\n return n;\n default:\n return undefined;\n }\n}\n\n/**\n * Short format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtShort(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return Math.round(ms / d) + 'd';\n }\n if (msAbs >= h) {\n return Math.round(ms / h) + 'h';\n }\n if (msAbs >= m) {\n return Math.round(ms / m) + 'm';\n }\n if (msAbs >= s) {\n return Math.round(ms / s) + 's';\n }\n return ms + 'ms';\n}\n\n/**\n * Long format for `ms`.\n *\n * @param {Number} ms\n * @return {String}\n * @api private\n */\n\nfunction fmtLong(ms) {\n var msAbs = Math.abs(ms);\n if (msAbs >= d) {\n return plural(ms, msAbs, d, 'day');\n }\n if (msAbs >= h) {\n return plural(ms, msAbs, h, 'hour');\n }\n if (msAbs >= m) {\n return plural(ms, msAbs, m, 'minute');\n }\n if (msAbs >= s) {\n return plural(ms, msAbs, s, 'second');\n }\n return ms + ' ms';\n}\n\n/**\n * Pluralization helper.\n */\n\nfunction plural(ms, msAbs, n, name) {\n var isPlural = msAbs >= n * 1.5;\n return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');\n}\n","import { randomUUID } from \"node:crypto\";\n\nimport type { PermissionEntry } from \"@tinycloud/sdk-core\";\nimport { z } from \"zod\";\n\nimport {\n canonicalizeCapabilities,\n evaluateOperationAuthority,\n evaluateAuthority,\n} from \"./authority.js\";\nimport type { OperationSpaceResolver } from \"./authority.js\";\nimport type {\n OperationOperatorType,\n TinyCloudPosture,\n} from \"./contract.js\";\nimport { readAuthRequests, updateProfileStore } from \"./state.js\";\n\nexport { evaluateAuthority } from \"./authority.js\";\n\nconst THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1_000;\n\n/** The shared wire schema for SDK PermissionEntry values, including signed ReCap caveats. */\nexport const PermissionEntrySchema = z.object({\n service: z.string().min(1),\n space: z.string().min(1).optional(),\n path: z.string(),\n actions: z.array(z.string().min(1)).min(1),\n caveats: z.array(z.record(z.unknown())).optional(),\n skipPrefix: z.boolean().optional(),\n expiry: z.string().min(1).optional(),\n description: z.string().optional(),\n}).strict();\n\nconst CommandSchema = z.object({\n argv: z.array(z.string()).min(1),\n cwd: z.string().min(1),\n}).strict();\n\nconst LegacyPermissionRequestArtifactSchema = z.object({\n kind: z.literal(\"tinycloud.auth.request\"),\n version: z.literal(1),\n requestId: z.string(),\n sessionDid: z.string().min(1),\n requested: z.array(PermissionEntrySchema),\n requestedExpiry: z.union([z.string(), z.number().finite()]).optional(),\n}).strict();\n\nconst PortableDelegationSchema: z.ZodType<Record<string, unknown>> = z.lazy(() => z.object({\n cid: z.string().min(1),\n spaceId: z.string().min(1),\n path: z.string(),\n actions: z.array(z.string().min(1)).min(1),\n delegateDID: z.string().min(1),\n ownerAddress: z.string().min(1),\n chainId: z.number().int().positive(),\n expiry: z.union([z.string().datetime({ offset: true }), z.date()]),\n delegationHeader: z.object({ Authorization: z.string().min(1) }).strict(),\n host: z.string().min(1).optional(),\n disableSubDelegation: z.boolean().optional(),\n publicDelegation: PortableDelegationSchema.optional(),\n resources: z.array(PermissionEntrySchema).optional(),\n}).passthrough());\n\n/** Canonical wire-format v1 request retained for CLI and OpenKey compatibility. */\nexport const PermissionRequestArtifactSchema = z.object({\n kind: z.literal(\"tinycloud.auth.request\"),\n version: z.literal(1),\n requestId: z.string().min(1),\n createdAt: z.string().datetime({ offset: true }),\n profile: z.string().min(1),\n posture: z.enum([\"owner-openkey\", \"delegate-session\", \"local-owner-key\", \"unauthenticated\"]),\n operatorType: z.enum([\"human\", \"agent\"]),\n host: z.string().min(1),\n sessionDid: z.string().min(1),\n ownerDid: z.string().min(1).optional(),\n spaceId: z.string().min(1).optional(),\n requestedExpiry: z.union([z.string().min(1), z.number().finite()]).optional(),\n requested: z.array(PermissionEntrySchema),\n command: CommandSchema.optional(),\n}).strict();\n\n/** Canonical structured v1 delegation import accepted by the auth operation. */\nexport const DelegationImportArtifactSchema = z.object({\n kind: z.literal(\"tinycloud.auth.delegation\"),\n version: z.literal(1),\n requestId: z.string().min(1),\n delegationCid: z.string().min(1).optional(),\n delegation: PortableDelegationSchema,\n // This field is presentation metadata only. A later import operation obtains\n // effective permissions from validated, installed delegation authority.\n permissions: z.array(PermissionEntrySchema).optional(),\n expiry: z.string().datetime({ offset: true }).optional(),\n prompted: z.boolean().optional(),\n}).strict();\n\n/**\n * The stored request an imported delegation was checked against, persisted\n * with the delegation. Its fields are exactly the request artifact's, so\n * replay accepts every binding import can write.\n */\nexport const DelegationRequestBindingSchema = PermissionRequestArtifactSchema\n .pick({ requestId: true, requested: true })\n .strict();\nexport type DelegationRequestBinding = z.infer<typeof DelegationRequestBindingSchema>;\n\nexport type PermissionRequestArtifact = z.infer<typeof PermissionRequestArtifactSchema>;\nexport type DelegationImportArtifact = z.infer<typeof DelegationImportArtifactSchema>;\n/** The minimal public node-sdk request shape accepted by the legacy reader. */\nexport type LegacyPermissionRequestArtifact = z.infer<typeof LegacyPermissionRequestArtifactSchema>;\nexport type ArtifactClock = () => Date;\nexport type RequestIdSource = () => string;\n\n/** Invocation-owned values used to safely complete a legacy request record. */\nexport interface LegacyRequestContext {\n readonly profile: string;\n readonly host: string;\n readonly sessionDid: string;\n readonly posture: TinyCloudPosture;\n readonly operatorType: OperationOperatorType;\n readonly ownerDid?: string;\n readonly spaceId?: string;\n readonly now?: Date;\n}\n\nexport interface PermissionRequestIdentityInput {\n readonly profile: string;\n readonly sessionDid: string;\n readonly host: string;\n readonly missing: readonly PermissionEntry[];\n}\n\nexport interface BuildPermissionRequestArtifactInput extends PermissionRequestIdentityInput {\n readonly posture: TinyCloudPosture;\n readonly operatorType: OperationOperatorType;\n readonly ownerDid?: string;\n readonly spaceId?: string;\n readonly requestedExpiry?: string | number;\n readonly command?: { readonly argv: readonly string[]; readonly cwd: string };\n readonly now?: ArtifactClock;\n readonly createRequestId?: RequestIdSource;\n}\n\nexport interface CreateOrReusePermissionRequestInput extends BuildPermissionRequestArtifactInput {\n readonly granted: readonly PermissionEntry[];\n /** Owner-aware identity resolver used by registered operation callers. */\n readonly resolveSpace?: OperationSpaceResolver;\n readonly replace?: boolean;\n}\n\nexport type PermissionRequestResolution =\n | Readonly<{ status: \"satisfied\"; reused: false }>\n | Readonly<{ status: \"created\"; reused: boolean; request: PermissionRequestArtifact }>;\n\n/** Parses a request artifact without accepting a newer wire format as v1. */\nexport function validatePermissionRequestArtifact(value: unknown): PermissionRequestArtifact {\n return PermissionRequestArtifactSchema.parse(value);\n}\n\n/** Parses a structured active-session import artifact without accepting a newer wire format as v1. */\nexport function validateDelegationImportArtifact(value: unknown): DelegationImportArtifact {\n return DelegationImportArtifactSchema.parse(value);\n}\n\nexport function isPermissionRequestArtifact(value: unknown): value is PermissionRequestArtifact {\n return PermissionRequestArtifactSchema.safeParse(value).success;\n}\n\nexport function isDelegationImportArtifact(value: unknown): value is DelegationImportArtifact {\n return DelegationImportArtifactSchema.safeParse(value).success;\n}\n\nfunction normalizeLegacyPermissionRequest(\n value: LegacyPermissionRequestArtifact,\n context: LegacyRequestContext,\n): PermissionRequestArtifact {\n if (principal(value.sessionDid) !== principal(context.sessionDid)) {\n throw new TypeError(\"Stored authority request belongs to a different session.\");\n }\n const createdAt = context.now ?? new Date();\n if (Number.isNaN(createdAt.getTime())) {\n throw new TypeError(\"The legacy authority request has no usable creation time.\");\n }\n return {\n kind: \"tinycloud.auth.request\",\n version: 1,\n requestId: value.requestId,\n createdAt: createdAt.toISOString(),\n profile: context.profile,\n posture: context.posture,\n operatorType: context.operatorType,\n host: context.host,\n sessionDid: context.sessionDid,\n ...(context.ownerDid === undefined ? {} : { ownerDid: context.ownerDid }),\n ...(context.spaceId === undefined ? {} : { spaceId: context.spaceId }),\n ...(value.requestedExpiry === undefined ? {} : { requestedExpiry: value.requestedExpiry }),\n requested: canonicalizeCapabilities(value.requested),\n } as PermissionRequestArtifact;\n}\n\nfunction principal(value: string): string {\n return value.split(\"#\", 1)[0]!;\n}\n\n/**\n * Stable exact identity used only for request reuse. It binds a request to its\n * selected profile, active session DID, host, and canonical missing subset.\n */\nexport function createPermissionRequestIdentity(input: PermissionRequestIdentityInput): string {\n return JSON.stringify({\n profile: input.profile,\n sessionDid: input.sessionDid,\n host: input.host,\n missing: canonicalizeCapabilities(input.missing),\n });\n}\n\n/** Builds an on-disk-compatible v1 record. Omitted values stay omitted. */\nexport function buildPermissionRequestArtifact(\n input: BuildPermissionRequestArtifactInput,\n): PermissionRequestArtifact {\n const now = input.now ?? (() => new Date());\n const createdAt = now();\n if (Number.isNaN(createdAt.getTime())) throw new TypeError(\"The artifact clock returned an invalid date.\");\n const requested = canonicalizeCapabilities(input.missing);\n if (requested.length === 0) throw new TypeError(\"Permission requests require at least one missing capability.\");\n\n return validatePermissionRequestArtifact({\n kind: \"tinycloud.auth.request\",\n version: 1,\n requestId: input.createRequestId?.() ?? defaultRequestId(createdAt),\n createdAt: createdAt.toISOString(),\n profile: input.profile,\n posture: input.posture,\n operatorType: input.operatorType,\n host: input.host,\n sessionDid: input.sessionDid,\n ...(input.ownerDid !== undefined ? { ownerDid: input.ownerDid } : {}),\n ...(input.spaceId !== undefined ? { spaceId: input.spaceId } : {}),\n ...(input.requestedExpiry !== undefined ? { requestedExpiry: input.requestedExpiry } : {}),\n requested,\n ...(input.command !== undefined\n ? { command: { argv: [...input.command.argv], cwd: input.command.cwd } }\n : {}),\n });\n}\n\n/** Reads only valid canonical v1 records and refuses malformed stored artifacts. */\nexport async function listPermissionRequests(\n profile: string,\n context?: LegacyRequestContext,\n): Promise<PermissionRequestArtifact[]> {\n const records: PermissionRequestArtifact[] = [];\n for (const value of await readAuthRequests<unknown>(profile)) {\n const canonical = PermissionRequestArtifactSchema.safeParse(value);\n if (canonical.success) {\n if (context !== undefined && principal(canonical.data.sessionDid) !== principal(context.sessionDid)) {\n continue;\n }\n records.push(canonical.data);\n continue;\n }\n const legacy = LegacyPermissionRequestArtifactSchema.safeParse(value);\n if (!legacy.success) throw new TypeError(\"Stored authority request is malformed.\");\n if (context === undefined) {\n throw new TypeError(\"Legacy authority requests require the selected invocation context.\");\n }\n if (principal(legacy.data.sessionDid) !== principal(context.sessionDid)) continue;\n records.push(normalizeLegacyPermissionRequest(legacy.data, context));\n }\n return records;\n}\n\nexport async function findPermissionRequest(\n profile: string,\n requestId: string,\n identity: Pick<PermissionRequestIdentityInput, \"sessionDid\" | \"host\"> &\n Partial<Omit<LegacyRequestContext, \"profile\" | \"host\" | \"sessionDid\">>,\n): Promise<PermissionRequestArtifact | null> {\n const records = await listPermissionRequests(profile, {\n profile,\n host: identity.host,\n sessionDid: identity.sessionDid,\n posture: identity.posture ?? \"delegate-session\",\n operatorType: identity.operatorType ?? \"human\",\n ...(identity.ownerDid === undefined ? {} : { ownerDid: identity.ownerDid }),\n ...(identity.spaceId === undefined ? {} : { spaceId: identity.spaceId }),\n });\n return records.find((record) =>\n record.requestId === requestId &&\n record.sessionDid === identity.sessionDid &&\n record.host === identity.host,\n ) ?? null;\n}\n\n/**\n * Atomically returns the newest identical unresolved request, or writes a new\n * v1 record. Retention, stale-session pruning, covered-record pruning, and\n * partial-grant supersession all happen in the same existing profile lock.\n */\nexport async function createOrReusePermissionRequest(\n input: CreateOrReusePermissionRequestInput,\n): Promise<PermissionRequestResolution> {\n // Registered operations already computed this exact missing set during\n // canonical preflight. Do not recompute it with the legacy global matcher:\n // that matcher intentionally treats same-named short/full spaces as equal.\n const missing = input.resolveSpace === undefined\n ? evaluateAuthority(input.granted, input.missing).missing\n : canonicalizeCapabilities(input.missing);\n const now = input.now ?? (() => new Date());\n const observedNow = now();\n if (Number.isNaN(observedNow.getTime())) throw new TypeError(\"The artifact clock returned an invalid date.\");\n\n return updateProfileStore<unknown, PermissionRequestResolution>(\n input.profile,\n \"auth-requests\",\n (rawRecords) => {\n const records: PermissionRequestArtifact[] = [];\n for (const value of rawRecords) {\n const canonical = PermissionRequestArtifactSchema.safeParse(value);\n if (canonical.success) {\n if (principal(canonical.data.sessionDid) !== principal(input.sessionDid)) continue;\n records.push(canonical.data);\n continue;\n }\n const legacy = LegacyPermissionRequestArtifactSchema.safeParse(value);\n if (!legacy.success) throw new TypeError(\"Stored authority request is malformed.\");\n if (principal(legacy.data.sessionDid) !== principal(input.sessionDid)) continue;\n records.push(normalizeLegacyPermissionRequest(legacy.data, {\n profile: input.profile,\n host: input.host,\n sessionDid: input.sessionDid,\n posture: input.posture,\n operatorType: input.operatorType,\n ...(input.ownerDid === undefined ? {} : { ownerDid: input.ownerDid }),\n ...(input.spaceId === undefined ? {} : { spaceId: input.spaceId }),\n now: observedNow,\n }));\n }\n const retained = prunePermissionRequests(records, {\n profile: input.profile,\n sessionDid: input.sessionDid,\n granted: input.granted,\n resolveSpace: input.resolveSpace,\n now: observedNow,\n });\n\n if (missing.length === 0) {\n return { records: retained, result: { status: \"satisfied\", reused: false } };\n }\n\n const identity = createPermissionRequestIdentity({ ...input, missing });\n const withoutSuperseded = retained.filter((record) => !isSupersededBy(record, input, missing));\n const equivalent = newestEquivalentRequest(withoutSuperseded, identity);\n if (equivalent !== undefined && input.replace !== true) {\n return { records: withoutSuperseded, result: { status: \"created\", reused: true, request: equivalent } };\n }\n\n const next = withoutSuperseded.filter((record) =>\n input.replace !== true || permissionRequestArtifactIdentity(record) !== identity,\n );\n const request = buildPermissionRequestArtifact({ ...input, missing, now: () => observedNow });\n return {\n records: [...next, request],\n result: { status: \"created\", reused: false, request },\n };\n },\n );\n}\n\nexport interface PrunePermissionRequestsInput {\n readonly profile: string;\n readonly sessionDid: string;\n readonly granted: readonly PermissionEntry[];\n readonly resolveSpace?: OperationSpaceResolver;\n readonly now: Date;\n}\n\n/** Pure retention policy used by the locked persistence operation. */\nexport function prunePermissionRequests(\n records: readonly PermissionRequestArtifact[],\n input: PrunePermissionRequestsInput,\n): PermissionRequestArtifact[] {\n const cutoff = input.now.getTime() - THIRTY_DAYS_MS;\n return records.filter((record) => {\n const createdAt = Date.parse(record.createdAt);\n if (!Number.isFinite(createdAt) || createdAt < cutoff) return false;\n if (record.profile === input.profile && principal(record.sessionDid) !== principal(input.sessionDid)) return false;\n // Empty request arrays are historical CLI records. They carry no\n // authority, but remain readable and are retained byte-for-byte by the\n // shared format-1 writer for compatibility.\n if (record.requested.length === 0) return true;\n return !evaluateArtifactAuthority(\n input.granted,\n record.requested,\n input.resolveSpace,\n ).satisfied;\n });\n}\n\nfunction newestEquivalentRequest(\n records: readonly PermissionRequestArtifact[],\n identity: string,\n): PermissionRequestArtifact | undefined {\n let newest: PermissionRequestArtifact | undefined;\n for (const record of records) {\n if (permissionRequestArtifactIdentity(record) !== identity) continue;\n if (newest === undefined || record.createdAt >= newest.createdAt) newest = record;\n }\n return newest;\n}\n\nfunction isSupersededBy(\n record: PermissionRequestArtifact,\n input: CreateOrReusePermissionRequestInput,\n missing: readonly PermissionEntry[],\n): boolean {\n if (\n record.profile !== input.profile ||\n record.sessionDid !== input.sessionDid ||\n record.host !== input.host\n ) {\n return false;\n }\n const remaining = evaluateArtifactAuthority(\n input.granted,\n record.requested,\n input.resolveSpace,\n ).missing;\n return remaining.length > 0 &&\n permissionRequestArtifactIdentity(record) !==\n createPermissionRequestIdentity({ ...input, missing }) &&\n createPermissionRequestIdentity({ ...input, missing: remaining }) ===\n createPermissionRequestIdentity({ ...input, missing });\n}\n\nfunction permissionRequestArtifactIdentity(record: PermissionRequestArtifact): string {\n return createPermissionRequestIdentity({\n profile: record.profile,\n sessionDid: record.sessionDid,\n host: record.host,\n missing: record.requested,\n });\n}\n\nfunction evaluateArtifactAuthority(\n granted: readonly PermissionEntry[],\n required: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n) {\n return resolveSpace === undefined\n ? evaluateAuthority(granted, required)\n : evaluateOperationAuthority(granted, required, resolveSpace);\n}\n\nfunction defaultRequestId(now: Date): string {\n return `req_${now.getTime().toString(36)}_${randomUUID().replace(/-/g, \"\")}`;\n}\n","/**\n * TinyCloud App Manifest\n *\n * A declarative description of an app's identity and the capabilities it\n * needs. The manifest drives the SIWE recap at sign-in time, enabling a\n * single wallet prompt that covers the app's own permissions plus any\n * pre-declared delegations.\n *\n * The SDK does NOT fetch external manifests. Apps compose their own manifest\n * (optionally including backend or agent addenda) before handing it to the\n * SDK.\n *\n * Canonical spec: `.claude/specs/manifest.md`.\n *\n * @packageDocumentation\n */\n\nimport ms from \"ms\";\nimport { ACCOUNT_MANIFEST_PERMISSIONS } from \"@tinycloud/bootstrap\";\nimport { resolveSecretPath, SECRET_NAME_RE } from \"@tinycloud/sdk-services\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A single permission entry inside a manifest. This is the shape apps write\n * in their `manifest.json` and the shape we compare against when performing\n * the capability-subset derivability check in the delegation flow.\n *\n * `service` uses the long form (e.g. `\"tinycloud.kv\"`, `\"tinycloud.sql\"`).\n * `\"tinycloud.vault\"` is an SDK-only shorthand that expands to the KV\n * resources the vault service uses; it is never encoded as a recap service.\n */\nexport interface PermissionEntry {\n /** Service namespace, e.g. \"tinycloud.kv\", \"tinycloud.sql\", \"tinycloud.duckdb\", \"tinycloud.capabilities\". */\n service: string;\n /** Space name or full space URI. Defaults to \"applications\" inside manifests. */\n space?: string;\n /**\n * Service-specific path.\n * - tinycloud.kv: hierarchical prefix. \"/\" = all, \"foo/\" = prefix match, \"foo\" = exact key\n * - tinycloud.sql: database name/file (e.g. \"data.sqlite\") or \"/\" for all\n * - tinycloud.duckdb: database name/file\n * - tinycloud.capabilities: capability key URI or \"/\" for all\n */\n path: string;\n /**\n * Short action names (e.g. \"get\", \"put\", \"read\", \"schema\"). The SDK expands\n * these to full URNs (e.g. `tinycloud.kv/get`) during resolution.\n * Already-expanded URNs are passed through unchanged.\n */\n actions: string[];\n /** Exact ReCap caveat branches when this authority came from a signed ReCap. */\n caveats?: Record<string, unknown>[];\n /** When true, the manifest prefix is NOT prepended to `path`. Default false. */\n skipPrefix?: boolean;\n /** Per-entry expiry override, ms-format. */\n expiry?: string;\n /** User/agent-facing context for why this permission is requested. */\n description?: string;\n}\n\nexport type ManifestSecretActions =\n | true\n | string\n | string[]\n | {\n /** Actual vault secret name. Defaults to the manifest object key. */\n name?: string;\n /** Optional scoped secret namespace. Omit for global secrets. */\n scope?: string;\n actions?: string | string[];\n expiry?: string;\n description?: string;\n };\n\n/**\n * The valid values for `Manifest.defaults`.\n *\n * - `false` → no auto-included permissions\n * - `true` → standard tier (KV + SQL read/write + capabilities:read)\n * - `\"admin\"` → standard + SQL schema\n * - `\"all\"` → everything the SDK supports (including DuckDB)\n *\n * Unknown string values silently fall back to `true`. Values are normalized\n * (lowercase + trim) before matching.\n */\nexport type ManifestDefaults = boolean | \"admin\" | \"all\";\n\n/**\n * Agent-readable app knowledge bundle pointer.\n *\n * - `true` means the default root: `knowledge/index.md`\n * - string values must point at a markdown root under `knowledge/`\n */\nexport type ManifestKnowledge = true | string;\n\n/** Default root used when `manifest.knowledge === true`. */\nexport const DEFAULT_KNOWLEDGE_ROOT = \"knowledge/index.md\";\n\n/**\n * The raw manifest shape an app declares. See `.claude/specs/manifest.md`.\n */\nexport interface Manifest {\n /** Schema version. Optional, defaults to 1. */\n manifest_version?: 1;\n /** Application identifier / namespace prefix. Required. */\n app_id: string;\n /** Display name. Required. */\n name: string;\n /** Description of what the app or delegate does. Optional. */\n description?: string;\n /** DID of this manifest's delegate target. Optional. Required only for delegation materialization. */\n did?: string;\n /** URL to app icon. Optional. */\n icon?: string;\n /** App version string. Optional. */\n appVersion?: string;\n /** Default expiry for permissions. ms-format (\"30d\", \"2h\", \"1y\"). Default \"30d\". */\n expiry?: string;\n /** Space name or full space URI. Optional, defaults to \"applications\". */\n space?: string;\n /**\n * Path prefix auto-prepended to permission paths. Optional, defaults to\n * `app_id`. Set to `\"\"` to disable entirely. Individual permissions can opt\n * out with `skipPrefix: true`.\n */\n prefix?: string;\n /**\n * Default permission set to auto-include. Optional, defaults to `true`.\n * See {@link ManifestDefaults}.\n */\n defaults?: ManifestDefaults | string;\n /** Whether to include the public-space companion delegation. Default `true`. */\n includePublicSpace?: boolean;\n /** Agent-readable knowledge bundle root. `true` means `knowledge/index.md`. */\n knowledge?: ManifestKnowledge;\n /**\n * Additional permissions beyond the defaults. Use for cross-space access,\n * DuckDB (opt-in), or `skipPrefix: true` entries.\n */\n permissions?: PermissionEntry[];\n /**\n * Secret name shorthand. Entries resolve to encrypted vault KV resources in\n * the `secrets` space.\n */\n secrets?: Record<string, ManifestSecretActions>;\n}\n\n/**\n * A resolved permission entry with fully-expanded paths and action URNs.\n * This is the shape the delegation flow compares against parsed recap\n * capabilities, and the shape the session-key delegation path actually uses.\n */\nexport interface ResourceCapability {\n /** Long-form service, e.g. \"tinycloud.kv\". */\n service: string;\n /** Space name or URI. Short names are resolved to full SpaceIds at sign-in time. */\n space: string;\n /** Path with the manifest prefix applied (or skipped per `skipPrefix`). */\n path: string;\n /** Full-URN actions, e.g. [\"tinycloud.kv/get\", \"tinycloud.kv/put\"]. */\n actions: string[];\n /** Per-entry expiry override in milliseconds. */\n expiryMs?: number;\n /** User/agent-facing context copied from the source permission entry. */\n description?: string;\n}\n\n/**\n * A resolved delegation entry with fully-expanded permissions.\n */\nexport interface ResolvedDelegate {\n /** DID of the delegate. */\n did: string;\n /** Informational display name. Optional. */\n name?: string;\n /** Expiry in milliseconds (per-delegation > manifest default > 30 days). */\n expiryMs: number;\n /** Fully resolved permissions. */\n permissions: ResourceCapability[];\n}\n\n/**\n * The output of {@link resolveManifest}: a fully-expanded capability set\n * ready to drive the SIWE recap.\n */\nexport interface ResolvedCapabilities {\n /** Application identifier copied from manifest.app_id. */\n app_id: string;\n /** Delegate DID copied from manifest.did, when present. */\n did?: string;\n /** Effective default space for this manifest. */\n space: string;\n /** All session-key resources with paths fully resolved (prefix applied). */\n resources: ResourceCapability[];\n /** Default expiry for the session, in milliseconds. */\n expiryMs: number;\n /** Whether to include the public-space companion. */\n includePublicSpace: boolean;\n /** Delegate targets derived from manifests that declare `did`. */\n additionalDelegates: ResolvedDelegate[];\n}\n\nexport interface ManifestRegistryRecord {\n /** KV key inside the account space. */\n key: string;\n /** App id this record describes. */\n app_id: string;\n /** Latest manifest payloads composed for this app id. */\n manifests: Manifest[];\n}\n\nexport interface ComposeManifestOptions {\n /** Include implicit account-space registry permissions. Default true. */\n includeAccountRegistryPermissions?: boolean;\n}\n\nexport interface ComposedManifestRequest {\n /** Validated manifests that were composed. */\n manifests: Manifest[];\n /** Full permission union requested from the user in one SIWE. */\n resources: ResourceCapability[];\n /** Delegations that can be materialized after sign-in. */\n delegationTargets: ResolvedDelegate[];\n /** Account-space registry records to write after successful sign-in. */\n registryRecords: ManifestRegistryRecord[];\n /** Effective session expiry, using the longest composed manifest expiry. */\n expiryMs: number;\n /** Whether to include the public-space companion behavior. */\n includePublicSpace: boolean;\n}\n\n/**\n * Thrown when the manifest fails validation (missing id/name, bad expiry,\n * empty actions on a permission, etc).\n */\nexport class ManifestValidationError extends Error {\n constructor(message: string) {\n super(`Manifest validation failed: ${message}`);\n this.name = \"ManifestValidationError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Default expiry when neither the manifest, delegation, nor permission\n * specifies one. APP tier — see `expiry.ts`. Spec: 30 days.\n *\n * Kept as an ms-format string because the manifest schema stores expiry\n * as a string and the parser is shared between this default and\n * caller-provided values; converting `EXPIRY.APP_MS` back to a string\n * here would duplicate that same `30d` literal in another form.\n */\nexport const DEFAULT_EXPIRY = \"30d\";\n\n/**\n * Default `defaults` value when the manifest omits it. Spec: standard tier.\n */\nexport const DEFAULT_DEFAULTS: ManifestDefaults = true;\n\n/** Default manifest schema version. */\nexport const DEFAULT_MANIFEST_VERSION = 1;\n\n/** Default space for manifest-declared app data. */\nexport const DEFAULT_MANIFEST_SPACE = \"applications\";\n\n/** Account-space name used for installed-application registry records. */\nexport const ACCOUNT_REGISTRY_SPACE = \"account\";\n\n/** Account-space KV prefix used for installed-application registry records. */\nexport const ACCOUNT_REGISTRY_PATH = \"applications/\";\n\nexport const SECRETS_SPACE = \"secrets\";\n\n/** SDK-only permission service for encrypted vault resources. */\nexport const VAULT_PERMISSION_SERVICE = \"tinycloud.vault\";\n\ntype VaultKVBase = \"vault\";\n\ninterface VaultActionExpansion {\n bases: readonly VaultKVBase[];\n action: string;\n}\n\n/**\n * Known services and their short-form (recap URI) names. The TinyCloud\n * node encodes the recap resource URI with the short service name, while\n * action URNs and manifest entries use the long `tinycloud.<short>` form.\n * This table is the canonical bridge between the two.\n */\nexport const SERVICE_SHORT_TO_LONG: Readonly<Record<string, string>> =\n Object.freeze({\n kv: \"tinycloud.kv\",\n sql: \"tinycloud.sql\",\n duckdb: \"tinycloud.duckdb\",\n capabilities: \"tinycloud.capabilities\",\n hooks: \"tinycloud.hooks\",\n encryption: \"tinycloud.encryption\",\n delegation: \"tinycloud.delegation\",\n });\n\n/**\n * Manifest service identifier for TinyCloud encryption network grants.\n *\n * Encryption permissions live on a network id URN\n * (`urn:tinycloud:encryption:<ownerDid>:<network>`), not on a space.\n * The `path` field is the literal networkId; `actions` are\n * `[\"decrypt\"]` (expanded to `[\"tinycloud.encryption/decrypt\"]`).\n *\n * Apps should omit `space` for encryption permissions. The SDK may emit\n * an internal `\"encryption\"` compatibility label after expansion so the\n * older `PermissionEntry`/`ResourceCapability` shape can still carry the\n * raw network URN through subset checks.\n */\nexport const ENCRYPTION_PERMISSION_SERVICE = \"tinycloud.encryption\";\n\n/** Synthetic space label used by encryption manifest entries. */\nexport const ENCRYPTION_MANIFEST_SPACE = \"encryption\";\n\n/**\n * Inverse of {@link SERVICE_SHORT_TO_LONG}.\n */\nexport const SERVICE_LONG_TO_SHORT: Readonly<Record<string, string>> =\n Object.freeze(\n Object.fromEntries(\n Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s]),\n ),\n );\n\n/**\n * Default permission entries for the `true` / standard tier.\n *\n * `tinycloud.capabilities/read` is added separately for every requested\n * space, without the app path prefix. That keeps capability introspection\n * space-scoped instead of app-data scoped.\n */\nconst DEFAULT_STANDARD_ENTRIES: readonly PermissionEntry[] = [\n {\n service: \"tinycloud.kv\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\"],\n },\n];\n\n/**\n * Default permission entries for the `\"admin\"` tier: standard + sql/schema.\n */\nconst DEFAULT_ADMIN_ENTRIES: readonly PermissionEntry[] = [\n {\n service: \"tinycloud.kv\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\", \"schema\"],\n },\n];\n\n/**\n * Default permission entries for the `\"all\"` tier: admin + DuckDB.\n *\n * DuckDB is opt-in and only appears in this tier or in explicit manifest\n * `permissions` entries.\n */\nconst DEFAULT_ALL_ENTRIES: readonly PermissionEntry[] = [\n {\n service: \"tinycloud.kv\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"get\", \"put\", \"del\", \"list\", \"metadata\"],\n },\n {\n service: \"tinycloud.sql\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\", \"schema\"],\n },\n {\n service: \"tinycloud.duckdb\",\n space: DEFAULT_MANIFEST_SPACE,\n path: \"/\",\n actions: [\"read\", \"write\"],\n },\n];\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse an ms-format duration string (e.g. \"30d\", \"2h\", \"1y\") into\n * milliseconds.\n *\n * @throws {ManifestValidationError} on empty string, non-string input, or\n * any input `ms()` cannot parse.\n */\nexport function parseExpiry(duration: string): number {\n if (typeof duration !== \"string\" || duration.length === 0) {\n throw new ManifestValidationError(\n `expiry must be a non-empty duration string (got ${JSON.stringify(duration)})`,\n );\n }\n // `ms` returns `undefined` for unparseable input and can return a number\n // or a string depending on the call signature; cast explicitly.\n const parsed = (ms as unknown as (v: string) => number | undefined)(duration);\n if (typeof parsed !== \"number\" || !Number.isFinite(parsed) || parsed <= 0) {\n throw new ManifestValidationError(\n `invalid expiry duration: ${JSON.stringify(duration)}`,\n );\n }\n return parsed;\n}\n\n/**\n * Expand a list of action short names (or already-expanded URNs) into full\n * ability URNs of the form `<service>/<action>`.\n *\n * Examples:\n * `expandActionShortNames(\"tinycloud.kv\", [\"get\", \"put\"])`\n * → `[\"tinycloud.kv/get\", \"tinycloud.kv/put\"]`\n * `expandActionShortNames(\"tinycloud.kv\", [\"tinycloud.kv/get\"])`\n * → `[\"tinycloud.kv/get\"]` (passed through unchanged)\n */\nexport function expandActionShortNames(\n service: string,\n actions: readonly string[],\n): string[] {\n return actions.map((a) => {\n if (a.includes(\"/\")) {\n // Already a full URN — pass through.\n return a;\n }\n return `${service}/${a}`;\n });\n}\n\n/**\n * Expand SDK virtual permission services into concrete recap-capable services.\n *\n * Today this handles `\"tinycloud.vault\"`, which is backed by inline\n * network-encrypted KV records:\n * - read/get: `vault/<path>` with `tinycloud.kv/get`\n * - write/put: `vault/<path>` with `tinycloud.kv/put`\n * - delete/del: `vault/<path>` with `tinycloud.kv/del`\n * - list: `vault/<path>` with `tinycloud.kv/list`\n * - head: `vault/<path>` with `tinycloud.kv/get`\n * - metadata: `vault/<path>` with `tinycloud.kv/metadata`\n */\nexport function expandPermissionEntry(entry: PermissionEntry): PermissionEntry[] {\n if (entry.service === ENCRYPTION_PERMISSION_SERVICE) {\n return expandEncryptionPermissionEntry(entry);\n }\n if (entry.service !== VAULT_PERMISSION_SERVICE) {\n return [\n {\n ...entry,\n actions: expandActionShortNames(entry.service, entry.actions),\n },\n ];\n }\n\n return expandVaultPermissionEntry(entry);\n}\n\n/**\n * Expand a `tinycloud.encryption` manifest entry.\n *\n * - `path` MUST be a networkId URN (`urn:tinycloud:encryption:...`).\n * The manifest prefix is always skipped — network ids are top-level\n * owner-scoped resources, not space-scoped paths.\n * - `actions` must be `decrypt`, `network.create`, or `network.revoke`\n * (or their already-expanded `tinycloud.encryption/*` forms).\n *\n * The returned entry uses an internal `space = \"encryption\"` compatibility\n * label so old manifest grouping code does not collapse it into the app's\n * data space, and `skipPrefix` is forced to `true` so the prefix application\n * step is a no-op.\n */\nfunction expandEncryptionPermissionEntry(\n entry: PermissionEntry,\n): PermissionEntry[] {\n if (\n typeof entry.path !== \"string\" ||\n !entry.path.startsWith(\"urn:tinycloud:encryption:\")\n ) {\n throw new ManifestValidationError(\n `tinycloud.encryption entries require path to be a networkId URN ` +\n `(got ${JSON.stringify(entry.path)})`,\n );\n }\n const normalizedActions: string[] = [];\n for (const action of entry.actions) {\n if (action === \"decrypt\" || action === \"tinycloud.encryption/decrypt\") {\n normalizedActions.push(\"tinycloud.encryption/decrypt\");\n continue;\n }\n if (\n action === \"network.create\" ||\n action === \"tinycloud.encryption/network.create\"\n ) {\n normalizedActions.push(\"tinycloud.encryption/network.create\");\n continue;\n }\n if (\n action === \"network.revoke\" ||\n action === \"tinycloud.encryption/network.revoke\"\n ) {\n normalizedActions.push(\"tinycloud.encryption/network.revoke\");\n continue;\n }\n if (action.includes(\"/\")) {\n throw new ManifestValidationError(\n `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`,\n );\n }\n throw new ManifestValidationError(\n `unknown encryption action ${JSON.stringify(action)}; expected decrypt, network.create, or network.revoke`,\n );\n }\n // Dedupe while preserving order\n const dedupedActions: string[] = [];\n const seen = new Set<string>();\n for (const a of normalizedActions) {\n if (!seen.has(a)) {\n dedupedActions.push(a);\n seen.add(a);\n }\n }\n // Encryption resources are network-scoped, not space-scoped. We\n // always emit the synthetic \"encryption\" space label so manifest\n // grouping can deal with it uniformly without collapsing the\n // networkId URN into an app data space.\n return [\n {\n service: ENCRYPTION_PERMISSION_SERVICE,\n space: ENCRYPTION_MANIFEST_SPACE,\n path: entry.path,\n actions: dedupedActions,\n skipPrefix: true,\n ...(entry.expiry !== undefined ? { expiry: entry.expiry } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n },\n ];\n}\n\n/**\n * Expand a list of permission entries using {@link expandPermissionEntry}.\n */\nexport function expandPermissionEntries(\n entries: readonly PermissionEntry[],\n): PermissionEntry[] {\n return entries.flatMap(expandPermissionEntry);\n}\n\n/**\n * Apply the manifest prefix to a permission path per the spec rules.\n *\n * - `skipPrefix: true` → path is returned as-is\n * - `prefix === \"\"` → path is returned as-is\n * - path starts with \"/\" → `prefix + path` (e.g. \"com.listen.app\" + \"/\" → \"com.listen.app/\")\n * - otherwise → `prefix + \"/\" + path` (e.g. \"com.listen.app\" + \"data.sqlite\" → \"com.listen.app/data.sqlite\")\n */\nexport function applyPrefix(\n prefix: string,\n path: string,\n skipPrefix: boolean,\n): string {\n if (skipPrefix) {\n return path;\n }\n if (prefix === \"\") {\n return path;\n }\n if (path.startsWith(\"/\")) {\n return `${prefix}${path}`;\n }\n return `${prefix}/${path}`;\n}\n\n/**\n * Fetch and parse a manifest from a URL (browser) or file path (node).\n * The runtime decides the fetch strategy via `globalThis.fetch`; this is\n * platform-agnostic. Callers that want custom loading should JSON.parse a\n * Manifest themselves and skip this helper.\n *\n * @throws if the fetch fails, the JSON is invalid, or the manifest fails\n * validation.\n */\nexport async function loadManifest(url: string): Promise<Manifest> {\n const fetchFn: typeof fetch | undefined = (\n globalThis as { fetch?: typeof fetch }\n ).fetch;\n if (typeof fetchFn !== \"function\") {\n throw new ManifestValidationError(\n \"loadManifest requires a global fetch; pass the manifest object directly on runtimes without fetch\",\n );\n }\n const res = await fetchFn(url);\n if (!res.ok) {\n throw new ManifestValidationError(\n `failed to fetch manifest from ${url}: HTTP ${res.status}`,\n );\n }\n const json = (await res.json()) as unknown;\n return validateManifest(json);\n}\n\n/**\n * Validate a manifest-shaped object and return it strongly-typed.\n * Throws {@link ManifestValidationError} on any hard failure.\n */\nexport function validateManifest(input: unknown): Manifest {\n if (input === null || typeof input !== \"object\") {\n throw new ManifestValidationError(\"manifest must be an object\");\n }\n const m = input as Manifest;\n if (\n m.manifest_version !== undefined &&\n m.manifest_version !== DEFAULT_MANIFEST_VERSION\n ) {\n throw new ManifestValidationError(\n `manifest.manifest_version must be ${DEFAULT_MANIFEST_VERSION}`,\n );\n }\n if (typeof m.app_id !== \"string\" || m.app_id.length === 0) {\n throw new ManifestValidationError(\n \"manifest.app_id is required and must be a non-empty string\",\n );\n }\n if (typeof m.name !== \"string\" || m.name.length === 0) {\n throw new ManifestValidationError(\n \"manifest.name is required and must be a non-empty string\",\n );\n }\n if (\n m.did !== undefined &&\n (typeof m.did !== \"string\" || m.did.length === 0)\n ) {\n throw new ManifestValidationError(\n \"manifest.did must be a non-empty DID string\",\n );\n }\n if (\n m.space !== undefined &&\n (typeof m.space !== \"string\" || m.space.length === 0)\n ) {\n throw new ManifestValidationError(\n \"manifest.space must be a non-empty string\",\n );\n }\n if (m.expiry !== undefined) {\n // Will throw with a clear error if invalid.\n parseExpiry(m.expiry);\n }\n if (m.knowledge !== undefined) {\n resolveManifestKnowledgeRoot(m.knowledge);\n }\n if (m.permissions !== undefined) {\n if (!Array.isArray(m.permissions)) {\n throw new ManifestValidationError(\n \"manifest.permissions must be an array\",\n );\n }\n m.permissions.forEach((p, i) =>\n validatePermissionEntry(p, `permissions[${i}]`),\n );\n }\n if (m.secrets !== undefined) {\n validateManifestSecrets(m.secrets);\n }\n return m;\n}\n\n/**\n * Resolve a manifest knowledge pointer to its root markdown file.\n *\n * This is metadata only; app knowledge does not create capabilities.\n */\nexport function resolveManifestKnowledgeRoot(\n knowledge: Manifest[\"knowledge\"] | undefined,\n): string | undefined {\n if (knowledge === undefined) {\n return undefined;\n }\n if (knowledge === true) {\n return DEFAULT_KNOWLEDGE_ROOT;\n }\n if (typeof knowledge !== \"string\" || knowledge.length === 0) {\n throw new ManifestValidationError(\n \"manifest.knowledge must be true or a knowledge/*.md root path\",\n );\n }\n if (!/^knowledge\\/.+\\.md$/.test(knowledge)) {\n throw new ManifestValidationError(\n \"manifest.knowledge must be true or a knowledge/*.md root path\",\n );\n }\n return knowledge;\n}\n\nfunction validateManifestSecrets(secrets: unknown): void {\n if (secrets === null || typeof secrets !== \"object\" || Array.isArray(secrets)) {\n throw new ManifestValidationError(\"manifest.secrets must be an object\");\n }\n\n for (const [name, spec] of Object.entries(secrets)) {\n if (!SECRET_NAME_RE.test(name)) {\n throw new ManifestValidationError(\n `manifest.secrets.${name} must match ${SECRET_NAME_RE.source}`,\n );\n }\n try {\n resolveSecretPath(\n secretNameFromSpec(name, spec as ManifestSecretActions),\n { scope: secretScopeFromSpec(spec as ManifestSecretActions) },\n );\n } catch (error) {\n throw new ManifestValidationError(\n `manifest.secrets.${name}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n const actions = secretActionsFromSpec(name, spec as ManifestSecretActions);\n if (actions.length === 0) {\n throw new ManifestValidationError(\n `manifest.secrets.${name} actions must be non-empty`,\n );\n }\n for (const action of actions) {\n if (typeof action !== \"string\" || action.length === 0) {\n throw new ManifestValidationError(\n `manifest.secrets.${name} actions must be non-empty strings`,\n );\n }\n }\n if (\n spec !== null &&\n typeof spec === \"object\" &&\n !Array.isArray(spec) &&\n (spec as { expiry?: unknown }).expiry !== undefined\n ) {\n parseExpiry((spec as { expiry: string }).expiry);\n }\n }\n}\n\nfunction validatePermissionEntry(p: unknown, path: string): void {\n if (p === null || typeof p !== \"object\") {\n throw new ManifestValidationError(`${path} must be an object`);\n }\n const entry = p as PermissionEntry;\n if (typeof entry.service !== \"string\" || entry.service.length === 0) {\n throw new ManifestValidationError(`${path}.service is required`);\n }\n if (\n entry.space !== undefined &&\n (typeof entry.space !== \"string\" || entry.space.length === 0)\n ) {\n throw new ManifestValidationError(\n `${path}.space must be a non-empty string`,\n );\n }\n if (typeof entry.path !== \"string\") {\n throw new ManifestValidationError(\n `${path}.path is required (use \"\" or \"/\" for root)`,\n );\n }\n if (!Array.isArray(entry.actions) || entry.actions.length === 0) {\n throw new ManifestValidationError(\n `${path}.actions must be a non-empty array`,\n );\n }\n for (const action of entry.actions) {\n if (typeof action !== \"string\" || action.length === 0) {\n throw new ManifestValidationError(\n `${path}.actions must contain non-empty strings`,\n );\n }\n if (entry.service === VAULT_PERMISSION_SERVICE) {\n vaultActionExpansion(action);\n }\n }\n if (entry.caveats !== undefined &&\n (!Array.isArray(entry.caveats) || entry.caveats.some((caveat) =>\n caveat === null || typeof caveat !== \"object\" || Array.isArray(caveat)\n ))\n ) {\n throw new ManifestValidationError(`${path}.caveats must be an array of objects`);\n }\n if (entry.expiry !== undefined) {\n parseExpiry(entry.expiry);\n }\n}\n\n/**\n * Normalize a `defaults` value: lowercase + trim, then match against known\n * tiers. Unknown string values silently fall back to `true` (standard).\n * Boolean values pass through.\n */\nexport function normalizeDefaults(\n value: Manifest[\"defaults\"] | undefined,\n): ManifestDefaults {\n if (value === undefined) {\n return DEFAULT_DEFAULTS;\n }\n if (typeof value === \"boolean\") {\n return value;\n }\n if (typeof value !== \"string\") {\n // Spec says unknown values silently fall back to `true`.\n return true;\n }\n const normalized = value.trim().toLowerCase();\n if (normalized === \"admin\" || normalized === \"all\") {\n return normalized;\n }\n // Anything else, including \"true\"/\"false\"/\"standard\"/garbage, falls back\n // to the standard tier per spec.\n return true;\n}\n\n/**\n * Return the default permission entries for the given tier. Entries are\n * deep-cloned so callers can mutate them without affecting the constants.\n */\nfunction defaultEntriesForTier(tier: ManifestDefaults): PermissionEntry[] {\n if (tier === false) {\n return [];\n }\n const source =\n tier === \"admin\"\n ? DEFAULT_ADMIN_ENTRIES\n : tier === \"all\"\n ? DEFAULT_ALL_ENTRIES\n : DEFAULT_STANDARD_ENTRIES;\n return source.map((e) => ({\n service: e.service,\n space: e.space,\n path: e.path,\n actions: [...e.actions],\n ...(e.skipPrefix !== undefined ? { skipPrefix: e.skipPrefix } : {}),\n }));\n}\n\n/**\n * Resolve a raw manifest into a {@link ResolvedCapabilities} object: expand\n * shortform actions, apply the prefix, merge defaults, and compute effective\n * expiries. Pure function — does no I/O.\n *\n * Resolution semantics (spec):\n * - `prefix` defaults to `app_id`; set to `\"\"` to disable prefix application entirely.\n * - `space` defaults to `applications`; per-permission `space` overrides it.\n * - `defaults` defaults to `true` (standard tier); unknown string values fall back to `true`.\n * - Per-entry expiry overrides per-delegation overrides manifest > `DEFAULT_EXPIRY`.\n * - Default entries use `skipPrefix: false` so they inherit the manifest prefix.\n */\nexport function resolveManifest(input: Manifest): ResolvedCapabilities {\n const manifest = validateManifest(input);\n\n const prefix =\n manifest.prefix !== undefined ? manifest.prefix : manifest.app_id;\n const space = manifest.space ?? DEFAULT_MANIFEST_SPACE;\n const expiryMs = parseExpiry(manifest.expiry ?? DEFAULT_EXPIRY);\n const includePublicSpace = manifest.includePublicSpace ?? true;\n const tier = normalizeDefaults(manifest.defaults);\n\n const defaultEntries = defaultEntriesForTier(tier);\n const explicitEntries = manifest.permissions ?? [];\n const secretEntries = secretEntriesForManifest(manifest.secrets);\n\n // Merge order: defaults first, then explicit entries, so explicit entries\n // for the same (service, space, path) tuple override defaults.\n const allEntries: PermissionEntry[] = [\n ...defaultEntries,\n ...explicitEntries,\n ...secretEntries,\n ];\n\n const resources: ResourceCapability[] = withCapabilitiesReadForSpaces(\n allEntries.flatMap((entry) => resolveEntry(entry, prefix, expiryMs, space)),\n );\n\n const additionalDelegates: ResolvedDelegate[] =\n manifest.did === undefined\n ? []\n : [\n {\n did: manifest.did,\n name: manifest.name,\n expiryMs,\n permissions: resources.map(cloneResourceCapability),\n },\n ];\n\n return {\n app_id: manifest.app_id,\n ...(manifest.did !== undefined ? { did: manifest.did } : {}),\n space,\n resources,\n expiryMs,\n includePublicSpace,\n additionalDelegates,\n };\n}\n\nfunction normalizeSecretActions(actions: readonly string[]): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n const add = (action: string) => {\n if (!seen.has(action)) {\n out.push(action);\n seen.add(action);\n }\n };\n\n for (const action of actions) {\n if (action === \"read\") {\n add(\"get\");\n continue;\n }\n if (action === \"write\") {\n add(\"put\");\n continue;\n }\n if (action === \"delete\") {\n add(\"del\");\n continue;\n }\n if (\n action === \"get\" ||\n action === \"put\" ||\n action === \"del\" ||\n action === \"list\" ||\n action === \"metadata\"\n ) {\n add(action);\n continue;\n }\n if (\n action === \"tinycloud.kv/get\" ||\n action === \"tinycloud.kv/put\" ||\n action === \"tinycloud.kv/del\" ||\n action === \"tinycloud.kv/list\" ||\n action === \"tinycloud.kv/metadata\"\n ) {\n add(action);\n continue;\n }\n throw new ManifestValidationError(\n `unknown secret action ${JSON.stringify(action)}; expected read, write, delete, list, or metadata`,\n );\n }\n\n return out;\n}\n\nfunction secretNameFromSpec(\n fallbackName: string,\n spec: ManifestSecretActions,\n): string {\n if (spec !== null && typeof spec === \"object\" && !Array.isArray(spec)) {\n return spec.name ?? fallbackName;\n }\n return fallbackName;\n}\n\nfunction secretScopeFromSpec(\n spec: ManifestSecretActions,\n): string | undefined {\n if (spec !== null && typeof spec === \"object\" && !Array.isArray(spec)) {\n return spec.scope;\n }\n return undefined;\n}\n\nfunction secretActionsFromSpec(\n name: string,\n spec: ManifestSecretActions,\n): string[] {\n if (spec === true) {\n return [\"read\"];\n }\n if (typeof spec === \"string\") {\n return [spec];\n }\n if (Array.isArray(spec)) {\n return spec;\n }\n if (spec === null || typeof spec !== \"object\") {\n throw new ManifestValidationError(\n `manifest.secrets.${name} must be true, a string action, an actions array, or an object`,\n );\n }\n if (spec.actions === undefined) {\n return [\"read\"];\n }\n if (typeof spec.actions === \"string\") {\n return [spec.actions];\n }\n if (Array.isArray(spec.actions)) {\n return spec.actions;\n }\n throw new ManifestValidationError(\n `manifest.secrets.${name}.actions must be a string or array`,\n );\n}\n\nfunction secretEntriesForManifest(\n secrets: Manifest[\"secrets\"] | undefined,\n): PermissionEntry[] {\n if (secrets === undefined) {\n return [];\n }\n\n const entries: PermissionEntry[] = [];\n for (const [name, spec] of Object.entries(secrets)) {\n const actions = secretActionsFromSpec(name, spec);\n const secretPath = resolveSecretPath(\n secretNameFromSpec(name, spec),\n { scope: secretScopeFromSpec(spec) },\n );\n const extra: { expiry?: string; description?: string } =\n spec !== true && typeof spec === \"object\" && !Array.isArray(spec)\n ? spec\n : {};\n entries.push({\n service: VAULT_PERMISSION_SERVICE,\n space: SECRETS_SPACE,\n path: secretPath.vaultKey,\n actions: normalizeSecretActions(actions),\n skipPrefix: true,\n ...(extra.expiry !== undefined ? { expiry: extra.expiry } : {}),\n ...(extra.description !== undefined\n ? { description: extra.description }\n : {}),\n });\n }\n return entries;\n}\n\n/**\n * Expand a single permission entry into a {@link ResourceCapability}:\n * apply the prefix to the path and expand short actions into full URNs.\n */\nfunction resolveEntry(\n entry: PermissionEntry,\n prefix: string,\n _inheritedExpiryMs: number,\n inheritedSpace: string,\n): ResourceCapability[] {\n // Encryption permissions reference a networkId URN as their resource\n // — the manifest prefix is meaningless against a top-level\n // owner-scoped resource and would corrupt the URN. Always skip.\n const skipPrefixForEntry =\n entry.skipPrefix === true ||\n entry.service === ENCRYPTION_PERMISSION_SERVICE;\n const resolvedPath = applyPrefix(prefix, entry.path, skipPrefixForEntry);\n const entryExpiryMs =\n entry.expiry !== undefined ? parseExpiry(entry.expiry) : undefined;\n return expandPermissionEntry({\n ...entry,\n space: entry.space ?? inheritedSpace,\n path: resolvedPath,\n skipPrefix: true,\n }).map((expanded) => ({\n service: expanded.service,\n space: expanded.space ?? inheritedSpace,\n path: expanded.path,\n actions: expanded.actions,\n // Only populate `expiryMs` when the entry had its own expiry override.\n // When absent, callers use the parent (delegation or manifest) expiry\n // which is carried on ResolvedDelegate.expiryMs / ResolvedCapabilities.expiryMs.\n ...(entryExpiryMs !== undefined ? { expiryMs: entryExpiryMs } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n }));\n}\n\nfunction expandVaultPermissionEntry(entry: PermissionEntry): PermissionEntry[] {\n const byBase = new Map<VaultKVBase, string[]>();\n\n for (const action of entry.actions) {\n const expansion = vaultActionExpansion(action);\n for (const base of expansion.bases) {\n const actions = byBase.get(base) ?? [];\n if (!actions.includes(expansion.action)) {\n actions.push(expansion.action);\n }\n byBase.set(base, actions);\n }\n }\n\n return [...byBase.entries()].map(([base, actions]) => ({\n ...entry,\n service: \"tinycloud.kv\",\n path: vaultKVPath(base, entry.path),\n actions,\n skipPrefix: true,\n }));\n}\n\nfunction vaultActionExpansion(action: string): VaultActionExpansion {\n const normalized = normalizeVaultAction(action);\n if (normalized === \"read\" || normalized === \"get\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/get\" };\n }\n if (normalized === \"write\" || normalized === \"put\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/put\" };\n }\n if (normalized === \"delete\" || normalized === \"del\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/del\" };\n }\n if (normalized === \"list\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/list\" };\n }\n if (normalized === \"head\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/get\" };\n }\n if (normalized === \"metadata\") {\n return { bases: [\"vault\"], action: \"tinycloud.kv/metadata\" };\n }\n\n throw new ManifestValidationError(\n `unknown vault action ${JSON.stringify(action)}; expected read, write, delete, get, put, del, list, head, or metadata`,\n );\n}\n\nfunction normalizeVaultAction(action: string): string {\n if (action.startsWith(`${VAULT_PERMISSION_SERVICE}/`)) {\n return action.slice(`${VAULT_PERMISSION_SERVICE}/`.length);\n }\n if (action.startsWith(\"tinycloud.kv/\")) {\n return action.slice(\"tinycloud.kv/\".length);\n }\n if (action.includes(\"/\")) {\n throw new ManifestValidationError(\n `unknown vault action ${JSON.stringify(action)}; expected a tinycloud.vault or tinycloud.kv action`,\n );\n }\n return action;\n}\n\nfunction vaultKVPath(base: VaultKVBase, path: string): string {\n const normalized = path.startsWith(\"/\") ? path.slice(1) : path;\n return `${base}/${normalized}`;\n}\n\nfunction cloneResourceCapability(\n entry: ResourceCapability,\n): ResourceCapability {\n return {\n service: entry.service,\n space: entry.space,\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.expiryMs !== undefined ? { expiryMs: entry.expiryMs } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n };\n}\n\nfunction clonePermissionEntry(entry: PermissionEntry): PermissionEntry {\n return {\n service: entry.service,\n ...(entry.space !== undefined ? { space: entry.space } : {}),\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.caveats !== undefined\n ? { caveats: JSON.parse(JSON.stringify(entry.caveats)) as Record<string, unknown>[] }\n : {}),\n ...(entry.skipPrefix !== undefined ? { skipPrefix: entry.skipPrefix } : {}),\n ...(entry.expiry !== undefined ? { expiry: entry.expiry } : {}),\n ...(entry.description !== undefined\n ? { description: entry.description }\n : {}),\n };\n}\n\nfunction dedupeResources(\n resources: readonly ResourceCapability[],\n): ResourceCapability[] {\n const byKey = new Map<string, ResourceCapability>();\n\n for (const resource of resources) {\n const key = `${resource.service}\\u0000${resource.space}\\u0000${resource.path}\\u0000${resource.expiryMs ?? \"\"}`;\n const existing = byKey.get(key);\n if (existing === undefined) {\n byKey.set(key, cloneResourceCapability(resource));\n continue;\n }\n\n const seen = new Set(existing.actions);\n for (const action of resource.actions) {\n if (!seen.has(action)) {\n existing.actions.push(action);\n seen.add(action);\n }\n }\n if (\n existing.description === undefined &&\n resource.description !== undefined\n ) {\n existing.description = resource.description;\n }\n }\n\n return [...byKey.values()];\n}\n\nfunction capabilitiesReadPermission(space: string): ResourceCapability {\n return {\n service: \"tinycloud.capabilities\",\n space,\n path: \"\",\n actions: [\"tinycloud.capabilities/read\"],\n };\n}\n\nfunction withCapabilitiesReadForSpaces(\n resources: readonly ResourceCapability[],\n): ResourceCapability[] {\n if (resources.length === 0) {\n return [];\n }\n\n const spaces = new Set(\n resources\n .filter((resource) => resource.service !== ENCRYPTION_PERMISSION_SERVICE)\n .map((resource) => resource.space),\n );\n return dedupeResources([\n ...resources,\n ...[...spaces].map(capabilitiesReadPermission),\n ]);\n}\n\n/**\n * Compose one or more manifests into the single capability request that should\n * be signed. Fetching manifests is intentionally out of band; callers pass the\n * already-loaded manifest objects.\n */\nexport function composeManifestRequest(\n inputs: readonly Manifest[],\n options: ComposeManifestOptions = {},\n): ComposedManifestRequest {\n if (!Array.isArray(inputs) || inputs.length === 0) {\n throw new ManifestValidationError(\n \"composeManifestRequest requires at least one manifest\",\n );\n }\n\n const includeAccountRegistryPermissions =\n options.includeAccountRegistryPermissions ?? true;\n const manifests = inputs.map(validateManifest);\n const resolved = manifests.map(resolveManifest);\n const resources = resolved.flatMap((entry) => entry.resources);\n const delegationTargets = resolved.flatMap((entry) =>\n entry.additionalDelegates.map((delegate) => ({\n ...delegate,\n permissions: dedupeResources(delegate.permissions),\n })),\n );\n\n if (includeAccountRegistryPermissions) {\n resources.push(\n ...ACCOUNT_MANIFEST_PERMISSIONS.map((resource) => ({\n ...resource,\n actions: [...resource.actions],\n })),\n );\n }\n const resourcesWithImplicitCapabilities =\n withCapabilitiesReadForSpaces(resources);\n\n const manifestsByAppId = new Map<string, Manifest[]>();\n for (const manifest of manifests) {\n const current = manifestsByAppId.get(manifest.app_id);\n if (current === undefined) {\n manifestsByAppId.set(manifest.app_id, [manifest]);\n } else {\n current.push(manifest);\n }\n }\n\n const registryRecords: ManifestRegistryRecord[] =\n includeAccountRegistryPermissions\n ? [...manifestsByAppId.entries()].map(([app_id, appManifests]) => ({\n key: `${ACCOUNT_REGISTRY_PATH}${app_id}`,\n app_id,\n manifests: appManifests.map((manifest) => ({\n ...manifest,\n permissions: manifest.permissions?.map(clonePermissionEntry),\n })),\n }))\n : [];\n\n return {\n manifests,\n resources: resourcesWithImplicitCapabilities,\n delegationTargets,\n registryRecords,\n expiryMs: Math.max(...resolved.map((entry) => entry.expiryMs)),\n includePublicSpace: resolved.some((entry) => entry.includePublicSpace),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Abilities map construction (bridge to WASM prepareSession / createDelegation)\n// ---------------------------------------------------------------------------\n\n/**\n * The shape `prepareSession` and the multi-resource `createDelegation` WASM\n * export both accept:\n *\n * ```\n * { [shortService]: { [path]: [fullUrnAction, ...] } }\n * ```\n *\n * - `shortService` is the recap-level service segment (`\"kv\"`, `\"sql\"`,\n * `\"duckdb\"`, `\"capabilities\"`, `\"hooks\"`) — not the manifest long form.\n * - `path` is the fully-resolved path (prefix already applied). An empty\n * string means \"no path segment\" on the resource URI.\n * - Action strings are full URNs like `\"tinycloud.kv/get\"`.\n *\n * This is a single source of truth for both the session's own recap (at\n * sign-in) and the delegations it can derive (post sign-in). We re-use it\n * for both so one manifest drives both sides.\n */\nexport type AbilitiesMap = Record<string, Record<string, string[]>>;\n\n/**\n * Per-space abilities map accepted by the newer WASM session config:\n *\n * ```\n * { [spaceIdOrName]: { [shortService]: { [path]: [fullUrnAction, ...] } } }\n * ```\n */\nexport type SpaceAbilitiesMap = Record<string, AbilitiesMap>;\n\n/**\n * Convert a list of {@link ResourceCapability} entries (manifest\n * long-form service, full-URN actions) into the {@link AbilitiesMap}\n * shape the WASM layer expects.\n *\n * When multiple entries target the same `(service, path)` pair, their\n * action lists are merged and deduped. Entries whose service has no\n * short-form mapping in {@link SERVICE_LONG_TO_SHORT} are rejected with\n * a {@link ManifestValidationError} — the SDK does not silently drop\n * unknown services because the recap encoding would lose them.\n *\n * Paths are kept verbatim: this function does NOT collapse\n * `\"com.listen.app/\"` and `\"com.listen.app\"` or reinterpret empty /\n * slash strings. Callers that care about path canonicalization should\n * normalize before calling.\n */\nexport function resourceCapabilitiesToAbilitiesMap(\n resources: readonly ResourceCapability[],\n): AbilitiesMap {\n const out: AbilitiesMap = {};\n for (const r of resources) {\n const shortService = SERVICE_LONG_TO_SHORT[r.service];\n if (shortService === undefined) {\n throw new ManifestValidationError(\n `unknown service '${r.service}' — no short-form mapping. Known services: ${Object.keys(SERVICE_LONG_TO_SHORT).join(\", \")}`,\n );\n }\n if (out[shortService] === undefined) {\n out[shortService] = {};\n }\n const pathsMap = out[shortService];\n const existing = pathsMap[r.path];\n if (existing === undefined) {\n // Copy so downstream mutation can't leak back into the input.\n pathsMap[r.path] = [...r.actions];\n } else {\n // Merge + dedupe while preserving first-seen order.\n const seen = new Set(existing);\n for (const action of r.actions) {\n if (!seen.has(action)) {\n existing.push(action);\n seen.add(action);\n }\n }\n }\n }\n return out;\n}\n\n/**\n * Group resolved capabilities by `space`, then convert each group into a WASM\n * abilities map. Short space names are left as-is here; platform layers that\n * know the wallet address and chain id turn them into full SpaceIds.\n */\nexport function resourceCapabilitiesToSpaceAbilitiesMap(\n resources: readonly ResourceCapability[],\n): SpaceAbilitiesMap {\n const grouped = new Map<string, ResourceCapability[]>();\n for (const resource of resources) {\n const entries = grouped.get(resource.space);\n if (entries === undefined) {\n grouped.set(resource.space, [resource]);\n } else {\n entries.push(resource);\n }\n }\n\n const out: SpaceAbilitiesMap = {};\n for (const [space, entries] of grouped.entries()) {\n out[space] = resourceCapabilitiesToAbilitiesMap(entries);\n }\n return out;\n}\n\n/**\n * Build the {@link AbilitiesMap} that a session should be signed with,\n * given a {@link ResolvedCapabilities} (i.e. the output of\n * {@link resolveManifest}).\n *\n * The resulting map is the **union** of:\n * 1. the app's own resources (`resolved.resources`), and\n * 2. every permission declared in every `additionalDelegates[*]` entry.\n *\n * The union is what makes the manifest's delegations ergonomic: at\n * sign-in, the session key acquires recap coverage for both the app's\n * runtime needs and every downstream delegation target. Post sign-in,\n * `delegateTo(backendDID, backendPermissions)` can then issue the\n * sub-delegation via the session key (no wallet prompt) because the\n * caps are already part of the granted set.\n *\n * Duplicate `(service, path, action)` triples across resources and\n * delegations are merged and deduped — the session SIWE doesn't need\n * them repeated.\n */\nexport function manifestAbilitiesUnion(\n resolved: ResolvedCapabilities,\n): AbilitiesMap {\n const all: ResourceCapability[] = [...resolved.resources];\n for (const delegate of resolved.additionalDelegates) {\n for (const perm of delegate.permissions) {\n all.push(perm);\n }\n }\n return resourceCapabilitiesToAbilitiesMap(all);\n}\n","/**\n * Capability subset checking and recap parsing.\n *\n * This module powers the capability-chain delegation flow. The key decision\n * a `delegateTo` call has to make is: \"are the requested capabilities a\n * subset of what the current session already grants?\"\n *\n * - If yes → issue the delegation via the session-key UCAN path (no wallet prompt).\n * - If no → raise {@link PermissionNotInManifestError} so the caller can\n * trigger an escalation flow via `requestPermissions`.\n *\n * Canonical spec: `.claude/specs/capability-chain.md`.\n *\n * @packageDocumentation\n */\n\nimport {\n DEFAULT_MANIFEST_SPACE,\n type PermissionEntry,\n SERVICE_SHORT_TO_LONG,\n expandActionShortNames,\n} from \"./manifest\";\n\n// ---------------------------------------------------------------------------\n// Errors\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a `delegateTo` call requests capabilities that the current\n * session does not already grant. The caller can catch this and trigger\n * `requestPermissions(missing)` to show an escalation modal.\n */\nexport class PermissionNotInManifestError extends Error {\n public readonly missing: PermissionEntry[];\n public readonly granted: PermissionEntry[];\n\n constructor(missing: PermissionEntry[], granted: PermissionEntry[]) {\n super(\n `Requested capabilities exceed current session. Missing ${missing.length} entries.`\n );\n this.name = \"PermissionNotInManifestError\";\n this.missing = missing;\n this.granted = granted;\n }\n}\n\n/**\n * Thrown when the current session has expired (or will expire within the\n * safety margin). The caller should trigger a fresh sign-in.\n */\nexport class SessionExpiredError extends Error {\n public readonly expiredAt: Date;\n\n constructor(expiredAt: Date) {\n super(`Session expired at ${expiredAt.toISOString()}`);\n this.name = \"SessionExpiredError\";\n this.expiredAt = expiredAt;\n }\n}\n\n/**\n * Thrown when the installed WASM delegation boundary cannot encode every\n * constrained ReCap branch in a requested child delegation.\n */\nexport class CaveatedDelegationUnsupportedError extends Error {\n /** Stable machine-readable error code for fail-closed caveated delegation. */\n code = \"CAVEATED_DELEGATION_UNSUPPORTED\" as const;\n /** Requested entries whose constrained ReCap branches cannot be preserved. */\n entries: PermissionEntry[];\n\n constructor(entries: PermissionEntry[]) {\n super(\n \"Cannot create a caveated delegation because this WASM delegation boundary cannot preserve every ReCap caveat branch exactly.\"\n );\n this.name = \"CaveatedDelegationUnsupportedError\";\n this.entries = entries.map(cloneEntry);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Space normalization\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a space identifier to its short-name form.\n *\n * Recap resource URIs in a signed SIWE encode the space as a full URI of the\n * form `tinycloud:pkh:eip155:{chainId}:{address}:{name}` (e.g.\n * `tinycloud:pkh:eip155:1:0xd559...:default`). Manifest permissions and\n * backend-advertised permissions use the short `{name}` form (e.g.\n * `\"default\"`).\n *\n * Strict string comparison between these two forms would always fail, so we\n * normalize both sides to the short name before comparing. The trailing\n * segment after the last `:` in a `tinycloud:` URI is the space name.\n *\n * Short names (`\"default\"`, `\"work-space\"`) are returned unchanged.\n * Any non-`tinycloud:` string is returned unchanged. A malformed URI with a\n * trailing colon is returned unchanged (so the check degrades to a strict\n * mismatch rather than collapsing to an empty string).\n *\n * @internal\n */\nexport function normalizeSpace(space: string): string {\n if (!space.startsWith(\"tinycloud:\")) {\n return space;\n }\n const lastColon = space.lastIndexOf(\":\");\n if (lastColon === -1 || lastColon === space.length - 1) {\n return space;\n }\n return space.slice(lastColon + 1);\n}\n\n// ---------------------------------------------------------------------------\n// Subset check\n// ---------------------------------------------------------------------------\n\nexport interface SubsetCheckResult {\n /** True when every requested entry is covered by a granted entry. */\n subset: boolean;\n /** Entries the granted set does not cover (empty when `subset` is true). */\n missing: PermissionEntry[];\n}\n\n/**\n * Check whether `requested` is a strict subset of `granted`.\n *\n * Matching rules for each `requested[i]`:\n * - `service` matches exactly.\n * - `space` matches exactly.\n * - Path containment:\n * - If `granted.path` ends with `/`, it covers any `requested.path` that\n * starts with `granted.path`.\n * - Otherwise, the paths must match exactly.\n * - Action containment: every requested URN must be matched exactly or by a\n * service-scoped wildcard such as `tinycloud.kv/*`.\n *\n * Grant entries with the same service, space, path coverage, and caveats are\n * considered together. This matters because ReCap parsers may return one\n * entry per action even when the original manifest grouped those actions.\n * Any `requested` entry whose actions are not collectively covered is added\n * to `missing` and the overall result is non-subset.\n *\n * Both sides are expected to be in the canonical long-form shape (service\n * starts with `tinycloud.`, actions are full URNs). Use {@link parseRecapCapabilities}\n * or `expandActionShortNames` to normalize inputs first.\n */\nexport function isCapabilitySubset(\n requested: readonly PermissionEntry[],\n granted: readonly PermissionEntry[]\n): SubsetCheckResult {\n const missing: PermissionEntry[] = [];\n\n for (const req of requested) {\n const matchingScope = granted.filter((entry) =>\n canonicalizeEntryScopeMatches(req, entry)\n );\n const requestedActions = expandActionShortNames(\n req.service,\n req.actions\n );\n const grantedActions = matchingScope.flatMap((entry) =>\n expandActionShortNames(entry.service, entry.actions)\n );\n const covered = requestedActions.every((requestedAction) =>\n grantedActions.some((grantedAction) =>\n actionContains(grantedAction, requestedAction)\n )\n );\n if (!covered) {\n missing.push(cloneEntry(req));\n }\n }\n\n return { subset: missing.length === 0, missing };\n}\n\n/**\n * Returns true when `granted` fully covers `requested` — same service, same\n * space, path containment per spec, and action set containment.\n */\nfunction canonicalizeEntryScopeMatches(\n requested: PermissionEntry,\n granted: PermissionEntry\n): boolean {\n if (requested.service !== granted.service) {\n return false;\n }\n // Normalize both sides so callers passing short names (`\"default\"`) match\n // recap-parsed full URIs (`\"tinycloud:pkh:eip155:1:0xd559...:default\"`) and\n // vice versa. Idempotent for short names.\n if (\n normalizeSpace(requested.space ?? DEFAULT_MANIFEST_SPACE) !==\n normalizeSpace(granted.space ?? DEFAULT_MANIFEST_SPACE)\n ) {\n return false;\n }\n if (!pathContains(granted.path, requested.path)) {\n return false;\n }\n // There is no safe general partial ordering for arbitrary ReCap caveat\n // objects. A caveated authority therefore only covers the same caveat set;\n // treating it as uncaveated would authorize a broader child delegation.\n if (!recapCaveatsEqual(requested.caveats, granted.caveats)) {\n return false;\n }\n return true;\n}\n\n/**\n * Compare ReCap caveat branch sets without losing signed branch structure.\n *\n * ReCap represents an unconstrained ability as `[{}]`, while SDK callers can\n * represent that same authority as `undefined` or `[]`. Those three forms are\n * equivalent. Every other branch, including an unconstrained branch mixed\n * with a constrained one, remains part of the canonical comparison.\n */\nexport function recapCaveatsEqual(\n left: readonly Record<string, unknown>[] | undefined,\n right: readonly Record<string, unknown>[] | undefined,\n): boolean {\n return canonicalizeRecapCaveats(left) === canonicalizeRecapCaveats(right);\n}\n\n/**\n * Canonicalize a ReCap caveat branch set for exact equality checks.\n *\n * Object keys and branch order do not affect equality. A lone unconstrained\n * branch is normalized to the SDK's empty-caveat representation, but an\n * unconstrained branch in a mixed set is deliberately retained.\n */\nexport function canonicalizeRecapCaveats(\n caveats: readonly Record<string, unknown>[] | undefined,\n): string {\n const canonicalize = (value: unknown): string => {\n if (value === null || typeof value === \"boolean\" || typeof value === \"string\") {\n return JSON.stringify(value);\n }\n if (typeof value === \"number\") {\n if (Number.isFinite(value)) return JSON.stringify(value);\n throw new Error(\"ReCap caveats must contain only JSON values.\");\n }\n if (Array.isArray(value)) return `[${value.map(canonicalize).join(\",\")}]`;\n if (typeof value === \"object\") {\n const object = value as Record<string, unknown>;\n const prototype = Object.getPrototypeOf(object);\n if (prototype !== Object.prototype && prototype !== null) {\n throw new Error(\"ReCap caveats must contain only JSON values.\");\n }\n return `{${Object.keys(object).sort().map((key) =>\n `${JSON.stringify(key)}:${canonicalize(object[key])}`,\n ).join(\",\")}}`;\n }\n throw new Error(\"ReCap caveats must contain only JSON values.\");\n };\n const branches = (caveats ?? []).map(canonicalize).sort();\n if (branches.length === 0 || (branches.length === 1 && branches[0] === \"{}\")) {\n return \"[]\";\n }\n return `[${branches.join(\",\")}]`;\n}\n\n/** Return whether a granted action pattern covers one requested action. */\nexport function actionContains(grantedAction: string, requestedAction: string): boolean {\n if (grantedAction === \"*\" || grantedAction === requestedAction) return true;\n if (!grantedAction.endsWith(\"/*\")) return false;\n const service = grantedAction.slice(0, -2);\n return requestedAction === service || requestedAction.startsWith(`${service}/`);\n}\n\n/**\n * Path containment per spec:\n * - `granted.path` ends with `/` → prefix match (requested starts with granted)\n * - otherwise → exact string match\n *\n * The empty string is treated as \"no path constraint\" and matches anything.\n * The single-character `\"/\"` also matches anything (it is a trailing-slash\n * prefix of zero-length).\n */\nfunction pathContains(grantedPath: string, requestedPath: string): boolean {\n if (grantedPath === \"\" || grantedPath === \"/\") {\n return true;\n }\n if (grantedPath.endsWith(\"/\")) {\n return requestedPath.startsWith(grantedPath);\n }\n return requestedPath === grantedPath;\n}\n\nfunction cloneEntry(entry: PermissionEntry): PermissionEntry {\n return {\n service: entry.service,\n ...(entry.space !== undefined ? { space: entry.space } : {}),\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.caveats !== undefined\n ? { caveats: JSON.parse(JSON.stringify(entry.caveats)) as Record<string, unknown>[] }\n : {}),\n ...(entry.skipPrefix !== undefined ? { skipPrefix: entry.skipPrefix } : {}),\n ...(entry.expiry !== undefined ? { expiry: entry.expiry } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Recap parsing (WASM wrapper)\n// ---------------------------------------------------------------------------\n\n/**\n * The raw shape returned from the WASM `parseRecapFromSiwe` export. The\n * Rust layer encodes the service in the short form (e.g. `\"kv\"`) because\n * that is what the SIWE recap resource URI actually contains. We normalize\n * to the manifest long form (`\"tinycloud.kv\"`) in {@link parseRecapCapabilities}.\n *\n * @internal\n */\nexport interface WasmRecapEntry {\n service: string;\n space: string;\n path: string;\n actions: string[];\n /** Exact ReCap caveats shared by every action in this entry. */\n caveats?: Record<string, unknown>[];\n}\n\n/**\n * Signature of the WASM `parseRecapFromSiwe` export. Accepts the signed\n * SIWE message string and returns an array of raw recap entries. Throws if\n * the SIWE is malformed or the recap statement has been tampered.\n *\n * Exposed as an interface so the SDK can inject the web or node binding\n * without `capabilities.ts` needing to know which.\n */\nexport type ParseRecapFromSiwe = (siweString: string) => WasmRecapEntry[];\n\n/**\n * Parse a signed SIWE message into an array of {@link PermissionEntry}\n * objects in the canonical long-form manifest shape.\n *\n * This is a thin wrapper around the WASM `parseRecapFromSiwe` export that:\n * 1. Normalizes short-form services (`\"kv\"`) to long-form (`\"tinycloud.kv\"`).\n * 2. Returns entries in a deterministic order (sorted by space, then service,\n * then path) so downstream equality checks are stable.\n *\n * Returns an empty array when the SIWE has no recap resource (plain auth\n * SIWE); this matches the WASM function's behavior and the spec.\n *\n * @param parseWasm The WASM `parseRecapFromSiwe` binding.\n * @param siwe The signed SIWE message string (exactly what `session.siwe` stores).\n */\nexport function parseRecapCapabilities(\n parseWasm: ParseRecapFromSiwe,\n siwe: string\n): PermissionEntry[] {\n const raw = parseWasm(siwe);\n if (!Array.isArray(raw)) {\n throw new Error(\n \"parseRecapFromSiwe returned a non-array value; wasm binding may be out of sync\"\n );\n }\n const normalized: PermissionEntry[] = raw.map((entry) => {\n const longService =\n SERVICE_SHORT_TO_LONG[entry.service] ??\n // Unknown short names pass through. If the recap already contained a\n // long-form service (e.g. a future tinycloud-node version emits long\n // form directly), don't double-prefix.\n (entry.service.startsWith(\"tinycloud.\")\n ? entry.service\n : `tinycloud.${entry.service}`);\n return {\n service: longService,\n // The Rust layer emits the space as a full `tinycloud:pkh:...:name`\n // URI (the recap target URI). Normalize to the short name so the\n // returned entries match the shape manifests use.\n space: normalizeSpace(entry.space),\n path: entry.path,\n actions: [...entry.actions],\n ...(entry.caveats === undefined\n ? {}\n : { caveats: JSON.parse(JSON.stringify(entry.caveats)) as Record<string, unknown>[] }),\n };\n });\n\n // Sort for determinism (callers do equality checks on arrays of entries\n // in tests; deterministic ordering keeps those stable).\n normalized.sort((a, b) => {\n const aSpace = a.space ?? DEFAULT_MANIFEST_SPACE;\n const bSpace = b.space ?? DEFAULT_MANIFEST_SPACE;\n if (aSpace !== bSpace) return aSpace < bSpace ? -1 : 1;\n if (a.service !== b.service) return a.service < b.service ? -1 : 1;\n if (a.path !== b.path) return a.path < b.path ? -1 : 1;\n return 0;\n });\n\n return normalized;\n}\n","import {\n canonicalizeRecapCaveats,\n isCapabilitySubset,\n type PermissionEntry,\n} from \"@tinycloud/sdk-core\";\n\n/**\n * A capability in the canonical form persisted in a v1 request artifact.\n *\n * The SDK remains the semantic authority for containment. This module only\n * establishes deterministic exact identity for persistence and display.\n */\nexport type CanonicalPermissionEntry = Readonly<PermissionEntry>;\n\nexport interface AuthorityEvaluation {\n readonly satisfied: boolean;\n readonly missing: readonly CanonicalPermissionEntry[];\n}\n\n/**\n * Operation-owned space resolution. The SDK's general capability evaluator\n * intentionally preserves its historical short/full-space compatibility; the\n * operation kernel supplies this narrower resolver when a space identifies a\n * remote owner's resource.\n */\nexport type OperationSpaceResolver = (space: string) => string;\n\nconst EXACT_PERMISSION_KEYS = new Set([\n \"service\",\n \"space\",\n \"path\",\n \"actions\",\n \"skipPrefix\",\n \"expiry\",\n \"description\",\n]);\n\n/**\n * Validate requirements that can be represented without semantic broadening in\n * a v1 artifact. Caveated authority is intentionally rejected: the v1 request\n * schema and grant path cannot preserve those signed branches end-to-end.\n */\nexport function validateExactCapabilities(value: unknown): PermissionEntry[] | undefined {\n if (!Array.isArray(value)) return undefined;\n\n const permissions: PermissionEntry[] = [];\n for (const candidate of value) {\n if (!isExactPermission(candidate)) return undefined;\n permissions.push(candidate);\n }\n return canonicalizeCapabilities(permissions);\n}\n\n/**\n * Evaluates required authority with the SDK's public subset primitive.\n *\n * In particular, this intentionally does not implement matching rules or\n * manufacture wildcards. A wildcard can cover a requirement only when it was\n * already present in the supplied granted authority and the SDK says so.\n */\nexport function evaluateAuthority(\n granted: readonly PermissionEntry[],\n required: readonly PermissionEntry[],\n): AuthorityEvaluation {\n const canonicalGranted = canonicalizeCapabilities(granted);\n const canonicalRequired = canonicalizeCapabilities(required);\n const result = isCapabilitySubset(canonicalRequired, canonicalGranted);\n\n return {\n satisfied: result.subset,\n missing: canonicalizeCapabilities(result.missing),\n };\n}\n\n/**\n * Evaluate operation capabilities without allowing two owners' same-named\n * spaces to compare equal. Path and action containment still come from the\n * SDK primitive; only the operation space identity is made owner-exact.\n */\nexport function evaluateOperationAuthority(\n granted: readonly PermissionEntry[],\n required: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): AuthorityEvaluation {\n const canonicalGranted = canonicalizeOperationCapabilities(granted, resolveSpace);\n const canonicalRequired = canonicalizeOperationCapabilities(required, resolveSpace);\n const missing: CanonicalPermissionEntry[] = [];\n\n for (const requested of canonicalRequired) {\n const covered = canonicalGranted.some((candidate) =>\n candidate.service === requested.service &&\n operationSpacesEqual(candidate.space, requested.space) &&\n isCapabilitySubset([requested], [candidate]).subset,\n );\n if (!covered) missing.push(requested);\n }\n\n return {\n satisfied: missing.length === 0,\n missing: canonicalizeCapabilities(missing),\n };\n}\n\n/**\n * Whether a delegation's signed capabilities stay inside the stored request\n * it answers. `tinycloud.auth.import` and runtime replay share this check, so\n * a delegation import accepts is never one replay discards.\n */\nexport function delegationWithinRequest(\n requested: readonly PermissionEntry[],\n effective: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): boolean {\n return requested.length > 0 &&\n evaluateOperationAuthority(requested, canonicalizeCapabilities(effective), resolveSpace).satisfied;\n}\n\n/**\n * Validate a runtime hint as an exact member of the registered plan. Runtime\n * nodes may report the one phase that failed, but they may not introduce a\n * broader, differently-owned, caveated, or otherwise new capability.\n */\nexport function isExactCapabilityMemberSubset(\n hinted: readonly PermissionEntry[],\n planned: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): boolean {\n const canonicalHinted = canonicalizeOperationCapabilities(hinted, resolveSpace);\n const canonicalPlanned = canonicalizeOperationCapabilities(planned, resolveSpace);\n return canonicalHinted.length > 0 && canonicalHinted.every((hint) => canonicalPlanned.some((plan) =>\n JSON.stringify(hint) === JSON.stringify(plan),\n ));\n}\n\n/** Canonical identity used by operation preflight, hints, and artifacts. */\nexport function canonicalizeOperationCapabilities(\n permissions: readonly PermissionEntry[],\n resolveSpace?: OperationSpaceResolver,\n): CanonicalPermissionEntry[] {\n return canonicalizeCapabilities(permissions.map((permission) => {\n const canonicalPath = canonicalizeOperationPath(permission);\n if (permission.space === undefined || resolveSpace === undefined) {\n return canonicalPath;\n }\n try {\n return { ...canonicalPath, space: resolveSpace(permission.space) };\n } catch {\n // An unresolvable identity remains a non-matching literal. It must never\n // be widened to a wildcard or to another authenticated owner.\n return canonicalPath;\n }\n }));\n}\n\nfunction canonicalizeOperationPath(permission: PermissionEntry): PermissionEntry {\n if (permission.service !== \"tinycloud.encryption\") return permission;\n const match = permission.path.match(\n /^(urn:tinycloud:encryption:did:pkh:eip155:\\d+:)(0x[0-9a-fA-F]{40})(:.*)$/,\n );\n if (!match) return permission;\n return { ...permission, path: `${match[1]}${match[2]!.toLowerCase()}${match[3]}` };\n}\n\nfunction operationSpacesEqual(\n granted: string | undefined,\n requested: string | undefined,\n): boolean {\n return granted === requested;\n}\n\n/** Returns a stable exact identity for one capability without containment. */\nexport function permissionIdentity(permission: PermissionEntry): string {\n return JSON.stringify(canonicalizeCapability(permission));\n}\n\n/**\n * Sorts actions and entries and removes only exactly-identical capabilities.\n * It does not coalesce paths, services, spaces, or actions into a broader\n * capability.\n */\nexport function canonicalizeCapabilities(\n permissions: readonly PermissionEntry[],\n): CanonicalPermissionEntry[] {\n const byIdentity = new Map<string, CanonicalPermissionEntry>();\n for (const permission of permissions) {\n const canonical = canonicalizeCapability(permission);\n byIdentity.set(permissionIdentityFromCanonical(canonical), canonical);\n }\n\n return [...byIdentity.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([, permission]) => permission);\n}\n\nfunction canonicalizeCapability(permission: PermissionEntry): CanonicalPermissionEntry {\n const actions = [...new Set(permission.actions)].sort((left, right) => left.localeCompare(right));\n return {\n service: permission.service,\n ...(permission.space !== undefined ? { space: permission.space } : {}),\n path: permission.path,\n actions,\n ...(permission.skipPrefix !== undefined ? { skipPrefix: permission.skipPrefix } : {}),\n ...(permission.expiry !== undefined ? { expiry: permission.expiry } : {}),\n ...(permission.description !== undefined ? { description: permission.description } : {}),\n ...(permission.caveats === undefined\n ? {}\n : { caveats: canonicalizeCaveats(permission.caveats) }),\n };\n}\n\nfunction permissionIdentityFromCanonical(permission: CanonicalPermissionEntry): string {\n return JSON.stringify({\n service: permission.service,\n ...(permission.space !== undefined ? { space: permission.space } : {}),\n path: permission.path,\n actions: permission.actions,\n ...(permission.skipPrefix !== undefined ? { skipPrefix: permission.skipPrefix } : {}),\n ...(permission.expiry !== undefined ? { expiry: permission.expiry } : {}),\n ...(permission.description !== undefined ? { description: permission.description } : {}),\n ...(permission.caveats === undefined ? {} : { caveats: permission.caveats }),\n });\n}\n\nfunction isExactPermission(value: unknown): value is PermissionEntry {\n if (\n !isExactRecord(value) ||\n Object.getOwnPropertyNames(value).some((key) => !EXACT_PERMISSION_KEYS.has(key)) ||\n Object.getOwnPropertySymbols(value).length > 0\n ) {\n return false;\n }\n if (\n !isExactText(value.service) ||\n !isExactPath(value.path, value.actions) ||\n (value.space !== undefined && !isExactText(value.space)) ||\n !Array.isArray(value.actions) ||\n value.actions.length === 0 ||\n !value.actions.every(isExactText) ||\n new Set(value.actions).size !== value.actions.length ||\n value.skipPrefix === true ||\n (value.skipPrefix !== undefined && value.skipPrefix !== false) ||\n (value.expiry !== undefined && !isExactText(value.expiry)) ||\n (value.description !== undefined && !isExactText(value.description))\n ) {\n return false;\n }\n return !(\"caveats\" in value);\n}\n\nfunction isExactRecord(value: unknown): value is Record<string, unknown> {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) return false;\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isExactText(value: unknown): value is string {\n return typeof value === \"string\" && value.length > 0 && !value.includes(\"*\");\n}\n\nfunction isExactPath(value: unknown, actions: unknown): value is string {\n if (typeof value !== \"string\" || value === \"/\" || value.includes(\"*\")) return false;\n if (value === \"\" || value.endsWith(\"/\")) {\n return Array.isArray(actions) && actions.includes(\"tinycloud.kv/list\");\n }\n return true;\n}\n\nfunction canonicalizeCaveats(\n caveats: readonly Record<string, unknown>[],\n): Record<string, unknown>[] {\n return [...caveats]\n .map((caveat) => ({\n identity: canonicalizeRecapCaveats([caveat]),\n value: JSON.parse(JSON.stringify(caveat)) as Record<string, unknown>,\n }))\n .sort((left, right) => left.identity.localeCompare(right.identity))\n .map(({ value }) => value);\n}\n","import { randomUUID } from \"node:crypto\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport {\n chmod,\n link,\n mkdir,\n open,\n readFile,\n readdir,\n rename,\n rmdir,\n rm,\n stat,\n unlink,\n writeFile,\n} from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, join, relative, resolve } from \"node:path\";\n\nconst DEFAULT_PROFILE = \"default\";\nconst DEFAULT_LOCK_TIMEOUT_MS = 2_000;\nconst DEFAULT_LOCK_RETRY_MS = 25;\nconst DEFAULT_STALE_LOCK_MS = 30_000;\nconst TEST_LOCK_CONTENTION_SIGNAL_PATH = \"TC_TEST_PROFILE_LOCK_CONTENTION_SIGNAL_PATH\";\n// Profile state holds keys, sessions and delegations: owner-only access.\nconst PRIVATE_FILE_MODE = 0o600;\nconst PRIVATE_DIR_MODE = 0o700;\nconst TEST_LOCK_RECOVERY_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RECOVERY_BARRIER_DIR\";\nconst TEST_LOCK_OWNERLESS_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_OWNERLESS_BARRIER_DIR\";\nconst TEST_LOCK_PUBLISH_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_PUBLISH_BARRIER_DIR\";\nconst TEST_LOCK_RELEASE_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_RELEASE_BARRIER_DIR\";\nconst TEST_LOCK_CLAIM_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIM_BARRIER_DIR\";\nconst TEST_LOCK_CLAIMED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_CLAIMED_BARRIER_DIR\";\nconst TEST_LOCK_VERIFIED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_VERIFIED_BARRIER_DIR\";\nconst TEST_LOCK_MOVED_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_MOVED_BARRIER_DIR\";\nconst TEST_LOCK_TURN_PUBLISH_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_TURN_PUBLISH_BARRIER_DIR\";\nconst TEST_LOCK_TURN_SETTLE_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_TURN_SETTLE_BARRIER_DIR\";\nconst TEST_LOCK_TURN_COLLECT_BARRIER_DIR = \"TC_TEST_PROFILE_LOCK_TURN_COLLECT_BARRIER_DIR\";\n/** One acquisition of a profile lock; `active` is cleared before release. */\ninterface HeldProfileLock {\n readonly lockPath: string;\n /** The number of the turn this acquisition holds (see acquireTurn). */\n readonly turnSlot: number;\n /** A deletion of the profile ran while this acquisition waited for the lock. */\n readonly deletedWhileWaiting: boolean;\n active: boolean;\n}\n/**\n * Async context shared by every copy of this module in the process. Each\n * bundled operations entry point (`state`, `delegation-binding`, the root)\n * carries its own copy, so module-level context would make lock reentrancy and\n * the invocation state root stop at an entry-point boundary.\n * Each key is independently versioned for the shape and meaning of the\n * value it stores. Held-lock entries gained `turnSlot` and\n * `deletedWhileWaiting` in TC-633, so `.v2` must not read TC-602's `.v1`\n * entries. The invocation root remains a string path with unchanged\n * semantics and stays `.v1`. Relinquished turn tokens use a separate\n * versioned process-wide key: only an explicitly given-up turn of this PID\n * may be settled across separately bundled entry points.\n */\nfunction processWideContext<T>(key: string): AsyncLocalStorage<T> {\n const registry = globalThis as unknown as Record<symbol, AsyncLocalStorage<T> | undefined>;\n return registry[Symbol.for(key)] ??= new AsyncLocalStorage<T>();\n}\nconst invocationStateRoot = processWideContext<string>(\"tinycloud.operations.invocationStateRoot.v1\");\n/** Profile lock acquisitions held by the current async call chain (see withProfileLock). */\nconst heldProfileLocks = processWideContext<readonly HeldProfileLock[]>(\"tinycloud.operations.heldProfileLocks.v2\");\n\nexport type ProfileStoreName =\n | \"session\"\n | \"additional-delegations\"\n | \"auth-requests\";\n\nexport interface StoreMetadata {\n formatVersion: number;\n}\n\nexport interface ProfileStoreContents<T> {\n formatVersion: number;\n records: T[];\n}\n\nexport interface ProfileLockOptions {\n timeoutMs?: number;\n retryMs?: number;\n staleAfterMs?: number;\n}\n\nexport class ProfileLockTimeoutError extends Error {\n readonly code = \"PROFILE_LOCK_TIMEOUT\";\n\n /** `detail` names the cause when it is not simply another process holding the lock. */\n constructor(profile: string, timeoutMs: number, detail?: string) {\n super(`Timed out waiting for the profile lock for \"${profile}\" after ${timeoutMs}ms.${detail ? ` ${detail}` : \"\"}`);\n this.name = \"ProfileLockTimeoutError\";\n }\n}\n\n/**\n * A store write that waited for the profile lock while a deletion of that\n * profile held it, and found no profile settings once it got the lock. The\n * write is refused rather than recreating a profile with only a session or\n * store in it.\n */\nexport class ProfileDeletedError extends Error {\n readonly code = \"PROFILE_NOT_FOUND\";\n\n constructor(profile: string) {\n super(`Profile \"${profile}\" was deleted while this change waited for its lock, so the change was not written.`);\n this.name = \"ProfileDeletedError\";\n }\n}\n\n/**\n * The CLI's delegated-secret path treats TC_HOME as a home directory, rather\n * than a direct TinyCloud directory. Keep the shared store on that convention.\n */\nexport function tinycloudHomePath(): string {\n const home = invocationStateRoot.getStore() ??\n process.env.TC_HOME ??\n process.env.HOME ??\n process.env.USERPROFILE ??\n homedir();\n return join(home, \".tinycloud\");\n}\n\n/** Run one operation against an isolated TinyCloud home directory. */\nexport function withTinyCloudStateRoot<T>(\n stateRoot: string | undefined,\n action: () => Promise<T>,\n): Promise<T> {\n if (stateRoot === undefined) return action();\n if (!isAbsolute(stateRoot) || stateRoot.includes(\"\\0\")) {\n throw new TypeError(\"The TinyCloud state root must be an absolute path.\");\n }\n return invocationStateRoot.run(resolve(stateRoot), action);\n}\n\nexport function tinycloudConfigPath(): string {\n return join(tinycloudHomePath(), \"config.json\");\n}\n\nexport function profilesPath(): string {\n return join(tinycloudHomePath(), \"profiles\");\n}\n\nexport function profilePath(profile: string): string {\n return join(profilesPath(), validateProfileName(profile));\n}\n\nexport function profileConfigPath(profile: string): string {\n return join(profilePath(profile), \"profile.json\");\n}\n\nexport function sessionPath(profile: string): string {\n return profileStorePath(profile, \"session\");\n}\n\nexport function additionalDelegationsPath(profile: string): string {\n return profileStorePath(profile, \"additional-delegations\");\n}\n\nexport function authRequestsPath(profile: string): string {\n return profileStorePath(profile, \"auth-requests\");\n}\n\nexport function profileStorePath(profile: string, store: ProfileStoreName): string {\n return join(profilePath(profile), `${store}.json`);\n}\n\n/** The format record is deliberately separate from each legacy JSON payload. */\nexport function profileStoreMetadataPath(profile: string, store: ProfileStoreName): string {\n return `${profileStorePath(profile, store)}.metadata.json`;\n}\n\nexport function profileLockPath(profile: string): string {\n return join(profilePath(profile), \".lock\");\n}\n\nexport function profileLockMetadataPath(profile: string): string {\n return join(profileLockPath(profile), \"owner.json\");\n}\n\n/**\n * The turn lock of a profile (see acquireTurn). It lives outside the profile\n * directory, so neither a profile deletion nor an older release's cleanup\n * removes it.\n */\nexport function profileTurnLockPath(profile: string): string {\n return join(tinycloudHomePath(), \"profile-locks\", validateProfileName(profile));\n}\n\n/**\n * Reads JSON without hiding malformed or inaccessible files. A missing file is\n * the only condition represented as null.\n */\nexport async function readJson<T>(filePath: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(filePath, \"utf8\")) as T;\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return null;\n throw error;\n }\n}\n\n/**\n * Writes the same pretty-printed, trailing-newline JSON shape used by the CLI,\n * but publishes it with an atomic rename from the same directory.\n */\nexport async function writeJsonAtomic(filePath: string, value: unknown): Promise<void> {\n await replaceJson(filePath, value, false);\n}\n\n/**\n * writeJsonAtomic; `durable` also flushes the data to disk before the rename,\n * so a crash or power loss leaves the old record or the new one, never a\n * truncated one (for lock records, whose damage would stop every writer).\n */\nasync function replaceJson(filePath: string, value: unknown, durable: boolean): Promise<void> {\n const directory = dirname(filePath);\n const temporaryPath = join(\n directory,\n `.${basename(filePath)}.${process.pid}.${randomUUID()}.tmp`,\n );\n const contents = `${JSON.stringify(value, null, 2)}\\n`;\n\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n try {\n if (durable) await writeFileDurably(temporaryPath, contents);\n else await writeFile(temporaryPath, contents, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE });\n await rename(temporaryPath, filePath);\n } catch (error) {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n throw error;\n }\n}\n\n/** Creates a file (exclusively) and, when it has contents, flushes them to disk. */\nasync function writeFileDurably(path: string, contents: string): Promise<void> {\n const handle = await open(path, \"wx\", PRIVATE_FILE_MODE);\n try {\n if (contents) {\n await handle.writeFile(contents, \"utf8\");\n await handle.datasync();\n }\n } finally {\n await handle.close();\n }\n}\n\nexport async function readProfile<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n return readJson<T>(profileConfigPath(profile));\n}\n\nexport async function readSession<T extends object = Record<string, unknown>>(\n profile: string,\n): Promise<T | null> {\n await readStoreMetadata(profile, \"session\");\n return readJson<T>(sessionPath(profile));\n}\n\nexport async function readAdditionalDelegations<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"additional-delegations\")).records;\n}\n\nexport async function readAuthRequests<T = Record<string, unknown>>(\n profile: string,\n): Promise<T[]> {\n return (await readProfileStore<T>(profile, \"auth-requests\")).records;\n}\n\nexport async function readStoreMetadata(\n profile: string,\n store: ProfileStoreName,\n): Promise<StoreMetadata> {\n const metadata = await readJson<StoreMetadata>(profileStoreMetadataPath(profile, store));\n if (metadata === null) return { formatVersion: 1 };\n if (\n typeof metadata !== \"object\" ||\n metadata.formatVersion !== 1\n ) {\n throw new TypeError(`Unsupported store format for \"${store}\".`);\n }\n return metadata;\n}\n\nexport async function readProfileStore<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n): Promise<ProfileStoreContents<T>> {\n const [metadata, raw] = await Promise.all([\n readStoreMetadata(profile, store),\n readJson<unknown>(profileStorePath(profile, store)),\n ]);\n return {\n formatVersion: metadata.formatVersion,\n records: Array.isArray(raw) ? raw as T[] : [],\n };\n}\n\n/**\n * Runs a small critical section under the one lock shared by all profile\n * stores (see acquireProfileLock). Lock release verifies its ownership token\n * before removing the exact metadata instance it acquired.\n *\n * Reentrant within one async call chain: a critical section that already\n * holds a profile's lock (for example a login's compare-and-commit) can call\n * store writers that take the same lock without deadlocking. Ownership is\n * keyed by the resolved lock path (so switching state roots inside a lock\n * still takes the other root's lock) and lasts exactly as long as the\n * acquisition: it is revoked before release, so work deferred past the\n * critical section waits for the lock like any other caller.\n */\nexport async function withProfileLock<T>(\n profile: string,\n action: () => Promise<T>,\n options: ProfileLockOptions = {},\n): Promise<T> {\n const normalizedProfile = validateProfileName(profile);\n const lockPath = profileLockPath(normalizedProfile);\n const held = (heldProfileLocks.getStore() ?? []).filter((ownership) => ownership.active);\n if (held.some((ownership) => ownership.lockPath === lockPath)) return action();\n const acquired = await acquireProfileLock(normalizedProfile, options);\n const ownership: HeldProfileLock = {\n lockPath,\n turnSlot: acquired.turnSlot,\n deletedWhileWaiting: acquired.deletedWhileWaiting,\n active: true,\n };\n try {\n return await heldProfileLocks.run([...held, ownership], action);\n } finally {\n ownership.active = false;\n await acquired.release();\n // Taking the lock recreated the deleted profile's directory; remove it\n // again unless something was written into it (rmdir needs it empty).\n if (acquired.deletedWhileWaiting) await rmdir(profilePath(normalizedProfile)).catch(() => undefined);\n }\n}\n\n/**\n * Records, inside a profile deletion's critical section and after its files\n * are gone, that the profile was deleted, with the number of the deleting\n * turn. A store write whose wait overlapped that turn (it arrived before the\n * deletion released the lock, whether before or after this record) then\n * refuses (ProfileDeletedError) instead of recreating a profile with only a\n * session or store in it. The caller must hold the profile's lock.\n */\nexport async function recordProfileDeletion(profile: string): Promise<void> {\n const lockPath = profileLockPath(profile);\n const ownership = (heldProfileLocks.getStore() ?? []).find((candidate) => candidate.active && candidate.lockPath === lockPath);\n if (!ownership) throw new Error(`Recording the deletion of profile \"${profile}\" requires holding its lock.`);\n await replaceJson(join(profileTurnLockPath(profile), \"deleted.json\"), { slot: ownership.turnSlot }, true);\n}\n\n/**\n * Refuses a write that would recreate a profile deleted while this critical\n * section waited for the lock (ProfileDeletedError). Every writer of profile\n * state other than the profile settings themselves calls this inside its\n * critical section, before writing. The caller must hold the profile's lock.\n */\nexport async function refuseWriteToDeletedProfile(profile: string): Promise<void> {\n const lockPath = profileLockPath(profile);\n const ownership = (heldProfileLocks.getStore() ?? []).find((candidate) => candidate.active && candidate.lockPath === lockPath);\n if (!ownership?.deletedWhileWaiting) return;\n // Recreated meanwhile (or by this critical section): writing is fine.\n if (await exists(profileConfigPath(profile))) return;\n throw new ProfileDeletedError(profile);\n}\n\n/**\n * Appends a record or replaces the existing record with the supplied explicit\n * key. The extractor is supplied by the owning store so state.ts never needs\n * to know permission-request or delegation record shapes.\n */\nexport async function upsertProfileRecord<T>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n key: string,\n record: T,\n getKey: (candidate: T) => string | undefined,\n options: ProfileLockOptions = {},\n): Promise<T[]> {\n if (!key) throw new TypeError(\"A non-empty record key is required.\");\n\n return withProfileLock(profile, async () => {\n await refuseWriteToDeletedProfile(profile);\n const current = (await readProfileStore<T>(profile, store)).records;\n const next = current.filter((candidate) => getKey(candidate) !== key);\n next.push(record);\n await writeJsonAtomic(profileStorePath(profile, store), next);\n await writeFormatOneMetadata(profile, store);\n return next;\n }, options);\n}\n\n/**\n * Performs a typed read-modify-write of a legacy array store under the one\n * per-profile lock. The payload stays an array and the sibling metadata stays\n * format 1, so existing CLI readers keep their byte/layout contract.\n */\nexport async function updateProfileStore<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n options: ProfileLockOptions = {},\n): Promise<Result> {\n return withProfileLock(\n profile,\n () => updateProfileStoreWhileLocked(profile, store, update),\n options,\n );\n}\n\n/** The caller must already own this profile's lock. */\nasync function updateProfileStoreWhileLocked<T, Result>(\n profile: string,\n store: Exclude<ProfileStoreName, \"session\">,\n update: (\n records: readonly T[],\n ) => Promise<{ readonly records: readonly T[]; readonly result: Result }> | {\n readonly records: readonly T[];\n readonly result: Result;\n },\n): Promise<Result> {\n await refuseWriteToDeletedProfile(profile);\n const current = await readProfileStore<T>(profile, store);\n const next = await update(current.records);\n await writeJsonAtomic(profileStorePath(profile, store), next.records);\n await writeFormatOneMetadata(profile, store);\n return next.result;\n}\n\nexport async function writeSession<T extends object>(\n profile: string,\n session: T,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await refuseWriteToDeletedProfile(profile);\n await readStoreMetadata(profile, \"session\");\n await writeJsonAtomic(sessionPath(profile), session);\n await writeFormatOneMetadata(profile, \"session\");\n }, options);\n}\n\nexport async function removeSession(\n profile: string,\n options: ProfileLockOptions = {},\n): Promise<void> {\n await withProfileLock(profile, async () => {\n await readStoreMetadata(profile, \"session\");\n await rm(sessionPath(profile), { force: true });\n }, options);\n}\n\nasync function writeFormatOneMetadata(profile: string, store: ProfileStoreName): Promise<void> {\n await writeJsonAtomic(profileStoreMetadataPath(profile, store), { formatVersion: 1 });\n}\n\ninterface AcquiredProfileLock {\n readonly deletedWhileWaiting: boolean;\n readonly turnSlot: number;\n release(): Promise<void>;\n}\n\ninterface LockDeadline {\n readonly startedAt: number;\n readonly timeoutMs: number;\n readonly retryMs: number;\n}\n\n/**\n * Takes a profile's lock in two layers:\n *\n * 1. The turn lock (acquireTurn), used by this release and later ones. It\n * decides nothing from elapsed time, so a process paused anywhere, for\n * any length of time, cannot let a second holder in.\n * 2. Holding a turn, the `.lock` directory every older release uses\n * (publishProfileLock), so they keep excluding this release and it them.\n * Only one process of this release at a time acquires, recovers or\n * releases `.lock`.\n *\n * Also reports whether a profile deletion (recordProfileDeletion) ran while\n * this acquisition waited.\n */\nasync function acquireProfileLock(\n profile: string,\n options: ProfileLockOptions,\n): Promise<AcquiredProfileLock> {\n const deadline: LockDeadline = {\n startedAt: Date.now(),\n timeoutMs: options.timeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS,\n retryMs: options.retryMs ?? DEFAULT_LOCK_RETRY_MS,\n };\n const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_LOCK_MS;\n const lockPath = profileLockPath(profile);\n const turnDirectory = profileTurnLockPath(profile);\n\n const directory = profilePath(profile);\n await mkdir(directory, { recursive: true, mode: PRIVATE_DIR_MODE });\n await mkdir(dirname(turnDirectory), { recursive: true, mode: PRIVATE_DIR_MODE });\n // Every store write takes this lock: tighten the tree older releases\n // created 0775 before writing sessions or delegations into it.\n for (const path of [tinycloudHomePath(), profilesPath(), directory]) {\n await chmod(path, PRIVATE_DIR_MODE).catch((error: unknown) => {\n // A profile deletion can remove the profile directory right after the\n // mkdir above; the lock attempt below recreates it 0700.\n if (path !== directory || !isErrno(error, \"ENOENT\")) throw error;\n });\n }\n\n const { turn, firstWaitedTurn } = await acquireTurn(profile, turnDirectory, deadline);\n try {\n // An unreadable deletion record is ignored rather than failing every\n // acquisition; it is written durably, so only damage makes it so.\n const deletion = await readJson<{ slot?: unknown }>(join(turnDirectory, \"deleted.json\")).catch(() => null);\n const deletedWhileWaiting = typeof deletion?.slot === \"number\" && deletion.slot >= firstWaitedTurn;\n while (true) {\n const token = randomUUID();\n if (await publishProfileLock(profile, lockPath, token)) {\n return {\n deletedWhileWaiting,\n turnSlot: turn.slot,\n release: async () => {\n try {\n await releaseProfileLock(profile, lockPath, token);\n } finally {\n await releaseTurn(profile, turn);\n }\n },\n };\n }\n await signalTestLockContention(profile);\n if (await recoverStaleLock(profile, lockPath, staleAfterMs)) continue;\n await waitOrTimeOut(profile, deadline);\n }\n } catch (error) {\n await releaseTurn(profile, turn);\n throw error;\n }\n}\n\n/** Throws once the acquisition's time is up; `detail` explains a damaged turn lock. */\nfunction checkDeadline(profile: string, deadline: LockDeadline, detail?: string): void {\n if (Date.now() - deadline.startedAt >= deadline.timeoutMs) {\n throw new ProfileLockTimeoutError(profile, deadline.timeoutMs, detail);\n }\n}\n\n/** Sleeps one retry interval, or throws once the acquisition's time is up. */\nasync function waitOrTimeOut(profile: string, deadline: LockDeadline, detail?: string): Promise<void> {\n checkDeadline(profile, deadline, detail);\n await sleep(Math.min(deadline.retryMs, deadline.timeoutMs - (Date.now() - deadline.startedAt)));\n}\n\n/** One granted turn of the turn lock. */\ninterface Turn {\n readonly directory: string;\n readonly slot: number;\n readonly token: string;\n}\n\n/** A turn as recorded on disk. */\ninterface TurnState {\n readonly slot: number;\n readonly token: string;\n readonly pid: number;\n /** The token of the turn before it that its creator observed; null for turn 0. */\n readonly after: string | null;\n readonly held: boolean;\n readonly done: boolean;\n}\n\n/** Turn directory names: decimal turn numbers. */\nconst TURN = /^(?:0|[1-9][0-9]*)$/;\n\n/**\n * Tokens of turns this process published and then gave up (a marker write or\n * a read failed after publication): the positive evidence that lets this\n * process settle a turn of its own PID. A live process's turn that is not in\n * here is never settled by it, so a module instance that cannot see another\n * instance's set (another bundled copy of this package) waits rather than\n * settling a turn that copy still holds.\n *\n * One set per process, not per module instance: the package's entry points\n * (state, index, artifacts, cli-runtime; ESM and CJS) each bundle their own\n * copy of this module, and one process may load several. The key is\n * versioned so that a release changing what the set holds uses a new one.\n */\nconst relinquishedTurns: Set<string> =\n (globalThis as unknown as Record<symbol, Set<string> | undefined>)[Symbol.for(\"tinycloud.operations.relinquishedProfileTurns.v1\")] ??=\n new Set<string>();\n\n/** Marker writes are retried this many times, after these delays (ms), before giving up. */\nconst MARKER_RETRY_DELAYS_MS = [10, 50, 250];\n/** A given-up turn is settled in the background every second for up to five minutes. */\nconst SETTLE_RETRY_MS = 1_000;\nconst SETTLE_RETRIES = 300;\n\n/**\n * The turn lock: mutual exclusion among processes of this release that does\n * not depend on how long any of them is paused. It uses only `mkdir`,\n * `rename`, exclusive file creation, and a check that a holder's PID is gone\n * (as `.lock` recovery always has), so it needs no hard links.\n *\n * Turns are numbered directories in profileTurnLockPath(profile). Turn n has\n * `owner.json` ({ pid, createdAt, token, after }), where `after` is the token\n * of the turn n-1 its creator observed, and gains `<token>.held` when granted\n * and `<token>.done` when over. Markers name the turn's own token, so one\n * written late into a recreated directory of the same number means nothing.\n *\n * - A turn is published by renaming a fully written staging directory to\n * `n`, which fails while `n` exists (rename never replaces a non-empty\n * directory, and turn directories are never empty). The turn directory\n * itself is published the same way, with a granted, finished turn 0.\n * - A process takes turn n only after reading turn n-1 granted and done.\n * Once `n` is published it checks that turn n-1 is still the one it read\n * (same token, granted, done) and only then writes `held`; otherwise the\n * turn is void: it writes `done` alone and starts over.\n * - Turns are removed by renaming them aside: a void turn at any time, a\n * granted one only once no older turn is left and turn n+1 is granted and\n * done. The latest turn is never removed.\n *\n * Why at most one granted turn is ever not done: a removed number can be\n * published again, but turn n-1 is always removed before turn n, so a\n * process whose stale read led it to republish n finds n-1 gone or holding\n * another token, and its turn is void. A process that finds n-1 as it read\n * it published `n` for the first time, so it is the only process granted n,\n * and n-1 was done before. A turn is marked done only by its holder, or by\n * another process once the holder's PID is gone (or by its own process,\n * once it has explicitly given that turn up); a paused holder is alive.\n * A turn is voided only on definite evidence (the turn it follows is gone or\n * holds another token), never because a read failed. Nothing above is\n * decided from elapsed time.\n *\n * Returns the turn and the number of the first turn this acquisition waited\n * on: the latest turn when it first looked if that was not done yet, else\n * the next one.\n */\nasync function acquireTurn(\n profile: string,\n directory: string,\n deadline: LockDeadline,\n): Promise<{ turn: Turn; firstWaitedTurn: number }> {\n let firstWaitedTurn: number | undefined;\n // Why the turn lock looks damaged, as of the last look, for the timeout message.\n let problem: string | undefined;\n const damaged = (what: string) =>\n `The turn lock ${directory} ${what}, so no process can take it. ` +\n `If no tc or MCP process is using profile \"${profile}\", remove that directory.`;\n for (let first = true; ; first = false) {\n if (!first) checkDeadline(profile, deadline, problem);\n problem = undefined;\n const latest = await readLatestTurn(directory);\n if (latest === \"missing\") {\n await createTurnDirectory(directory);\n continue;\n }\n if (latest === \"empty\") {\n // Usually a listing that straddled a turn's publication and an older\n // turn's removal (readdir is not a snapshot); damaged if it persists.\n problem = damaged(\"has no turns\");\n await waitOrTimeOut(profile, deadline, problem);\n continue;\n }\n if (latest === null) continue;\n if (typeof latest === \"number\") {\n problem = damaged(`has an unreadable turn ${latest}`);\n await waitOrTimeOut(profile, deadline, problem);\n continue;\n }\n firstWaitedTurn ??= latest.done ? latest.slot + 1 : latest.slot;\n if (latest.held && latest.done) {\n const turn = await takeTurn(profile, directory, latest);\n if (turn !== null) return { turn, firstWaitedTurn };\n await waitOrTimeOut(profile, deadline);\n } else if (latest.done) {\n problem = damaged(`ends with turn ${latest.slot}, which was never granted`);\n await waitOrTimeOut(profile, deadline, problem);\n } else if (latest.pid === process.pid ? relinquishedTurns.has(latest.token) : !isProcessAlive(latest.pid)) {\n await waitForTestBarrier(TEST_LOCK_TURN_SETTLE_BARRIER_DIR, profile);\n await settleAbandonedTurn(directory, latest);\n relinquishedTurns.delete(latest.token);\n } else {\n await signalTestLockContention(profile);\n await waitOrTimeOut(profile, deadline);\n }\n }\n}\n\n/**\n * The highest-numbered turn: its state, its number if it is damaged, null if\n * it went away while being read.\n */\nasync function readLatestTurn(directory: string): Promise<TurnState | number | \"missing\" | \"empty\" | null> {\n let names: string[];\n try {\n names = await readdir(directory);\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return \"missing\";\n throw error;\n }\n const slots = names.filter((name) => TURN.test(name)).map(Number);\n if (slots.length === 0) return \"empty\";\n const slot = Math.max(...slots);\n const turn = await readTurn(directory, slot);\n return turn === \"damaged\" ? slot : turn;\n}\n\n/**\n * Turn `slot` as recorded now; null if it is gone; \"damaged\" if its owner\n * record is missing or unreadable (turns are renamed into place and aside\n * whole, with their owner record written durably). Any other read error is\n * thrown: it says nothing about the turn. `done` is read before `held`,\n * which every writer creates first, so a turn read as done is never misread\n * as void.\n */\nasync function readTurn(directory: string, slot: number): Promise<TurnState | \"damaged\" | null> {\n const path = join(directory, String(slot));\n let text: string;\n try {\n text = await readFile(join(path, \"owner.json\"), \"utf8\");\n } catch (error) {\n if (!isErrno(error, \"ENOENT\") && !isErrno(error, \"ENOTDIR\")) throw error;\n return await exists(path) ? \"damaged\" : null;\n }\n let owner: { pid?: unknown; token?: unknown; after?: unknown } | null;\n try {\n owner = JSON.parse(text) as typeof owner;\n } catch {\n return \"damaged\";\n }\n if (owner === null || typeof owner !== \"object\" || typeof owner.token !== \"string\" || typeof owner.pid !== \"number\") {\n return \"damaged\";\n }\n const done = await exists(join(path, `${owner.token}.done`));\n const held = await exists(join(path, `${owner.token}.held`));\n return {\n slot,\n token: owner.token,\n pid: owner.pid,\n after: typeof owner.after === \"string\" ? owner.after : null,\n held,\n done,\n };\n}\n\n/**\n * Whether turn `slot` is still the granted, finished turn with `token`. False\n * only on definite evidence: it is gone, or holds another token. Throws if it\n * cannot be read, or is damaged, so the caller leaves its own turn undecided.\n */\nasync function isFinishedTurn(directory: string, slot: number, token: string): Promise<boolean> {\n const turn = await readTurn(directory, slot);\n if (turn === \"damaged\") throw new Error(`Turn ${slot} of the turn lock ${directory} is unreadable.`);\n return turn !== null && turn.token === token && turn.held && turn.done;\n}\n\n/**\n * Publishes the turn after `latest` and claims it. Null if another process\n * published that number first, or the turn is void. If a step after\n * publication fails, the turn is handed to settleOwnTurn and the error thrown.\n */\nasync function takeTurn(profile: string, directory: string, latest: TurnState): Promise<Turn | null> {\n const token = randomUUID();\n const slot = latest.slot + 1;\n const path = join(directory, String(slot));\n const owner = { pid: process.pid, createdAt: new Date().toISOString(), token, after: latest.token };\n let published = false;\n try {\n published = await publishDirectory(\n join(directory, `.stage-${process.pid}-${token}`),\n path,\n { \"owner.json\": `${JSON.stringify(owner, null, 2)}\\n` },\n () => waitForTestBarrier(TEST_LOCK_TURN_PUBLISH_BARRIER_DIR, profile),\n );\n if (published && await isFinishedTurn(directory, latest.slot, latest.token) && await markTurn(path, token, \"held\")) {\n return { directory, slot, token };\n }\n if (published) await markTurn(path, token, \"done\");\n return null;\n } catch (error) {\n if (published) settleOwnTurn(directory, slot, token);\n throw error;\n }\n}\n\n/**\n * Finishes the turn of a process that is gone (or one this process gave up).\n * A turn its creator never got to grant is granted first if the turn before\n * it is still the one its creator read (the check the creator would have\n * made; that turn cannot have been removed, since that needs this turn\n * done), and left void only if it definitely is not.\n */\nasync function settleAbandonedTurn(directory: string, abandoned: TurnState): Promise<void> {\n const path = join(directory, String(abandoned.slot));\n if (!abandoned.held && abandoned.after !== null &&\n await isFinishedTurn(directory, abandoned.slot - 1, abandoned.after)) {\n await markTurn(path, abandoned.token, \"held\");\n }\n await markTurn(path, abandoned.token, \"done\");\n}\n\n/**\n * Settles a turn this process published but could not grant or finish (a\n * marker write or a read failed). Other processes wait on this live PID, so\n * it is retried in the background until it succeeds or the turn is settled\n * otherwise (by this process's next acquisition, or by another process once\n * this one has exited). The timer does not keep the process alive.\n */\nfunction settleOwnTurn(directory: string, slot: number, token: string, retries = SETTLE_RETRIES): void {\n relinquishedTurns.add(token);\n void (async () => {\n try {\n const turn = await readTurn(directory, slot);\n if (turn !== null && turn !== \"damaged\" && turn.token === token && !turn.done) await settleAbandonedTurn(directory, turn);\n relinquishedTurns.delete(token);\n } catch {\n // Still relinquished: this process's next acquisition settles it too.\n if (retries > 0) setTimeout(() => settleOwnTurn(directory, slot, token, retries - 1), SETTLE_RETRY_MS).unref();\n }\n })();\n}\n\n/**\n * Creates `<token>.<kind>` in a turn directory; false if the directory is\n * gone. Other failures (a full disk, a permission error) are retried a few\n * times, then thrown.\n */\nasync function markTurn(path: string, token: string, kind: \"held\" | \"done\"): Promise<boolean> {\n for (let attempt = 0; ; attempt++) {\n try {\n await writeFile(join(path, `${token}.${kind}`), \"\", { mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n return true;\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) return true;\n if (isErrno(error, \"ENOENT\")) return false;\n if (attempt >= MARKER_RETRY_DELAYS_MS.length) throw error;\n await sleep(MARKER_RETRY_DELAYS_MS[attempt]!);\n }\n }\n}\n\n/** Creates the turn directory with a granted, finished turn 0, unless another process did. */\nasync function createTurnDirectory(directory: string): Promise<void> {\n const token = randomUUID();\n const owner = { pid: process.pid, createdAt: new Date().toISOString(), token, after: null };\n await publishDirectory(join(dirname(directory), `.stage-${basename(directory)}-${process.pid}-${token}`), directory, {\n \"0/owner.json\": `${JSON.stringify(owner, null, 2)}\\n`,\n [`0/${token}.held`]: \"\",\n [`0/${token}.done`]: \"\",\n });\n}\n\n/**\n * Writes `files` (durably, when not empty) into a new staging directory and\n * renames it to `target`. False if `target` already exists (or the turn\n * directory is gone); the caller starts over. Nothing else removes a live\n * process's staging directory (see collectTurns), so `target` never appears\n * incomplete.\n */\nasync function publishDirectory(\n stage: string,\n target: string,\n files: Readonly<Record<string, string>>,\n beforeRename?: () => Promise<void>,\n): Promise<boolean> {\n try {\n for (const [name, contents] of Object.entries(files)) {\n await mkdir(dirname(join(stage, name)), { recursive: true, mode: PRIVATE_DIR_MODE });\n await writeFileDurably(join(stage, name), contents);\n }\n await beforeRename?.();\n await rename(stage, target);\n return true;\n } catch (error) {\n // ENOTEMPTY/EEXIST: `target` exists, though it may be gone again by now.\n // Windows reports an existing target directory as EPERM.\n if ([\"ENOTEMPTY\", \"EEXIST\", \"ENOENT\"].some((code) => isErrno(error, code)) || await exists(target)) return false;\n throw error;\n } finally {\n await rm(stage, { recursive: true, force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Finishes this process's turn. If `done` cannot be written even after\n * retries, the critical section is still over and its writes stand: the turn\n * is handed to settleOwnTurn rather than failing the caller.\n */\nasync function releaseTurn(profile: string, turn: Turn): Promise<void> {\n try {\n await markTurn(join(turn.directory, String(turn.slot)), turn.token, \"done\");\n } catch {\n settleOwnTurn(turn.directory, turn.slot, turn.token);\n return;\n }\n await collectTurns(profile, turn).catch(() => undefined);\n}\n\n/**\n * Leftovers in a turn directory whose writer is gone: trash (a removed turn\n * renamed aside, garbage from then on), and staging directories and\n * deletion-record temporaries named after their writer's PID.\n */\nfunction isAbandonedTurnEntry(name: string): boolean {\n if (name.startsWith(\".trash-\")) return true;\n const writer = /^\\.(?:stage|deleted\\.json)[-.]([0-9]+)[-.]/.exec(name)?.[1];\n return writer !== undefined && !isProcessAlive(Number(writer));\n}\n\n/** A turn directory's staging directory left beside it by a process that is gone. */\nfunction isAbandonedTurnDirectoryStage(name: string): boolean {\n const writer = /^\\.stage-.+-([0-9]+)-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.exec(name)?.[1];\n return writer !== undefined && !isProcessAlive(Number(writer));\n}\n\n/**\n * Removes the turns before `turn`, which just finished: void turns, and\n * granted ones oldest first, each once no older turn is left and the next is\n * granted and done (see acquireTurn). A turn is renamed aside before it is\n * deleted, so its number is never an empty directory. A turn that is damaged\n * or cannot be read is left alone, and a read error stops the collection.\n * Leftovers of crashed processes go too, judged by PID, never by age:\n * removing a paused process's staging directory could publish an incomplete\n * turn.\n */\nasync function collectTurns(profile: string, turn: Turn): Promise<void> {\n for (const [directory, abandoned] of [\n [turn.directory, isAbandonedTurnEntry],\n [dirname(turn.directory), isAbandonedTurnDirectoryStage],\n ] as const) {\n for (const name of (await readdir(directory)).filter(abandoned)) {\n await rm(join(directory, name), { recursive: true, force: true }).catch(() => undefined);\n }\n }\n const older = (await readdir(turn.directory)).filter((name) => TURN.test(name)).map(Number)\n .filter((slot) => slot < turn.slot)\n .sort((left, right) => left - right);\n for (const slot of older) {\n const candidate = await readTurn(turn.directory, slot);\n if (candidate === null || candidate === \"damaged\" || !candidate.done) continue;\n if (candidate.held) {\n if (await exists(join(turn.directory, String(slot - 1)))) continue;\n const next = await readTurn(turn.directory, slot + 1);\n if (next === null || next === \"damaged\" || !next.held || !next.done) continue;\n }\n await waitForTestBarrier(TEST_LOCK_TURN_COLLECT_BARRIER_DIR, profile);\n const trash = join(turn.directory, `.trash-${randomUUID()}`);\n if (await rename(join(turn.directory, String(slot)), trash).then(() => true, () => false)) {\n await rm(trash, { recursive: true, force: true }).catch(() => undefined);\n }\n }\n}\n\n/**\n * One attempt to take `.lock`, made while holding a turn. Nothing here\n * replaces an existing path:\n *\n * 1. `mkdir(.lock)` (never recursive) is exclusive, and is the primitive\n * older releases use too, so they exclude each other.\n * 2. Ownership is published by `link`ing a fully written owner file (staged\n * beside `.lock`, on the same filesystem) to `.lock/owner.json`. `link`\n * fails with EEXIST if an owner exists, and with ENOENT if the directory\n * was removed meanwhile (an aged ownerless-lock reclaim by an older\n * release). Only a successful link holds the lock; on either failure\n * nothing is released, since nothing was acquired, and the caller retries.\n * On a filesystem without hard links (FAT/exFAT, some SMB mounts) the\n * owner file is created in place with an exclusive create instead, with\n * the same EEXIST/ENOENT outcomes. A reader may then briefly see it\n * incomplete; every release treats an owner file it cannot parse as a\n * held lock, and this release removes one only once it has aged (see\n * isAbandonedOwner).\n *\n * A profile deletion removes the profile directory once it has released its\n * lock, so `mkdir(.lock)` can see ENOENT; the directory is recreated (0700)\n * and the caller retries within its deadline, as for a first write to a new\n * profile.\n *\n * Only one owner.json can exist at the lock path, and it is removed only by\n * its holder's release or by recovery of an abandoned holder, so at most one\n * process holds the lock. Returns false when the lock is not acquired.\n */\nasync function publishProfileLock(profile: string, lockPath: string, token: string): Promise<boolean> {\n try {\n await mkdir(lockPath, { mode: PRIVATE_DIR_MODE });\n } catch (error) {\n if (isErrno(error, \"EEXIST\")) return false;\n if (!isErrno(error, \"ENOENT\")) throw error;\n await mkdir(profilePath(profile), { recursive: true, mode: PRIVATE_DIR_MODE });\n return false;\n }\n await waitForTestBarrier(TEST_LOCK_PUBLISH_BARRIER_DIR, profile);\n const ownerPath = join(lockPath, \"owner.json\");\n const staged = join(dirname(lockPath), `.lock-owner-${token}.tmp`);\n const owner = `${JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString(), token }, null, 2)}\\n`;\n try {\n await writeFile(staged, owner, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n try {\n await link(staged, ownerPath);\n } catch (error) {\n if (!lacksHardLinks(error)) throw error;\n await writeFile(ownerPath, owner, { encoding: \"utf8\", mode: PRIVATE_FILE_MODE, flag: \"wx\" });\n }\n return true;\n } catch (error) {\n if (isErrno(error, \"EEXIST\") || isErrno(error, \"ENOENT\")) return false;\n // Do not leave an ownerless lock behind for others to wait out. rmdir\n // removes only an empty directory; anyone who created it after ours went\n // away sees ENOENT on link and retries.\n await rmdir(lockPath).catch(() => undefined);\n throw error;\n } finally {\n await rm(staged, { force: true }).catch(() => undefined);\n }\n}\n\n/**\n * Errors `link` reports on a filesystem without hard links: EPERM (Linux\n * FAT/exFAT), ENOTSUP/EOPNOTSUPP (macOS FAT/exFAT, SMB), ENOSYS, EISDIR\n * (libuv's code for Windows' ERROR_INVALID_FUNCTION on FAT32), and EMLINK\n * (too many links), for which the same fallback is just as correct.\n */\nconst NO_HARD_LINK_ERRORS = new Set([\"EPERM\", \"ENOTSUP\", \"EOPNOTSUPP\", \"ENOSYS\", \"EISDIR\", \"EMLINK\"]);\n\nfunction lacksHardLinks(error: unknown): boolean {\n return typeof error === \"object\" && error !== null && \"code\" in error &&\n NO_HARD_LINK_ERRORS.has(String((error as { code?: unknown }).code));\n}\n\n/**\n * Releases the lock if `.lock/owner.json` is still this acquisition's. No\n * one else can put an owner record there while it exists (owners are only\n * published into a directory their own `mkdir` created, and recovery\n * removes only an abandoned holder's record), so the record read is the one\n * unlinked.\n */\nasync function releaseProfileLock(profile: string, lockPath: string, token: string): Promise<void> {\n const ownerPath = join(lockPath, \"owner.json\");\n const owner = await readJson<{ token?: unknown }>(ownerPath).catch(() => null);\n if (owner?.token !== token) return;\n await rm(ownerPath, { force: true });\n // `.lock` is empty from here until the rmdir; a contender that removes it\n // and creates its own meanwhile is not harmed: rmdir fails on its owner\n // file, or the contender's link sees ENOENT and retries. If a recoverer's\n // claim file is still inside, rmdir fails and that recoverer removes the\n // directory when it drops its claim.\n await waitForTestBarrier(TEST_LOCK_RELEASE_BARRIER_DIR, profile);\n await rmdir(lockPath).catch(() => undefined);\n}\n\n/**\n * Gives process-level contention tests an event-driven witness that a second\n * writer has reached an already-held lock. This deliberately has no effect\n * outside an explicit test environment, and only creates a new signal file\n * beneath TC_HOME; it never changes lock acquisition or release semantics.\n */\nasync function signalTestLockContention(profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n\n const configuredPath = process.env[TEST_LOCK_CONTENTION_SIGNAL_PATH];\n const configuredHome = process.env.TC_HOME;\n if (!configuredPath || !configuredHome) return;\n\n const home = resolve(configuredHome);\n const signalPath = resolve(configuredPath);\n const pathFromHome = relative(home, signalPath);\n if (\n pathFromHome === \"\" ||\n pathFromHome === \"..\" ||\n pathFromHome.startsWith(`..${process.platform === \"win32\" ? \"\\\\\" : \"/\"}`) ||\n pathFromHome.startsWith(\"../\") ||\n pathFromHome.startsWith(\"..\\\\\")\n ) {\n return;\n }\n\n await writeFile(signalPath, `${profile}\\n`, { encoding: \"utf8\", flag: \"wx\" })\n .catch(() => undefined);\n}\n\n/**\n * Reclaims a `.lock` with no owner record, while holding a turn. A\n * `.recover-*` claim may contain a live legacy owner's record moved by a\n * no-hard-links recoverer just before it crashed. Restore that record first;\n * only orphaned claims holding dead or malformed records can be removed.\n * Otherwise the directory must be older than the stale threshold: one left\n * by a crash, or by an older release, whose `.stale-*` / `.release-*` claim\n * files are removed too. owner.json is never unlinked, and the directory\n * itself is only `rmdir`ed, which succeeds only while it is empty.\n *\n * Holding a turn, no other process of this release reclaims concurrently.\n * That closes the race in which two reclaimers' age checks let the later one\n * remove a directory a 1.0.0-beta.16-or-older release had just created,\n * which holds the lock from its `mkdir`. That race remains only if a\n * 1.0.0-beta.17 … 1.0.1-beta.4 release reclaims at the same moment, or the\n * older process stays paused between its `mkdir` and its owner write for\n * longer than the stale threshold; recoveryFenced bounds how stale this\n * process's age check can be.\n */\nasync function recoverOwnerlessLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n let aged: boolean;\n try {\n const { mtimeMs } = await stat(lockPath);\n aged = Date.now() - mtimeMs >= staleAfterMs;\n } catch {\n return false;\n }\n const checkedAt = performance.now();\n await waitForTestBarrier(TEST_LOCK_OWNERLESS_BARRIER_DIR, profile);\n try {\n const entries = await readdir(lockPath);\n if (entries.includes(\"owner.json\")) return false;\n const onlyOrphanedClaims = entries.length > 0 && entries.every((entry) => RECOVERY_CLAIM.test(entry));\n if (!onlyOrphanedClaims && (!aged || recoveryFenced(checkedAt, staleAfterMs))) return false;\n for (const name of entries.filter((entry) => RECOVERY_CLAIM.test(entry))) {\n const claimPath = join(lockPath, name);\n if (await hasLiveRecoveryOwner(claimPath)) {\n // The claim keeps the directory in place. Only the original mkdir\n // owner could publish owner.json here; never overwrite that record.\n if (!await exists(join(lockPath, \"owner.json\"))) await rename(claimPath, join(lockPath, \"owner.json\"));\n return false;\n }\n await rm(claimPath, { force: true });\n }\n for (const name of entries.filter((entry) => ABANDONED_CLAIM.test(entry))) {\n await rm(join(lockPath, name), { force: true });\n }\n await rmdir(lockPath);\n } catch {\n return false;\n }\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/** A no-hard-links claim may contain a live owner's record moved before a crash. */\nasync function hasLiveRecoveryOwner(claimPath: string): Promise<boolean> {\n let contents: string;\n try {\n contents = await readFile(claimPath, \"utf8\");\n } catch (error) {\n if (isErrno(error, \"ENOENT\")) return false;\n throw error; // An I/O failure is not evidence that the holder is gone.\n }\n let owner: unknown;\n try {\n owner = JSON.parse(contents);\n } catch {\n return false;\n }\n const pid = typeof owner === \"object\" && owner !== null && \"pid\" in owner ? owner.pid : null;\n return typeof pid === \"number\" && Number.isInteger(pid) && pid > 0 && isProcessAlive(pid);\n}\n\n/**\n * Whether an ownerless-reclaim decision made at `since` (a monotonic\n * timestamp) is too old to act on. The age check is only good for a while:\n * past half the stale threshold the reclaim starts over.\n */\nfunction recoveryFenced(since: number, staleAfterMs: number): boolean {\n return performance.now() - since >= staleAfterMs / 2;\n}\n\n/** Claim files of older releases' stale recovery and (up to 1.0.0-beta.16) release. */\nconst ABANDONED_CLAIM = /^\\.(?:release|stale)-[0-9a-f-]+\\.json$/;\n/**\n * Claim files of this release's stale recovery. Releases up to 1.0.1-beta.4\n * never remove them, which is what makes this recovery safe against them; a\n * recoverer that crashes leaves one that only a release with the turn lock\n * clears (see REFERENCE.md, \"Profile lock\").\n */\nconst RECOVERY_CLAIM = /^\\.recover-[0-9a-f-]+\\.json$/;\n/** Owner files staged by publishProfileLock beside `.lock`. */\nconst STAGED_OWNER = /^\\.lock-owner-[0-9a-f-]+\\.tmp$/;\n/**\n * Removes owner files a crashed acquirer staged beside `.lock`. A live\n * acquirer's staged file exists only for its write and link, so only files\n * older than the stale threshold are removed. Runs after a recovery, never\n * on the uncontended path.\n */\nasync function removeAgedOwnerFiles(lockPath: string, staleAfterMs: number): Promise<void> {\n const directory = dirname(lockPath);\n const names = await readdir(directory).catch(() => []);\n for (const name of names.filter((entry) => STAGED_OWNER.test(entry))) {\n const path = join(directory, name);\n const aged = await stat(path).then(({ mtimeMs }) => Date.now() - mtimeMs >= staleAfterMs, () => false);\n if (aged) await rm(path, { force: true }).catch(() => undefined);\n }\n}\n\n\n/**\n * Removes an abandoned owner record (see isAbandonedOwner) and its `.lock`,\n * while holding a turn, so no other process of this release recovers,\n * reclaims or acquires `.lock` meanwhile.\n *\n * The record is claimed first: hard-linked to `.recover-<uuid>.json`, and the\n * claim compared with the exact bytes read. A record that replaced the\n * observed one is left in place. While the claim is inside `.lock`, the\n * directory cannot be removed and recreated: older releases never remove a\n * `.recover-*` claim (they clean up only their own claim names), and other\n * processes of this release are kept out by the turn lock. So no other owner\n * record can appear in it, and the unlink of owner.json can only remove the\n * claimed, dead record. A live record is never unlinked or moved. `.lock`\n * itself is removed only after this process's claim is unlinked from it,\n * which proves it is still the dead holder's directory; if the claim cannot\n * be made, `.lock` is left alone (it may be a directory a 1.0.0-beta.16-or-\n * older writer just created, which it holds from that `mkdir`). No step\n * depends on how long this process takes.\n *\n * Without hard links (where 1.0.0-beta.17 … 1.0.1-beta.4 cannot run at all),\n * the record is renamed into the claim instead, and put back if it is not\n * the observed one: one a 1.0.0-beta.16-or-older process published after\n * recovering the same lock at the same moment. The claim keeps `.lock` held\n * meanwhile, so no second writer gets in, but if that holder releases\n * between the two renames, the put-back restores a released record, and\n * `.lock` stays held until that PID is gone and the record is 30 s old.\n */\nasync function recoverStaleLock(profile: string, lockPath: string, staleAfterMs: number): Promise<boolean> {\n const ownerPath = join(lockPath, \"owner.json\");\n const observed = await readFile(ownerPath, \"utf8\").catch(() => null);\n if (observed === null) return recoverOwnerlessLock(profile, lockPath, staleAfterMs);\n if (!await isAbandonedOwner(observed, ownerPath, staleAfterMs)) return false;\n\n await waitForTestBarrier(TEST_LOCK_RECOVERY_BARRIER_DIR, profile);\n const claimPath = join(lockPath, `.recover-${randomUUID()}.json`);\n let moved = false;\n try {\n await link(ownerPath, claimPath);\n } catch (error) {\n // Gone or replaced already (another release recovered it): leave `.lock`.\n if (!lacksHardLinks(error)) return false;\n try {\n await rename(ownerPath, claimPath);\n moved = true;\n } catch {\n return false;\n }\n }\n if (moved) await waitForTestBarrier(TEST_LOCK_MOVED_BARRIER_DIR, profile);\n const sameInstance = await readFile(claimPath, \"utf8\").then((claimed) => claimed === observed, () => false);\n await waitForTestBarrier(TEST_LOCK_VERIFIED_BARRIER_DIR, profile);\n if (!sameInstance) {\n if (moved) {\n // No other owner.json can have appeared meanwhile: only a process\n // whose mkdir created this `.lock` publishes one, and it already has.\n await rename(claimPath, ownerPath).catch(() => undefined);\n } else {\n await unlink(claimPath).catch(() => undefined);\n }\n return false;\n }\n if (!moved) await rm(ownerPath, { force: true });\n await waitForTestBarrier(TEST_LOCK_CLAIM_BARRIER_DIR, profile);\n await waitForTestBarrier(TEST_LOCK_CLAIMED_BARRIER_DIR, profile);\n // Drop any claim a crashed recoverer of this release left, then this one.\n for (const name of (await readdir(lockPath).catch(() => [])).filter((entry) => RECOVERY_CLAIM.test(entry))) {\n if (join(lockPath, name) !== claimPath) await rm(join(lockPath, name), { force: true });\n }\n // Empty now, and still the dead holder's directory if this claim was in\n // it: remove it rather than leave an ownerless lock to age out.\n if (await unlink(claimPath).then(() => true, () => false)) await rmdir(lockPath).catch(() => undefined);\n await removeAgedOwnerFiles(lockPath, staleAfterMs);\n return true;\n}\n\n/**\n * Whether an owner record (its exact bytes, read from `ownerPath`) is\n * abandoned: a holder's whose PID is gone, older than the stale threshold;\n * or, if it is not a complete record, a file older than that threshold.\n * Every release publishes complete records (by link or rename) except this\n * one's no-hard-link fallback, which writes in place while holding a turn;\n * so an incomplete record seen while holding a turn was left by a crash.\n */\nasync function isAbandonedOwner(observed: string, ownerPath: string, staleAfterMs: number): Promise<boolean> {\n let owner: unknown;\n try {\n owner = JSON.parse(observed);\n } catch {\n owner = null;\n }\n if (typeof owner === \"object\" && owner !== null && \"pid\" in owner) {\n return isStaleOwner(owner as { pid?: unknown; createdAt?: unknown }, staleAfterMs);\n }\n const modifiedAt = await stat(ownerPath).then(({ mtimeMs }) => mtimeMs, () => Number.NaN);\n return Date.now() - modifiedAt >= staleAfterMs;\n}\n\n/** Test-only rendezvous (NODE_ENV=test and the named barrier directory set). */\nasync function waitForTestBarrier(environmentName: string, profile: string): Promise<void> {\n if (process.env.NODE_ENV !== \"test\") return;\n const barrierDirectory = process.env[environmentName];\n if (!barrierDirectory) return;\n await mkdir(barrierDirectory, { recursive: true });\n const readyPath = join(barrierDirectory, `ready-${process.pid}-${profile}`);\n await writeFile(readyPath, \"ready\\n\", { encoding: \"utf8\", flag: \"wx\" }).catch(() => undefined);\n const releasePath = join(barrierDirectory, \"release\");\n while (true) {\n try {\n await readFile(releasePath, \"utf8\");\n return;\n } catch {\n await sleep(1);\n }\n }\n}\n\nfunction isStaleOwner(\n owner: { pid?: unknown; createdAt?: unknown } | null,\n staleAfterMs: number,\n): boolean {\n const now = Date.now();\n const createdAt = typeof owner?.createdAt === \"string\"\n ? Date.parse(owner.createdAt)\n : Number.NaN;\n\n // Reclaim only a fully published lock whose owner PID is confirmed dead.\n // A lock without an owner record is handled by recoverOwnerlessLock: a\n // process holds the lock only once its owner.json is linked in.\n if (!Number.isFinite(createdAt) || now - createdAt < staleAfterMs) return false;\n if (typeof owner?.pid !== \"number\" || !Number.isInteger(owner.pid) || owner.pid <= 0) {\n return false;\n }\n return !isProcessAlive(owner.pid);\n}\n\n/** Whether `path` exists. Only ENOENT/ENOTDIR mean it does not; other errors are thrown. */\nasync function exists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch (error) {\n if (isErrno(error, \"ENOENT\") || isErrno(error, \"ENOTDIR\")) return false;\n throw error;\n }\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return !isErrno(error, \"ESRCH\");\n }\n}\n\nfunction validateProfileName(profile: string): string {\n if (\n !profile ||\n profile === \".\" ||\n profile === \"..\" ||\n profile.includes(\"/\") ||\n profile.includes(\"\\\\\") ||\n profile.includes(\"\\0\")\n ) {\n throw new TypeError(\"Profile names must be non-empty path segments.\");\n }\n return profile;\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n return typeof error === \"object\" && error !== null && \"code\" in error &&\n (error as { code?: unknown }).code === code;\n}\n\nfunction sleep(milliseconds: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, milliseconds));\n}\n\nexport { DEFAULT_PROFILE };\n"],"mappings":"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","names":["ms","randomUUID","_a","ownership","resolve","randomUUID"]}
|