@cloudflare/sandbox 0.13.0-next.681.1 → 0.13.0-next.709.1
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/Dockerfile +6 -6
- package/README.md +49 -3
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/index.js +1364 -1072
- package/dist/bridge/index.js.map +1 -1
- package/dist/{contexts-DY1LHU1v.d.ts → contexts-1EsLHByO.d.ts} +156 -20
- package/dist/contexts-1EsLHByO.d.ts.map +1 -0
- package/dist/{dist-BStBkGIC.js → dist-Duor5GbS.js} +7 -56
- package/dist/dist-Duor5GbS.js.map +1 -0
- package/dist/errors/index.d.ts +4 -4
- package/dist/errors/index.js +3 -3
- package/dist/{errors-k3B8orjH.js → errors-CXR0xBpw.js} +42 -10
- package/dist/errors-CXR0xBpw.js.map +1 -0
- package/dist/{errors-ewgSNicb.js → errors-QYlSkVGz.js} +402 -250
- package/dist/errors-QYlSkVGz.js.map +1 -0
- package/dist/extensions/index.d.ts +4 -2
- package/dist/extensions/index.js +5 -3
- package/dist/extensions-CFB2xHqY.js +1023 -0
- package/dist/extensions-CFB2xHqY.js.map +1 -0
- package/dist/filesystem-BWAZCZER.d.ts +732 -0
- package/dist/filesystem-BWAZCZER.d.ts.map +1 -0
- package/dist/git/index.d.ts +8 -19
- package/dist/git/index.d.ts.map +1 -1
- package/dist/git/index.js +16 -23
- package/dist/git/index.js.map +1 -1
- package/dist/index-Bs4bqXDR.d.ts +438 -0
- package/dist/index-Bs4bqXDR.d.ts.map +1 -0
- package/dist/{index-Dy6u9r60.d.ts → index-HNYBk-az.d.ts} +233 -179
- package/dist/index-HNYBk-az.d.ts.map +1 -0
- package/dist/index.d.ts +480 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/interpreter/index.d.ts +3 -1
- package/dist/interpreter/index.d.ts.map +1 -1
- package/dist/interpreter/index.js +46 -18
- package/dist/interpreter/index.js.map +1 -1
- package/dist/openai/index.d.ts +5 -5
- package/dist/openai/index.d.ts.map +1 -1
- package/dist/openai/index.js +11 -6
- package/dist/openai/index.js.map +1 -1
- package/dist/opencode/index.d.ts +7 -11
- package/dist/opencode/index.d.ts.map +1 -1
- package/dist/opencode/index.js +56 -85
- package/dist/opencode/index.js.map +1 -1
- package/dist/process-types-GStiZ8f8.d.ts +73 -0
- package/dist/process-types-GStiZ8f8.d.ts.map +1 -0
- package/dist/{sandbox-boKWPIcd.js → sandbox-Auuwfnur.js} +4152 -3359
- package/dist/sandbox-Auuwfnur.js.map +1 -0
- package/dist/sandbox-BbAabq93.d.ts +42 -0
- package/dist/sandbox-BbAabq93.d.ts.map +1 -0
- package/dist/xterm/index.d.ts +5 -1
- package/dist/xterm/index.d.ts.map +1 -1
- package/dist/xterm/index.js +55 -12
- package/dist/xterm/index.js.map +1 -1
- package/package.json +3 -2
- package/dist/codes-y-U76vnQ.d.ts +0 -79
- package/dist/codes-y-U76vnQ.d.ts.map +0 -1
- package/dist/contexts-DY1LHU1v.d.ts.map +0 -1
- package/dist/dist-BStBkGIC.js.map +0 -1
- package/dist/errors-ewgSNicb.js.map +0 -1
- package/dist/errors-k3B8orjH.js.map +0 -1
- package/dist/extensions-CepYdzro.js +0 -191
- package/dist/extensions-CepYdzro.js.map +0 -1
- package/dist/index-B7QgIs0N.d.ts +0 -1841
- package/dist/index-B7QgIs0N.d.ts.map +0 -1
- package/dist/index-Dy6u9r60.d.ts.map +0 -1
- package/dist/sandbox-DHNO89IF.d.ts +0 -828
- package/dist/sandbox-DHNO89IF.d.ts.map +0 -1
- package/dist/sandbox-boKWPIcd.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-Auuwfnur.js","names":["currentEvent: SSEPartialEvent","metadata: FileMetadata | null","chunks: Array<string | Uint8Array>","reader: ReadableStreamDefaultReader<TerminalOutputEvent> | undefined","timeout: ReturnType<typeof setTimeout> | undefined","context: TerminalControlErrorContext","code?: string","sanitizedSandboxId: string","runtimeReader: RuntimeIdentityReader","currentLifetime: CurrentSandboxLifetime","release!: () => void","gitCredentialProxyHandler: OutboundHandler<\n Cloudflare.Env,\n GitAuthInterceptorParams\n>","env","parts: UploadedPart[]","meta: R2HTTPMetadata","listOpts: R2ListOptions","result","part: R2UploadedPart","r2Parts: R2UploadedPart[]","r2EgressHandler: OutboundHandler<\n Cloudflare.Env,\n R2EgressParams\n>","credentialProxyDiagnosticEvents: CredentialProxyDiagnosticEvent[]","env","headers: [string, string][]","headerEntries: [string, string][]","s3CredentialProxyHandler: OutboundHandler<\n Cloudflare.Env,\n S3CredentialProxyParams\n>","mountId: string | null","realPath: string","requestInfo: CredentialProxyRequestInfo","ContainerProxy","BaseContainerProxy","hostOverrides: Record<\n string,\n { method: string; params: GitAuthInterceptorParams }\n >","hostOverrides: Record<\n string,\n { method: string; params: S3CredentialProxyParams }\n >","S3_CREDENTIAL_PROXY_SELF_TEST_PATH","R2_DEFAULT_S3FS_OPTIONS: Readonly<\n Record<string, string | boolean>\n>","result: Record<string, string | boolean>","s3fsOptions: Record<string, string | boolean>","buckets: R2EgressParams['buckets']","params: S3CredentialProxyParams['mounts']","cleanedMountPaths: string[]","cleanedMountIds: string[]","currentEvent: SSEPartialEvent","changed: Array<{ key: string; action: 'created' | 'modified' }>","results: Array<{ key: string; etag: string; size: number }>","cursor: string | undefined","mountOutcome: 'success' | 'error'","mountError: Error | undefined","syncManager: LocalMountSyncManager | null","mountInfo: LocalSyncMountInfo","mountOutcome: 'success' | 'error'","mountError: Error | undefined","passwordFilePath: string | undefined","additionalHeaderFilePath: string | undefined","mountInfo: R2BindingMountInfo | undefined","mountOutcome: 'success' | 'error'","mountError: Error | undefined","passwordFilePath: string | undefined","additionalHeaderFilePath: string | undefined","provider: BucketProvider | null","credentialProxyMountId: string | undefined","mountInfo: FuseMountInfo | undefined","effectiveOptions: RemoteMountBucketOptions","unmountOutcome: 'success' | 'error'","unmountError: Error | undefined","deps: BucketMountServiceDeps","normalizedExcludes: string[]","deps: BackupCreatorDeps","backupId: string | undefined","sizeBytes: number | undefined","outcome: 'success' | 'error'","caughtError: Error | undefined","storage: DurableObjectStorage","getEnv: () => unknown","record: SandboxLifetimeRecord","storage: SandboxLifetimeStorage","storage: OperationRecordStorage","deps: RestoreLifecycleDeps","operation: BackupRestoreOperationRecord","runtime: RuntimeIdentity | undefined","context: RestoreLifecycleContext","interrupted","deps: BackupTransferDeps","parsed: URL","missing: string[]","uploadResult: UploadPartsResponse","deps: BackupServiceDeps","outcome: 'success' | 'error'","caughtError: Error | undefined","interruptedError: OperationInterruptedError | undefined","closeAssignedResponse:\n | ((error: OperationInterruptedError) => void)\n | undefined","once","controller: ReadableStreamDefaultController<Uint8Array> | undefined","normalized: Record<string, PortTokenEntry>","deps: PreviewServiceDeps","outcome: 'success' | 'error'","caughtError: Error | undefined","committed: PreviewExposureCommit | undefined","interrupted","entry","activePorts: CurrentPreviewPort[]","runtime","#id","#pid","#runtime","#lifecycle","#verifiedStatus","#subscription","#operation","#isTerminal","#allowCleanClose","#releaseConnection","#released","#reader","#release","result: ReadableStreamReadResult<T>","#id","#control","renewActivity: () => void","stopInactive: () => Promise<void>","failure: unknown","availability: ResourceActivityAvailability","processesActive: boolean","terminalsActive: boolean","#error","#ws","#receiveResolver","#receiveRejecter","#receiveQueue","#fail","#sendQueue","options: RuntimeBootstrapProbeOptions","options: SandboxRuntimeLifecycleOptions","sameIdentity","transition: ReplacementStartTransition","release!: () => void","RUNTIME_ABSENT: RuntimeAbsent","options: RuntimeOperationRunnerOptions","runtime: RuntimeIdentity","lease: RuntimeLease","once","activity: ActivityAdmission","options: RuntimeSessionManagerOptions","connection: ContainerControlConnection","rejectInterrupted!: (error: Error) => void","session: CachedSession","onInterrupt: (() => void) | undefined","onRelease: () => void","interrupted","subscription: PortWatchSubscriptionAPI | undefined","reader: ReadableStreamDefaultReader<PortWatchEvent> | undefined","listeners: Array<() => void>","timer: ReturnType<typeof setTimeout> | undefined","env","body: TokenVerifyResponse","listBody: AccountsListResponse","verifyBody: AccountTokenVerifyResponse","body: ZonesListResponse","env","#getEnv","#fetcher","#env","#getAccountId","#getZoneId","#accountIdPromise","#zoneIdPromise","init: RequestInit","response: Response","envelope: CloudflareResponse<T>","state: TunnelPortState","writes: Array<Promise<void>>","config: { token: string; accountId: string; zoneId: string }","#host","#ensureTunnelRun","#getZoneName","tunnelId: string","tunnelToken: string","info: NamedTunnelInfo","#zoneNamePromise","nextMap: TunnelMap","nextMeta: TunnelMetaMap","#host","#portLocks","#provisioner","outcome: 'success' | 'error'","cacheState: TunnelGetCacheState","caughtError: Error | undefined","recovery: TunnelGetRecoveryState","#withPortLock","#getLocked","#assertSameOptions","#provisionNamedTunnel","#validateCachedRuntime","#provisionQuickTunnel","#resumePortCleanup","#updatePortCleanup","#assertStoredRuntime","interrupted","committedInfo: TunnelInfo | undefined","#assertSandboxLifetime","#invalidateCommittedPortIfUnchanged","committedRunID: string | undefined","#requireCleanupEntry","tunnelId: string | undefined","map","meta","runtime","service: TunnelService","RpcTarget","getHandler: () => TunnelExitHandler | null","logger: Logger","expectedRuntime?: RuntimeIdentity","getCurrentRuntime?: () =>\n | RuntimeIdentity\n | null\n | Promise<RuntimeIdentity | null>","isSessionCurrent?: () => boolean","interruptedError: Error | undefined","hold: ReturnType<RuntimeLease['retain']> | undefined","interrupt!: (error: Error) => void","interrupted","context: OperationInterruptedContext","reader: ReadableStreamDefaultReader<TerminalOutputEvent> | undefined","controller:\n | ReadableStreamDefaultController<TerminalOutputEvent>\n | undefined","configuration: SandboxConfiguration","operations: Promise<void>[]","path","options","id","sanitized: ExecOptions","env","outcome: 'success' | 'error'","caughtError: Error | undefined","timeoutID: ReturnType<typeof setTimeout> | undefined","result: 'timeout' | { mountsProcessed: number; mountFailures: number }","errorBody: ErrorResponse","errorBody","request: Request","port: number | undefined","port","#createPortRequestToken","token","retained: ReturnType<typeof retainWebSocketResponse> | undefined","result: T | typeof RUNTIME_ABSENT","retained: InterruptibleTerminalSubscription | undefined","callbackOutcome: Promise<T> | undefined"],"sources":["../src/file-stream.ts","../src/platform-errors.ts","../src/pty/proxy.ts","../src/security.ts","../src/preview/protocol.ts","../src/preview/url.ts","../src/preview/route.ts","../src/storage-mount/errors.ts","../src/storage-mount/lifecycle.ts","../src/storage-mount/operation-queue.ts","../src/storage-mount/outbound/git-credential-proxy-handler.ts","../src/storage-mount/validation/index.ts","../src/storage-mount/outbound/r2-egress-handler.ts","../src/storage-mount/outbound/s3-credential-proxy-handler.ts","../src/storage-mount/outbound/container-proxy.ts","../src/storage-mount/outbound/index.ts","../src/storage-mount/registry.ts","../src/storage-mount/validation/provider.ts","../src/storage-mount/s3fs/support-files.ts","../src/storage-mount/s3fs/index.ts","../src/storage-mount/outbound/params.ts","../src/storage-mount/lifecycle-cleanup.ts","../src/sse-parser.ts","../src/local-mount-sync.ts","../src/storage-mount/operations/local-sync-mount.ts","../src/storage-mount/operations/fuse-cleanup.ts","../src/storage-mount/operations/r2-egress-mount.ts","../src/storage-mount/validation/credentials.ts","../src/storage-mount/operations/remote-fuse-mount.ts","../src/storage-mount/operations/unmount.ts","../src/storage-mount/runtime-call.ts","../src/storage-mount/service.ts","../src/backup/constants.ts","../../shared/src/backup.ts","../src/backup/validation.ts","../src/backup/create.ts","../src/backup/restore-fault-injection.ts","../src/runtime/types.ts","../src/sandbox-lifetime.ts","../src/backup/restore-operation-store.ts","../src/backup/restore-lifecycle.ts","../src/backup/transfer.ts","../src/backup/backup-service.ts","../src/preview/forwarding.ts","../src/preview/proxy-request.ts","../src/preview/state.ts","../src/preview/token.ts","../src/preview/service.ts","../src/processes/process-capability.ts","../src/pty/terminal-capability.ts","../src/resource-activity-gate.ts","../src/container-control/connection.ts","../src/runtime/bootstrap-probe.ts","../src/runtime/lifecycle.ts","../src/runtime/operation-runner.ts","../src/container-control/rpc-proxy.ts","../src/container-control/client.ts","../src/runtime/session-manager.ts","../src/runtime/port-readiness.ts","../src/tunnels/credentials.ts","../src/tunnels/named-tunnel-config.ts","../src/tunnels/cloudflare-api.ts","../src/tunnels/storage.ts","../src/tunnels/cleanup.ts","../src/tunnels/lifecycle.ts","../src/tunnels/random-id.ts","../src/tunnels/provisioner.ts","../src/tunnels/restart.ts","../src/tunnels/tunnel-service.ts","../src/tunnels/rpc-target.ts","../src/tunnels/sandbox-control-callback.ts","../src/sandbox.ts"],"sourcesContent":["import {\n type FileChunk,\n type FileMetadata,\n type FileStreamEvent,\n parseSSEFrames,\n type SSEPartialEvent\n} from '@repo/shared';\n\n/**\n * Parse SSE (Server-Sent Events) lines from a stream\n */\nasync function* parseSSE(\n stream: ReadableStream<Uint8Array>\n): AsyncGenerator<FileStreamEvent> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let currentEvent: SSEPartialEvent = { data: [] };\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const parsed = parseSSEFrames(buffer, currentEvent);\n buffer = parsed.remaining;\n currentEvent = parsed.currentEvent;\n\n for (const frame of parsed.events) {\n try {\n const event = JSON.parse(frame.data) as FileStreamEvent;\n yield event;\n } catch {\n // Skip invalid JSON events and continue processing\n }\n }\n }\n\n // Flush complete frame from final trailing buffer\n const finalParsed = parseSSEFrames(`${buffer}\\n\\n`, currentEvent);\n for (const frame of finalParsed.events) {\n try {\n const event = JSON.parse(frame.data) as FileStreamEvent;\n yield event;\n } catch {\n // Skip invalid JSON events and continue processing\n }\n }\n } finally {\n // Cancel the stream first to properly terminate HTTP connections when breaking early\n try {\n await reader.cancel();\n } catch {\n // Ignore cancel errors (stream may already be closed)\n }\n reader.releaseLock();\n }\n}\n\n/**\n * Stream a file from the sandbox with automatic base64 decoding for binary files\n *\n * @param stream - The ReadableStream from readFileStream()\n * @returns AsyncGenerator that yields FileChunk (string for text, Uint8Array for binary)\n *\n * @example\n * ```ts\n * const stream = await sandbox.readFileStream('/path/to/file.png');\n * for await (const chunk of streamFile(stream)) {\n * if (chunk instanceof Uint8Array) {\n * // Binary chunk\n * console.log('Binary chunk:', chunk.length, 'bytes');\n * } else {\n * // Text chunk\n * console.log('Text chunk:', chunk);\n * }\n * }\n * ```\n */\nexport async function* streamFile(\n stream: ReadableStream<Uint8Array>\n): AsyncGenerator<FileChunk, FileMetadata> {\n let metadata: FileMetadata | null = null;\n\n for await (const event of parseSSE(stream)) {\n switch (event.type) {\n case 'metadata':\n metadata = {\n mimeType: event.mimeType,\n size: event.size,\n isBinary: event.isBinary,\n encoding: event.encoding\n };\n break;\n\n case 'chunk':\n if (!metadata) {\n throw new Error('Received chunk before metadata');\n }\n\n if (metadata.isBinary && metadata.encoding === 'base64') {\n // Decode base64 to Uint8Array for binary files\n const binaryString = atob(event.data);\n const bytes = new Uint8Array(binaryString.length);\n for (let i = 0; i < binaryString.length; i++) {\n bytes[i] = binaryString.charCodeAt(i);\n }\n yield bytes;\n } else {\n // Text files - yield as-is\n yield event.data;\n }\n break;\n\n case 'complete':\n if (!metadata) {\n throw new Error('Stream completed without metadata');\n }\n return metadata;\n\n case 'error':\n throw new Error(`File streaming error: ${event.error}`);\n }\n }\n\n throw new Error('Stream ended unexpectedly');\n}\n\n/**\n * Collect an entire file into memory from a stream\n *\n * @param stream - The ReadableStream from readFileStream()\n * @returns Object containing the file content and metadata\n *\n * @example\n * ```ts\n * const stream = await sandbox.readFileStream('/path/to/file.txt');\n * const { content, metadata } = await collectFile(stream);\n * console.log('Content:', content);\n * console.log('MIME type:', metadata.mimeType);\n * ```\n */\nexport async function collectFile(stream: ReadableStream<Uint8Array>): Promise<{\n content: string | Uint8Array;\n metadata: FileMetadata;\n}> {\n const chunks: Array<string | Uint8Array> = [];\n\n // Iterate through the generator and get the return value (metadata)\n const generator = streamFile(stream);\n let result = await generator.next();\n\n while (!result.done) {\n chunks.push(result.value);\n result = await generator.next();\n }\n\n const metadata = result.value;\n\n if (!metadata) {\n throw new Error('Failed to get file metadata');\n }\n\n // Combine chunks based on type\n if (metadata.isBinary) {\n // Binary file - combine Uint8Arrays\n const totalLength = chunks.reduce(\n (sum, chunk) => sum + (chunk instanceof Uint8Array ? chunk.length : 0),\n 0\n );\n const combined = new Uint8Array(totalLength);\n let offset = 0;\n for (const chunk of chunks) {\n if (chunk instanceof Uint8Array) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n }\n return { content: combined, metadata };\n } else {\n // Text file - combine strings\n const combined = chunks.filter((c) => typeof c === 'string').join('');\n return { content: combined, metadata };\n }\n}\n","const SUPERSEDED_ISOLATE_PATTERN =\n /reset because its code was updated|this script has been upgraded/i;\nconst CONNECTION_LOST_PATTERN = /network connection lost/i;\nconst DO_STORAGE_STARTUP_RESET_PATTERN =\n /internal error while starting up durable object storage caused object to be reset/i;\n\nfunction errorMessageOf(error: unknown): string {\n return error instanceof Error\n ? error.message\n : typeof error === 'string'\n ? error\n : '';\n}\n\nfunction* selfAndCauses(error: unknown): Generator<unknown> {\n let current = error;\n for (let depth = 0; depth < 8 && current != null; depth += 1) {\n yield current;\n current =\n typeof current === 'object'\n ? (current as { cause?: unknown }).cause\n : undefined;\n }\n}\n\nfunction isErrorRetryable(error: unknown): boolean {\n if (typeof error !== 'object' || error === null) return false;\n const message = String(error);\n const typed = error as { retryable?: boolean; overloaded?: boolean };\n return (\n typed.retryable === true &&\n typed.overloaded !== true &&\n !message.includes('Durable Object is overloaded')\n );\n}\n\n/**\n * Whether an error was raised because the current Durable Object isolate was\n * superseded by a deploy/code update. In-process retries are futile for this\n * class because the invocation keeps running on the old isolate; callers\n * should return control to the platform or retry from a fresh request.\n */\nexport function isDurableObjectCodeUpdateReset(error: unknown): boolean {\n for (const candidate of selfAndCauses(error)) {\n if (SUPERSEDED_ISOLATE_PATTERN.test(errorMessageOf(candidate))) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Whether an error represents a transient platform lifecycle/storage failure,\n * not an application-level sandbox failure.\n */\nexport function isPlatformTransientError(error: unknown): boolean {\n for (const candidate of selfAndCauses(error)) {\n const message = errorMessageOf(candidate);\n if (SUPERSEDED_ISOLATE_PATTERN.test(message)) return true;\n if (CONNECTION_LOST_PATTERN.test(message)) return true;\n if (DO_STORAGE_STARTUP_RESET_PATTERN.test(message)) return true;\n if (isErrorRetryable(candidate)) return true;\n }\n return false;\n}\n","import { switchPort } from '@cloudflare/containers';\nimport type {\n CreateTerminalOptions,\n ErrorResponse,\n ProcessExit,\n Terminal,\n TerminalOutputCursor,\n TerminalOutputEvent,\n TerminalOutputOptions,\n TerminalOutputSubscriptionAPI,\n TerminalSnapshot,\n WaitForExitOptions\n} from '@repo/shared';\nimport {\n ErrorCode,\n type TerminalControlErrorContext\n} from '@repo/shared/errors';\nimport { TerminalControlError } from '../errors';\nimport { openRemoteSubscription } from '../processes/remote-subscription';\nimport type { ProcessPullSubscriptionRPC } from '../processes/rpc-types';\nimport type { TerminalRPCDescriptor } from './rpc-types';\n\ninterface TerminalHandleStub {\n get(id: string): Promise<TerminalSnapshot | null>;\n output(\n id: string,\n options?: Omit<TerminalOutputOptions, 'signal'>\n ): Promise<\n | TerminalOutputSubscriptionAPI\n | ProcessPullSubscriptionRPC<TerminalOutputEvent>\n >;\n write(id: string, data: Uint8Array): Promise<void>;\n resize(id: string, cols: number, rows: number): Promise<void>;\n interrupt(id: string): Promise<void>;\n terminate(id: string): Promise<void>;\n fetch(request: Request): Promise<Response>;\n}\n\ninterface SandboxTerminalStub extends TerminalHandleStub {\n create(options: CreateTerminalOptions): Promise<TerminalSnapshot>;\n list(): Promise<TerminalSnapshot[]>;\n}\n\nexport async function createTerminalHandle(\n stub: SandboxTerminalStub,\n options: CreateTerminalOptions\n): Promise<Terminal> {\n return terminalHandle(stub, await stub.create(options));\n}\n\nexport async function getTerminalHandle(\n stub: SandboxTerminalStub,\n id: string\n): Promise<Terminal | null> {\n const snapshot = await stub.get(id);\n return snapshot ? terminalHandle(stub, snapshot) : null;\n}\n\nexport async function listTerminalHandles(\n stub: SandboxTerminalStub\n): Promise<Terminal[]> {\n return Promise.all(\n (await stub.list()).map((snapshot) => terminalHandle(stub, snapshot))\n );\n}\n\nexport function terminalHandle(\n stub: TerminalHandleStub,\n snapshot: TerminalSnapshot,\n runtimeIncarnationID?: string,\n outputProtocol: 'stream' | 'pull' = 'stream'\n): Terminal {\n const id = snapshot.id;\n return {\n id,\n getSnapshot: async () => {\n const current = await stub.get(id);\n if (!current) throw new Error(`Terminal not found: ${id}`);\n return current;\n },\n write: (data) => stub.write(id, data),\n resize: (cols, rows) => stub.resize(id, cols, rows),\n output: (options) => terminalOutput(stub, id, options, outputProtocol),\n waitForExit: (options) =>\n waitForTerminalExit(stub, id, options, outputProtocol),\n interrupt: () => stub.interrupt(id),\n terminate: () => stub.terminate(id),\n connect: (request, options) => {\n if (!runtimeIncarnationID) {\n throw new Error('terminal.connect() requires a runtime incarnation ID');\n }\n return proxyTerminal(stub, id, request, {\n ...options,\n runtimeIncarnationID\n });\n }\n };\n}\n\nexport function terminalHandleFromRPCDescriptor(\n descriptor: TerminalRPCDescriptor,\n fetch: (request: Request) => Promise<Response>\n): Terminal {\n const capability = descriptor.capability;\n const stub: TerminalHandleStub = {\n get: () => capability.getSnapshot(),\n output: (_id, options) => capability.openOutput(options),\n write: (_id, data) => capability.write(data),\n resize: (_id, cols, rows) => capability.resize(cols, rows),\n interrupt: () => capability.interrupt(),\n terminate: () => capability.terminate(),\n fetch\n };\n return terminalHandle(\n stub,\n descriptor.snapshot,\n descriptor.runtimeIncarnationID,\n 'pull'\n );\n}\n\nasync function terminalOutput(\n stub: TerminalHandleStub,\n id: string,\n options: TerminalOutputOptions | undefined,\n protocol: 'stream' | 'pull'\n) {\n const { signal, ...rpcOptions } = options ?? {};\n const stream = await openRemoteSubscription<TerminalOutputEvent>(\n stub.output(id, rpcOptions),\n { operation: 'open terminal output', protocol, signal }\n );\n if (!signal) return stream;\n let reader: ReadableStreamDefaultReader<TerminalOutputEvent> | undefined;\n return new ReadableStream({\n start(controller) {\n const activeReader = stream.getReader();\n reader = activeReader;\n let settled = false;\n const close = () => {\n if (settled) return;\n settled = true;\n controller.close();\n };\n const fail = (error: unknown) => {\n if (settled) return;\n settled = true;\n controller.error(error);\n };\n const abort = () => {\n activeReader.cancel().catch(() => {});\n close();\n };\n if (signal.aborted) {\n abort();\n return;\n }\n signal.addEventListener('abort', abort, { once: true });\n void (async () => {\n try {\n while (!signal.aborted) {\n const result = await activeReader.read();\n if (result.done) break;\n controller.enqueue(result.value);\n }\n close();\n } catch (error) {\n fail(error);\n } finally {\n signal.removeEventListener('abort', abort);\n }\n })();\n },\n cancel() {\n return reader?.cancel();\n }\n });\n}\n\nasync function waitForTerminalExit(\n stub: TerminalHandleStub,\n id: string,\n options: WaitForExitOptions = {},\n protocol: 'stream' | 'pull'\n): Promise<ProcessExit> {\n const abortController = new AbortController();\n let timeout: ReturnType<typeof setTimeout> | undefined;\n let abortedBySignal = false;\n const abort = () => {\n if (abortController.signal.aborted) return;\n abortedBySignal = true;\n abortController.abort(options.signal?.reason);\n };\n const timeoutAbort = () => {\n if (abortController.signal.aborted) return;\n abortController.abort(new Error('Terminal wait timed out'));\n };\n if (options.signal) {\n if (options.signal.aborted) throw abortReason(options.signal.reason);\n options.signal.addEventListener('abort', abort, { once: true });\n }\n if (options.timeout !== undefined)\n timeout = setTimeout(timeoutAbort, options.timeout);\n let reader: ReadableStreamDefaultReader<TerminalOutputEvent> | undefined;\n try {\n const stream = await openRemoteSubscription<TerminalOutputEvent>(\n stub.output(id, { replay: true, follow: true }),\n {\n operation: 'wait for terminal exit',\n protocol,\n signal: abortController.signal\n }\n );\n reader = stream.getReader();\n abortController.signal.addEventListener(\n 'abort',\n () => {\n void reader?.cancel().catch(() => {});\n },\n { once: true }\n );\n while (!abortController.signal.aborted) {\n const result = await reader.read();\n if (result.done) break;\n if (result.value.type === 'terminal') {\n if (result.value.state === 'exited') return result.value.exit;\n throw terminalRuntimeError(id, result.value);\n }\n }\n if (abortController.signal.aborted) {\n throw abortedBySignal\n ? abortReason(abortController.signal.reason)\n : new Error('Terminal wait timed out');\n }\n throw new Error('Terminal wait aborted');\n } finally {\n if (timeout) clearTimeout(timeout);\n if (options.signal) options.signal.removeEventListener('abort', abort);\n await reader?.cancel().catch(() => {});\n }\n}\n\nfunction terminalRuntimeError(\n terminalId: string,\n failure: TerminalOutputEvent & { type: 'terminal'; state: 'error' }\n): TerminalControlError {\n const context: TerminalControlErrorContext = {\n terminalId,\n operation: 'waitForExit',\n reason: failure.error.message,\n failure: { ...failure.error }\n };\n const response: ErrorResponse<TerminalControlErrorContext> = {\n code: ErrorCode.TERMINAL_CONTROL_ERROR,\n message: failure.error.message,\n context,\n httpStatus: 500,\n timestamp: failure.timestamp\n };\n return new TerminalControlError(response);\n}\n\nfunction abortReason(reason: unknown): Error {\n if (reason instanceof Error) return reason;\n if (reason === undefined) return new Error('Terminal wait aborted');\n return new Error(String(reason));\n}\n\nexport async function proxyTerminal(\n stub: Pick<TerminalHandleStub, 'fetch'>,\n terminalId: string,\n request: Request,\n options: {\n cursor?: TerminalOutputCursor;\n cols?: number;\n rows?: number;\n runtimeIncarnationID: string;\n }\n): Promise<Response> {\n const upgradeHeader = request.headers.get('Upgrade');\n if (upgradeHeader?.toLowerCase() !== 'websocket')\n throw new Error('terminal.connect() requires a WebSocket upgrade request');\n const params = new URLSearchParams({ terminalId });\n if (!options.runtimeIncarnationID) {\n throw new Error('terminal.connect() requires a runtime incarnation ID');\n }\n if (options.cursor) params.set('cursor', options.cursor);\n if (options.cols) params.set('cols', String(options.cols));\n if (options.rows) params.set('rows', String(options.rows));\n params.set('runtimeIncarnationID', options.runtimeIncarnationID);\n return stub.fetch(\n switchPort(\n new Request(`http://localhost/ws/terminal?${params}`, request),\n 3000\n )\n );\n}\n","/**\n * Security utilities for URL construction and input validation\n *\n * This module contains critical security functions to prevent:\n * - URL injection attacks\n * - SSRF (Server-Side Request Forgery) attacks\n * - DNS rebinding attacks\n * - Host header injection\n * - Open redirect vulnerabilities\n */\n\nexport class SandboxSecurityError extends Error {\n constructor(\n message: string,\n public readonly code?: string\n ) {\n super(message);\n this.name = 'SandboxSecurityError';\n }\n}\n\n/**\n * Validates port numbers for sandbox services.\n *\n * Rules:\n * - Range: 1024-65535 (privileged ports require root, which containers don't have)\n * - Reserved: 3000 (sandbox control plane)\n */\nexport function validatePort(port: number): boolean {\n // Must be a valid integer\n if (!Number.isInteger(port)) {\n return false;\n }\n\n // Only allow non-system ports (1024-65535)\n if (port < 1024 || port > 65535) {\n return false;\n }\n\n const reservedPorts = [3000];\n\n if (reservedPorts.includes(port)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Sanitizes and validates sandbox IDs for DNS compliance and security\n * Only enforces critical requirements - allows maximum developer flexibility\n */\nexport function sanitizeSandboxId(id: string): string {\n // Basic validation: not empty, reasonable length limit (DNS subdomain limit is 63 chars)\n if (!id || id.length > 63) {\n throw new SandboxSecurityError(\n 'Sandbox ID must be 1-63 characters long.',\n 'INVALID_SANDBOX_ID_LENGTH'\n );\n }\n\n // DNS compliance: cannot start or end with hyphens (RFC requirement)\n if (id.startsWith('-') || id.endsWith('-')) {\n throw new SandboxSecurityError(\n 'Sandbox ID cannot start or end with hyphens (DNS requirement).',\n 'INVALID_SANDBOX_ID_HYPHENS'\n );\n }\n\n // Prevent reserved names that cause technical conflicts\n const reservedNames = [\n 'www',\n 'api',\n 'admin',\n 'root',\n 'system',\n 'cloudflare',\n 'workers'\n ];\n\n const lowerCaseId = id.toLowerCase();\n if (reservedNames.includes(lowerCaseId)) {\n throw new SandboxSecurityError(\n `Reserved sandbox ID '${id}' is not allowed.`,\n 'RESERVED_SANDBOX_ID'\n );\n }\n\n return id;\n}\n\n/**\n * Validates language for code interpreter\n * Only allows supported languages\n */\nexport function validateLanguage(language: string | undefined): void {\n if (!language) {\n return; // undefined is valid, will default to python\n }\n\n const supportedLanguages = [\n 'python',\n 'python3',\n 'javascript',\n 'js',\n 'node',\n 'typescript',\n 'ts'\n ];\n const normalized = language.toLowerCase();\n\n if (!supportedLanguages.includes(normalized)) {\n throw new SandboxSecurityError(\n `Unsupported language '${language}'. Supported languages: python, javascript, typescript`,\n 'INVALID_LANGUAGE'\n );\n }\n}\n\n/**\n * Validates a single DNS label for use as a Cloudflare Tunnel hostname.\n *\n * Used by `sandbox.tunnels.get(port, { name })` to reject obviously-bad\n * input client-side before any network call. Whether the chosen label is\n * actually available under the configured zone is left to the Cloudflare\n * API (returned as a typed error).\n *\n * Rules:\n * - 1–63 characters\n * - Lowercase letters, digits, and internal hyphens only\n * - No leading or trailing hyphen\n * - No dots — multi-label hostnames need a delegated subdomain zone or\n * Advanced Certificate Manager, which are out of scope for this\n * feature. Universal SSL only covers `<label>.<zone>`.\n *\n * Throws `SandboxSecurityError` on any violation. Designed to be called\n * before any other tunnel work so callers see a fast, deterministic\n * failure.\n */\nconst TUNNEL_NAME_REGEX = /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/;\n\nexport function validateTunnelName(name: string): void {\n if (typeof name !== 'string') {\n throw new SandboxSecurityError(\n `Tunnel name must be a string. Received: ${typeof name}`,\n 'INVALID_TUNNEL_NAME'\n );\n }\n if (name.length === 0 || name.length > 63) {\n throw new SandboxSecurityError(\n `Tunnel name '${name}' must be 1–63 characters long.`,\n 'INVALID_TUNNEL_NAME_LENGTH'\n );\n }\n if (!TUNNEL_NAME_REGEX.test(name)) {\n throw new SandboxSecurityError(\n `Tunnel name '${name}' is not a valid DNS label. Use lowercase ` +\n 'letters, digits, and internal hyphens only (no dots, no ' +\n 'leading/trailing hyphens).',\n 'INVALID_TUNNEL_NAME_FORMAT'\n );\n }\n}\n","import { validatePort } from '../security';\n\n/** @internal */\nexport const PREVIEW_PROXY_HEADER = 'x-sandbox-preview-proxy';\n/** @internal */\nexport const PREVIEW_PROXY_PORT_HEADER = 'x-sandbox-preview-port';\n/** @internal */\nexport const PREVIEW_PROXY_TOKEN_HEADER = 'x-sandbox-preview-token';\n/** @internal */\nexport const PREVIEW_PROXY_SANDBOX_ID_HEADER = 'x-sandbox-preview-sandbox-id';\n\n/** @internal */\nexport const PREVIEW_PROXY_HEADERS = [\n PREVIEW_PROXY_HEADER,\n PREVIEW_PROXY_PORT_HEADER,\n PREVIEW_PROXY_TOKEN_HEADER,\n PREVIEW_PROXY_SANDBOX_ID_HEADER\n] as const;\n\nexport interface PreviewProxyMetadata {\n port: number;\n token: string;\n sandboxId: string;\n}\n\nexport function isPreviewProxyRequest(request: Request): boolean {\n return request.headers.get(PREVIEW_PROXY_HEADER) === '1';\n}\n\nexport function readPreviewProxyMetadata(\n request: Request\n): PreviewProxyMetadata | null {\n if (!isPreviewProxyRequest(request)) {\n return null;\n }\n\n const portValue = request.headers.get(PREVIEW_PROXY_PORT_HEADER);\n const token = request.headers.get(PREVIEW_PROXY_TOKEN_HEADER);\n const sandboxId = request.headers.get(PREVIEW_PROXY_SANDBOX_ID_HEADER);\n const port = portValue === null ? Number.NaN : Number.parseInt(portValue, 10);\n\n if (!Number.isFinite(port) || !validatePort(port) || !token || !sandboxId) {\n return null;\n }\n\n return { port, token, sandboxId };\n}\n\nexport function withPreviewProxyMetadata(\n request: Request,\n { port, token, sandboxId }: PreviewProxyMetadata\n): Request {\n const headers = new Headers(request.headers);\n for (const header of PREVIEW_PROXY_HEADERS) {\n headers.delete(header);\n }\n headers.set(PREVIEW_PROXY_HEADER, '1');\n headers.set(PREVIEW_PROXY_PORT_HEADER, port.toString());\n headers.set(PREVIEW_PROXY_TOKEN_HEADER, token);\n headers.set(PREVIEW_PROXY_SANDBOX_ID_HEADER, sandboxId);\n\n return new Request(request, { headers });\n}\n","export function isLocalhostPattern(hostname: string): boolean {\n // Handle IPv6 addresses in brackets (with or without port)\n if (hostname.startsWith('[')) {\n if (hostname.includes(']:')) {\n // [::1]:port format\n const ipv6Part = hostname.substring(0, hostname.indexOf(']:') + 1);\n return ipv6Part === '[::1]';\n }\n\n // [::1] format without port\n return hostname === '[::1]';\n }\n\n // Handle bare IPv6 without brackets\n if (hostname === '::1') {\n return true;\n }\n\n // For IPv4 and regular hostnames, split on colon to remove port\n const hostPart = hostname.split(':')[0];\n\n return (\n hostPart === 'localhost' ||\n hostPart === '127.0.0.1' ||\n hostPart === '0.0.0.0'\n );\n}\n","import {\n SandboxSecurityError,\n sanitizeSandboxId,\n validatePort\n} from '../security';\nimport { isLocalhostPattern } from './url';\n\nexport interface PreviewRouteInfo {\n port: number;\n sandboxId: string;\n token: string;\n}\n\nexport interface ConstructPreviewURLOptions {\n port: number;\n sandboxId: string;\n effectiveId: string;\n hostname: string;\n token: string;\n normalizeId: boolean;\n}\n\nexport function parsePreviewRoute(url: URL): PreviewRouteInfo | null {\n // URL format: {port}-{sandboxId}-{token}.{domain}\n // Tokens are [a-z0-9_]+, so split at the last hyphen to handle sandbox IDs\n // with hyphens such as UUIDs.\n const dotIndex = url.hostname.indexOf('.');\n if (dotIndex === -1) {\n return null;\n }\n\n const subdomain = url.hostname.slice(0, dotIndex);\n\n const firstHyphen = subdomain.indexOf('-');\n if (firstHyphen === -1) {\n return null;\n }\n\n const portStr = subdomain.slice(0, firstHyphen);\n if (!/^\\d{4,5}$/.test(portStr)) {\n return null;\n }\n\n const port = Number.parseInt(portStr, 10);\n if (!validatePort(port)) {\n return null;\n }\n\n const rest = subdomain.slice(firstHyphen + 1);\n const lastHyphen = rest.lastIndexOf('-');\n if (lastHyphen === -1) {\n return null;\n }\n\n const sandboxId = rest.slice(0, lastHyphen);\n const token = rest.slice(lastHyphen + 1);\n\n if (!/^[a-z0-9_]+$/.test(token) || token.length === 0 || token.length > 63) {\n return null;\n }\n\n if (sandboxId.length === 0 || sandboxId.length > 63) {\n return null;\n }\n\n let sanitizedSandboxId: string;\n try {\n sanitizedSandboxId = sanitizeSandboxId(sandboxId);\n } catch {\n return null;\n }\n\n return {\n port,\n sandboxId: sanitizedSandboxId,\n token\n };\n}\n\nexport function constructPreviewURL({\n port,\n sandboxId,\n effectiveId,\n hostname,\n token,\n normalizeId\n}: ConstructPreviewURLOptions): string {\n if (!validatePort(port)) {\n throw new SandboxSecurityError(\n `Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`\n );\n }\n\n const hasUppercase = /[A-Z]/.test(effectiveId);\n if (!normalizeId && hasUppercase) {\n throw new SandboxSecurityError(\n `Preview URLs require lowercase sandbox IDs. Your ID \"${effectiveId}\" contains uppercase letters.\\n\\n` +\n `To fix this:\\n` +\n `1. Create a new sandbox with: getSandbox(ns, \"${effectiveId}\", { normalizeId: true })\\n` +\n `2. This will create a sandbox with ID: \"${effectiveId.toLowerCase()}\"\\n\\n` +\n `Note: Due to DNS case-insensitivity, IDs with uppercase letters cannot be used with preview URLs.`\n );\n }\n\n const sanitizedSandboxId = sanitizeSandboxId(sandboxId).toLowerCase();\n const isLocalhost = isLocalhostPattern(hostname);\n\n if (isLocalhost) {\n const [host, portStr] = hostname.split(':');\n const mainPort = portStr || '80';\n\n try {\n const baseURL = new URL(`http://${host}:${mainPort}`);\n const subdomainHost = `${port}-${sanitizedSandboxId}-${token}.${host}`;\n baseURL.hostname = subdomainHost;\n\n return baseURL.toString();\n } catch (error) {\n throw new SandboxSecurityError(\n `Failed to construct preview URL: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n }\n\n try {\n const baseURL = new URL(`https://${hostname}`);\n const subdomainHost = `${port}-${sanitizedSandboxId}-${token}.${hostname}`;\n baseURL.hostname = subdomainHost;\n\n return baseURL.toString();\n } catch (error) {\n throw new SandboxSecurityError(\n `Failed to construct preview URL: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n}\n","/**\n * Bucket mount and unmount error classes\n *\n * Validation errors (InvalidMountConfigError, MissingCredentialsError) are thrown\n * before any container interaction. BucketUnmountError is thrown after a failed\n * fusermount call inside the container.\n */\n\nimport { ErrorCode } from '@repo/shared/errors';\n\n/**\n * Base error for bucket mounting operations\n */\nexport class BucketMountError extends Error {\n public readonly code: ErrorCode;\n\n constructor(message: string, code: ErrorCode = ErrorCode.BUCKET_MOUNT_ERROR) {\n super(message);\n this.name = 'BucketMountError';\n this.code = code;\n }\n}\n\n/**\n * Thrown when S3FS mount command fails\n */\nexport class S3FSMountError extends BucketMountError {\n constructor(message: string) {\n super(message, ErrorCode.S3FS_MOUNT_ERROR);\n this.name = 'S3FSMountError';\n }\n}\n\n/**\n * Thrown when fusermount -u fails to unmount a FUSE filesystem\n */\nexport class BucketUnmountError extends BucketMountError {\n constructor(message: string) {\n super(message, ErrorCode.BUCKET_UNMOUNT_ERROR);\n this.name = 'BucketUnmountError';\n }\n}\n\n/**\n * Thrown when no credentials found in environment\n */\nexport class MissingCredentialsError extends BucketMountError {\n constructor(message: string) {\n super(message, ErrorCode.MISSING_CREDENTIALS);\n this.name = 'MissingCredentialsError';\n }\n}\n\n/**\n * Thrown when bucket name, mount path, or options are invalid\n */\nexport class InvalidMountConfigError extends BucketMountError {\n constructor(message: string) {\n super(message, ErrorCode.INVALID_MOUNT_CONFIG);\n this.name = 'InvalidMountConfigError';\n }\n}\n","import type { RuntimeIdentity, RuntimeIdentityReader } from '../runtime';\nimport type {\n CurrentSandboxLifetime,\n SandboxLifetime\n} from '../sandbox-lifetime';\n\nexport type MountLifecycleSnapshot = {\n runtime: RuntimeIdentity;\n lifetime: SandboxLifetime;\n};\n\nexport class MountLifecycle {\n constructor(\n private readonly runtimeReader: RuntimeIdentityReader,\n private readonly currentLifetime: CurrentSandboxLifetime\n ) {}\n\n async capture(runtime: RuntimeIdentity): Promise<MountLifecycleSnapshot> {\n return {\n runtime,\n lifetime: await this.currentLifetime.getOrCreate()\n };\n }\n\n async assertCurrent(snapshot: MountLifecycleSnapshot): Promise<void> {\n await this.currentLifetime.assertCurrent(snapshot.lifetime);\n await this.runtimeReader.assertActive(snapshot.runtime);\n }\n}\n","export class MountOperationQueue {\n private tail: Promise<void> = Promise.resolve();\n\n async run<T>(operation: () => Promise<T>): Promise<T> {\n const previous = this.tail;\n let release!: () => void;\n this.tail = new Promise<void>((resolve) => {\n release = resolve;\n });\n\n await previous.catch(() => {});\n try {\n return await operation();\n } finally {\n release();\n }\n }\n}\n","import type {\n OutboundHandler,\n OutboundHandlerContext\n} from '@cloudflare/containers';\nimport type {\n HTTPAuthHostConfig,\n HTTPAuthInterceptorParams\n} from '../../extensions/index.js';\n\n// Types re-declared here so the core outbound-handler stack stays independent\n// of any specific extension. The git extension package uses matching aliases\n// (`GitHostAuth`, `GitAuthInterceptorParams`) but structurally these are just\n// the generic HTTP-auth-interceptor params from the extension framework.\ntype GitHostAuth = HTTPAuthHostConfig;\ntype GitAuthInterceptorParams = HTTPAuthInterceptorParams;\n\nfunction encodeBasicAuth(username: string, token: string): string {\n return btoa(`${username}:${token}`);\n}\n\nfunction authorizationHeader(auth: GitHostAuth): string {\n if (auth.type === 'bearer') {\n return `Bearer ${auth.token}`;\n }\n return `Basic ${encodeBasicAuth(auth.username ?? 'x-access-token', auth.token)}`;\n}\n\nexport const gitCredentialProxyHandler: OutboundHandler<\n Cloudflare.Env,\n GitAuthInterceptorParams\n> = async (\n request: Request,\n _env: Cloudflare.Env,\n ctx: OutboundHandlerContext<GitAuthInterceptorParams>\n): Promise<Response> => {\n const url = new URL(request.url);\n const auth = ctx.params?.hosts[url.hostname];\n if (!auth) {\n return fetch(request);\n }\n\n const headers = new Headers(request.headers);\n if (!headers.has('authorization')) {\n headers.set('authorization', authorizationHeader(auth));\n }\n\n return fetch(new Request(request, { headers }));\n};\n","import type { RemoteMountBucketOptions } from '@repo/shared';\nimport { InvalidMountConfigError } from '../errors';\nimport type { MountInfo } from '../types';\n\n/**\n * Type guard for R2Bucket binding.\n * Checks for the minimal R2Bucket interface methods we use.\n */\nexport function isR2Bucket(value: unknown): value is R2Bucket {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'put' in value &&\n typeof (value as Record<string, unknown>).put === 'function' &&\n 'get' in value &&\n typeof (value as Record<string, unknown>).get === 'function' &&\n 'head' in value &&\n typeof (value as Record<string, unknown>).head === 'function' &&\n 'delete' in value &&\n typeof (value as Record<string, unknown>).delete === 'function' &&\n 'list' in value &&\n typeof (value as Record<string, unknown>).list === 'function'\n );\n}\n\nexport function validatePrefix(prefix: string): void {\n if (!prefix.startsWith('/')) {\n throw new InvalidMountConfigError(\n `Prefix must start with '/': \"${prefix}\"`\n );\n }\n}\n\nexport function validateBucketName(bucket: string, mountPath: string): void {\n if (bucket.includes(':')) {\n const [bucketName, prefixPart] = bucket.split(':');\n throw new InvalidMountConfigError(\n `Bucket name cannot contain ':'. To mount a prefix, use the 'prefix' option:\\n` +\n ` mountBucket('${bucketName}', '${mountPath}', { ...options, prefix: '${prefixPart}' })`\n );\n }\n\n const bucketNameRegex = /^[a-z0-9]([a-z0-9.-]{0,61}[a-z0-9])?$/;\n if (!bucketNameRegex.test(bucket)) {\n throw new InvalidMountConfigError(\n `Invalid bucket name: \"${bucket}\". Bucket names must be 3-63 characters, ` +\n `lowercase alphanumeric, dots, or hyphens, and cannot start/end with dots or hyphens.`\n );\n }\n}\n\nexport function validateMountPath(\n activeMounts: Map<string, MountInfo>,\n mountPath: string\n): void {\n if (!mountPath.startsWith('/')) {\n throw new InvalidMountConfigError(\n `Mount path must be absolute (start with /): \"${mountPath}\"`\n );\n }\n\n if (activeMounts.has(mountPath)) {\n const existingMount = activeMounts.get(mountPath);\n throw new InvalidMountConfigError(\n `Mount path \"${mountPath}\" is already in use by bucket \"${existingMount?.bucket}\". ` +\n 'Unmount the existing bucket first or use a different mount path.'\n );\n }\n}\n\nexport function validateRemoteMountOptions(\n activeMounts: Map<string, MountInfo>,\n bucket: string,\n mountPath: string,\n options: RemoteMountBucketOptions\n): void {\n try {\n new URL(options.endpoint);\n } catch (error) {\n throw new InvalidMountConfigError(\n `Invalid endpoint URL: \"${options.endpoint}\". Must be a valid HTTP(S) URL.`\n );\n }\n\n validateBucketName(bucket, mountPath);\n validateMountPath(activeMounts, mountPath);\n}\n\nexport function validateBucketBindingName(\n bucketBinding: string,\n mountPath: string\n): void {\n if (bucketBinding.includes(':')) {\n const [bucketName, prefixPart] = bucketBinding.split(':');\n throw new InvalidMountConfigError(\n `Bucket name cannot contain ':'. To mount a prefix, use the 'prefix' option:\\n` +\n ` mountBucket('${bucketName}', '${mountPath}', { ...options, prefix: '${prefixPart}' })`\n );\n }\n\n // Worker binding names follow JavaScript identifier syntax.\n const bindingNameRegex = /^[A-Za-z_][A-Za-z0-9_]*$/;\n if (!bindingNameRegex.test(bucketBinding)) {\n throw new InvalidMountConfigError(\n `Invalid R2 binding name: \"${bucketBinding}\". Binding names must start with a letter or underscore and contain only letters, numbers, or underscores.`\n );\n }\n}\n\n/**\n * Builds the s3fs source string from bucket name and optional prefix.\n * Format: \"bucket\" or \"bucket:/prefix/\" for subdirectory mounts.\n *\n * @param bucket - The bucket name\n * @param prefix - Optional prefix/subdirectory path\n * @returns The s3fs source string\n */\nexport function buildS3fsSource(bucket: string, prefix?: string): string {\n return prefix ? `${bucket}:${prefix}` : bucket;\n}\n","/**\n * Egress handler that translates S3 API requests from s3fs into R2 binding calls.\n *\n * When s3fs inside the container makes requests to http://r2.internal/<bucket>/...,\n * the Cloudflare egress interception layer routes them here. The bucket name is read\n * from the first path segment, resolved to a Worker R2 binding, and the request is\n * executed via the R2 binding API — no S3 credentials ever touch the container.\n *\n * S3 path-style layout: /bucketName/objectKey\n * Query params drive operation selection (list-type=2, uploads, uploadId, location).\n */\n\nimport type {\n OutboundHandler,\n OutboundHandlerContext\n} from '@cloudflare/containers';\nimport { isR2Bucket } from '../validation';\n\n// ---------------------------------------------------------------------------\n// Per-mount bucket params\n//\n// Serialized into ContainerProxy props and transmitted with every intercepted\n// request so each Worker isolate receives the active mount configuration.\n// ---------------------------------------------------------------------------\n\nexport type R2EgressParams = {\n buckets: Record<string, { prefix?: string; readOnly?: boolean }>;\n};\n\n// ---------------------------------------------------------------------------\n// XML helpers\n// ---------------------------------------------------------------------------\n\nconst XML_NS = 'xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"';\nconst XML_DECL = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n';\n\nfunction escapeXML(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\n}\n\nfunction xmlResponse(body: string, status = 200): Response {\n return new Response(XML_DECL + body, {\n status,\n headers: { 'Content-Type': 'application/xml' }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Path / query parsing\n// ---------------------------------------------------------------------------\n\ninterface ParsedPath {\n bucket: string;\n key: string;\n}\n\nfunction normalizeObjectKey(value: string): string {\n return value.replace(/^\\/+/, '');\n}\n\nfunction trimTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s[end - 1] === '/') end--;\n return s.slice(0, end);\n}\n\nfunction parsePath(pathname: string): ParsedPath | null {\n const stripped = pathname.startsWith('/') ? pathname.slice(1) : pathname;\n if (!stripped) return null;\n const slash = stripped.indexOf('/');\n if (slash === -1) return { bucket: stripped, key: '' };\n return {\n bucket: stripped.slice(0, slash),\n key: normalizeObjectKey(stripped.slice(slash + 1))\n };\n}\n\n// ---------------------------------------------------------------------------\n// R2 binding resolution\n// ---------------------------------------------------------------------------\n\nfunction resolveR2Bucket(env: unknown, name: string): R2Bucket | null {\n if (typeof env !== 'object' || env === null) return null;\n const val = (env as Record<string, unknown>)[name];\n return isR2Bucket(val) ? val : null;\n}\n\n// ---------------------------------------------------------------------------\n// Range header parsing\n// ---------------------------------------------------------------------------\n\nfunction parseRange(header: string | null): R2Range | undefined {\n if (!header) return undefined;\n const m = header.match(/^bytes=(\\d*)-(\\d*)$/);\n if (!m) return undefined;\n const start = m[1] ? parseInt(m[1], 10) : undefined;\n const end = m[2] ? parseInt(m[2], 10) : undefined;\n\n if (start === undefined && end !== undefined) {\n return { suffix: end };\n }\n if (start !== undefined && end !== undefined) {\n return { offset: start, length: end - start + 1 };\n }\n if (start !== undefined) {\n return { offset: start };\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// S3 XML response builders\n// ---------------------------------------------------------------------------\n\ninterface ListDisplayObject {\n key: string;\n uploaded: Date;\n httpEtag: string;\n size: number;\n}\n\ninterface ListDisplayResult {\n objects: ListDisplayObject[];\n delimitedPrefixes: string[];\n truncated: boolean;\n cursor?: string;\n}\n\nfunction buildListObjectsV2Xml(\n bucketName: string,\n prefix: string,\n delimiter: string,\n maxKeys: number,\n result: ListDisplayResult\n): string {\n const contents = result.objects\n .map(\n (obj) =>\n `<Contents><Key>${escapeXML(obj.key)}</Key><LastModified>${obj.uploaded.toISOString()}</LastModified><ETag>${escapeXML(obj.httpEtag)}</ETag><Size>${obj.size}</Size><StorageClass>STANDARD</StorageClass></Contents>`\n )\n .join('');\n\n const commonPrefixes = result.delimitedPrefixes\n .map(\n (p) => `<CommonPrefixes><Prefix>${escapeXML(p)}</Prefix></CommonPrefixes>`\n )\n .join('');\n\n const nextToken =\n result.truncated && result.cursor\n ? `<NextContinuationToken>${escapeXML(result.cursor)}</NextContinuationToken>`\n : '';\n\n const keyCount = result.objects.length + result.delimitedPrefixes.length;\n\n return (\n `<ListBucketResult ${XML_NS}>` +\n `<Name>${escapeXML(bucketName)}</Name>` +\n `<Prefix>${escapeXML(prefix)}</Prefix>` +\n `<KeyCount>${keyCount}</KeyCount>` +\n `<MaxKeys>${maxKeys}</MaxKeys>` +\n (delimiter ? `<Delimiter>${escapeXML(delimiter)}</Delimiter>` : '') +\n `<IsTruncated>${result.truncated}</IsTruncated>` +\n nextToken +\n contents +\n commonPrefixes +\n `</ListBucketResult>`\n );\n}\n\nfunction buildLocationXml(): string {\n return `<LocationConstraint ${XML_NS}/>`;\n}\n\nfunction buildInitiateMultipartUploadXml(\n bucketName: string,\n key: string,\n uploadId: string\n): string {\n return (\n `<InitiateMultipartUploadResult ${XML_NS}>` +\n `<Bucket>${escapeXML(bucketName)}</Bucket>` +\n `<Key>${escapeXML(key)}</Key>` +\n `<UploadId>${escapeXML(uploadId)}</UploadId>` +\n `</InitiateMultipartUploadResult>`\n );\n}\n\nfunction buildCompleteMultipartUploadXml(\n bucketName: string,\n key: string,\n etag: string\n): string {\n return (\n `<CompleteMultipartUploadResult ${XML_NS}>` +\n `<Location>http://r2.internal/${escapeXML(bucketName)}/${escapeXML(key)}</Location>` +\n `<Bucket>${escapeXML(bucketName)}</Bucket>` +\n `<Key>${escapeXML(key)}</Key>` +\n `<ETag>${escapeXML(etag)}</ETag>` +\n `</CompleteMultipartUploadResult>`\n );\n}\n\nfunction buildCopyObjectXml(etag: string, uploaded: Date): string {\n return (\n `<CopyObjectResult ${XML_NS}>` +\n `<LastModified>${uploaded.toISOString()}</LastModified>` +\n `<ETag>${escapeXML(etag)}</ETag>` +\n `</CopyObjectResult>`\n );\n}\n\n// ---------------------------------------------------------------------------\n// CompleteMultipartUpload XML body parser\n// ---------------------------------------------------------------------------\n\ninterface UploadedPart {\n partNumber: number;\n etag: string;\n}\n\nfunction extractXmlTagContent(segment: string, tagName: string): string | null {\n const openTag = `<${tagName}>`;\n const closeTag = `</${tagName}>`;\n const start = segment.indexOf(openTag);\n if (start === -1) return null;\n const contentStart = start + openTag.length;\n const end = segment.indexOf(closeTag, contentStart);\n if (end === -1) return null;\n return segment.slice(contentStart, end);\n}\n\nfunction parseCompleteMultipartUploadBody(body: string): UploadedPart[] {\n const parts: UploadedPart[] = [];\n let pos = 0;\n while (pos < body.length) {\n const start = body.indexOf('<Part>', pos);\n if (start === -1) break;\n const end = body.indexOf('</Part>', start + 6);\n if (end === -1) break;\n const segment = body.slice(start, end + 7);\n pos = end + 7;\n const partNumberText = extractXmlTagContent(segment, 'PartNumber');\n const etagText = extractXmlTagContent(segment, 'ETag');\n const partNumber = partNumberText ? parseInt(partNumberText, 10) : NaN;\n if (Number.isFinite(partNumber) && etagText) {\n parts.push({\n partNumber,\n etag: etagText.replace(/^\"|\"$/g, '')\n });\n }\n }\n return parts;\n}\n\n// ---------------------------------------------------------------------------\n// HTTP metadata helpers\n// ---------------------------------------------------------------------------\n\nfunction buildResponseHeaders(obj: R2Object): Headers {\n const headers = new Headers();\n headers.set('ETag', obj.httpEtag);\n headers.set('Content-Length', String(obj.size));\n headers.set('Last-Modified', obj.uploaded.toUTCString());\n headers.set('Accept-Ranges', 'bytes');\n if (obj.httpMetadata?.contentType) {\n headers.set('Content-Type', obj.httpMetadata.contentType);\n }\n if (obj.httpMetadata?.contentDisposition) {\n headers.set('Content-Disposition', obj.httpMetadata.contentDisposition);\n }\n if (obj.httpMetadata?.contentEncoding) {\n headers.set('Content-Encoding', obj.httpMetadata.contentEncoding);\n }\n if (obj.httpMetadata?.contentLanguage) {\n headers.set('Content-Language', obj.httpMetadata.contentLanguage);\n }\n if (obj.httpMetadata?.cacheControl) {\n headers.set('Cache-Control', obj.httpMetadata.cacheControl);\n }\n return headers;\n}\n\nfunction buildContentRange(range: R2Range, totalSize: number): string {\n if ('suffix' in range) {\n const start = Math.max(0, totalSize - range.suffix);\n return `bytes ${start}-${totalSize - 1}/${totalSize}`;\n }\n const start = range.offset ?? 0;\n const end =\n range.length !== undefined ? start + range.length - 1 : totalSize - 1;\n return `bytes ${start}-${end}/${totalSize}`;\n}\n\nfunction getRangeContentLength(range: R2Range, totalSize: number): number {\n if ('suffix' in range) {\n return Math.min(range.suffix, totalSize);\n }\n const start = range.offset ?? 0;\n if (start >= totalSize) {\n return 0;\n }\n const requestedLength =\n range.length !== undefined ? range.length : totalSize - start;\n return Math.min(requestedLength, totalSize - start);\n}\n\nfunction extractHttpMetadata(request: Request): R2HTTPMetadata {\n const meta: R2HTTPMetadata = {};\n const ct = request.headers.get('Content-Type');\n if (ct) meta.contentType = ct;\n const cd = request.headers.get('Content-Disposition');\n if (cd) meta.contentDisposition = cd;\n const ce = request.headers.get('Content-Encoding');\n if (ce) meta.contentEncoding = ce;\n const cl = request.headers.get('Content-Language');\n if (cl) meta.contentLanguage = cl;\n const cc = request.headers.get('Cache-Control');\n if (cc) meta.cacheControl = cc;\n return meta;\n}\n\nfunction parseCopySource(header: string): ParsedPath | null {\n const sourcePath = header.split('?')[0] ?? '';\n if (!sourcePath) return null;\n const decoded = decodeURIComponent(sourcePath);\n const parsed = parsePath(decoded.startsWith('/') ? decoded : `/${decoded}`);\n return parsed\n ? {\n bucket: parsed.bucket,\n key: normalizeObjectKey(parsed.key)\n }\n : null;\n}\n\nfunction normalizeStorageClass(\n storageClass: string | undefined\n): 'Standard' | 'InfrequentAccess' | undefined {\n if (storageClass === 'Standard' || storageClass === 'InfrequentAccess') {\n return storageClass;\n }\n return undefined;\n}\n\nasync function putRequestBody(\n r2: R2Bucket,\n key: string,\n request: Request,\n options: R2PutOptions\n): Promise<R2Object | Response> {\n const contentLength = request.headers.get('Content-Length');\n const length = contentLength ? Number.parseInt(contentLength, 10) : NaN;\n if (!Number.isFinite(length) || length < 0) {\n return new Response('Bad Request: missing or invalid Content-Length', {\n status: 400\n });\n }\n\n if (length === 0) {\n return r2.put(key, new Uint8Array(0), options);\n }\n\n if (!request.body) {\n return new Response('Bad Request: missing request body', { status: 400 });\n }\n\n const { readable, writable } = new FixedLengthStream(length);\n const pipe = request.body.pipeTo(writable);\n const result = await r2.put(key, readable, options);\n await pipe;\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Operation handlers\n// ---------------------------------------------------------------------------\n\nasync function handleListObjects(\n r2: R2Bucket,\n bucketName: string,\n url: URL,\n mountPrefix?: string\n): Promise<Response> {\n const queryPrefix = normalizeObjectKey(url.searchParams.get('prefix') ?? '');\n const delimiter = url.searchParams.get('delimiter') ?? '';\n const maxKeys = Math.min(\n parseInt(url.searchParams.get('max-keys') ?? '1000', 10) || 1000,\n 1000\n );\n const continuationToken =\n url.searchParams.get('continuation-token') ?? undefined;\n\n const r2Prefix = mountPrefix ? `${mountPrefix}/${queryPrefix}` : queryPrefix;\n\n const listOpts: R2ListOptions = {\n prefix: r2Prefix || undefined,\n delimiter: delimiter || undefined,\n limit: maxKeys,\n cursor: continuationToken\n };\n\n const result = await r2.list(listOpts);\n\n const stripKey = mountPrefix\n ? (k: string): string =>\n k.startsWith(`${mountPrefix}/`) ? k.slice(mountPrefix.length + 1) : k\n : (k: string): string => k;\n\n const displayResult: ListDisplayResult = {\n objects: result.objects.map((obj) => ({\n key: stripKey(obj.key),\n uploaded: obj.uploaded,\n httpEtag: obj.httpEtag,\n size: obj.size\n })),\n delimitedPrefixes: result.delimitedPrefixes.map(stripKey),\n truncated: result.truncated,\n cursor: result.truncated ? result.cursor : undefined\n };\n\n return xmlResponse(\n buildListObjectsV2Xml(\n bucketName,\n queryPrefix,\n delimiter,\n maxKeys,\n displayResult\n )\n );\n}\n\nasync function handleHeadObject(r2: R2Bucket, key: string): Promise<Response> {\n const obj = await r2.head(key);\n if (!obj) {\n return new Response(null, { status: 404 });\n }\n // NOTE: The outbound proxy rewrites Content-Length to 0 for bodiless\n // responses. s3fs stat cache must be pre-populated from LIST (readdir)\n // so that getattr never falls through to HEAD for file size information.\n return new Response(null, {\n status: 200,\n headers: buildResponseHeaders(obj)\n });\n}\n\nasync function handleGetObject(\n r2: R2Bucket,\n key: string,\n request: Request\n): Promise<Response> {\n const range = parseRange(request.headers.get('Range'));\n\n if (!range) {\n const obj = await r2.get(key);\n if (!obj) {\n return new Response(null, { status: 404 });\n }\n return new Response(obj.body, {\n status: 200,\n headers: buildResponseHeaders(obj)\n });\n }\n\n // R2 range bodies do not include the full object size needed for Content-Range.\n const [headObj, rangeObj] = await Promise.all([\n r2.head(key),\n r2.get(key, { range })\n ]);\n if (!headObj || !rangeObj) {\n return new Response(null, { status: 404 });\n }\n\n const headers = buildResponseHeaders(rangeObj);\n headers.set('Content-Range', buildContentRange(range, headObj.size));\n headers.set(\n 'Content-Length',\n String(getRangeContentLength(range, headObj.size))\n );\n\n return new Response(rangeObj.body, { status: 206, headers });\n}\n\nasync function handlePutObject(\n r2: R2Bucket,\n bucketName: string,\n key: string,\n request: Request,\n env: Cloudflare.Env,\n permitted: Set<string>,\n mountPrefix?: string\n): Promise<Response> {\n const copySourceHeader = request.headers.get('x-amz-copy-source');\n if (copySourceHeader) {\n const copySource = parseCopySource(copySourceHeader);\n if (!copySource || !copySource.key) {\n return new Response('Bad Request: invalid x-amz-copy-source', {\n status: 400\n });\n }\n\n if (!permitted.has(copySource.bucket)) {\n return new Response(\n `Access to R2 bucket \"${copySource.bucket}\" is not permitted. ` +\n 'Call mountBucket() with this bucket before accessing it.',\n { status: 403 }\n );\n }\n\n const sourceBucket =\n copySource.bucket === bucketName\n ? r2\n : resolveR2Bucket(env, copySource.bucket);\n if (!sourceBucket) {\n return new Response(\n `R2 binding \"${copySource.bucket}\" not found in Worker env. ` +\n 'Ensure the binding name matches the bucket name passed to mountBucket().',\n { status: 500 }\n );\n }\n\n const sourceKey =\n mountPrefix && copySource.bucket === bucketName\n ? `${mountPrefix}/${copySource.key}`\n : copySource.key;\n const sourceObject = await sourceBucket.get(sourceKey);\n if (!sourceObject) {\n return new Response(null, { status: 404 });\n }\n\n const metadataDirective = request.headers.get('x-amz-metadata-directive');\n const httpMetadata =\n metadataDirective?.toUpperCase() === 'REPLACE'\n ? extractHttpMetadata(request)\n : sourceObject.httpMetadata;\n const result = await r2.put(key, sourceObject.body, {\n httpMetadata,\n customMetadata: sourceObject.customMetadata,\n storageClass: normalizeStorageClass(sourceObject.storageClass)\n });\n return xmlResponse(buildCopyObjectXml(result.httpEtag, result.uploaded));\n }\n\n const httpMetadata = extractHttpMetadata(request);\n const result = await putRequestBody(r2, key, request, { httpMetadata });\n if (result instanceof Response) return result;\n const headers = new Headers();\n headers.set('ETag', result.httpEtag);\n return new Response(null, { status: 200, headers });\n}\n\nasync function handleDeleteObject(\n r2: R2Bucket,\n key: string\n): Promise<Response> {\n await r2.delete(key);\n return new Response(null, { status: 204 });\n}\n\nasync function handleCreateMultipartUpload(\n r2: R2Bucket,\n bucketName: string,\n key: string,\n request: Request\n): Promise<Response> {\n const httpMetadata = extractHttpMetadata(request);\n const upload = await r2.createMultipartUpload(key, { httpMetadata });\n return xmlResponse(\n buildInitiateMultipartUploadXml(bucketName, key, upload.uploadId)\n );\n}\n\nasync function handleUploadPart(\n r2: R2Bucket,\n key: string,\n url: URL,\n request: Request\n): Promise<Response> {\n const uploadId = url.searchParams.get('uploadId') ?? '';\n const partNumber = parseInt(url.searchParams.get('partNumber') ?? '0', 10);\n if (!uploadId || !partNumber) {\n return new Response('Bad Request: missing uploadId or partNumber', {\n status: 400\n });\n }\n if (!request.body) {\n return new Response('Bad Request: missing request body', { status: 400 });\n }\n const contentLength = request.headers.get('Content-Length');\n const partLength = contentLength ? Number.parseInt(contentLength, 10) : NaN;\n if (!Number.isFinite(partLength) || partLength < 0) {\n return new Response('Bad Request: missing or invalid Content-Length', {\n status: 400\n });\n }\n const upload = r2.resumeMultipartUpload(key, uploadId);\n let part: R2UploadedPart;\n if (partLength === 0) {\n part = await upload.uploadPart(partNumber, new Uint8Array(0));\n } else {\n const { readable, writable } = new FixedLengthStream(partLength);\n const pipe = request.body.pipeTo(writable);\n part = await upload.uploadPart(partNumber, readable);\n await pipe;\n }\n const headers = new Headers();\n headers.set('ETag', `\"${part.etag}\"`);\n return new Response(null, { status: 200, headers });\n}\n\nasync function handleCompleteMultipartUpload(\n r2: R2Bucket,\n bucketName: string,\n key: string,\n url: URL,\n request: Request\n): Promise<Response> {\n const uploadId = url.searchParams.get('uploadId') ?? '';\n if (!uploadId) {\n return new Response('Bad Request: missing uploadId', { status: 400 });\n }\n const bodyText = await request.text();\n const parsedParts = parseCompleteMultipartUploadBody(bodyText);\n const r2Parts: R2UploadedPart[] = parsedParts.map((p) => ({\n partNumber: p.partNumber,\n etag: p.etag\n }));\n const upload = r2.resumeMultipartUpload(key, uploadId);\n const result = await upload.complete(r2Parts);\n return xmlResponse(\n buildCompleteMultipartUploadXml(bucketName, key, result.httpEtag)\n );\n}\n\nasync function handleAbortMultipartUpload(\n r2: R2Bucket,\n key: string,\n url: URL\n): Promise<Response> {\n const uploadId = url.searchParams.get('uploadId') ?? '';\n if (!uploadId) {\n return new Response('Bad Request: missing uploadId', { status: 400 });\n }\n const upload = r2.resumeMultipartUpload(key, uploadId);\n await upload.abort();\n return new Response(null, { status: 204 });\n}\n\n// ---------------------------------------------------------------------------\n// Main handler\n// ---------------------------------------------------------------------------\n\nexport const r2EgressHandler: OutboundHandler<\n Cloudflare.Env,\n R2EgressParams\n> = async (\n request: Request,\n env: Cloudflare.Env,\n ctx: OutboundHandlerContext<R2EgressParams>\n): Promise<Response> => {\n const url = new URL(request.url);\n const parsed = parsePath(url.pathname);\n\n if (!parsed) {\n return new Response('Bad Request: empty path', { status: 400 });\n }\n\n const { bucket: bucketName, key } = parsed;\n\n if (!ctx.params?.buckets || !(bucketName in ctx.params.buckets)) {\n return new Response(\n `Access to R2 bucket \"${bucketName}\" is not permitted. ` +\n 'Call mountBucket() with this bucket before accessing it.',\n { status: 403 }\n );\n }\n\n const bucketParams = ctx.params.buckets[bucketName];\n const rawPrefix = bucketParams.prefix;\n const mountPrefix = rawPrefix\n ? trimTrailingSlashes(normalizeObjectKey(rawPrefix))\n : undefined;\n const readOnly = bucketParams.readOnly ?? false;\n\n const r2 = resolveR2Bucket(env, bucketName);\n if (!r2) {\n return new Response(\n `R2 binding \"${bucketName}\" not found in Worker env. ` +\n 'Ensure the binding name matches the bucket name passed to mountBucket().',\n { status: 500 }\n );\n }\n\n const { method } = request;\n\n // Bucket-level operations (no key)\n if (!key) {\n if (method === 'GET' && url.searchParams.has('location')) {\n return xmlResponse(buildLocationXml());\n }\n if (method === 'GET' && url.searchParams.get('list-type') === '2') {\n return handleListObjects(r2, bucketName, url, mountPrefix);\n }\n // Legacy ListObjects (v1) — s3fs may use this for some operations\n if (method === 'GET') {\n return handleListObjects(r2, bucketName, url, mountPrefix);\n }\n return new Response('Method Not Allowed', { status: 405 });\n }\n\n const fullKey = mountPrefix ? `${mountPrefix}/${key}` : key;\n const permitted = new Set(Object.keys(ctx.params.buckets));\n\n if (\n readOnly &&\n (method === 'PUT' ||\n method === 'DELETE' ||\n (method === 'POST' &&\n (url.searchParams.has('uploads') || url.searchParams.has('uploadId'))))\n ) {\n return new Response('Forbidden: bucket mount is read-only', {\n status: 403\n });\n }\n\n // Multipart upload: POST /bucket/key?uploads — initiate\n if (method === 'POST' && url.searchParams.has('uploads')) {\n return handleCreateMultipartUpload(r2, bucketName, fullKey, request);\n }\n\n // Multipart upload: POST /bucket/key?uploadId=X — complete\n if (method === 'POST' && url.searchParams.has('uploadId')) {\n return handleCompleteMultipartUpload(r2, bucketName, fullKey, url, request);\n }\n\n // Multipart upload: PUT /bucket/key?partNumber=N&uploadId=X — upload part\n if (\n method === 'PUT' &&\n url.searchParams.has('partNumber') &&\n url.searchParams.has('uploadId')\n ) {\n return handleUploadPart(r2, fullKey, url, request);\n }\n\n // Multipart upload: DELETE /bucket/key?uploadId=X — abort\n if (method === 'DELETE' && url.searchParams.has('uploadId')) {\n return handleAbortMultipartUpload(r2, fullKey, url);\n }\n\n // Standard object operations\n switch (method) {\n case 'HEAD':\n return handleHeadObject(r2, fullKey);\n case 'GET':\n return handleGetObject(r2, fullKey, request);\n case 'PUT':\n return handlePutObject(\n r2,\n bucketName,\n fullKey,\n request,\n env,\n permitted,\n mountPrefix\n );\n case 'DELETE':\n return handleDeleteObject(r2, fullKey);\n default:\n return new Response('Method Not Allowed', { status: 405 });\n }\n};\n","/**\n * Outbound handler that signs and forwards S3-compatible requests from s3fs\n * to the configured endpoint using real credentials held in the Durable Object.\n *\n * s3fs inside the container sends requests to the stable internal proxy host\n * `s3-credential-proxy.internal`. The mount ID is encoded as the first path\n * segment so the handler can resolve the correct mount config without per-mount\n * subdomain registration.\n *\n * Request path layout: /${mountId}/${bucket}/${objectKey}\n * Real upstream URL: ${endpoint}/${bucket}/${objectKey}\n *\n * The handler also accepts the legacy per-mount-host shape\n * (${mountId}.s3-credential-proxy.internal) for backward compatibility.\n */\n\nimport type {\n OutboundHandler,\n OutboundHandlerContext\n} from '@cloudflare/containers';\nimport type { BucketProvider } from '@repo/shared';\nimport { AwsClient } from 'aws4fetch';\nimport type { S3CredentialProxyParams } from '../types';\n\nconst PER_MOUNT_SUFFIX = '.s3-credential-proxy.internal';\nexport const SELF_TEST_PATH = '/__sandbox_credential_proxy_self_test__';\nexport const DIAGNOSTICS_PATH = '/__sandbox_credential_proxy_diagnostics__';\nconst DEFAULT_SLOW_REQUEST_MS = 1000;\nconst ERROR_RESPONSE_BODY_LIMIT = 2048;\nconst MAX_DIAGNOSTIC_EVENTS = 500;\n\nexport const DUMMY_AUTH_HEADERS = new Set([\n 'authorization',\n 'x-amz-date',\n 'x-amz-content-sha256',\n 'x-amz-security-token',\n 'x-goog-date',\n 'x-goog-content-sha256'\n]);\n\ntype CredentialProxyDebugConfig = {\n diagnosticsEndpointEnabled: boolean;\n enabled: boolean;\n slowRequestMs: number;\n};\n\ntype CredentialProxyRequestInfo = {\n authStrategy: S3CredentialProxyParams['mounts'][string]['authStrategy'];\n bucket: string;\n bodyPresent?: boolean;\n contentLength: string | null;\n method: string;\n mountId: string;\n payloadHashMode?: 'signed' | 'unsigned';\n query: string[];\n clientSetupMs?: number;\n upstreamMs?: number;\n};\n\ntype CredentialProxyDiagnosticEvent = CredentialProxyRequestInfo & {\n containerId: string;\n durationMs: number;\n ok: boolean;\n path: string;\n status: number;\n timestamp: string;\n};\n\ntype SigV4ClientCacheEntry = {\n client: AwsClient;\n accessKeyId: string;\n secretAccessKey: string;\n endpoint: string;\n provider: BucketProvider | null;\n region: string;\n};\n\nconst sigV4ClientCache = new Map<string, SigV4ClientCacheEntry>();\nconst directoryMarkerCache = new Map<string, [string, string][]>();\nconst credentialProxyDiagnosticEvents: CredentialProxyDiagnosticEvent[] = [];\nlet credentialProxyDiagnosticEventCount = 0;\n\nexport function evictSigV4ClientCacheEntry(mountId: string): void {\n sigV4ClientCache.delete(mountId);\n}\n\nexport function evictDirectoryMarkerCacheForMount(mountId: string): void {\n const prefix = `${mountId}:`;\n for (const key of directoryMarkerCache.keys()) {\n if (key.startsWith(prefix)) {\n directoryMarkerCache.delete(key);\n }\n }\n}\n\nfunction toHex(buffer: ArrayBuffer): string {\n return Array.from(new Uint8Array(buffer))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\nasync function sha256Hex(data: string): Promise<string> {\n return toHex(\n await crypto.subtle.digest('SHA-256', new TextEncoder().encode(data))\n );\n}\n\nasync function hmacSHA256(\n key: BufferSource,\n data: string\n): Promise<ArrayBuffer> {\n const cryptoKey = await crypto.subtle.importKey(\n 'raw',\n key,\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign']\n );\n return crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(data));\n}\n\nfunction detectS3Region(\n provider: BucketProvider | null,\n endpoint: string\n): string {\n if (provider === 'r2') return 'auto';\n try {\n const host = new URL(endpoint).hostname;\n const m = host.match(/s3[.-]([a-z0-9-]+)\\.amazonaws\\.com/);\n if (m && m[1] !== 'amazonaws') return m[1];\n if (host === 's3.amazonaws.com') return 'us-east-1';\n } catch {\n // ignore\n }\n return 'auto';\n}\n\nfunction buildCleanHeaders(original: Headers): Headers {\n const clean = new Headers();\n for (const [k, v] of original as unknown as Iterable<[string, string]>) {\n const lower = k.toLowerCase();\n // Strip dummy signing headers and the intercepted proxy host; x-amz-content-sha256\n // is stripped here too (it's in DUMMY_AUTH_HEADERS) so it can be re-added below\n // only when the original value is a genuine payload hash (not a dummy signing value).\n if (!DUMMY_AUTH_HEADERS.has(lower) && lower !== 'host') {\n clean.set(k, v);\n }\n }\n const contentSHA256 = original.get('x-amz-content-sha256');\n if (contentSHA256 && isValidContentSHA256(contentSHA256)) {\n clean.set('x-amz-content-sha256', contentSHA256);\n }\n return clean;\n}\n\nfunction isValidContentSHA256(value: string): boolean {\n return value === 'UNSIGNED-PAYLOAD' || /^[a-fA-F0-9]{64}$/.test(value);\n}\n\nfunction getCredentialProxyDebugConfig(\n env: Cloudflare.Env\n): CredentialProxyDebugConfig {\n const envRecord = env as unknown as Record<string, unknown>;\n const enabled = envRecord.SANDBOX_CREDENTIAL_PROXY_DEBUG === 'true';\n const diagnosticsEndpointEnabled =\n envRecord.SANDBOX_CREDENTIAL_PROXY_DIAGNOSTICS_ENDPOINT === 'true';\n const configuredSlowRequestMs = Number(\n envRecord.SANDBOX_CREDENTIAL_PROXY_SLOW_REQUEST_MS\n );\n const slowRequestMs =\n Number.isFinite(configuredSlowRequestMs) && configuredSlowRequestMs >= 0\n ? configuredSlowRequestMs\n : DEFAULT_SLOW_REQUEST_MS;\n return { diagnosticsEndpointEnabled, enabled, slowRequestMs };\n}\n\nfunction recordCredentialProxyDiagnosticEvent(\n event: CredentialProxyDiagnosticEvent\n): void {\n credentialProxyDiagnosticEvents.push(event);\n credentialProxyDiagnosticEventCount++;\n while (credentialProxyDiagnosticEvents.length > MAX_DIAGNOSTIC_EVENTS) {\n credentialProxyDiagnosticEvents.shift();\n }\n}\n\nfunction getCredentialProxyDiagnosticsResponse(\n url: URL,\n containerId: string\n): Response {\n const since = Number(url.searchParams.get('since') ?? '0');\n const bufferStartCount =\n credentialProxyDiagnosticEventCount -\n credentialProxyDiagnosticEvents.length;\n const events = credentialProxyDiagnosticEvents.filter((event, index) => {\n if (event.containerId !== containerId) return false;\n return !Number.isFinite(since) || bufferStartCount + index >= since;\n });\n return Response.json({\n nextCursor: credentialProxyDiagnosticEventCount,\n events\n });\n}\n\nasync function withCredentialProxyDiagnostics(\n requestInfo: CredentialProxyRequestInfo,\n debugConfig: CredentialProxyDebugConfig,\n containerId: string,\n path: string,\n operation: () => Promise<Response>\n): Promise<Response> {\n const started = Date.now();\n try {\n const response = await operation();\n const durationMs = Date.now() - started;\n if (debugConfig.enabled) {\n recordCredentialProxyDiagnosticEvent({\n ...requestInfo,\n containerId,\n durationMs,\n ok: response.ok,\n path,\n status: response.status,\n timestamp: new Date().toISOString()\n });\n }\n if (debugConfig.enabled || durationMs >= debugConfig.slowRequestMs) {\n console.info('sandbox.s3_credential_proxy.request', {\n ...requestInfo,\n durationMs,\n ok: response.ok,\n status: response.status,\n responseContentLength: response.headers.get('content-length')\n });\n }\n if (!response.ok) {\n // Read the error body asynchronously so we don't delay returning the\n // response to s3fs (important for 4xx/throttle responses).\n const responseForLog = response.clone();\n const requestInfoSnapshot = { ...requestInfo };\n responseForLog\n .text()\n .then((body) => {\n console.warn('sandbox.s3_credential_proxy.upstream_error', {\n ...requestInfoSnapshot,\n durationMs,\n status: response.status,\n statusText: response.statusText,\n errorBody: body.slice(0, ERROR_RESPONSE_BODY_LIMIT)\n });\n })\n .catch(() => {});\n }\n return response;\n } catch (error) {\n const durationMs = Date.now() - started;\n console.warn('sandbox.s3_credential_proxy.request_error', {\n ...requestInfo,\n durationMs,\n error: error instanceof Error ? error.message : String(error)\n });\n throw error;\n }\n}\n\nfunction getSigV4Client(\n mountId: string,\n endpoint: string,\n provider: BucketProvider | null,\n credentials: { accessKeyId: string; secretAccessKey: string },\n region: string\n): AwsClient {\n const cached = sigV4ClientCache.get(mountId);\n if (\n cached &&\n cached.accessKeyId === credentials.accessKeyId &&\n cached.secretAccessKey === credentials.secretAccessKey &&\n cached.endpoint === endpoint &&\n cached.provider === provider &&\n cached.region === region\n ) {\n return cached.client;\n }\n\n const client = new AwsClient({\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n service: 's3',\n region,\n retries: 0\n });\n sigV4ClientCache.set(mountId, {\n client,\n accessKeyId: credentials.accessKeyId,\n secretAccessKey: credentials.secretAccessKey,\n endpoint,\n provider,\n region\n });\n return client;\n}\n\nfunction encodeCanonicalQueryPart(value: string): string {\n return encodeURIComponent(value).replace(\n /[!'()*]/g,\n (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`\n );\n}\n\nfunction getCanonicalURI(url: URL): string {\n return url.pathname\n .split('/')\n .map((segment) =>\n segment\n .split(/(%[0-9A-Fa-f]{2})/g)\n .map((part) =>\n /^%[0-9A-Fa-f]{2}$/.test(part)\n ? part.toUpperCase()\n : encodeCanonicalQueryPart(part)\n )\n .join('')\n )\n .join('/');\n}\n\nfunction getCanonicalQueryString(url: URL): string {\n const query = url.search.startsWith('?') ? url.search.slice(1) : url.search;\n if (!query) return '';\n return query\n .split('&')\n .map((part): [string, string] => {\n const separatorIndex = part.indexOf('=');\n if (separatorIndex === -1) return [part, ''];\n return [part.slice(0, separatorIndex), part.slice(separatorIndex + 1)];\n })\n .map(([key, value]): [string, string] => [\n encodeCanonicalQueryPart(decodeURIEncodedQueryPart(key)),\n encodeCanonicalQueryPart(decodeURIEncodedQueryPart(value))\n ])\n .sort(([leftKey, leftValue], [rightKey, rightValue]) => {\n if (leftKey < rightKey) return -1;\n if (leftKey > rightKey) return 1;\n if (leftValue < rightValue) return -1;\n if (leftValue > rightValue) return 1;\n return 0;\n })\n .map(([key, value]) => `${key}=${value}`)\n .join('&');\n}\n\nfunction decodeURIEncodedQueryPart(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\nfunction getSigV4PayloadHash(headers: Headers): {\n hash: string;\n mode: 'signed' | 'unsigned';\n} {\n const existingHash = headers.get('x-amz-content-sha256');\n if (existingHash && existingHash !== 'UNSIGNED-PAYLOAD') {\n return { hash: existingHash, mode: 'signed' };\n }\n return { hash: 'UNSIGNED-PAYLOAD', mode: 'unsigned' };\n}\n\nfunction isZeroLengthDirectoryMarkerPUT(\n request: Request,\n realPath: string\n): boolean {\n return (\n request.method.toUpperCase() === 'PUT' &&\n request.headers.get('content-length') === '0' &&\n realPath.endsWith('/')\n );\n}\n\nfunction isDirectoryMarkerHEAD(request: Request): boolean {\n return request.method.toUpperCase() === 'HEAD';\n}\n\nfunction getDirectoryMarkerCacheKey(mountId: string, realPath: string): string {\n return `${mountId}:${realPath.replace(/\\/+$/, '')}`;\n}\n\nfunction getDirectoryMarkerResponseHeaders(\n request: Request\n): [string, string][] {\n const headers: [string, string][] = [\n ['Accept-Ranges', 'bytes'],\n ['Content-Length', '0'],\n ['ETag', '\"d41d8cd98f00b204e9800998ecf8427e\"'],\n ['Last-Modified', new Date().toUTCString()]\n ];\n const contentType = request.headers.get('content-type');\n if (contentType) {\n headers.push(['Content-Type', contentType]);\n }\n for (const [name, value] of request.headers as unknown as Iterable<\n [string, string]\n >) {\n if (name.toLowerCase().startsWith('x-amz-meta-')) {\n headers.push([name, value]);\n }\n }\n return headers;\n}\n\nfunction normalizePrefix(prefix: string | undefined): string | undefined {\n if (!prefix) return undefined;\n return prefix.replace(/^\\/+/, '').replace(/\\/+$/, '');\n}\n\nfunction getObjectKeyForPath(realPath: string, bucket: string): string | null {\n const pathSegments = realPath.split('/').filter(Boolean);\n const requestBucket = pathSegments[0];\n if (requestBucket !== bucket) {\n return null;\n }\n return pathSegments.slice(1).join('/');\n}\n\nfunction isObjectKeyWithinPrefix(\n objectKey: string,\n prefix: string | undefined\n): boolean {\n const normalizedPrefix = normalizePrefix(prefix);\n if (!normalizedPrefix) {\n return true;\n }\n return (\n objectKey === normalizedPrefix ||\n objectKey.startsWith(`${normalizedPrefix}/`)\n );\n}\n\nfunction isRequestWithinMountScope(\n realPath: string,\n url: URL,\n bucket: string,\n prefix: string | undefined\n): boolean {\n const objectKey = getObjectKeyForPath(realPath, bucket);\n if (objectKey === null) {\n return false;\n }\n\n const requestedPrefix = url.searchParams.get('prefix');\n if (objectKey !== '' && !isObjectKeyWithinPrefix(objectKey, prefix)) {\n return false;\n }\n\n if (\n objectKey === '' &&\n normalizePrefix(prefix) !== undefined &&\n url.search !== '' &&\n requestedPrefix === null\n ) {\n return false;\n }\n\n if (requestedPrefix !== null) {\n return isObjectKeyWithinPrefix(requestedPrefix, prefix);\n }\n\n return true;\n}\n\nfunction isBucketRootProbe(\n request: Request,\n realPath: string,\n url: URL,\n bucket: string\n): boolean {\n const method = request.method.toUpperCase();\n return (\n (method === 'GET' || method === 'HEAD') &&\n url.search === '' &&\n getObjectKeyForPath(realPath, bucket) === ''\n );\n}\n\nfunction deleteDirectoryMarkerCacheEntry(\n mountId: string,\n realPath: string\n): void {\n directoryMarkerCache.delete(getDirectoryMarkerCacheKey(mountId, realPath));\n}\n\nfunction getContentLength(request: Request): number | null {\n const contentLength = request.headers.get('content-length');\n if (contentLength === null) {\n return null;\n }\n const parsed = Number(contentLength);\n if (!Number.isSafeInteger(parsed) || parsed < 0) {\n return null;\n }\n return parsed;\n}\n\nfunction getSigV4ForwardInit(request: Request): RequestInit {\n const contentLength = getContentLength(request);\n if (contentLength === 0) {\n return { body: new Uint8Array(0) };\n }\n if (contentLength === null || request.body === null) {\n return {};\n }\n\n const { readable, writable } = new FixedLengthStream(contentLength);\n request.body.pipeTo(writable).catch((error) => {\n writable.abort(error).catch(() => {});\n });\n return { body: readable };\n}\n\nfunction getGCSHeaders(request: Request): Headers {\n const headers = new Headers();\n for (const [k, v] of request.headers as unknown as Iterable<\n [string, string]\n >) {\n const lower = k.toLowerCase();\n if (\n DUMMY_AUTH_HEADERS.has(lower) ||\n lower === 'host' ||\n lower === 'content-length' ||\n lower === 'expect'\n ) {\n continue;\n }\n if (lower.startsWith('x-amz-meta-')) {\n headers.set(`x-goog-meta-${lower.slice('x-amz-meta-'.length)}`, v);\n continue;\n }\n if (lower.startsWith('x-amz-')) {\n continue;\n }\n headers.set(k, v);\n }\n return headers;\n}\n\nasync function signAndForwardSigV4(\n request: Request,\n mountId: string,\n endpoint: string,\n provider: BucketProvider | null,\n credentials: { accessKeyId: string; secretAccessKey: string },\n requestInfo: CredentialProxyRequestInfo\n): Promise<Response> {\n const signingStarted = Date.now();\n const region = detectS3Region(provider, endpoint);\n const payload = getSigV4PayloadHash(request.headers);\n const client = getSigV4Client(\n mountId,\n endpoint,\n provider,\n credentials,\n region\n );\n requestInfo.payloadHashMode = payload.mode;\n // clientSetupMs captures region detection + client cache lookup + payload hash;\n // the actual SigV4 signing happens inside client.fetch() and is included in upstreamMs.\n requestInfo.clientSetupMs = Date.now() - signingStarted;\n\n const upstreamStarted = Date.now();\n const forwardInit = getSigV4ForwardInit(request);\n requestInfo.bodyPresent =\n forwardInit?.body !== undefined || request.body !== null;\n const response = await client.fetch(request, forwardInit);\n requestInfo.upstreamMs = Date.now() - upstreamStarted;\n return response;\n}\n\nasync function signAndForwardGCS(\n request: Request,\n credentials: { accessKeyId: string; secretAccessKey: string },\n requestInfo: CredentialProxyRequestInfo\n): Promise<Response> {\n const url = new URL(request.url);\n const gcsHeaders = getGCSHeaders(request);\n const now = new Date();\n const dateStr = `${now\n .toISOString()\n .replace(/[:-]/g, '')\n .replace(/\\.\\d+Z$/, '')}Z`;\n const dateOnly = dateStr.slice(0, 8);\n const location = 'auto';\n const service = 'storage';\n const credentialScope = `${dateOnly}/${location}/${service}/goog4_request`;\n\n const bodyHash = 'UNSIGNED-PAYLOAD';\n\n const headerEntries: [string, string][] = [\n ['host', url.host],\n ['x-goog-content-sha256', bodyHash],\n ['x-goog-date', dateStr]\n ];\n\n for (const [k, v] of gcsHeaders as unknown as Iterable<[string, string]>) {\n headerEntries.push([k.toLowerCase(), v.trim()]);\n }\n\n headerEntries.sort((a, b) => a[0].localeCompare(b[0]));\n const signedHeaders = headerEntries.map(([k]) => k).join(';');\n const canonicalHeaders = headerEntries\n .map(([k, v]) => `${k}:${v}\\n`)\n .join('');\n\n const canonicalRequest = [\n request.method,\n getCanonicalURI(url),\n getCanonicalQueryString(url),\n canonicalHeaders,\n signedHeaders,\n bodyHash\n ].join('\\n');\n\n const canonicalRequestHash = await sha256Hex(canonicalRequest);\n\n const stringToSign = [\n 'GOOG4-HMAC-SHA256',\n dateStr,\n credentialScope,\n canonicalRequestHash\n ].join('\\n');\n\n const enc = new TextEncoder();\n const kDate = await hmacSHA256(\n enc.encode(`GOOG4${credentials.secretAccessKey}`) as unknown as ArrayBuffer,\n dateOnly\n );\n const kRegion = await hmacSHA256(kDate, location);\n const kService = await hmacSHA256(kRegion, service);\n const kSigning = await hmacSHA256(kService, 'goog4_request');\n const sigBytes = await hmacSHA256(kSigning, stringToSign);\n const signature = toHex(sigBytes);\n\n const authorization = `GOOG4-HMAC-SHA256 Credential=${credentials.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n\n const newHeaders = new Headers(gcsHeaders);\n newHeaders.set('x-goog-date', dateStr);\n newHeaders.set('x-goog-content-sha256', bodyHash);\n newHeaders.set('Authorization', authorization);\n\n const contentLength = getContentLength(request);\n const gcsBody = contentLength === 0 ? new Uint8Array(0) : request.body;\n const upstreamStarted = Date.now();\n const response = await fetch(\n new Request(request.url, {\n method: request.method,\n headers: newHeaders,\n body: gcsBody\n })\n );\n requestInfo.upstreamMs = Date.now() - upstreamStarted;\n return response;\n}\n\nexport const s3CredentialProxyHandler: OutboundHandler<\n Cloudflare.Env,\n S3CredentialProxyParams\n> = async (\n request: Request,\n env: Cloudflare.Env,\n ctx: OutboundHandlerContext<S3CredentialProxyParams>\n): Promise<Response> => {\n const url = new URL(request.url);\n\n if (url.pathname === SELF_TEST_PATH) {\n return new Response('OK', { status: 200 });\n }\n\n const debugConfig = getCredentialProxyDebugConfig(env);\n\n if (url.pathname === DIAGNOSTICS_PATH) {\n if (!debugConfig.enabled || !debugConfig.diagnosticsEndpointEnabled) {\n return new Response('Not Found', { status: 404 });\n }\n return getCredentialProxyDiagnosticsResponse(url, ctx.containerId);\n }\n\n const segments = url.pathname.split('/').filter(Boolean);\n const hostname = url.hostname;\n\n let mountId: string | null;\n let realPath: string;\n\n if (hostname.endsWith(PER_MOUNT_SUFFIX)) {\n // Legacy per-mount-host shape: mountId is the subdomain\n mountId = hostname.slice(0, -PER_MOUNT_SUFFIX.length);\n realPath = url.pathname;\n } else {\n // Primary path-based shape: /${mountId}/${bucket}/${objectKey}\n mountId = segments[0] ?? null;\n realPath = mountId ? url.pathname.slice(`/${mountId}`.length) || '/' : '/';\n }\n\n if (!mountId) {\n return new Response('Bad Request: missing mount ID', { status: 400 });\n }\n\n const mount = ctx.params?.mounts[mountId];\n if (!mount) {\n return new Response(`Forbidden: unknown mount ID \"${mountId}\"`, {\n status: 403\n });\n }\n\n if (mount.readOnly) {\n const method = request.method.toUpperCase();\n if (method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {\n return new Response('Forbidden: bucket mount is read-only', {\n status: 403\n });\n }\n }\n\n const realUrl = new URL(realPath + (url.search || ''), mount.endpoint);\n if (isBucketRootProbe(request, realPath, url, mount.bucket)) {\n return new Response(null, { status: 200 });\n }\n if (!isRequestWithinMountScope(realPath, url, mount.bucket, mount.prefix)) {\n return new Response('Forbidden: request is outside mounted bucket scope', {\n status: 403\n });\n }\n const cleanHeaders = buildCleanHeaders(request.headers);\n const cleanRequest = new Request(realUrl.toString(), {\n method: request.method,\n headers: cleanHeaders,\n body: request.body\n });\n const requestInfo: CredentialProxyRequestInfo = {\n authStrategy: mount.authStrategy,\n bucket: mount.bucket,\n contentLength: request.headers.get('content-length'),\n method: request.method,\n mountId,\n query: [...url.searchParams.keys()].sort()\n };\n\n if (isZeroLengthDirectoryMarkerPUT(cleanRequest, realPath)) {\n const responseHeaders = getDirectoryMarkerResponseHeaders(cleanRequest);\n requestInfo.bodyPresent = request.body !== null;\n if (mount.authStrategy === 's3-sigv4') {\n requestInfo.payloadHashMode = getSigV4PayloadHash(\n cleanRequest.headers\n ).mode;\n }\n return withCredentialProxyDiagnostics(\n requestInfo,\n debugConfig,\n ctx.containerId,\n realPath,\n async () => {\n const response =\n mount.authStrategy === 'gcs'\n ? await signAndForwardGCS(\n cleanRequest,\n mount.credentials,\n requestInfo\n )\n : await signAndForwardSigV4(\n cleanRequest,\n mountId,\n mount.endpoint,\n mount.provider,\n mount.credentials,\n requestInfo\n );\n\n if (response.ok) {\n directoryMarkerCache.set(\n getDirectoryMarkerCacheKey(mountId, realPath),\n responseHeaders\n );\n }\n\n return response;\n }\n );\n }\n\n if (isDirectoryMarkerHEAD(cleanRequest)) {\n const responseHeaders = directoryMarkerCache.get(\n getDirectoryMarkerCacheKey(mountId, realPath)\n );\n if (responseHeaders) {\n requestInfo.bodyPresent = false;\n if (mount.authStrategy === 's3-sigv4') {\n requestInfo.payloadHashMode = getSigV4PayloadHash(\n cleanRequest.headers\n ).mode;\n }\n return withCredentialProxyDiagnostics(\n requestInfo,\n debugConfig,\n ctx.containerId,\n realPath,\n () =>\n Promise.resolve(\n new Response(null, {\n status: 200,\n headers: responseHeaders\n })\n )\n );\n }\n }\n\n if (cleanRequest.method.toUpperCase() !== 'HEAD') {\n deleteDirectoryMarkerCacheEntry(mountId, realPath);\n }\n\n if (mount.authStrategy === 'gcs') {\n return withCredentialProxyDiagnostics(\n requestInfo,\n debugConfig,\n ctx.containerId,\n realPath,\n () => signAndForwardGCS(cleanRequest, mount.credentials, requestInfo)\n );\n }\n\n return withCredentialProxyDiagnostics(\n requestInfo,\n debugConfig,\n ctx.containerId,\n realPath,\n () =>\n signAndForwardSigV4(\n cleanRequest,\n mountId,\n mount.endpoint,\n mount.provider,\n mount.credentials,\n requestInfo\n )\n );\n};\n","import {\n ContainerProxy as BaseContainerProxy,\n type OutboundHandlerContext\n} from '@cloudflare/containers';\nimport type { HTTPAuthInterceptorParams } from '../../extensions/index.js';\nimport { gitCredentialProxyHandler } from './git-credential-proxy-handler';\n\ntype GitAuthInterceptorParams = HTTPAuthInterceptorParams;\n\nimport type { S3CredentialProxyParams } from '../types';\nimport { type R2EgressParams, r2EgressHandler } from './r2-egress-handler';\nimport { s3CredentialProxyHandler } from './s3-credential-proxy-handler';\n\nexport const CONTAINER_PROXY_CLASS_NAME = 'ContainerProxy';\nexport const S3_CREDENTIAL_PROXY_HOST = 's3-credential-proxy.internal';\nexport const S3_CREDENTIAL_PROXY_DIAGNOSTIC_HOST =\n 's3-credential-proxy.sandbox.test';\n\nexport type SDKOutboundMethod = keyof typeof SDK_OUTBOUND_HANDLERS;\n\nexport type OutboundHandlerRegistry = {\n outboundHandlers?: Record<string, unknown>;\n};\n\nexport const SDK_OUTBOUND_HANDLERS = {\n r2EgressMount: r2EgressHandler,\n s3CredentialProxyMount: s3CredentialProxyHandler,\n gitCredentialProxy: gitCredentialProxyHandler\n};\n\nexport function installSDKOutboundHandlers(constructorRef: Function): void {\n const registry = constructorRef as unknown as OutboundHandlerRegistry;\n registry.outboundHandlers = {\n ...(registry.outboundHandlers ?? {}),\n ...SDK_OUTBOUND_HANDLERS\n };\n}\n\ntype ContainerProxyProps = {\n outboundByHostOverrides?: Record<\n string,\n { method: SDKOutboundMethod | string; params?: unknown }\n >;\n containerId?: string;\n className?: string;\n};\n\nexport class ContainerProxy extends BaseContainerProxy {\n async fetch(request: Request): Promise<Response> {\n const hostname = new URL(request.url).hostname;\n const props = this.ctx.props as ContainerProxyProps;\n const override = props.outboundByHostOverrides?.[hostname];\n if (!override) return super.fetch(request);\n\n const handlerCtx = {\n containerId: props.containerId ?? '',\n className: props.className ?? '',\n params: override.params\n };\n\n switch (override.method) {\n case 'r2EgressMount':\n return r2EgressHandler(\n request,\n this.env as Cloudflare.Env,\n handlerCtx as OutboundHandlerContext<R2EgressParams>\n );\n case 's3CredentialProxyMount':\n return s3CredentialProxyHandler(\n request,\n this.env as Cloudflare.Env,\n handlerCtx as OutboundHandlerContext<S3CredentialProxyParams>\n );\n case 'gitCredentialProxy':\n return gitCredentialProxyHandler(\n request,\n this.env as Cloudflare.Env,\n handlerCtx as OutboundHandlerContext<GitAuthInterceptorParams>\n );\n default:\n return super.fetch(request);\n }\n }\n}\n\n(ContainerProxy as unknown as OutboundHandlerRegistry).outboundHandlers =\n SDK_OUTBOUND_HANDLERS;\n","import type { Logger } from '@repo/shared';\nimport type { HTTPAuthInterceptorParams } from '../../extensions/index.js';\n\ntype GitAuthInterceptorParams = HTTPAuthInterceptorParams;\n\nimport { InvalidMountConfigError } from '../errors';\nimport type { S3CredentialProxyParams } from '../types';\nimport {\n CONTAINER_PROXY_CLASS_NAME,\n installSDKOutboundHandlers,\n S3_CREDENTIAL_PROXY_DIAGNOSTIC_HOST,\n S3_CREDENTIAL_PROXY_HOST\n} from './container-proxy';\nimport type { R2EgressParams } from './r2-egress-handler';\nimport { SELF_TEST_PATH as S3_CREDENTIAL_PROXY_SELF_TEST_PATH } from './s3-credential-proxy-handler';\n\nexport type EgressContainerState = DurableObjectState<{}> & {\n exports?: {\n ContainerProxy?: (options: {\n props: {\n enableInternet?: boolean;\n containerId: string;\n className: string;\n outboundByHostOverrides: Record<\n string,\n {\n method: string;\n params:\n | R2EgressParams\n | S3CredentialProxyParams\n | GitAuthInterceptorParams;\n }\n >;\n };\n }) => Fetcher;\n };\n container?: {\n interceptOutboundHttp(host: string, fetcher: Fetcher): Promise<void>;\n };\n};\n\nexport interface MountOutboundHost {\n ctx: EgressContainerState;\n constructorRef: Function;\n enableInternet?: boolean;\n logger: Logger;\n setOutboundByHost<T>(host: string, method: string, params: T): Promise<void>;\n removeOutboundByHost(host: string): Promise<void>;\n}\n\nfunction isFetcher(value: unknown): value is Fetcher {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'fetch' in value &&\n typeof value.fetch === 'function'\n );\n}\n\nexport async function configureR2EgressOutbound(\n host: MountOutboundHost,\n params: R2EgressParams\n): Promise<void> {\n const ctx = host.ctx;\n if (!ctx.container?.interceptOutboundHttp) {\n throw new InvalidMountConfigError(\n 'R2 binding mounts require container outbound interception support'\n );\n }\n if (!ctx.exports?.ContainerProxy) {\n throw new InvalidMountConfigError(\n 'R2 binding mounts require exporting ContainerProxy from the Worker entrypoint'\n );\n }\n\n installSDKOutboundHandlers(host.constructorRef);\n if (Object.keys(params.buckets).length > 0) {\n await host.setOutboundByHost<R2EgressParams>(\n 'r2.internal',\n 'r2EgressMount',\n params\n );\n } else {\n await host.removeOutboundByHost('r2.internal');\n }\n\n host.logger.debug('r2 egress: registering host interception', {\n host: 'r2.internal',\n method: 'r2EgressMount',\n targetClassName: CONTAINER_PROXY_CLASS_NAME\n });\n\n const fetcher = ctx.exports.ContainerProxy({\n props: {\n enableInternet: host.enableInternet,\n containerId: ctx.id.toString(),\n className: CONTAINER_PROXY_CLASS_NAME,\n outboundByHostOverrides: {\n 'r2.internal': {\n method: 'r2EgressMount',\n params\n }\n }\n }\n });\n if (!isFetcher(fetcher)) {\n throw new InvalidMountConfigError(\n 'R2 binding mounts require ContainerProxy to return a valid Fetcher'\n );\n }\n\n await ctx.container.interceptOutboundHttp('r2.internal', fetcher);\n}\n\nexport async function configureGitAuthInterceptor(\n host: MountOutboundHost,\n params: GitAuthInterceptorParams\n): Promise<void> {\n const ctx = host.ctx;\n if (!ctx.container?.interceptOutboundHttp) {\n throw new InvalidMountConfigError(\n 'Git extension authentication requires container outbound interception support'\n );\n }\n if (!ctx.exports?.ContainerProxy) {\n throw new InvalidMountConfigError(\n 'Git extension authentication requires exporting ContainerProxy from the Worker entrypoint. Import ContainerProxy from @cloudflare/sandbox and export it from your Worker to use git auth interception.'\n );\n }\n\n installSDKOutboundHandlers(host.constructorRef);\n\n const hostOverrides: Record<\n string,\n { method: string; params: GitAuthInterceptorParams }\n > = {};\n for (const outboundHost of Object.keys(params.hosts)) {\n hostOverrides[outboundHost] = { method: 'gitCredentialProxy', params };\n await host.setOutboundByHost<GitAuthInterceptorParams>(\n outboundHost,\n 'gitCredentialProxy',\n params\n );\n }\n\n const fetcher = ctx.exports.ContainerProxy({\n props: {\n enableInternet: host.enableInternet,\n containerId: ctx.id.toString(),\n className: CONTAINER_PROXY_CLASS_NAME,\n outboundByHostOverrides: hostOverrides\n }\n });\n if (!isFetcher(fetcher)) {\n throw new InvalidMountConfigError(\n 'Git extension authentication requires ContainerProxy to return a valid Fetcher'\n );\n }\n\n for (const outboundHost of Object.keys(params.hosts)) {\n await ctx.container.interceptOutboundHttp(outboundHost, fetcher);\n }\n}\n\nexport async function configureS3CredentialProxyOutbound(\n host: MountOutboundHost,\n params: S3CredentialProxyParams\n): Promise<void> {\n const ctx = host.ctx;\n if (!ctx.container?.interceptOutboundHttp) {\n throw new InvalidMountConfigError(\n 'Credential proxy bucket mounts require container outbound interception support'\n );\n }\n if (!ctx.exports?.ContainerProxy) {\n throw new InvalidMountConfigError(\n 'Credential proxy bucket mounts require exporting ContainerProxy from the Worker entrypoint'\n );\n }\n\n const hosts = [S3_CREDENTIAL_PROXY_HOST, S3_CREDENTIAL_PROXY_DIAGNOSTIC_HOST];\n\n installSDKOutboundHandlers(host.constructorRef);\n if (Object.keys(params.mounts).length > 0) {\n for (const outboundHost of hosts) {\n await host.setOutboundByHost<S3CredentialProxyParams>(\n outboundHost,\n 's3CredentialProxyMount',\n params\n );\n }\n } else {\n for (const outboundHost of hosts) {\n await host.removeOutboundByHost(outboundHost);\n }\n }\n\n const hostOverrides: Record<\n string,\n { method: string; params: S3CredentialProxyParams }\n > = {};\n for (const outboundHost of hosts) {\n hostOverrides[outboundHost] = {\n method: 's3CredentialProxyMount',\n params\n };\n }\n\n host.logger.debug('s3 credential proxy: registering host interception', {\n hosts,\n method: 's3CredentialProxyMount',\n targetClassName: CONTAINER_PROXY_CLASS_NAME\n });\n\n const fetcher = ctx.exports.ContainerProxy({\n props: {\n enableInternet: host.enableInternet,\n containerId: ctx.id.toString(),\n className: CONTAINER_PROXY_CLASS_NAME,\n outboundByHostOverrides: hostOverrides\n }\n });\n if (!isFetcher(fetcher)) {\n throw new InvalidMountConfigError(\n 'Credential proxy bucket mounts require ContainerProxy to return a valid Fetcher'\n );\n }\n\n try {\n const selfTest = await fetcher.fetch(\n new Request(\n `http://${S3_CREDENTIAL_PROXY_HOST}${S3_CREDENTIAL_PROXY_SELF_TEST_PATH}`\n )\n );\n await selfTest.text();\n host.logger.debug('s3 credential proxy: fetcher self-test complete', {\n status: selfTest.status\n });\n } catch (error) {\n host.logger.warn('s3 credential proxy: fetcher self-test failed', {\n error: error instanceof Error ? error.message : String(error)\n });\n }\n\n for (const outboundHost of hosts) {\n await ctx.container.interceptOutboundHttp(outboundHost, fetcher);\n }\n}\n","import type { MountInfo } from './types';\n\nexport class MountRegistry {\n private readonly mounts = new Map<string, MountInfo>();\n\n get activeMounts(): Map<string, MountInfo> {\n return this.mounts;\n }\n\n has(mountPath: string): boolean {\n return this.mounts.has(mountPath);\n }\n\n get(mountPath: string): MountInfo | undefined {\n return this.mounts.get(mountPath);\n }\n\n set(mountPath: string, mountInfo: MountInfo): void {\n this.mounts.set(mountPath, mountInfo);\n }\n\n delete(mountPath: string): void {\n this.mounts.delete(mountPath);\n }\n\n clear(): void {\n this.mounts.clear();\n }\n\n entries(): IterableIterator<[string, MountInfo]> {\n return this.mounts.entries();\n }\n\n [Symbol.iterator](): IterableIterator<[string, MountInfo]> {\n return this.mounts.entries();\n }\n}\n","/**\n * Provider detection and s3fs flag configuration\n *\n * Based on s3fs-fuse documentation:\n * https://github.com/s3fs-fuse/s3fs-fuse/wiki/Non-Amazon-S3\n */\n\nimport type { BucketProvider } from '@repo/shared';\n\n/**\n * Detect provider from endpoint URL using pattern matching\n */\nexport function detectProviderFromUrl(endpoint: string): BucketProvider | null {\n try {\n const url = new URL(endpoint);\n const hostname = url.hostname.toLowerCase();\n\n if (hostname.endsWith('.r2.cloudflarestorage.com')) {\n return 'r2';\n }\n\n // Match AWS S3: *.amazonaws.com or s3.amazonaws.com\n if (\n hostname.endsWith('.amazonaws.com') ||\n hostname === 's3.amazonaws.com'\n ) {\n return 's3';\n }\n\n if (hostname === 'storage.googleapis.com') {\n return 'gcs';\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get s3fs flags for a given provider\n *\n * Based on s3fs-fuse wiki recommendations:\n * https://github.com/s3fs-fuse/s3fs-fuse/wiki/Non-Amazon-S3\n */\nexport function getProviderFlags(provider: BucketProvider | null): string[] {\n if (!provider) {\n return ['use_path_request_style'];\n }\n\n switch (provider) {\n case 'r2':\n return ['nomixupload'];\n\n case 's3':\n return [];\n\n case 'gcs':\n return [];\n\n default:\n return ['use_path_request_style'];\n }\n}\n\n/**\n * Resolve s3fs options by combining provider defaults with user overrides\n */\nexport function resolveS3fsOptions(\n provider: BucketProvider | null,\n userOptions?: string[]\n): string[] {\n const providerFlags = getProviderFlags(provider);\n\n if (!userOptions || userOptions.length === 0) {\n return providerFlags;\n }\n\n // Merge provider flags with user options\n // User options take precedence (come last in the array)\n const allFlags = [...providerFlags, ...userOptions];\n\n // Deduplicate flags (keep last occurrence)\n const flagMap = new Map<string, string>();\n\n for (const flag of allFlags) {\n // Split on '=' to get the flag name\n const [flagName] = flag.split('=');\n flagMap.set(flagName, flag);\n }\n\n return Array.from(flagMap.values());\n}\n","import type { BucketCredentials } from '@repo/shared';\nimport type { S3FSHost } from './host';\n\nconst S3FS_DISABLE_EXPECT_HEADER_CONFIG = ' Expect:\\n';\n\nexport function generatePasswordFilePath(): string {\n return `/tmp/.passwd-s3fs-${crypto.randomUUID()}`;\n}\n\nexport function generateS3FSAdditionalHeaderFilePath(): string {\n return `/tmp/.s3fs-ahbe-${crypto.randomUUID()}.conf`;\n}\n\nexport async function createDisableExpectHeaderFile(\n host: S3FSHost,\n headerFilePath: string\n): Promise<void> {\n await host.runRuntimeCall('mount.s3fs.writeHeaderFile', (control) =>\n control.files.writeFile(headerFilePath, S3FS_DISABLE_EXPECT_HEADER_CONFIG)\n );\n await host.runRuntimeCall('mount.s3fs.chmodHeaderFile', (control) =>\n control.mounts.chmodOwnerOnly(headerFilePath)\n );\n}\n\nexport async function createPasswordFile(\n host: S3FSHost,\n passwordFilePath: string,\n bucket: string,\n credentials: BucketCredentials\n): Promise<void> {\n const content = `${bucket}:${credentials.accessKeyId}:${credentials.secretAccessKey}`;\n await host.runRuntimeCall('mount.s3fs.writePasswordFile', (control) =>\n control.files.writeFile(passwordFilePath, content)\n );\n await host.runRuntimeCall('mount.s3fs.chmodPasswordFile', (control) =>\n control.mounts.chmodOwnerOnly(passwordFilePath)\n );\n}\n\nexport async function deletePasswordFile(\n host: S3FSHost,\n passwordFilePath: string\n): Promise<void> {\n try {\n await host.runRuntimeCall('mount.s3fs.deletePasswordFile', (control) =>\n control.mounts.deleteFile(passwordFilePath)\n );\n } catch (error) {\n host.logger.warn('password file cleanup failed', {\n passwordFilePath,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n}\n\nexport async function deleteAdditionalHeaderFile(\n host: S3FSHost,\n headerFilePath: string\n): Promise<void> {\n try {\n await host.runRuntimeCall('mount.s3fs.deleteHeaderFile', (control) =>\n control.mounts.deleteFile(headerFilePath)\n );\n } catch (error) {\n host.logger.warn('s3fs additional header file cleanup failed', {\n headerFilePath,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n}\n","import type { BucketProvider, RemoteMountBucketOptions } from '@repo/shared';\nimport { shellEscape } from '@repo/shared';\nimport { InvalidMountConfigError, S3FSMountError } from '../errors';\nimport type { FuseMountInfo, R2BindingMountInfo } from '../types';\nimport { resolveS3fsOptions } from '../validation/provider';\n\nexport type { S3FSHost } from './host';\n\nimport type { S3FSHost } from './host';\n\nexport {\n createDisableExpectHeaderFile,\n createPasswordFile,\n deleteAdditionalHeaderFile,\n deletePasswordFile,\n generatePasswordFilePath,\n generateS3FSAdditionalHeaderFilePath\n} from './support-files';\nexport const R2_DEFAULT_S3FS_OPTIONS: Readonly<\n Record<string, string | boolean>\n> = {\n stat_cache_expire: '60',\n enable_noobj_cache: true,\n multipart_size: '5'\n};\n\nexport const R2_DEFAULT_S3FS_OPTION_ENTRIES = Object.entries(\n R2_DEFAULT_S3FS_OPTIONS\n).map(([key, value]) => (value === true ? key : `${key}=${value}`));\n\nexport function sh(\n strings: TemplateStringsArray,\n ...values: unknown[]\n): string {\n let out = strings[0];\n for (let i = 0; i < values.length; i++) {\n out += shellEscape(String(values[i])) + strings[i + 1];\n }\n return out;\n}\n\nexport function randomHex(bytes: number): string {\n const buf = new Uint8Array(bytes);\n crypto.getRandomValues(buf);\n return Array.from(buf, (b) => b.toString(16).padStart(2, '0')).join('');\n}\n\nexport function parseS3fsOptions(\n entries: string[]\n): Record<string, string | boolean> {\n const result: Record<string, string | boolean> = {};\n for (const entry of entries) {\n const eq = entry.indexOf('=');\n if (eq === -1) {\n result[entry] = true;\n } else {\n result[entry.slice(0, eq)] = entry.slice(eq + 1);\n }\n }\n return result;\n}\n\nexport function serializeS3fsOptions(\n options: Record<string, string | boolean>\n): string {\n return Object.entries(options)\n .filter(([, value]) => value !== false)\n .map(([key, value]) => (value === true ? key : `${key}=${value}`))\n .join(',');\n}\n\nexport function validateProtectedS3fsOptions(\n options: string[] | undefined,\n mountLabel: string,\n extraProtected: string[] = []\n): void {\n if (!options) return;\n const protectedOptions = new Set(['passwd_file', 'url', ...extraProtected]);\n for (const option of options) {\n const [key] = option.split('=');\n if (protectedOptions.has(key)) {\n throw new InvalidMountConfigError(\n `s3fs option \"${key}\" cannot be overridden for ${mountLabel} mounts`\n );\n }\n }\n}\n\nexport async function executeS3FSMount(\n host: S3FSHost,\n params: {\n bucket: string;\n mountPath: string;\n options: RemoteMountBucketOptions;\n provider: BucketProvider | null;\n passwordFilePath: string;\n }\n): Promise<void> {\n const logSuffix = randomHex(4);\n const sdkDefaults: Record<string, string | boolean> = {\n logfile: `/tmp/.s3fs-log-${logSuffix}`\n };\n const s3fsOptions: Record<string, string | boolean> = {\n ...sdkDefaults,\n ...parseS3fsOptions(resolveS3fsOptions(params.provider)),\n ...parseS3fsOptions(params.options.s3fsOptions ?? []),\n passwd_file: params.passwordFilePath,\n url: params.options.endpoint,\n ...(params.options.readOnly ? { ro: true } : {})\n };\n const result = await host.runRuntimeCall('mount.s3fs.mount', (control) =>\n control.mounts.mountS3FSAndVerify({\n source: params.bucket,\n mountPath: params.mountPath,\n options: s3fsOptions\n })\n );\n if (result.success) return;\n\n const detail = result.stdout?.trim() || result.stderr?.trim() || '';\n if (result.exitCode === 2) {\n throw new S3FSMountError(`S3FS mount failed: ${detail || 'Unknown error'}`);\n }\n\n const diagMessage = detail\n ? `s3fs log: ${detail}`\n : 'No s3fs log output captured. The s3fs daemon may have exited before writing logs.';\n throw new S3FSMountError(\n `S3FS mount failed: FUSE filesystem never appeared at ${params.mountPath}. ${diagMessage}`\n );\n}\n\nexport async function unmountTrackedFuseMount(\n host: S3FSHost,\n mountPath: string,\n mountInfo: FuseMountInfo | R2BindingMountInfo\n): Promise<boolean> {\n if (!mountInfo.mounted) return true;\n\n host.logger.debug(`Unmounting bucket ${mountInfo.bucket} from ${mountPath}`);\n const result = await host.runRuntimeCall('mount.s3fs.unmount', (control) =>\n control.mounts.unmountFuse(mountPath)\n );\n if (!result.success) {\n throw new Error(\n `fusermount -u failed (exit ${result.exitCode}): ${result.stderr || 'unknown error'}`\n );\n }\n mountInfo.mounted = false;\n return true;\n}\n","import type {\n FuseMountInfo,\n MountInfo,\n S3CredentialProxyParams\n} from '../types';\nimport type { R2EgressParams } from './r2-egress-handler';\n\nfunction hasExcludedID(\n excludedIDs: Iterable<string> | undefined,\n mountId: string\n): boolean {\n if (!excludedIDs) return false;\n for (const excludedID of excludedIDs) {\n if (excludedID === mountId) return true;\n }\n return false;\n}\n\nexport function buildR2EgressParams(\n mounts: Iterable<[string, MountInfo]>,\n options?: {\n excludeMountId?: string;\n excludeMountIds?: Iterable<string>;\n }\n): R2EgressParams {\n const buckets: R2EgressParams['buckets'] = {};\n for (const [, mountInfo] of mounts) {\n if (\n mountInfo.mountType === 'r2-egress' &&\n mountInfo.mountId !== options?.excludeMountId &&\n !hasExcludedID(options?.excludeMountIds, mountInfo.mountId)\n ) {\n buckets[mountInfo.bucket] = {\n prefix: mountInfo.prefix,\n readOnly: mountInfo.readOnly\n };\n }\n }\n return { buckets };\n}\n\nexport function buildS3CredentialProxyParams(\n mounts: Iterable<[string, MountInfo]>,\n options?: {\n excludeMountId?: string;\n excludeMountIds?: Iterable<string>;\n includeMount?: FuseMountInfo;\n }\n): S3CredentialProxyParams {\n const params: S3CredentialProxyParams['mounts'] = {};\n const addMount = (mountInfo: FuseMountInfo) => {\n if (\n !mountInfo.credentialProxy ||\n mountInfo.mountId === options?.excludeMountId ||\n hasExcludedID(options?.excludeMountIds, mountInfo.mountId)\n ) {\n return;\n }\n params[mountInfo.mountId] = {\n endpoint: mountInfo.credentialProxy.endpoint,\n bucket: mountInfo.credentialProxy.bucket,\n ...(mountInfo.credentialProxy.prefix !== undefined\n ? { prefix: mountInfo.credentialProxy.prefix }\n : {}),\n credentials: mountInfo.credentialProxy.credentials,\n readOnly: mountInfo.credentialProxy.readOnly,\n provider: mountInfo.credentialProxy.provider,\n authStrategy: mountInfo.credentialProxy.authStrategy\n };\n };\n\n for (const [, mountInfo] of mounts) {\n if (mountInfo.mountType === 'fuse') addMount(mountInfo);\n }\n if (options?.includeMount) addMount(options.includeMount);\n return { mounts: params };\n}\n","import type { Logger } from '@repo/shared';\nimport {\n configureR2EgressOutbound,\n configureS3CredentialProxyOutbound,\n type MountOutboundHost\n} from './outbound';\nimport {\n buildR2EgressParams,\n buildS3CredentialProxyParams\n} from './outbound/params';\nimport {\n evictDirectoryMarkerCacheForMount,\n evictSigV4ClientCacheEntry\n} from './outbound/s3-credential-proxy-handler';\nimport type { MountRegistry } from './registry';\nimport {\n deleteAdditionalHeaderFile,\n deletePasswordFile,\n type S3FSHost,\n unmountTrackedFuseMount\n} from './s3fs';\n\nexport interface BucketMountDestroyCleanupResult {\n mountsProcessed: number;\n mountFailures: number;\n}\n\nexport interface BucketMountLifecycleCleanupHost {\n registry: MountRegistry;\n logger: Logger;\n s3fsHost: S3FSHost | null;\n getOutboundHost(): MountOutboundHost;\n runMountOperation<T>(operation: () => Promise<T>): Promise<T>;\n}\n\nexport async function cleanupBucketMountsForDestroy(\n host: BucketMountLifecycleCleanupHost\n): Promise<BucketMountDestroyCleanupResult> {\n return host.runMountOperation(async () => {\n let mountsProcessed = 0;\n let mountFailures = 0;\n\n const cleanedMountPaths: string[] = [];\n const cleanedMountIds: string[] = [];\n let hadR2EgressMount = false;\n let hadCredentialProxyMount = false;\n\n for (const [mountPath, mountInfo] of host.registry.entries()) {\n mountsProcessed++;\n if (mountInfo.mountType === 'local-sync') {\n try {\n if (host.s3fsHost) {\n await mountInfo.syncManager.stop();\n } else {\n mountInfo.syncManager.interrupt();\n }\n mountInfo.mounted = false;\n cleanedMountPaths.push(mountPath);\n cleanedMountIds.push(mountInfo.mountId);\n } catch (error) {\n mountFailures++;\n host.logger.warn(\n `Failed to stop local sync for ${mountPath}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n } else {\n if (mountInfo.mountType === 'r2-egress') {\n hadR2EgressMount = true;\n } else if (\n mountInfo.mountType === 'fuse' &&\n mountInfo.credentialProxy\n ) {\n hadCredentialProxyMount = true;\n }\n\n let supportFilesSafeToDelete = false;\n if (host.s3fsHost) {\n try {\n supportFilesSafeToDelete = await unmountTrackedFuseMount(\n host.s3fsHost,\n mountPath,\n mountInfo\n );\n } catch (error) {\n mountFailures++;\n host.logger.warn(\n `Failed to unmount bucket ${mountInfo.bucket} from ${mountPath}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n if (supportFilesSafeToDelete) {\n await deletePasswordFile(host.s3fsHost, mountInfo.passwordFilePath);\n if (mountInfo.additionalHeaderFilePath) {\n await deleteAdditionalHeaderFile(\n host.s3fsHost,\n mountInfo.additionalHeaderFilePath\n );\n }\n }\n } else if (mountInfo.mounted) {\n mountFailures++;\n }\n\n cleanedMountPaths.push(mountPath);\n cleanedMountIds.push(mountInfo.mountId);\n\n if (mountInfo.mountType === 'fuse' && mountInfo.credentialProxy) {\n evictSigV4ClientCacheEntry(mountInfo.mountId);\n evictDirectoryMarkerCacheForMount(mountInfo.mountId);\n }\n }\n }\n\n if (hadR2EgressMount) {\n try {\n await configureR2EgressOutbound(\n host.getOutboundHost(),\n buildR2EgressParams(host.registry, {\n excludeMountIds: cleanedMountIds\n })\n );\n } catch (error) {\n mountFailures++;\n host.logger.warn(\n `Failed to update R2 egress outbound configuration during destroy cleanup: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n if (hadCredentialProxyMount) {\n try {\n await configureS3CredentialProxyOutbound(\n host.getOutboundHost(),\n buildS3CredentialProxyParams(host.registry, {\n excludeMountIds: cleanedMountIds\n })\n );\n } catch (error) {\n mountFailures++;\n host.logger.warn(\n `Failed to update credential proxy outbound configuration during destroy cleanup: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n for (const mountPath of cleanedMountPaths) {\n host.registry.delete(mountPath);\n }\n\n return { mountsProcessed, mountFailures };\n });\n}\n\nexport async function cleanupBucketMountsForStop(\n host: BucketMountLifecycleCleanupHost\n): Promise<void> {\n return host.runMountOperation(async () => {\n let hadR2EgressMount = false;\n let hadCredentialProxyMount = false;\n for (const [, mountInfo] of host.registry) {\n if (mountInfo.mountType === 'local-sync') {\n mountInfo.syncManager.interrupt();\n } else if (mountInfo.mountType === 'r2-egress') {\n hadR2EgressMount = true;\n } else if (mountInfo.mountType === 'fuse' && mountInfo.credentialProxy) {\n hadCredentialProxyMount = true;\n evictSigV4ClientCacheEntry(mountInfo.mountId);\n evictDirectoryMarkerCacheForMount(mountInfo.mountId);\n }\n }\n if (hadR2EgressMount) {\n await configureR2EgressOutbound(host.getOutboundHost(), {\n buckets: {}\n }).catch(() => {});\n }\n if (hadCredentialProxyMount) {\n await configureS3CredentialProxyOutbound(host.getOutboundHost(), {\n mounts: {}\n }).catch(() => {});\n }\n host.registry.clear();\n });\n}\n","import { parseSSEFrames, type SSEPartialEvent } from '@repo/shared';\n\n/**\n * Server-Sent Events (SSE) parser for streaming responses\n * Converts ReadableStream<Uint8Array> to typed AsyncIterable<T>\n */\n\n/**\n * Parse a ReadableStream of SSE events into typed AsyncIterable\n * @param stream - The ReadableStream from fetch response\n * @param signal - Optional AbortSignal for cancellation\n */\nexport async function* parseSSEStream<T>(\n stream: ReadableStream<Uint8Array>,\n signal?: AbortSignal\n): AsyncIterable<T> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let currentEvent: SSEPartialEvent = { data: [] };\n let isAborted = signal?.aborted ?? false;\n\n const emitEvent = (data: string): T | undefined => {\n if (data === '[DONE]' || data.trim() === '') {\n return undefined;\n }\n\n try {\n return JSON.parse(data) as T;\n } catch {\n return undefined;\n }\n };\n\n const onAbort = () => {\n isAborted = true;\n reader.cancel().catch(() => {\n // Reader may already be closed or canceled.\n });\n };\n\n if (signal && !signal.aborted) {\n signal.addEventListener('abort', onAbort);\n }\n\n try {\n while (true) {\n if (isAborted) {\n throw new Error('Operation was aborted');\n }\n\n const { done, value } = await reader.read();\n\n if (isAborted) {\n throw new Error('Operation was aborted');\n }\n\n if (done) break;\n\n // Decode chunk and parse complete SSE frames\n buffer += decoder.decode(value, { stream: true });\n const parsed = parseSSEFrames(buffer, currentEvent);\n buffer = parsed.remaining;\n currentEvent = parsed.currentEvent;\n\n for (const frame of parsed.events) {\n const event = emitEvent(frame.data);\n if (event !== undefined) {\n yield event;\n }\n }\n }\n\n if (isAborted) {\n throw new Error('Operation was aborted');\n }\n\n // Flush any complete frame that can be parsed from remaining data\n const finalParsed = parseSSEFrames(`${buffer}\\n\\n`, currentEvent);\n for (const frame of finalParsed.events) {\n const event = emitEvent(frame.data);\n if (event !== undefined) {\n yield event;\n }\n }\n } finally {\n if (signal) {\n signal.removeEventListener('abort', onAbort);\n }\n\n // Clean up resources\n try {\n await reader.cancel();\n } catch {}\n reader.releaseLock();\n }\n}\n\n/**\n * Helper to convert a Response with SSE stream directly to AsyncIterable\n * @param response - Response object with SSE stream\n * @param signal - Optional AbortSignal for cancellation\n */\nexport async function* responseToAsyncIterable<T>(\n response: Response,\n signal?: AbortSignal\n): AsyncIterable<T> {\n if (!response.ok) {\n throw new Error(\n `Response not ok: ${response.status} ${response.statusText}`\n );\n }\n\n if (!response.body) {\n throw new Error('No response body');\n }\n\n yield* parseSSEStream<T>(response.body, signal);\n}\n\n/**\n * Create an SSE-formatted ReadableStream from an AsyncIterable\n * (Useful for Worker endpoints that need to forward AsyncIterable as SSE)\n * @param events - AsyncIterable of events\n * @param options - Stream options\n */\nexport function asyncIterableToSSEStream<T>(\n events: AsyncIterable<T>,\n options?: {\n signal?: AbortSignal;\n serialize?: (event: T) => string;\n }\n): ReadableStream<Uint8Array> {\n const encoder = new TextEncoder();\n const serialize = options?.serialize || JSON.stringify;\n\n return new ReadableStream({\n async start(controller) {\n try {\n for await (const event of events) {\n if (options?.signal?.aborted) {\n controller.error(new Error('Operation was aborted'));\n break;\n }\n\n const data = serialize(event);\n const sseEvent = `data: ${data}\\n\\n`;\n controller.enqueue(encoder.encode(sseEvent));\n }\n\n // Send completion marker\n controller.enqueue(encoder.encode('data: [DONE]\\n\\n'));\n } catch (error) {\n controller.error(error);\n } finally {\n controller.close();\n }\n },\n\n cancel() {\n // Handle stream cancellation\n }\n });\n}\n","import path from 'node:path/posix';\nimport type { FileWatchSSEEvent, Logger } from '@repo/shared';\nimport type { ContainerControlClient } from './container-control';\nimport { openRemoteSubscription } from './processes/remote-subscription';\nimport { parseSSEStream } from './sse-parser';\nimport { validatePrefix } from './storage-mount';\nimport type {\n MountRuntimeCall,\n MountRuntimeHold\n} from './storage-mount/runtime-call';\n\nconst DEFAULT_POLL_INTERVAL_MS = 1000;\nconst DEFAULT_ECHO_SUPPRESS_TTL_MS = 2000;\nconst MAX_BACKOFF_MS = 30_000;\nconst SYNC_CONCURRENCY = 5;\n\ninterface R2ObjectSnapshot {\n etag: string;\n size: number;\n}\n\ninterface LocalMountSyncOptions {\n bucket: R2Bucket;\n mountPath: string;\n prefix: string | undefined;\n readOnly: boolean;\n runRuntimeCall: MountRuntimeCall;\n runtimeHold?: MountRuntimeHold;\n logger: Logger;\n pollIntervalMs?: number;\n echoSuppressTtlMs?: number;\n}\n\n/**\n * Manages bidirectional sync between an R2 binding and a container directory.\n *\n * R2 -> Container: polls bucket.list() to detect changes, then transfers diffs.\n * Container -> R2: uses inotifywait via the watch API to detect file changes.\n */\nexport class LocalMountSyncManager {\n private readonly bucket: R2Bucket;\n private readonly mountPath: string;\n private readonly prefix: string | undefined;\n private readonly readOnly: boolean;\n private readonly runRuntimeCall: MountRuntimeCall;\n private readonly runtimeHold: MountRuntimeHold;\n private readonly logger: Logger;\n private readonly pollIntervalMs: number;\n\n private readonly echoSuppressTtlMs: number;\n\n private snapshot: Map<string, R2ObjectSnapshot> = new Map();\n private echoSuppressSet: Set<string> = new Set();\n private pollTimer: ReturnType<typeof setTimeout> | null = null;\n private watchReconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private watchAbortController: AbortController | null = null;\n private running = false;\n private generation = 0;\n private activePollCycle: Promise<void> | null = null;\n private activeWatchLoop: Promise<void> | null = null;\n private consecutivePollFailures = 0;\n private consecutiveWatchFailures = 0;\n private runtimeHoldReleased = false;\n\n constructor(options: LocalMountSyncOptions) {\n this.bucket = options.bucket;\n this.mountPath = options.mountPath;\n if (options.prefix !== undefined) {\n validatePrefix(options.prefix);\n }\n // R2 keys never have leading slashes. Convert the validated '/'-prefixed\n // value into bare R2 key format for list() and put().\n this.prefix = options.prefix?.replace(/^\\//, '') || undefined;\n this.readOnly = options.readOnly;\n this.runRuntimeCall = options.runRuntimeCall;\n this.runtimeHold = options.runtimeHold ?? { release: () => {} };\n this.logger = options.logger.child({ operation: 'local-mount-sync' });\n this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.echoSuppressTtlMs =\n options.echoSuppressTtlMs ?? DEFAULT_ECHO_SUPPRESS_TTL_MS;\n }\n\n /**\n * Start bidirectional sync. Performs initial full sync, then starts\n * the R2 poll loop and (if not readOnly) the container watch loop.\n */\n async start(): Promise<void> {\n this.running = true;\n this.generation += 1;\n const generation = this.generation;\n\n await this.runRuntimeCallIfCurrent(\n generation,\n 'mount.local.mkdir',\n (control) =>\n control.files.mkdir(this.mountPath, {\n recursive: true\n })\n );\n\n if (!this.isCurrentGeneration(generation)) return;\n await this.fullSyncR2ToContainer(generation);\n if (!this.isCurrentGeneration(generation)) return;\n this.schedulePoll();\n\n if (!this.readOnly) {\n this.startContainerWatch();\n }\n\n this.logger.info('Local mount sync started', {\n mountPath: this.mountPath,\n prefix: this.prefix,\n readOnly: this.readOnly,\n pollIntervalMs: this.pollIntervalMs\n });\n }\n\n /**\n * Stop all sync activity and clean up resources.\n */\n async stop(): Promise<void> {\n this.interrupt();\n\n const pollCycle = this.activePollCycle;\n const watchLoop = this.activeWatchLoop;\n await Promise.allSettled([pollCycle, watchLoop].filter(isPromise));\n\n this.snapshot.clear();\n this.echoSuppressSet.clear();\n\n this.logger.info('Local mount sync stopped', {\n mountPath: this.mountPath\n });\n }\n\n interrupt(): void {\n this.running = false;\n\n if (this.pollTimer) {\n clearTimeout(this.pollTimer);\n this.pollTimer = null;\n }\n\n if (this.watchReconnectTimer) {\n clearTimeout(this.watchReconnectTimer);\n this.watchReconnectTimer = null;\n }\n\n if (this.watchAbortController) {\n this.watchAbortController.abort();\n this.watchAbortController = null;\n }\n\n if (!this.runtimeHoldReleased) {\n this.runtimeHoldReleased = true;\n this.runtimeHold.release();\n }\n }\n\n private async fullSyncR2ToContainer(generation: number): Promise<void> {\n const objects = await this.listAllR2Objects();\n if (!this.isCurrentGeneration(generation)) return;\n const newSnapshot = new Map<string, R2ObjectSnapshot>();\n\n // No echo suppression needed: this runs before startContainerWatch() in start().\n // Process in batches to limit concurrent HTTP requests\n for (let i = 0; i < objects.length; i += SYNC_CONCURRENCY) {\n if (!this.isCurrentGeneration(generation)) return;\n const batch = objects.slice(i, i + SYNC_CONCURRENCY);\n await Promise.all(\n batch.map(async (obj) => {\n if (!this.isCurrentGeneration(generation)) return;\n const containerPath = this.r2KeyToContainerPath(obj.key);\n newSnapshot.set(obj.key, { etag: obj.etag, size: obj.size });\n await this.ensureParentDir(containerPath, generation);\n await this.transferR2ObjectToContainer(\n obj.key,\n containerPath,\n generation\n );\n })\n );\n }\n\n if (!this.isCurrentGeneration(generation)) return;\n this.snapshot = newSnapshot;\n this.logger.debug('Initial R2 -> Container sync complete', {\n objectCount: objects.length\n });\n }\n\n private schedulePoll(): void {\n if (!this.running) return;\n\n const backoffMs =\n this.consecutivePollFailures > 0\n ? Math.min(\n this.pollIntervalMs * 2 ** this.consecutivePollFailures,\n MAX_BACKOFF_MS\n )\n : this.pollIntervalMs;\n\n const generation = this.generation;\n this.pollTimer = setTimeout(() => {\n this.pollTimer = null;\n if (!this.isCurrentGeneration(generation)) return;\n const cycle = this.pollR2ForChanges(generation)\n .then(() => {\n if (this.isCurrentGeneration(generation)) {\n this.consecutivePollFailures = 0;\n }\n })\n .catch((error) => {\n if (!this.isCurrentGeneration(generation)) return;\n this.consecutivePollFailures++;\n this.logger.error(\n 'R2 poll cycle failed',\n error instanceof Error ? error : new Error(String(error))\n );\n })\n .finally(() => {\n if (this.activePollCycle === cycle) {\n this.activePollCycle = null;\n }\n if (this.isCurrentGeneration(generation)) {\n this.schedulePoll();\n }\n });\n this.activePollCycle = cycle;\n }, backoffMs);\n }\n\n private async pollR2ForChanges(generation: number): Promise<void> {\n const objects = await this.listAllR2Objects();\n if (!this.isCurrentGeneration(generation)) return;\n const newSnapshot = new Map<string, R2ObjectSnapshot>();\n\n // Collect changed objects first, then transfer in batches\n const changed: Array<{ key: string; action: 'created' | 'modified' }> = [];\n for (const obj of objects) {\n newSnapshot.set(obj.key, { etag: obj.etag, size: obj.size });\n const existing = this.snapshot.get(obj.key);\n if (!existing || existing.etag !== obj.etag) {\n changed.push({\n key: obj.key,\n action: existing ? 'modified' : 'created'\n });\n }\n }\n\n for (let i = 0; i < changed.length; i += SYNC_CONCURRENCY) {\n const batch = changed.slice(i, i + SYNC_CONCURRENCY);\n await Promise.all(\n batch.map(async ({ key, action }) => {\n try {\n if (!this.isCurrentGeneration(generation)) return;\n const containerPath = this.r2KeyToContainerPath(key);\n await this.ensureParentDir(containerPath, generation);\n if (!this.isCurrentGeneration(generation)) return;\n this.suppressEcho(containerPath);\n await this.transferR2ObjectToContainer(\n key,\n containerPath,\n generation\n );\n this.logger.debug('R2 -> Container: synced object', {\n key,\n action\n });\n } catch (error) {\n this.logger.error(\n `R2 -> Container: failed to sync object ${key}`,\n error instanceof Error ? error : new Error(String(error))\n );\n }\n })\n );\n }\n\n for (const [key] of this.snapshot) {\n if (!newSnapshot.has(key)) {\n const containerPath = this.r2KeyToContainerPath(key);\n this.suppressEcho(containerPath);\n\n try {\n if (!this.isCurrentGeneration(generation)) return;\n await this.runRuntimeCallIfCurrent(\n generation,\n 'mount.local.deleteFile',\n (control) => control.files.deleteFile(containerPath)\n );\n this.logger.debug('R2 -> Container: deleted file', { key });\n } catch (error) {\n this.logger.error(\n 'R2 -> Container: failed to delete',\n error instanceof Error ? error : new Error(String(error))\n );\n }\n }\n }\n\n if (!this.isCurrentGeneration(generation)) return;\n this.snapshot = newSnapshot;\n }\n\n private async listAllR2Objects(): Promise<\n Array<{ key: string; etag: string; size: number }>\n > {\n const results: Array<{ key: string; etag: string; size: number }> = [];\n let cursor: string | undefined;\n\n do {\n const listResult = await this.bucket.list({\n ...(this.prefix && { prefix: this.prefix }),\n ...(cursor && { cursor })\n });\n\n for (const obj of listResult.objects) {\n results.push({ key: obj.key, etag: obj.etag, size: obj.size });\n }\n\n cursor = listResult.truncated ? listResult.cursor : undefined;\n } while (cursor);\n\n return results;\n }\n\n private async transferR2ObjectToContainer(\n key: string,\n containerPath: string,\n generation?: number\n ): Promise<void> {\n const obj = await this.bucket.get(key);\n if (!obj) return;\n\n const arrayBuffer = await obj.arrayBuffer();\n const base64 = uint8ArrayToBase64(new Uint8Array(arrayBuffer));\n\n await this.runRuntimeCallIfCurrent(\n generation,\n 'mount.local.writeFile',\n (control) =>\n control.files.writeFile(containerPath, base64, {\n encoding: 'base64'\n })\n );\n }\n\n private async ensureParentDir(\n containerPath: string,\n generation?: number\n ): Promise<void> {\n const parentDir = containerPath.substring(\n 0,\n containerPath.lastIndexOf('/')\n );\n if (parentDir && parentDir !== this.mountPath) {\n await this.runRuntimeCallIfCurrent(\n generation,\n 'mount.local.mkdir',\n (control) =>\n control.files.mkdir(parentDir, {\n recursive: true\n })\n );\n }\n }\n\n private startContainerWatch(): void {\n this.watchAbortController = new AbortController();\n this.runWatchWithRetry();\n }\n\n private runWatchWithRetry(): void {\n if (!this.running) return;\n\n const generation = this.generation;\n const loop = this.runContainerWatchLoop(generation)\n .then(() => {\n if (!this.isCurrentGeneration(generation)) return;\n // Stream ended cleanly (e.g. server closed it). Reconnect unless stopped.\n this.consecutiveWatchFailures = 0;\n this.scheduleWatchReconnect();\n })\n .catch((error) => {\n if (!this.isCurrentGeneration(generation)) return;\n this.consecutiveWatchFailures++;\n this.logger.error(\n 'Container watch loop failed',\n error instanceof Error ? error : new Error(String(error))\n );\n this.scheduleWatchReconnect();\n })\n .finally(() => {\n if (this.activeWatchLoop === loop) {\n this.activeWatchLoop = null;\n }\n });\n this.activeWatchLoop = loop;\n }\n\n private scheduleWatchReconnect(): void {\n if (!this.running) return;\n\n const backoffMs =\n this.consecutiveWatchFailures > 0\n ? Math.min(\n this.pollIntervalMs * 2 ** this.consecutiveWatchFailures,\n MAX_BACKOFF_MS\n )\n : this.pollIntervalMs;\n\n this.logger.debug('Reconnecting container watch', {\n backoffMs,\n failures: this.consecutiveWatchFailures\n });\n\n this.watchReconnectTimer = setTimeout(() => {\n this.watchReconnectTimer = null;\n if (!this.running) return;\n this.watchAbortController = new AbortController();\n this.runWatchWithRetry();\n }, backoffMs);\n }\n\n private async runContainerWatchLoop(generation: number): Promise<void> {\n await this.runRuntimeCallIfCurrent(\n generation,\n 'mount.local.watch',\n async (control) => {\n const stream = await openRemoteSubscription(\n control.watch.watch({\n path: this.mountPath,\n recursive: true\n }),\n {\n signal: this.watchAbortController?.signal,\n operation: 'open local mount filesystem watch',\n protocol: 'stream'\n }\n );\n\n for await (const event of parseSSEStream<FileWatchSSEEvent>(\n stream,\n this.watchAbortController?.signal\n )) {\n if (!this.isCurrentGeneration(generation)) break;\n\n // Successful event received — reset failure counter\n this.consecutiveWatchFailures = 0;\n\n if (event.type !== 'event') continue;\n if (event.isDirectory) continue;\n\n const containerPath = event.path;\n\n // Skip echo from our own R2 -> Container writes\n if (this.echoSuppressSet.has(containerPath)) continue;\n\n const r2Key = this.containerPathToR2Key(containerPath);\n if (!r2Key) continue;\n\n try {\n switch (event.eventType) {\n case 'create':\n case 'modify':\n case 'move_to': {\n await this.uploadFileToR2(containerPath, r2Key, generation);\n this.logger.debug('Container -> R2: synced file', {\n path: containerPath,\n key: r2Key,\n action: event.eventType\n });\n break;\n }\n\n case 'delete':\n case 'move_from': {\n if (!this.isCurrentGeneration(generation)) break;\n await this.bucket.delete(r2Key);\n if (!this.isCurrentGeneration(generation)) break;\n this.snapshot.delete(r2Key);\n this.logger.debug('Container -> R2: deleted object', {\n path: containerPath,\n key: r2Key\n });\n break;\n }\n }\n } catch (error) {\n this.logger.error(\n `Container -> R2 sync failed for ${containerPath}`,\n error instanceof Error ? error : new Error(String(error))\n );\n }\n }\n }\n );\n }\n\n /**\n * Read a container file and upload it to R2, then update the local\n * snapshot so the next poll cycle doesn't echo the write back.\n */\n private async uploadFileToR2(\n containerPath: string,\n r2Key: string,\n generation?: number\n ): Promise<void> {\n const result = await this.runRuntimeCallIfCurrent(\n generation,\n 'mount.local.readFile',\n (control) =>\n control.files.readFile(containerPath, {\n encoding: 'base64'\n })\n );\n if (!this.isCurrentOrUnscoped(generation)) return;\n const bytes = base64ToUint8Array(result.content);\n await this.bucket.put(r2Key, bytes);\n if (!this.isCurrentOrUnscoped(generation)) return;\n\n const head = await this.bucket.head(r2Key);\n if (head) {\n this.snapshot.set(r2Key, { etag: head.etag, size: head.size });\n }\n }\n\n private async runRuntimeCallIfCurrent<T>(\n generation: number | undefined,\n operation: string,\n call: (control: ContainerControlClient) => Promise<T>\n ): Promise<T> {\n if (generation !== undefined && !this.isCurrentGeneration(generation)) {\n throw new Error('local mount sync stopped');\n }\n return await this.runRuntimeCall(operation, async (control) => {\n if (generation !== undefined && !this.isCurrentGeneration(generation)) {\n throw new Error('local mount sync stopped');\n }\n return await call(control);\n });\n }\n\n private isCurrentOrUnscoped(generation: number | undefined): boolean {\n return generation === undefined || this.isCurrentGeneration(generation);\n }\n\n private isCurrentGeneration(generation: number): boolean {\n return this.running && this.generation === generation;\n }\n\n private suppressEcho(containerPath: string): void {\n this.echoSuppressSet.add(containerPath);\n setTimeout(() => {\n this.echoSuppressSet.delete(containerPath);\n }, this.echoSuppressTtlMs);\n }\n\n private r2KeyToContainerPath(key: string): string {\n let relativePath = key;\n if (this.prefix) {\n relativePath = key.startsWith(this.prefix)\n ? key.slice(this.prefix.length)\n : key;\n }\n return path.join(this.mountPath, relativePath);\n }\n\n private containerPathToR2Key(containerPath: string): string | null {\n const resolved = path.resolve(containerPath);\n const mount = path.resolve(this.mountPath);\n\n if (!resolved.startsWith(mount)) return null;\n\n const relativePath = path.relative(mount, resolved);\n if (!relativePath || relativePath.startsWith('..')) return null;\n\n return this.prefix ? path.join(this.prefix, relativePath) : relativePath;\n }\n}\n\nfunction isPromise<T>(value: Promise<T> | null): value is Promise<T> {\n return value !== null;\n}\n\nfunction uint8ArrayToBase64(bytes: Uint8Array): string {\n return Buffer.from(bytes).toString('base64');\n}\n\nfunction base64ToUint8Array(base64: string): Uint8Array {\n return new Uint8Array(Buffer.from(base64, 'base64'));\n}\n","import type { LocalMountBucketOptions } from '@repo/shared';\nimport { logCanonicalEvent } from '@repo/shared';\nimport { LocalMountSyncManager } from '../../local-mount-sync';\nimport { InvalidMountConfigError } from '../errors';\nimport type { MountLifecycle } from '../lifecycle';\nimport type { MountRegistry } from '../registry';\nimport type { MountRuntimeLease } from '../runtime-call';\nimport type { S3FSHost } from '../s3fs';\nimport type { LocalSyncMountInfo } from '../types';\nimport { isR2Bucket } from '../validation';\nimport type { BucketMountOperationContext } from './context';\n\nexport interface LocalSyncMountContext extends BucketMountOperationContext {\n getEnv(): unknown;\n lifecycle: MountLifecycle;\n runtime: MountRuntimeLease['runtime'];\n s3fsHost: S3FSHost;\n retainRuntime: MountRuntimeLease['retain'];\n}\n\nexport function validateLocalSyncMount(\n context: Pick<LocalSyncMountContext, 'getEnv' | 'registry'>,\n bucket: string,\n mountPath: string\n): R2Bucket {\n const envObj = context.getEnv() as Record<string, unknown>;\n const r2Binding = envObj[bucket];\n if (!r2Binding || !isR2Bucket(r2Binding)) {\n throw new InvalidMountConfigError(\n `R2 binding \"${bucket}\" not found in env or is not an R2Bucket. ` +\n 'Make sure the binding name matches your wrangler.jsonc R2 binding.'\n );\n }\n\n if (!mountPath || !mountPath.startsWith('/')) {\n throw new InvalidMountConfigError(\n `Invalid mount path: \"${mountPath}\". Must be an absolute path starting with /`\n );\n }\n\n if (context.registry.has(mountPath)) {\n throw new InvalidMountConfigError(\n `Mount path already in use: ${mountPath}`\n );\n }\n return r2Binding;\n}\n\nexport async function mountLocalSyncBucket(\n context: LocalSyncMountContext,\n bucket: string,\n mountPath: string,\n options: LocalMountBucketOptions\n): Promise<void> {\n const mountStartTime = Date.now();\n let mountOutcome: 'success' | 'error' = 'error';\n let mountError: Error | undefined;\n try {\n const r2Binding = validateLocalSyncMount(context, bucket, mountPath);\n\n let syncManager: LocalMountSyncManager | null = null;\n const runtimeHold = context.retainRuntime(() => {\n syncManager?.interrupt();\n });\n syncManager = new LocalMountSyncManager({\n bucket: r2Binding,\n mountPath,\n prefix: options.prefix,\n readOnly: options.readOnly ?? false,\n runRuntimeCall: context.runRuntimeCall,\n runtimeHold,\n logger: context.logger\n });\n\n const mountInfo: LocalSyncMountInfo = {\n mountId: crypto.randomUUID(),\n mountType: 'local-sync',\n bucket,\n mountPath,\n syncManager,\n mounted: false\n };\n const lifecycle = await context.lifecycle.capture(context.runtime);\n try {\n await syncManager.start();\n await context.lifecycle.assertCurrent(lifecycle);\n mountInfo.mounted = true;\n context.registry.set(mountPath, mountInfo);\n } catch (error) {\n await syncManager.stop();\n throw error;\n }\n\n mountOutcome = 'success';\n } catch (error) {\n mountError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(context.logger, {\n event: 'bucket.mount',\n outcome: mountOutcome,\n durationMs: Date.now() - mountStartTime,\n bucket,\n mountPath,\n provider: 'local-sync',\n prefix: options.prefix,\n error: mountError\n });\n }\n}\n","import type { BucketMountOperationContext } from './context';\n\nexport async function unmountFuseIfMountedForCleanup(\n context: BucketMountOperationContext,\n mountPath: string\n): Promise<boolean> {\n const isMountpoint = await context.runRuntimeCall(\n 'mount.cleanup.isMountpoint',\n (control) => control.mounts.isMountpoint(mountPath)\n );\n if (!isMountpoint) return true;\n\n const result = await context.runRuntimeCall(\n 'mount.cleanup.unmountFuse',\n (control) => control.mounts.unmountFuse(mountPath)\n );\n if (result.success) return true;\n\n context.logger.warn('FUSE mount cleanup unmount failed', {\n mountPath,\n exitCode: result.exitCode,\n stderr: result.stderr\n });\n return false;\n}\n","import type { R2BindingMountBucketOptions } from '@repo/shared';\nimport { logCanonicalEvent } from '@repo/shared';\nimport { InvalidMountConfigError } from '../errors';\nimport type { MountLifecycle } from '../lifecycle';\nimport { configureR2EgressOutbound } from '../outbound';\nimport { buildR2EgressParams } from '../outbound/params';\nimport type { MountRuntimeLease } from '../runtime-call';\nimport type { S3FSHost } from '../s3fs';\nimport {\n createDisableExpectHeaderFile,\n createPasswordFile,\n deleteAdditionalHeaderFile,\n deletePasswordFile,\n executeS3FSMount,\n generatePasswordFilePath,\n generateS3FSAdditionalHeaderFilePath,\n R2_DEFAULT_S3FS_OPTION_ENTRIES,\n validateProtectedS3fsOptions\n} from '../s3fs';\nimport type { R2BindingMountInfo } from '../types';\nimport { validateBucketBindingName, validateMountPath } from '../validation';\nimport type { BucketMountOperationContext } from './context';\nimport { unmountFuseIfMountedForCleanup } from './fuse-cleanup';\n\nexport interface R2EgressMountContext extends BucketMountOperationContext {\n lifecycle: MountLifecycle;\n runtime: MountRuntimeLease['runtime'];\n s3fsHost: S3FSHost;\n}\n\nexport function validateR2EgressMount(\n context: Pick<R2EgressMountContext, 'registry'>,\n bucket: string,\n mountPath: string,\n options: R2BindingMountBucketOptions\n): void {\n const prefix = options.prefix;\n validateBucketBindingName(bucket, mountPath);\n validateMountPath(context.registry.activeMounts, mountPath);\n validateProtectedS3fsOptions(options.s3fsOptions, 'R2 binding');\n\n for (const [existingMountPath, existingMount] of context.registry) {\n if (\n existingMount.mountType === 'r2-egress' &&\n existingMount.bucket === bucket &&\n existingMount.prefix !== prefix\n ) {\n throw new InvalidMountConfigError(\n `R2 binding \"${bucket}\" is already mounted at ${existingMountPath} with a different prefix. ` +\n 'Mount the same binding only once, or use the same prefix for additional mounts.'\n );\n }\n if (\n existingMount.mountType === 'r2-egress' &&\n existingMount.bucket === bucket &&\n existingMount.readOnly !== (options.readOnly ?? false)\n ) {\n throw new InvalidMountConfigError(\n `R2 binding \"${bucket}\" is already mounted at ${existingMountPath} with a different readOnly setting. ` +\n 'Mount the same binding only once, or use the same readOnly value for additional mounts.'\n );\n }\n }\n}\n\nexport async function mountR2EgressBucket(\n context: R2EgressMountContext,\n bucket: string,\n mountPath: string,\n options: R2BindingMountBucketOptions\n): Promise<void> {\n const mountStartTime = Date.now();\n const prefix = options.prefix;\n let mountOutcome: 'success' | 'error' = 'error';\n let mountError: Error | undefined;\n let passwordFilePath: string | undefined;\n let additionalHeaderFilePath: string | undefined;\n let s3fsStarted = false;\n let mountInfo: R2BindingMountInfo | undefined;\n\n try {\n validateR2EgressMount(context, bucket, mountPath, options);\n\n const lifecycle = await context.lifecycle.capture(context.runtime);\n passwordFilePath = generatePasswordFilePath();\n additionalHeaderFilePath = generateS3FSAdditionalHeaderFilePath();\n await createPasswordFile(context.s3fsHost, passwordFilePath, bucket, {\n accessKeyId: 'x',\n secretAccessKey: 'x'\n });\n await createDisableExpectHeaderFile(\n context.s3fsHost,\n additionalHeaderFilePath\n );\n\n mountInfo = {\n mountId: crypto.randomUUID(),\n mountType: 'r2-egress',\n bucket,\n mountPath,\n passwordFilePath,\n additionalHeaderFilePath,\n mounted: false,\n prefix,\n readOnly: options.readOnly ?? false\n };\n await configureR2EgressOutbound(context.getOutboundHost(), {\n buckets: {\n ...buildR2EgressParams(context.registry).buckets,\n [bucket]: {\n prefix,\n readOnly: options.readOnly ?? false\n }\n }\n });\n\n await context.runRuntimeCall('mount.ensureDirectory', (control) =>\n control.mounts.ensureDirectory(mountPath)\n );\n\n s3fsStarted = true;\n await executeS3FSMount(context.s3fsHost, {\n bucket,\n mountPath,\n provider: 'r2',\n passwordFilePath,\n options: {\n ...options,\n endpoint: 'http://r2.internal',\n s3fsOptions: [\n ...R2_DEFAULT_S3FS_OPTION_ENTRIES,\n ...(options.s3fsOptions ?? []),\n 'use_path_request_style',\n `ahbe_conf=${additionalHeaderFilePath}`\n ]\n }\n });\n\n await context.lifecycle.assertCurrent(lifecycle);\n mountInfo.mounted = true;\n context.registry.set(mountPath, mountInfo);\n mountOutcome = 'success';\n } catch (error) {\n mountError = error instanceof Error ? error : new Error(String(error));\n const failedMount = context.registry.get(mountPath);\n context.registry.delete(mountPath);\n let supportFilesSafeToDelete = !s3fsStarted;\n if (s3fsStarted) {\n try {\n supportFilesSafeToDelete = await unmountFuseIfMountedForCleanup(\n context,\n mountPath\n );\n } catch (cleanupError) {\n context.logger.warn('R2 egress FUSE mount cleanup check failed', {\n mountPath,\n error:\n cleanupError instanceof Error\n ? cleanupError.message\n : String(cleanupError)\n });\n }\n }\n\n if (supportFilesSafeToDelete) {\n const cleanupPasswordFilePath =\n failedMount?.mountType === 'r2-egress'\n ? failedMount.passwordFilePath\n : passwordFilePath;\n const cleanupAdditionalHeaderFilePath =\n failedMount?.mountType === 'r2-egress'\n ? failedMount.additionalHeaderFilePath\n : additionalHeaderFilePath;\n\n if (cleanupPasswordFilePath) {\n await deletePasswordFile(\n context.s3fsHost,\n cleanupPasswordFilePath\n ).catch(() => {});\n }\n if (cleanupAdditionalHeaderFilePath) {\n await deleteAdditionalHeaderFile(\n context.s3fsHost,\n cleanupAdditionalHeaderFilePath\n ).catch(() => {});\n }\n }\n\n try {\n await configureR2EgressOutbound(\n context.getOutboundHost(),\n buildR2EgressParams(context.registry)\n );\n } catch (cleanupError) {\n if (mountInfo) {\n mountInfo.mounted = !supportFilesSafeToDelete;\n context.registry.set(mountPath, mountInfo);\n }\n throw new Error(\n `R2 egress mount failed and outbound cleanup failed: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}; original mount error: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n throw error;\n } finally {\n logCanonicalEvent(context.logger, {\n event: 'bucket.mount',\n outcome: mountOutcome,\n durationMs: Date.now() - mountStartTime,\n bucket,\n mountPath,\n provider: 'r2',\n prefix,\n error: mountError\n });\n }\n}\n","import type { BucketCredentials, RemoteMountBucketOptions } from '@repo/shared';\nimport { MissingCredentialsError } from '../errors';\n\n/**\n * Detect credentials for bucket mounting from environment variables\n * Priority order:\n * 1. Explicit options.credentials\n * 2. Standard AWS env vars: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY\n * 3. Standard R2 env vars: R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY\n * 4. Error: no credentials found\n *\n * @param options - Mount options\n * @param envVars - Environment variables\n * @returns Detected credentials\n * @throws MissingCredentialsError if no credentials found\n */\nexport function detectCredentials(\n options: RemoteMountBucketOptions,\n envVars: Record<string, string | undefined>\n): BucketCredentials {\n // Priority 1: Explicit credentials in options\n if (options.credentials) {\n return options.credentials;\n }\n\n // Priority 2: Standard AWS env vars\n const awsAccessKeyId = envVars.AWS_ACCESS_KEY_ID;\n const awsSecretAccessKey = envVars.AWS_SECRET_ACCESS_KEY;\n\n if (awsAccessKeyId && awsSecretAccessKey) {\n return {\n accessKeyId: awsAccessKeyId,\n secretAccessKey: awsSecretAccessKey\n };\n }\n\n /**\n * Priority 3: Standard R2 env vars\n *\n * AWS vars still take precedence over R2 vars in case both are set\n */\n const r2AccessKeyId = envVars.R2_ACCESS_KEY_ID;\n const r2SecretAccessKey = envVars.R2_SECRET_ACCESS_KEY;\n\n if (r2AccessKeyId && r2SecretAccessKey) {\n return {\n accessKeyId: r2AccessKeyId,\n secretAccessKey: r2SecretAccessKey\n };\n }\n\n // No credentials found - throw error with helpful message\n throw new MissingCredentialsError(\n `No credentials found. Set R2_ACCESS_KEY_ID and R2_SECRET_ACCESS_KEY ` +\n `or AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY ` +\n `environment variables, or pass explicit credentials in options.`\n );\n}\n","import type { BucketProvider, RemoteMountBucketOptions } from '@repo/shared';\nimport { getEnvString, logCanonicalEvent } from '@repo/shared';\nimport type { MountLifecycle } from '../lifecycle';\nimport { configureS3CredentialProxyOutbound } from '../outbound';\nimport { S3_CREDENTIAL_PROXY_HOST } from '../outbound/container-proxy';\nimport { buildS3CredentialProxyParams } from '../outbound/params';\nimport {\n evictDirectoryMarkerCacheForMount,\n evictSigV4ClientCacheEntry\n} from '../outbound/s3-credential-proxy-handler';\nimport type { MountRuntimeLease } from '../runtime-call';\nimport type { S3FSHost } from '../s3fs';\nimport {\n createDisableExpectHeaderFile,\n createPasswordFile,\n deleteAdditionalHeaderFile,\n deletePasswordFile,\n executeS3FSMount,\n generatePasswordFilePath,\n generateS3FSAdditionalHeaderFilePath,\n R2_DEFAULT_S3FS_OPTION_ENTRIES,\n validateProtectedS3fsOptions\n} from '../s3fs';\nimport type { CredentialProxyAuthStrategy, FuseMountInfo } from '../types';\nimport { buildS3fsSource, validateRemoteMountOptions } from '../validation';\nimport { detectCredentials } from '../validation/credentials';\nimport { detectProviderFromUrl } from '../validation/provider';\nimport type { BucketMountOperationContext } from './context';\nimport { unmountFuseIfMountedForCleanup } from './fuse-cleanup';\n\nexport interface RemoteFuseMountContext extends BucketMountOperationContext {\n getEnv(): unknown;\n getEnvVars(): Record<string, string>;\n getR2AccessKeyID(): string | null;\n getR2SecretAccessKey(): string | null;\n lifecycle: MountLifecycle;\n runtime: MountRuntimeLease['runtime'];\n s3fsHost: S3FSHost;\n}\n\nexport function validateRemoteFuseMount(\n context: Pick<\n RemoteFuseMountContext,\n | 'registry'\n | 'logger'\n | 'getEnv'\n | 'getEnvVars'\n | 'getR2AccessKeyID'\n | 'getR2SecretAccessKey'\n >,\n bucket: string,\n mountPath: string,\n options: RemoteMountBucketOptions\n): {\n provider: BucketProvider | null;\n credentials: ReturnType<typeof detectCredentials>;\n} {\n const prefix = options.prefix;\n validateRemoteMountOptions(context.registry.activeMounts, bucket, mountPath, {\n ...options,\n prefix\n });\n const provider = options.provider || detectProviderFromUrl(options.endpoint);\n context.logger.debug(`Detected provider: ${provider || 'unknown'}`, {\n explicitProvider: options.provider,\n prefix\n });\n const envObj = context.getEnv() as Record<string, unknown>;\n const envCredentials = {\n AWS_ACCESS_KEY_ID: getEnvString(envObj, 'AWS_ACCESS_KEY_ID'),\n AWS_SECRET_ACCESS_KEY: getEnvString(envObj, 'AWS_SECRET_ACCESS_KEY'),\n R2_ACCESS_KEY_ID: context.getR2AccessKeyID() || undefined,\n R2_SECRET_ACCESS_KEY: context.getR2SecretAccessKey() || undefined\n };\n const credentials = detectCredentials(options, {\n ...envCredentials,\n ...context.getEnvVars()\n });\n if (options.credentialProxy === true) {\n validateProtectedS3fsOptions(options.s3fsOptions, 'credential proxy', [\n 'ahbe_conf',\n 'use_path_request_style'\n ]);\n }\n return { provider, credentials };\n}\n\nexport async function mountRemoteFuseBucket(\n context: RemoteFuseMountContext,\n bucket: string,\n mountPath: string,\n options: RemoteMountBucketOptions\n): Promise<void> {\n const mountStartTime = Date.now();\n const prefix = options.prefix;\n let mountOutcome: 'success' | 'error' = 'error';\n let mountError: Error | undefined;\n let passwordFilePath: string | undefined;\n let additionalHeaderFilePath: string | undefined;\n let provider: BucketProvider | null = null;\n let dirExisted = true;\n let credentialProxyEnabled = false;\n let credentialProxyMountId: string | undefined;\n let mountInfo: FuseMountInfo | undefined;\n try {\n const validation = validateRemoteFuseMount(\n context,\n bucket,\n mountPath,\n options\n );\n const s3fsSource = buildS3fsSource(bucket, prefix);\n provider = validation.provider;\n const credentials = validation.credentials;\n\n credentialProxyEnabled = options.credentialProxy === true;\n\n passwordFilePath = generatePasswordFilePath();\n if (credentialProxyEnabled) {\n additionalHeaderFilePath = generateS3FSAdditionalHeaderFilePath();\n }\n\n const mountId = crypto.randomUUID();\n credentialProxyMountId = mountId;\n mountInfo = {\n mountId,\n mountType: 'fuse',\n bucket: s3fsSource,\n mountPath,\n endpoint: options.endpoint,\n provider,\n passwordFilePath,\n ...(additionalHeaderFilePath ? { additionalHeaderFilePath } : {}),\n mounted: false,\n ...(credentialProxyEnabled\n ? {\n credentialProxy: {\n endpoint: options.endpoint,\n bucket,\n ...(prefix !== undefined ? { prefix } : {}),\n credentials,\n readOnly: options.readOnly ?? false,\n provider,\n authStrategy: resolveCredentialProxyAuthStrategy(provider)\n }\n }\n : {})\n };\n const lifecycle = await context.lifecycle.capture(context.runtime);\n\n await createPasswordFile(\n context.s3fsHost,\n passwordFilePath,\n bucket,\n credentialProxyEnabled\n ? { accessKeyId: 'x', secretAccessKey: 'x' }\n : credentials\n );\n if (credentialProxyEnabled) {\n if (additionalHeaderFilePath) {\n await createDisableExpectHeaderFile(\n context.s3fsHost,\n additionalHeaderFilePath\n );\n }\n await configureS3CredentialProxyOutbound(\n context.getOutboundHost(),\n buildS3CredentialProxyParams(context.registry, {\n includeMount: mountInfo\n })\n );\n }\n\n dirExisted = await context.runRuntimeCall('mount.pathExists', (control) =>\n control.mounts.pathExists(mountPath)\n );\n await context.runRuntimeCall('mount.ensureDirectory', (control) =>\n control.mounts.ensureDirectory(mountPath)\n );\n\n const effectiveOptions: RemoteMountBucketOptions = credentialProxyEnabled\n ? {\n ...options,\n endpoint: `http://${S3_CREDENTIAL_PROXY_HOST}/${mountId}`,\n s3fsOptions: [\n ...(provider === 'r2' ? R2_DEFAULT_S3FS_OPTION_ENTRIES : []),\n ...(options.s3fsOptions ?? []),\n ...(additionalHeaderFilePath\n ? [`ahbe_conf=${additionalHeaderFilePath}`]\n : []),\n 'use_path_request_style'\n ]\n }\n : options;\n await executeS3FSMount(context.s3fsHost, {\n bucket: s3fsSource,\n mountPath,\n options: effectiveOptions,\n provider,\n passwordFilePath\n });\n\n await context.lifecycle.assertCurrent(lifecycle);\n mountInfo.mounted = true;\n context.registry.set(mountPath, mountInfo);\n mountOutcome = 'success';\n } catch (error) {\n mountError = error instanceof Error ? error : new Error(String(error));\n let supportFilesSafeToDelete = false;\n try {\n supportFilesSafeToDelete = await unmountFuseIfMountedForCleanup(\n context,\n mountPath\n );\n } catch (cleanupError) {\n context.logger.warn('FUSE mount cleanup check failed', {\n mountPath,\n error:\n cleanupError instanceof Error\n ? cleanupError.message\n : String(cleanupError)\n });\n }\n\n if (supportFilesSafeToDelete) {\n if (passwordFilePath) {\n await deletePasswordFile(context.s3fsHost, passwordFilePath);\n }\n if (additionalHeaderFilePath) {\n await deleteAdditionalHeaderFile(\n context.s3fsHost,\n additionalHeaderFilePath\n );\n }\n }\n\n if (!dirExisted) {\n try {\n await context.runRuntimeCall('mount.removeMountDirectory', (control) =>\n control.mounts.removeMountDirectory({\n path: mountPath,\n onlyIfNotMountpoint: false\n })\n );\n } catch {\n // best-effort cleanup\n }\n }\n\n const failedMount = context.registry.get(mountPath);\n if (failedMount?.mountType === 'fuse' && failedMount.credentialProxy) {\n await configureS3CredentialProxyOutbound(\n context.getOutboundHost(),\n buildS3CredentialProxyParams(context.registry, {\n excludeMountId: failedMount.mountId\n })\n );\n context.registry.delete(mountPath);\n evictSigV4ClientCacheEntry(failedMount.mountId);\n evictDirectoryMarkerCacheForMount(failedMount.mountId);\n } else {\n if (credentialProxyEnabled) {\n try {\n await configureS3CredentialProxyOutbound(\n context.getOutboundHost(),\n buildS3CredentialProxyParams(context.registry)\n );\n } catch (cleanupError) {\n if (mountInfo) {\n mountInfo.mounted = !supportFilesSafeToDelete;\n context.registry.set(mountPath, mountInfo);\n }\n throw new Error(\n `Credential proxy mount failed and outbound cleanup failed: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}; original mount error: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n if (credentialProxyMountId) {\n evictSigV4ClientCacheEntry(credentialProxyMountId);\n evictDirectoryMarkerCacheForMount(credentialProxyMountId);\n }\n }\n context.registry.delete(mountPath);\n }\n throw error;\n } finally {\n logCanonicalEvent(context.logger, {\n event: 'bucket.mount',\n outcome: mountOutcome,\n durationMs: Date.now() - mountStartTime,\n bucket,\n mountPath,\n provider: provider || 'unknown',\n prefix,\n error: mountError\n });\n }\n}\n\nfunction resolveCredentialProxyAuthStrategy(\n provider: BucketProvider | null\n): CredentialProxyAuthStrategy {\n return provider === 'gcs' ? 'gcs' : 's3-sigv4';\n}\n","import { logCanonicalEvent } from '@repo/shared';\nimport { BucketUnmountError, InvalidMountConfigError } from '../errors';\nimport {\n configureR2EgressOutbound,\n configureS3CredentialProxyOutbound\n} from '../outbound';\nimport {\n buildR2EgressParams,\n buildS3CredentialProxyParams\n} from '../outbound/params';\nimport {\n evictDirectoryMarkerCacheForMount,\n evictSigV4ClientCacheEntry\n} from '../outbound/s3-credential-proxy-handler';\nimport { deleteAdditionalHeaderFile, deletePasswordFile } from '../s3fs';\nimport type { BucketMountOperationContext } from './context';\n\nexport async function unmountBucketOperation(\n context: BucketMountOperationContext,\n mountPath: string\n): Promise<void> {\n const unmountStartTime = Date.now();\n let unmountOutcome: 'success' | 'error' = 'error';\n let unmountError: Error | undefined;\n const mountInfo = context.registry.get(mountPath);\n\n try {\n if (!mountInfo) {\n throw new InvalidMountConfigError(\n `No active mount found at path: ${mountPath}`\n );\n }\n\n if (mountInfo.mountType === 'local-sync') {\n if (context.s3fsHost) {\n await mountInfo.syncManager.stop();\n } else {\n mountInfo.syncManager.interrupt();\n }\n mountInfo.mounted = false;\n context.registry.delete(mountPath);\n } else {\n let unmounted = !mountInfo.mounted;\n if (mountInfo.mounted && context.s3fsHost) {\n const result = await context.runRuntimeCall(\n 'mount.unmountFuse',\n (control) => control.mounts.unmountFuse(mountPath)\n );\n if (!result.success) {\n const stderr = result.stderr || 'unknown error';\n throw new BucketUnmountError(\n `fusermount -u failed (exit ${result.exitCode}): ${stderr}`\n );\n }\n mountInfo.mounted = false;\n unmounted = true;\n } else if (mountInfo.mounted) {\n mountInfo.mounted = false;\n unmounted = false;\n }\n\n if (mountInfo.mountType === 'r2-egress') {\n await configureR2EgressOutbound(\n context.getOutboundHost(),\n buildR2EgressParams(context.registry, {\n excludeMountId: mountInfo.mountId\n })\n );\n } else if (mountInfo.mountType === 'fuse' && mountInfo.credentialProxy) {\n await configureS3CredentialProxyOutbound(\n context.getOutboundHost(),\n buildS3CredentialProxyParams(context.registry, {\n excludeMountId: mountInfo.mountId\n })\n );\n evictSigV4ClientCacheEntry(mountInfo.mountId);\n evictDirectoryMarkerCacheForMount(mountInfo.mountId);\n }\n\n context.registry.delete(mountPath);\n\n try {\n if (context.s3fsHost) {\n const cleanup = await context.runRuntimeCall(\n 'mount.removeMountDirectory',\n (control) =>\n control.mounts.removeMountDirectory({\n path: mountPath,\n onlyIfNotMountpoint: true\n })\n );\n if (!cleanup.success) {\n context.logger.warn('mount directory removal failed', {\n mountPath,\n exitCode: cleanup.exitCode,\n stderr: cleanup.stderr\n });\n }\n }\n } catch (err) {\n context.logger.warn('mount directory removal failed', {\n mountPath,\n error: err instanceof Error ? err.message : String(err)\n });\n }\n\n if (unmounted && context.s3fsHost) {\n await deletePasswordFile(context.s3fsHost, mountInfo.passwordFilePath);\n if (mountInfo.additionalHeaderFilePath) {\n await deleteAdditionalHeaderFile(\n context.s3fsHost,\n mountInfo.additionalHeaderFilePath\n );\n }\n }\n }\n\n unmountOutcome = 'success';\n } catch (error) {\n unmountError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(context.logger, {\n event: 'bucket.unmount',\n outcome: unmountOutcome,\n durationMs: Date.now() - unmountStartTime,\n mountPath,\n bucket: mountInfo?.bucket,\n error: unmountError\n });\n }\n}\n","import type { ContainerControlClient } from '../container-control';\nimport type { RuntimeIdentity } from '../runtime';\n\nexport type MountRuntimeHold = { release(): void };\n\nexport type MountRuntimeLease = {\n runtime: RuntimeIdentity;\n control: ContainerControlClient;\n retain(onInterrupt?: () => void): MountRuntimeHold;\n};\n\nexport type MountRuntimeCall = <T>(\n operation: string,\n call: (control: ContainerControlClient) => Promise<T>\n) => Promise<T>;\n\nexport type MountRuntimeAttempt = <T>(\n operation: string,\n call: (lease: MountRuntimeLease) => Promise<T>\n) => Promise<T>;\n\nexport type MountExistingRuntimeAttempt = <T>(\n operation: string,\n call: (lease: MountRuntimeLease) => Promise<T>\n) => Promise<{ status: 'absent' } | { status: 'completed'; value: T }>;\n\nexport function callWithMountControl(\n control: ContainerControlClient\n): MountRuntimeCall {\n return async (_operation, call) => await call(control);\n}\n","import type {\n LocalMountBucketOptions,\n Logger,\n MountBucketOptions,\n R2BindingMountBucketOptions,\n RemoteMountBucketOptions\n} from '@repo/shared';\nimport { OperationInterruptedError } from '../errors';\nimport type { RuntimeIdentityReader } from '../runtime';\nimport type { CurrentSandboxLifetime } from '../sandbox-lifetime';\nimport { InvalidMountConfigError } from './errors';\nimport { MountLifecycle } from './lifecycle';\nimport {\n type BucketMountDestroyCleanupResult,\n cleanupBucketMountsForDestroy,\n cleanupBucketMountsForStop\n} from './lifecycle-cleanup';\nimport { MountOperationQueue } from './operation-queue';\nimport {\n mountLocalSyncBucket,\n validateLocalSyncMount\n} from './operations/local-sync-mount';\nimport {\n mountR2EgressBucket,\n validateR2EgressMount\n} from './operations/r2-egress-mount';\nimport {\n mountRemoteFuseBucket,\n validateRemoteFuseMount\n} from './operations/remote-fuse-mount';\nimport { unmountBucketOperation } from './operations/unmount';\nimport type { MountOutboundHost } from './outbound';\nimport { MountRegistry } from './registry';\nimport {\n callWithMountControl,\n type MountExistingRuntimeAttempt,\n type MountRuntimeAttempt,\n type MountRuntimeCall\n} from './runtime-call';\nimport { isR2Bucket, validateBucketName, validatePrefix } from './validation';\n\nexport interface BucketMountServiceDeps {\n getEnv(): unknown;\n getEnvVars(): Record<string, string>;\n runMountAttempt: MountRuntimeAttempt;\n runExistingMountAttempt: MountExistingRuntimeAttempt;\n logger: Logger;\n runtimeReader: RuntimeIdentityReader;\n currentLifetime: CurrentSandboxLifetime;\n getR2AccessKeyID(): string | null;\n getR2SecretAccessKey(): string | null;\n getOutboundHost(): MountOutboundHost;\n}\n\nexport class BucketMountService {\n private readonly registry = new MountRegistry();\n private readonly operations = new MountOperationQueue();\n private readonly lifecycle: MountLifecycle;\n constructor(private readonly deps: BucketMountServiceDeps) {\n this.lifecycle = new MountLifecycle(\n deps.runtimeReader,\n deps.currentLifetime\n );\n }\n\n /**\n * Mount an S3-compatible bucket as a local directory.\n *\n * Requires explicit endpoint URL for production. Credentials are auto-detected from environment\n * variables or can be provided explicitly.\n *\n * @param bucket - Bucket name (or R2 binding name when localBucket is true)\n * @param mountPath - Absolute path in container to mount at\n * @param options - Mount configuration\n * @throws MissingCredentialsError if no credentials found in environment\n * @throws S3FSMountError if S3FS mount command fails\n * @throws InvalidMountConfigError if bucket name, mount path, or endpoint is invalid\n */\n async mountBucket(\n bucket: string,\n mountPath: string,\n options: MountBucketOptions\n ): Promise<void> {\n return this.operations.run(async () => {\n await this.mountBucketUnlocked(bucket, mountPath, options);\n });\n }\n\n private async mountBucketUnlocked(\n bucket: string,\n mountPath: string,\n options: MountBucketOptions\n ): Promise<void> {\n if (options.prefix !== undefined) {\n validatePrefix(options.prefix);\n }\n\n if ('localBucket' in options && options.localBucket) {\n await this.mountBucketLocal(bucket, mountPath, options);\n return;\n }\n\n const remoteOptions = options as RemoteMountBucketOptions;\n if (remoteOptions.endpoint === undefined) {\n const envObj = this.deps.getEnv() as Record<string, unknown>;\n const binding = envObj[bucket];\n if (isR2Bucket(binding)) {\n await this.mountBucketR2Egress(\n bucket,\n mountPath,\n options as R2BindingMountBucketOptions\n );\n return;\n }\n throw new InvalidMountConfigError(\n `R2 binding \"${bucket}\" not found in Worker env. ` +\n 'Ensure the binding name matches the bucket binding configured in wrangler.jsonc.'\n );\n }\n\n await this.mountBucketFuse(bucket, mountPath, remoteOptions);\n }\n\n private async mountBucketLocal(\n bucket: string,\n mountPath: string,\n options: LocalMountBucketOptions\n ): Promise<void> {\n validateLocalSyncMount(\n {\n registry: this.registry,\n getEnv: () => this.deps.getEnv()\n },\n bucket,\n mountPath\n );\n await this.deps.runMountAttempt('mount.local', async (lease) => {\n const runRuntimeCall = callWithMountControl(lease.control);\n await mountLocalSyncBucket(\n {\n registry: this.registry,\n logger: this.deps.logger,\n runRuntimeCall,\n getOutboundHost: () => this.deps.getOutboundHost(),\n s3fsHost: { runRuntimeCall, logger: this.deps.logger },\n getEnv: () => this.deps.getEnv(),\n lifecycle: this.lifecycle,\n runtime: lease.runtime,\n retainRuntime: lease.retain\n },\n bucket,\n mountPath,\n options\n );\n });\n }\n\n private async mountBucketR2Egress(\n bucket: string,\n mountPath: string,\n options: R2BindingMountBucketOptions\n ): Promise<void> {\n validateR2EgressMount(\n { registry: this.registry },\n bucket,\n mountPath,\n options\n );\n await this.deps.runMountAttempt('mount.r2-egress', async (lease) => {\n const runRuntimeCall = callWithMountControl(lease.control);\n await mountR2EgressBucket(\n {\n registry: this.registry,\n logger: this.deps.logger,\n runRuntimeCall,\n getOutboundHost: () => this.deps.getOutboundHost(),\n s3fsHost: { runRuntimeCall, logger: this.deps.logger },\n lifecycle: this.lifecycle,\n runtime: lease.runtime\n },\n bucket,\n mountPath,\n options\n );\n });\n }\n\n private async mountBucketFuse(\n bucket: string,\n mountPath: string,\n options: RemoteMountBucketOptions\n ): Promise<void> {\n validateRemoteFuseMount(\n {\n registry: this.registry,\n logger: this.deps.logger,\n getEnv: () => this.deps.getEnv(),\n getEnvVars: () => this.deps.getEnvVars(),\n getR2AccessKeyID: () => this.deps.getR2AccessKeyID(),\n getR2SecretAccessKey: () => this.deps.getR2SecretAccessKey()\n },\n bucket,\n mountPath,\n options\n );\n await this.deps.runMountAttempt('mount.fuse', async (lease) => {\n const runRuntimeCall = callWithMountControl(lease.control);\n await mountRemoteFuseBucket(\n {\n registry: this.registry,\n logger: this.deps.logger,\n runRuntimeCall,\n getOutboundHost: () => this.deps.getOutboundHost(),\n s3fsHost: { runRuntimeCall, logger: this.deps.logger },\n getEnv: () => this.deps.getEnv(),\n getEnvVars: () => this.deps.getEnvVars(),\n getR2AccessKeyID: () => this.deps.getR2AccessKeyID(),\n getR2SecretAccessKey: () => this.deps.getR2SecretAccessKey(),\n lifecycle: this.lifecycle,\n runtime: lease.runtime\n },\n bucket,\n mountPath,\n options\n );\n });\n }\n\n /**\n * Manually unmount a bucket filesystem\n *\n * @param mountPath - Absolute path where the bucket is mounted\n * @throws InvalidMountConfigError if mount path doesn't exist or isn't mounted\n */\n async unmountBucket(mountPath: string): Promise<void> {\n return this.operations.run(async () => {\n await this.unmountBucketUnlocked(mountPath);\n });\n }\n\n private async unmountBucketUnlocked(mountPath: string): Promise<void> {\n try {\n const result = await this.deps.runExistingMountAttempt(\n 'mount.unmount',\n async (lease) => {\n const runRuntimeCall = callWithMountControl(lease.control);\n await unmountBucketOperation(\n {\n registry: this.registry,\n logger: this.deps.logger,\n runRuntimeCall,\n getOutboundHost: () => this.deps.getOutboundHost(),\n s3fsHost: { runRuntimeCall, logger: this.deps.logger }\n },\n mountPath\n );\n }\n );\n if (result.status === 'completed') return;\n } catch (error) {\n if (!(error instanceof OperationInterruptedError)) throw error;\n if (!this.registry.has(mountPath)) return;\n }\n await this.unmountBucketWithoutRuntime(mountPath);\n }\n\n private async unmountBucketWithoutRuntime(mountPath: string): Promise<void> {\n await unmountBucketOperation(\n {\n registry: this.registry,\n logger: this.deps.logger,\n runRuntimeCall: async () => {\n throw new Error('runtime is not active');\n },\n getOutboundHost: () => this.deps.getOutboundHost(),\n s3fsHost: null\n },\n mountPath\n );\n }\n\n async cleanupForDestroy(): Promise<BucketMountDestroyCleanupResult> {\n const result = await this.deps.runExistingMountAttempt(\n 'mount.destroyCleanup',\n async (lease) =>\n await this.cleanupForDestroyUsing(callWithMountControl(lease.control))\n );\n if (result.status === 'completed') return result.value;\n return this.cleanupForDestroyWithoutRuntime();\n }\n\n async cleanupForDestroyUsing(\n runRuntimeCall: MountRuntimeCall\n ): Promise<BucketMountDestroyCleanupResult> {\n return cleanupBucketMountsForDestroy({\n registry: this.registry,\n logger: this.deps.logger,\n s3fsHost: {\n runRuntimeCall,\n logger: this.deps.logger\n },\n getOutboundHost: () => this.deps.getOutboundHost(),\n runMountOperation: (operation) => this.operations.run(operation)\n });\n }\n\n async cleanupForDestroyWithoutRuntime(): Promise<BucketMountDestroyCleanupResult> {\n return cleanupBucketMountsForDestroy({\n registry: this.registry,\n logger: this.deps.logger,\n s3fsHost: null,\n getOutboundHost: () => this.deps.getOutboundHost(),\n runMountOperation: (operation) => operation()\n });\n }\n\n async cleanupForStop(): Promise<void> {\n return cleanupBucketMountsForStop({\n registry: this.registry,\n logger: this.deps.logger,\n s3fsHost: null,\n getOutboundHost: () => this.deps.getOutboundHost(),\n runMountOperation: (operation) => this.operations.run(operation)\n });\n }\n}\n","export const BACKUP_DEFAULT_TTL_SECONDS = 259200;\nexport const BACKUP_MAX_NAME_LENGTH = 256;\nexport const BACKUP_CONTAINER_DIR = '/var/backups';\nexport const BACKUP_STORAGE_PREFIX = 'backups';\nexport const BACKUP_ARCHIVE_OBJECT_NAME = 'data.sqsh';\nexport const BACKUP_METADATA_OBJECT_NAME = 'meta.json';\nexport const BACKUP_DEFAULT_COMPRESSION = 'lz4';\nexport const BACKUP_DEFAULT_COMPRESS_THREADS = 8;\nexport const BACKUP_MULTIPART_MIN_SIZE = 10 * 1024 * 1024;\nexport const BACKUP_MULTIPART_TARGET_PARTS = 16;\nexport const BACKUP_MULTIPART_MIN_PART_SIZE = 5 * 1024 * 1024;\nexport const BACKUP_MULTIPART_MAX_PARTS = 64;\nexport const BACKUP_DOWNLOAD_PARALLEL_PARTS = 8;\nexport const BACKUP_DOWNLOAD_PARALLEL_MIN_SIZE = 10 * 1024 * 1024;\nexport const BACKUP_DOWNLOAD_MAX_PARTS = 64;\nexport const BACKUP_RESTORE_MAX_RECOVERY_ATTEMPTS = 2;\n\nexport function calculatePartCount(\n sizeBytes: number,\n defaultParts: number,\n maxParts: number\n): number {\n if (sizeBytes < 100 * 1024 * 1024) {\n return defaultParts;\n }\n if (sizeBytes < 1024 * 1024 * 1024) {\n return Math.min(32, defaultParts * 2);\n }\n return maxParts;\n}\n","/**\n * Absolute directory prefixes supported by backup and restore operations.\n */\nexport const BACKUP_ALLOWED_PREFIXES = [\n '/workspace',\n '/home',\n '/tmp',\n '/var/tmp',\n '/app'\n] as const;\n\nexport function normalizeBackupExcludePattern(pattern: string): string | null {\n let normalized = pattern;\n\n while (normalized.startsWith('**/')) {\n normalized = normalized.slice(3);\n }\n\n while (normalized.includes('/**/')) {\n normalized = normalized.replace(/\\/\\*\\*\\//g, '/');\n }\n\n if (normalized.endsWith('/**')) {\n normalized = normalized.slice(0, -3);\n }\n\n if (!normalized || normalized === '**') {\n return null;\n }\n\n return normalized;\n}\n","import type { createLogger } from '@repo/shared';\nimport {\n BACKUP_ALLOWED_PREFIXES,\n normalizeBackupExcludePattern\n} from '@repo/shared/backup';\nimport { ErrorCode, InvalidBackupConfigError } from '../errors';\nimport {\n BACKUP_DEFAULT_COMPRESS_THREADS,\n BACKUP_DEFAULT_COMPRESSION\n} from './constants';\n\nexport function validateBackupDir(dir: string, label: string): void {\n if (!dir || !dir.startsWith('/')) {\n throw new InvalidBackupConfigError({\n message: `${label} must be an absolute path`,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: `${label} must be an absolute path` },\n timestamp: new Date().toISOString()\n });\n }\n if (dir.includes('\\0')) {\n throw new InvalidBackupConfigError({\n message: `${label} must not contain null bytes`,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: `${label} must not contain null bytes` },\n timestamp: new Date().toISOString()\n });\n }\n if (dir.split('/').includes('..')) {\n throw new InvalidBackupConfigError({\n message: `${label} must not contain \"..\" path segments`,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: `${label} must not contain \"..\" path segments` },\n timestamp: new Date().toISOString()\n });\n }\n const isAllowed = BACKUP_ALLOWED_PREFIXES.some(\n (prefix) => dir === prefix || dir.startsWith(`${prefix}/`)\n );\n if (!isAllowed) {\n throw new InvalidBackupConfigError({\n message: `${label} must be inside one of the supported backup roots (${BACKUP_ALLOWED_PREFIXES.join(', ')})`,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: {\n reason: `${label} must be inside one of the supported backup roots`\n },\n timestamp: new Date().toISOString()\n });\n }\n}\n\nexport function normalizeBackupExcludes(\n excludes: string[],\n logger: ReturnType<typeof createLogger>\n): string[] {\n const normalizedExcludes: string[] = [];\n\n for (const pattern of excludes) {\n const normalized = normalizeBackupExcludePattern(pattern);\n if (normalized === null) {\n logger.warn(\n 'Exclude pattern reduced to empty after globstar normalization; skipping',\n { original: pattern }\n );\n continue;\n }\n if (normalized !== pattern) {\n logger.warn(\n 'Exclude pattern contained ** (globstar) which mksquashfs does not support; normalized automatically',\n { original: pattern, normalized }\n );\n }\n normalizedExcludes.push(normalized);\n }\n\n return normalizedExcludes;\n}\n\nexport function resolveBackupCompression(compression: unknown): {\n format: 'gzip' | 'lz4' | 'zstd';\n threads: number;\n} {\n if (compression !== undefined) {\n if (typeof compression !== 'object' || compression === null) {\n throw new InvalidBackupConfigError({\n message: 'BackupOptions.compression must be an object',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'compression must be an object' },\n timestamp: new Date().toISOString()\n });\n }\n }\n\n const compressionOptions = compression as\n | { format?: unknown; threads?: unknown }\n | undefined;\n const format = compressionOptions?.format ?? BACKUP_DEFAULT_COMPRESSION;\n const threads =\n compressionOptions?.threads ?? BACKUP_DEFAULT_COMPRESS_THREADS;\n const allowedCompressions = ['gzip', 'lz4', 'zstd'];\n\n if (\n typeof format !== 'string' ||\n !allowedCompressions.includes(\n format as (typeof allowedCompressions)[number]\n )\n ) {\n throw new InvalidBackupConfigError({\n message:\n 'BackupOptions.compression.format must be one of: gzip, lz4, zstd',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: {\n reason: 'compression.format must be one of: gzip, lz4, zstd'\n },\n timestamp: new Date().toISOString()\n });\n }\n\n if (\n typeof threads !== 'number' ||\n !Number.isInteger(threads) ||\n threads < 1\n ) {\n throw new InvalidBackupConfigError({\n message: 'BackupOptions.compression.threads must be a positive integer',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: {\n reason: 'compression.threads must be a positive integer'\n },\n timestamp: new Date().toISOString()\n });\n }\n\n return {\n format: format as 'gzip' | 'lz4' | 'zstd',\n threads\n };\n}\n","import type { BackupOptions, DirectoryBackup } from '@repo/shared';\nimport { type createLogger, logCanonicalEvent } from '@repo/shared';\nimport {\n BackupCreateError,\n ErrorCode,\n InvalidBackupConfigError\n} from '../errors';\nimport { streamFile } from '../file-stream';\nimport { isR2Bucket } from '../storage-mount';\nimport type { BackupAttemptLease } from './backup-service';\nimport {\n BACKUP_ARCHIVE_OBJECT_NAME,\n BACKUP_CONTAINER_DIR,\n BACKUP_DEFAULT_TTL_SECONDS,\n BACKUP_MAX_NAME_LENGTH,\n BACKUP_METADATA_OBJECT_NAME,\n BACKUP_MULTIPART_MIN_SIZE,\n BACKUP_STORAGE_PREFIX\n} from './constants';\nimport type { BackupTransfer } from './transfer';\nimport {\n normalizeBackupExcludes,\n resolveBackupCompression,\n validateBackupDir\n} from './validation';\n\ntype BackupCreatorDeps = {\n getEnv: () => unknown;\n runBackupAttempt<T>(\n operation: string,\n call: (lease: BackupAttemptLease) => Promise<T>\n ): Promise<T>;\n logger: ReturnType<typeof createLogger>;\n transfer: BackupTransfer;\n};\n\nexport class BackupCreator {\n constructor(private readonly deps: BackupCreatorDeps) {}\n\n private get env(): unknown {\n return this.deps.getEnv();\n }\n\n private get logger(): ReturnType<typeof createLogger> {\n return this.deps.logger;\n }\n\n private get transfer(): BackupTransfer {\n return this.deps.transfer;\n }\n\n async createBackup(options: BackupOptions): Promise<DirectoryBackup> {\n if (options.localBucket) {\n return await this.doCreateBackupLocal(options);\n }\n this.transfer.requireBackupBucket();\n return await this.doCreateBackup(options);\n }\n\n private async doCreateBackup(\n options: BackupOptions\n ): Promise<DirectoryBackup> {\n const bucket = this.transfer.requireBackupBucket();\n this.transfer.requirePresignedURLSupport();\n const {\n dir,\n name,\n ttl = BACKUP_DEFAULT_TTL_SECONDS,\n gitignore = false,\n excludes = [],\n compression,\n multipart = true\n } = options;\n\n const backupStartTime = Date.now();\n let backupId: string | undefined;\n let sizeBytes: number | undefined;\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n\n try {\n this.validateOptions({ dir, name, ttl, gitignore, excludes });\n\n const resolvedCompression = resolveBackupCompression(compression);\n const normalizedExcludes = normalizeBackupExcludes(excludes, this.logger);\n const currentBackupId = crypto.randomUUID();\n backupId = currentBackupId;\n const archivePath = `${BACKUP_CONTAINER_DIR}/${currentBackupId}.sqsh`;\n const r2Key = `${BACKUP_STORAGE_PREFIX}/${currentBackupId}/${BACKUP_ARCHIVE_OBJECT_NAME}`;\n const metaKey = `${BACKUP_STORAGE_PREFIX}/${currentBackupId}/${BACKUP_METADATA_OBJECT_NAME}`;\n\n const result = await this.deps.runBackupAttempt(\n 'backup.create',\n async ({ control }) => {\n try {\n const createResult = await control.backup.createArchive(\n dir,\n archivePath,\n {\n gitignore,\n excludes: normalizedExcludes,\n compression: resolvedCompression\n }\n );\n\n if (!createResult.success) {\n throw new BackupCreateError({\n message: 'Container failed to create backup archive',\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n\n sizeBytes = createResult.sizeBytes;\n if (\n multipart &&\n createResult.sizeBytes >= BACKUP_MULTIPART_MIN_SIZE\n ) {\n await this.transfer.uploadBackupMultipart(\n archivePath,\n r2Key,\n createResult.sizeBytes,\n currentBackupId,\n dir,\n control\n );\n } else {\n await this.transfer.uploadBackupPresigned(\n archivePath,\n r2Key,\n createResult.sizeBytes,\n currentBackupId,\n dir,\n control\n );\n }\n\n const metadata = {\n id: currentBackupId,\n dir,\n name: name || null,\n sizeBytes: createResult.sizeBytes,\n ttl,\n createdAt: new Date().toISOString()\n };\n await bucket.put(metaKey, JSON.stringify(metadata));\n\n outcome = 'success';\n await control.backup.cleanupArchive(archivePath).catch(() => {});\n return { id: currentBackupId, dir };\n } catch (error) {\n await control.backup.cleanupArchive(archivePath).catch(() => {});\n await bucket.delete(r2Key).catch(() => {});\n await bucket.delete(metaKey).catch(() => {});\n throw error;\n }\n }\n );\n\n return result;\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.logger, {\n event: 'backup.create',\n outcome,\n durationMs: Date.now() - backupStartTime,\n backupId,\n dir,\n name,\n sizeBytes,\n error: caughtError\n });\n }\n }\n\n private validateOptions(options: {\n dir: string;\n name: string | undefined;\n ttl: number;\n gitignore: boolean;\n excludes: string[];\n }): void {\n validateBackupDir(options.dir, 'BackupOptions.dir');\n if (options.name !== undefined) {\n if (\n typeof options.name !== 'string' ||\n options.name.length > BACKUP_MAX_NAME_LENGTH\n ) {\n throw new InvalidBackupConfigError({\n message: `BackupOptions.name must be a string of at most ${BACKUP_MAX_NAME_LENGTH} characters`,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: {\n reason: `name must be a string of at most ${BACKUP_MAX_NAME_LENGTH} characters`\n },\n timestamp: new Date().toISOString()\n });\n }\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentionally matching control chars\n if (/[\\u0000-\\u001f\\u007f]/.test(options.name)) {\n throw new InvalidBackupConfigError({\n message: 'BackupOptions.name must not contain control characters',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'name must not contain control characters' },\n timestamp: new Date().toISOString()\n });\n }\n }\n if (options.ttl <= 0) {\n throw new InvalidBackupConfigError({\n message: 'BackupOptions.ttl must be a positive number of seconds',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'ttl must be a positive number of seconds' },\n timestamp: new Date().toISOString()\n });\n }\n if (typeof options.gitignore !== 'boolean') {\n throw new InvalidBackupConfigError({\n message: 'BackupOptions.gitignore must be a boolean',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'gitignore must be a boolean' },\n timestamp: new Date().toISOString()\n });\n }\n if (\n !Array.isArray(options.excludes) ||\n !options.excludes.every((e: unknown) => typeof e === 'string')\n ) {\n throw new InvalidBackupConfigError({\n message: 'BackupOptions.excludes must be an array of strings',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'excludes must be an array of strings' },\n timestamp: new Date().toISOString()\n });\n }\n }\n\n /**\n * Local-dev implementation of createBackup.\n * Uses the R2 binding directly instead of presigned URLs.\n * Archive format is identical to production (squashfs + meta.json).\n */\n private async doCreateBackupLocal(\n options: BackupOptions\n ): Promise<DirectoryBackup> {\n const {\n dir,\n name,\n ttl = BACKUP_DEFAULT_TTL_SECONDS,\n gitignore = false,\n excludes = [],\n compression\n } = options;\n\n const backupStartTime = Date.now();\n let backupId: string | undefined;\n let sizeBytes: number | undefined;\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n\n const envObj = this.env as Record<string, unknown>;\n const bucket = envObj.BACKUP_BUCKET;\n if (!bucket || !isR2Bucket(bucket)) {\n throw new InvalidBackupConfigError({\n message:\n 'BACKUP_BUCKET R2 binding not found in env. ' +\n 'Add a BACKUP_BUCKET R2 binding to your wrangler.jsonc for local backup support.',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'Missing BACKUP_BUCKET R2 binding' },\n timestamp: new Date().toISOString()\n });\n }\n\n try {\n this.validateOptions({ dir, name, ttl, gitignore, excludes });\n const resolvedCompression = resolveBackupCompression(compression);\n const normalizedExcludes = normalizeBackupExcludes(excludes, this.logger);\n\n const currentBackupId = crypto.randomUUID();\n backupId = currentBackupId;\n const archivePath = `${BACKUP_CONTAINER_DIR}/${currentBackupId}.sqsh`;\n const r2Key = `${BACKUP_STORAGE_PREFIX}/${currentBackupId}/${BACKUP_ARCHIVE_OBJECT_NAME}`;\n const metaKey = `${BACKUP_STORAGE_PREFIX}/${currentBackupId}/${BACKUP_METADATA_OBJECT_NAME}`;\n\n const result = await this.deps.runBackupAttempt(\n 'backup.create',\n async ({ control }) => {\n try {\n const createResult = await control.backup.createArchive(\n dir,\n archivePath,\n {\n gitignore,\n excludes: normalizedExcludes,\n compression: resolvedCompression\n }\n );\n\n if (!createResult.success) {\n throw new BackupCreateError({\n message: 'Container failed to create backup archive',\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n\n sizeBytes = createResult.sizeBytes;\n const archiveStream = await control.files.readFileStream(\n archivePath,\n {}\n );\n const fixedStream = new FixedLengthStream(createResult.sizeBytes);\n const writer = fixedStream.writable.getWriter();\n const uploadArchive = bucket\n .put(r2Key, fixedStream.readable)\n .catch(async (error) => {\n await writer.abort(error).catch(() => {});\n throw error;\n });\n const decodeAndWrite = (async () => {\n try {\n for await (const chunk of streamFile(archiveStream)) {\n if (chunk instanceof Uint8Array) {\n await writer.write(chunk);\n }\n }\n await writer.close();\n } catch (error) {\n await writer.abort(error).catch(() => {});\n throw error;\n }\n })();\n const results = await Promise.allSettled([\n decodeAndWrite,\n uploadArchive\n ]);\n const rejected = [...results]\n .reverse()\n .find((result) => result.status === 'rejected');\n if (rejected) throw rejected.reason;\n\n const head = await bucket.head(r2Key);\n if (!head || head.size !== createResult.sizeBytes) {\n throw new BackupCreateError({\n message: `Upload verification failed: expected ${createResult.sizeBytes} bytes, got ${head?.size ?? 0}`,\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n\n const metadata = {\n id: currentBackupId,\n dir,\n name: name || null,\n sizeBytes: createResult.sizeBytes,\n ttl,\n createdAt: new Date().toISOString()\n };\n await bucket.put(metaKey, JSON.stringify(metadata));\n\n outcome = 'success';\n await control.backup.cleanupArchive(archivePath).catch(() => {});\n return { id: currentBackupId, dir, localBucket: true };\n } catch (error) {\n await control.backup.cleanupArchive(archivePath).catch(() => {});\n await bucket.delete(r2Key).catch(() => {});\n await bucket.delete(metaKey).catch(() => {});\n throw error;\n }\n }\n );\n\n return result;\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.logger, {\n event: 'backup.create',\n outcome,\n durationMs: Date.now() - backupStartTime,\n backupId,\n dir,\n name,\n sizeBytes,\n provider: 'local-binding',\n error: caughtError\n });\n }\n }\n\n /**\n * Restore a backup from R2 into a directory.\n *\n * **Production flow** (`localBucket` not set):\n * 1. DO reads metadata from R2 and checks TTL\n * 2. Container mounts the backup archive from R2 via s3fs\n * 3. Container mounts the squashfs archive with FUSE overlayfs\n *\n * The target directory becomes an overlay mount with the backup as a\n * read-only lower layer and a writable upper layer for copy-on-write.\n * Any processes writing to the directory should be stopped first.\n *\n * **Mount Lifecycle**: The FUSE overlay mount persists only while the\n * container is running. When the sandbox sleeps or the container restarts,\n * the mount is lost and the directory becomes empty. Re-restore from the\n * backup handle to recover. This is an ephemeral restore, not a persistent\n * extraction.\n *\n * **Local-dev flow** (`localBucket: true` on the originating `createBackup` call):\n * 1. DO reads metadata and checks TTL via R2 binding\n * 2. DO downloads the archive from R2 and writes it to the container\n * 3. Container extracts the archive with `unsquashfs` (no FUSE needed)\n *\n * The backup is restored into `backup.dir`. This may differ from the\n * directory that was originally backed up, allowing cross-directory restore.\n *\n * Overlapping backups are independent: restoring a parent directory\n * overwrites everything inside it, including subdirectories that were\n * backed up separately. When restoring both, restore the parent first.\n *\n * Concurrent backup/restore calls on the same sandbox are serialized.\n */\n}\n","import type { BackupRestoreOperationRecord } from './restore-operation-store';\n\nexport type BackupRestoreFaultPhase = 'after_archive_ready';\n\nexport type BackupRestoreTestFault = {\n phase: BackupRestoreFaultPhase;\n mode: 'transport_disposed';\n times: number;\n};\n\nexport type BackupRestoreFaultDecision = {\n reason: 'transport_disposed';\n admitted: 'unknown';\n};\n\nexport interface BackupRestoreFaultInjector {\n maybeFault(\n phase: BackupRestoreFaultPhase,\n operation: BackupRestoreOperationRecord\n ): Promise<BackupRestoreFaultDecision | null>;\n}\n\nconst BACKUP_RESTORE_TEST_FAULT_STORAGE_KEY = 'test:backupRestoreFault';\n\nexport class StorageBackedBackupRestoreFaultInjector implements BackupRestoreFaultInjector {\n constructor(\n private readonly storage: DurableObjectStorage,\n private readonly getEnv: () => unknown\n ) {}\n\n async setFaultForTesting(\n fault: BackupRestoreTestFault | null\n ): Promise<void> {\n const envObj = this.getEnv() as Record<string, unknown>;\n if (envObj.SANDBOX_ENABLE_TEST_HOOKS !== 'true') {\n throw new Error('Sandbox test hooks are not enabled');\n }\n\n if (fault === null || fault.times <= 0) {\n await this.storage.delete(BACKUP_RESTORE_TEST_FAULT_STORAGE_KEY);\n return;\n }\n\n await this.storage.put(BACKUP_RESTORE_TEST_FAULT_STORAGE_KEY, fault);\n }\n\n async maybeFault(\n phase: BackupRestoreFaultPhase,\n _operation: BackupRestoreOperationRecord\n ): Promise<BackupRestoreFaultDecision | null> {\n const envObj = this.getEnv() as Record<string, unknown>;\n if (envObj.SANDBOX_ENABLE_TEST_HOOKS !== 'true') {\n return null;\n }\n\n const fault =\n (await this.storage.get<BackupRestoreTestFault>(\n BACKUP_RESTORE_TEST_FAULT_STORAGE_KEY\n )) ?? null;\n if (!fault || fault.phase !== phase || fault.times <= 0) {\n return null;\n }\n\n const nextTimes = fault.times - 1;\n if (nextTimes > 0) {\n await this.storage.put(BACKUP_RESTORE_TEST_FAULT_STORAGE_KEY, {\n ...fault,\n times: nextTimes\n });\n } else {\n await this.storage.delete(BACKUP_RESTORE_TEST_FAULT_STORAGE_KEY);\n }\n\n return {\n reason: fault.mode,\n admitted: 'unknown'\n };\n }\n}\n","import type { RuntimeMetadata } from '@repo/shared';\nimport type { ContainerControlClient } from '../container-control/client';\nimport { RuntimeIdentityInactiveError } from '../errors/classes/runtime-inactive';\n\nexport { RuntimeIdentityInactiveError };\n\nexport type RuntimeIdentityID = string & {\n readonly __runtimeIdentityID: unique symbol;\n};\n\nexport type RuntimeScoped<T extends object> = T & {\n readonly runtimeIdentityID: RuntimeIdentityID;\n};\n\nexport type RuntimeIncarnationID = string & {\n readonly __runtimeIncarnationID: unique symbol;\n};\n\nexport class RuntimeIdentity {\n readonly id: RuntimeIdentityID;\n readonly runtimeIncarnationID: RuntimeIncarnationID;\n\n constructor(record: {\n readonly id: RuntimeIdentityID;\n readonly runtimeIncarnationID: RuntimeIncarnationID;\n }) {\n this.id = record.id;\n this.runtimeIncarnationID = record.runtimeIncarnationID;\n }\n\n owns(record: { readonly runtimeIdentityID: RuntimeIdentityID }): boolean {\n return record.runtimeIdentityID === this.id;\n }\n\n scope<T extends object>(value: T): RuntimeScoped<T> {\n return {\n ...value,\n runtimeIdentityID: this.id\n };\n }\n}\n\nexport type RuntimeRecord = {\n readonly schemaVersion: 1;\n readonly id: RuntimeIdentityID;\n readonly runtimeIncarnationID: RuntimeIncarnationID;\n};\n\nexport type RuntimeRecordStorage = {\n get<T = unknown>(key: string): Promise<T | undefined>;\n};\n\nexport interface RuntimeIdentityReader {\n get(): Promise<RuntimeIdentity | null>;\n getStored(storage?: RuntimeRecordStorage): Promise<RuntimeIdentity | null>;\n isActive(runtime: RuntimeIdentity): Promise<boolean>;\n assertActive(runtime: RuntimeIdentity): Promise<void>;\n}\n\nexport interface RuntimeBootstrapProbe {\n probe(): Promise<RuntimeMetadata>;\n}\n\nexport type RuntimeConnectionHold = {\n release(): void;\n};\n\nexport type RuntimeSession = {\n readonly client: ContainerControlClient;\n readonly interrupted: Promise<never>;\n isInterrupted(): boolean;\n retain(onInterrupt?: () => void): RuntimeConnectionHold;\n};\n\nexport interface RuntimeSessionManager {\n acquire(runtime: RuntimeIdentity): Promise<ContainerControlClient>;\n acquireSession(runtime: RuntimeIdentity): Promise<RuntimeSession>;\n closeActive(): void;\n dispose(): void;\n}\n","export type SandboxLifetimeID = string & {\n readonly __sandboxLifetimeID: unique symbol;\n};\n\nexport type SandboxLifetimeRecord = {\n id: SandboxLifetimeID;\n generation: number;\n createdAt: string;\n updatedAt: string;\n};\n\ntype SandboxLifetimeStorage = Pick<\n DurableObjectStorage | DurableObjectTransaction,\n 'get' | 'put'\n>;\n\nconst SANDBOX_LIFETIME_STORAGE_KEY = 'sandbox:lifetime';\n\nexport class SandboxLifetimeChangedError extends Error {\n constructor() {\n super('Sandbox lifetime is no longer current');\n this.name = 'SandboxLifetimeChangedError';\n }\n}\n\nexport class SandboxLifetime {\n readonly id: SandboxLifetimeID;\n readonly generation: number;\n readonly createdAt: string;\n readonly updatedAt: string;\n\n constructor(readonly record: SandboxLifetimeRecord) {\n this.id = record.id;\n this.generation = record.generation;\n this.createdAt = record.createdAt;\n this.updatedAt = record.updatedAt;\n }\n\n owns(record: { readonly sandboxLifetimeID: SandboxLifetimeID }): boolean {\n return record.sandboxLifetimeID === this.id;\n }\n\n scope<T extends object>(\n value: T\n ): T & { sandboxLifetimeID: SandboxLifetimeID } {\n return {\n ...value,\n sandboxLifetimeID: this.id\n };\n }\n}\n\nexport class CurrentSandboxLifetime {\n constructor(private readonly storage: SandboxLifetimeStorage) {}\n\n async get(): Promise<SandboxLifetime | null> {\n const record =\n (await this.storage.get<SandboxLifetimeRecord>(\n SANDBOX_LIFETIME_STORAGE_KEY\n )) ?? null;\n return record ? new SandboxLifetime(record) : null;\n }\n\n async getOrCreate(): Promise<SandboxLifetime> {\n const existing = await this.get();\n if (existing) {\n return existing;\n }\n\n const now = new Date().toISOString();\n const record: SandboxLifetimeRecord = {\n id: crypto.randomUUID() as SandboxLifetimeID,\n generation: 1,\n createdAt: now,\n updatedAt: now\n };\n await this.storage.put(SANDBOX_LIFETIME_STORAGE_KEY, record);\n return new SandboxLifetime(record);\n }\n\n async rotate(): Promise<SandboxLifetime> {\n const existing = await this.get();\n const now = new Date().toISOString();\n const record: SandboxLifetimeRecord = {\n id: crypto.randomUUID() as SandboxLifetimeID,\n generation: (existing?.generation ?? 0) + 1,\n createdAt: now,\n updatedAt: now\n };\n await this.storage.put(SANDBOX_LIFETIME_STORAGE_KEY, record);\n return new SandboxLifetime(record);\n }\n\n async isCurrent(lifetime: SandboxLifetime): Promise<boolean> {\n const current = await this.get();\n return current?.id === lifetime.id;\n }\n\n async assertCurrent(lifetime: SandboxLifetime): Promise<void> {\n if (!(await this.isCurrent(lifetime))) {\n throw new SandboxLifetimeChangedError();\n }\n }\n}\n","import type { RuntimeIncarnationID } from '../runtime';\nimport type { RuntimeIdentityID } from '../runtime/types';\nimport type { SandboxLifetimeID } from '../sandbox-lifetime';\n\nexport type BackupRestoreOperationStatus =\n | 'running'\n | 'committed'\n | 'failed'\n | 'interrupted';\n\nexport type BackupRestoreOperationPhase =\n | 'validating'\n | 'runtime_ready'\n | 'archive_ready'\n | 'verified'\n | 'failed'\n | 'interrupted';\n\nexport type BackupRestoreOperationError = {\n code: string;\n message: string;\n retryable: boolean;\n};\n\nexport type BackupRestoreOperationPayload = {\n backupId: string;\n dir: string;\n archiveSize?: number;\n};\n\nexport type BackupRestoreOperationResult = {\n success: true;\n id: string;\n dir: string;\n};\n\nexport type BackupRestoreOperationRecord = {\n operationId: string;\n operationKey: string;\n kind: 'backup.restore';\n sandboxLifetimeID: SandboxLifetimeID;\n phase: BackupRestoreOperationPhase;\n status: BackupRestoreOperationStatus;\n runtimeIdentityID?: RuntimeIdentityID;\n runtimeIncarnationID?: RuntimeIncarnationID;\n payload: BackupRestoreOperationPayload;\n result?: BackupRestoreOperationResult;\n error?: BackupRestoreOperationError;\n /** The 1-based attempt number for this operation record. */\n attempt: number;\n createdAt: string;\n updatedAt: string;\n completedAt?: string;\n /** ISO timestamp of the most recent interruption, if any. */\n lastInterruptedAt?: string;\n};\n\ntype OperationRecordStorage = Pick<DurableObjectStorage, 'get' | 'put'>;\n\nconst OPERATION_STORAGE_PREFIX = 'operations:';\n\nexport function backupRestoreOperationKey(\n backupId: string,\n dir: string\n): string {\n return `restore:${backupId}:${dir}`;\n}\n\nexport function createBackupRestoreOperationRecord(params: {\n operationId: string;\n sandboxLifetimeID: SandboxLifetimeID;\n backupId: string;\n dir: string;\n now: string;\n}): BackupRestoreOperationRecord {\n return {\n operationId: params.operationId,\n operationKey: backupRestoreOperationKey(params.backupId, params.dir),\n kind: 'backup.restore',\n sandboxLifetimeID: params.sandboxLifetimeID,\n phase: 'validating',\n status: 'running',\n attempt: 1,\n payload: {\n backupId: params.backupId,\n dir: params.dir\n },\n createdAt: params.now,\n updatedAt: params.now\n };\n}\n\n/**\n * Produce a next-attempt record that resets phase and status while\n * preserving the operationId so callers can reconcile restore history.\n */\nexport function nextBackupRestoreAttempt(\n record: BackupRestoreOperationRecord,\n now: string\n): BackupRestoreOperationRecord {\n return {\n ...record,\n phase: 'validating',\n status: 'running',\n runtimeIdentityID: undefined,\n runtimeIncarnationID: undefined,\n error: undefined,\n result: undefined,\n completedAt: undefined,\n lastInterruptedAt: undefined,\n updatedAt: now,\n attempt: (record.attempt ?? 0) + 1\n };\n}\n\nexport class BackupRestoreOperationStore {\n constructor(private readonly storage: OperationRecordStorage) {}\n\n async get(\n operationKey: string\n ): Promise<BackupRestoreOperationRecord | null> {\n return (\n (await this.storage.get<BackupRestoreOperationRecord>(\n this.storageKey(operationKey)\n )) ?? null\n );\n }\n\n async getCurrent(\n operationKey: string,\n sandboxLifetimeID: SandboxLifetimeID\n ): Promise<BackupRestoreOperationRecord | null> {\n const record = await this.get(operationKey);\n if (!record || record.sandboxLifetimeID !== sandboxLifetimeID) {\n return null;\n }\n return record;\n }\n\n async put(record: BackupRestoreOperationRecord): Promise<void> {\n await this.storage.put(this.storageKey(record.operationKey), record);\n }\n\n storageKey(operationKey: string): string {\n return `${OPERATION_STORAGE_PREFIX}${operationKey}`;\n }\n}\n","import {\n ErrorCode,\n OperationInterruptedError,\n RPCTransportError\n} from '../errors';\nimport type { RuntimeIdentity, RuntimeIdentityReader } from '../runtime';\nimport { RuntimeIdentityInactiveError } from '../runtime/types';\nimport type {\n CurrentSandboxLifetime,\n SandboxLifetime\n} from '../sandbox-lifetime';\nimport { SandboxLifetimeChangedError } from '../sandbox-lifetime';\nimport type { BackupRestoreFaultInjector } from './restore-fault-injection';\nimport {\n type BackupRestoreOperationPhase,\n type BackupRestoreOperationRecord,\n type BackupRestoreOperationResult,\n BackupRestoreOperationStore,\n backupRestoreOperationKey,\n createBackupRestoreOperationRecord,\n nextBackupRestoreAttempt\n} from './restore-operation-store';\n\ntype RestoreLifecycleDeps = {\n storage: DurableObjectStorage;\n runtimeReader: RuntimeIdentityReader;\n currentLifetime: CurrentSandboxLifetime;\n faultInjector: BackupRestoreFaultInjector;\n};\n\nexport type RestoreLifecycleContext = {\n lifetime: SandboxLifetime;\n runtimeReady: (\n runtime: RuntimeIdentity,\n archiveSize?: number\n ) => Promise<{\n runtime: RuntimeIdentity;\n operation: BackupRestoreOperationRecord;\n }>;\n archiveReady: (archiveSize?: number) => Promise<{\n runtime: RuntimeIdentity;\n operation: BackupRestoreOperationRecord;\n }>;\n verify: (\n result: BackupRestoreOperationResult,\n archiveSize?: number\n ) => Promise<BackupRestoreOperationRecord>;\n};\n\nexport class RestoreLifecycleRunner {\n private readonly operationRecords: BackupRestoreOperationStore;\n\n constructor(private readonly deps: RestoreLifecycleDeps) {\n this.operationRecords = new BackupRestoreOperationStore(deps.storage);\n }\n\n async execute(params: {\n backupId: string;\n dir: string;\n attempt: (\n context: RestoreLifecycleContext\n ) => Promise<BackupRestoreOperationResult>;\n }): Promise<BackupRestoreOperationResult> {\n const lifetime = await this.deps.currentLifetime.getOrCreate();\n const operationKey = backupRestoreOperationKey(params.backupId, params.dir);\n const existing = await this.operationRecords.getCurrent(\n operationKey,\n lifetime.id\n );\n\n const now = new Date().toISOString();\n let operation: BackupRestoreOperationRecord;\n\n const isRetryable =\n existing !== null &&\n (existing.status === 'interrupted' || existing.status === 'running') &&\n existing.error?.retryable !== false;\n\n if (isRetryable) {\n // Resume with the same operationId so callers can reconcile the record.\n operation = nextBackupRestoreAttempt(existing, now);\n } else {\n operation = createBackupRestoreOperationRecord({\n operationId: crypto.randomUUID(),\n sandboxLifetimeID: lifetime.id,\n backupId: params.backupId,\n dir: params.dir,\n now\n });\n }\n await this.operationRecords.put(operation);\n\n return await this.runAttempt(operation, lifetime, params.attempt);\n }\n\n private async runAttempt(\n operation: BackupRestoreOperationRecord,\n lifetime: SandboxLifetime,\n attempt: (\n context: RestoreLifecycleContext\n ) => Promise<BackupRestoreOperationResult>\n ): Promise<BackupRestoreOperationResult> {\n let currentOperation = operation;\n let runtime: RuntimeIdentity | undefined;\n\n const context: RestoreLifecycleContext = {\n lifetime,\n runtimeReady: async (admittedRuntime, archiveSize) => {\n try {\n runtime = admittedRuntime;\n await this.assertFences(runtime, lifetime);\n } catch (error) {\n const interrupted = await this.translateFenceError(\n error,\n currentOperation,\n 'validating',\n 'unknown'\n );\n throw interrupted ?? error;\n }\n currentOperation = await this.markRuntimeReady(\n currentOperation,\n runtime,\n archiveSize\n );\n return { runtime, operation: currentOperation };\n },\n archiveReady: async (archiveSize) => {\n if (!runtime) {\n throw new Error(\n 'Backup restore archiveReady requires runtimeReady()'\n );\n }\n try {\n await this.assertFences(runtime, lifetime);\n } catch (error) {\n const interrupted = await this.translateFenceError(\n error,\n currentOperation,\n currentOperation.phase,\n true\n );\n throw interrupted ?? error;\n }\n currentOperation = await this.markArchiveReady(\n currentOperation,\n runtime,\n archiveSize\n );\n const fault = await this.deps.faultInjector.maybeFault(\n 'after_archive_ready',\n currentOperation\n );\n if (fault) {\n const message =\n 'Backup restore was interrupted before completion could be verified';\n const interrupted = await this.markInterrupted(\n currentOperation,\n message\n );\n throw this.createInterruptedError({\n operation: interrupted,\n reason: fault.reason,\n phase: currentOperation.phase,\n admitted: fault.admitted,\n message\n });\n }\n return { runtime, operation: currentOperation };\n },\n verify: async (result, archiveSize) => {\n if (!runtime) {\n throw new Error(\n 'Backup restore verification requires archiveReady()'\n );\n }\n try {\n await this.assertFences(runtime, lifetime);\n } catch (error) {\n const interrupted = await this.translateFenceError(\n error,\n currentOperation,\n 'archive_ready'\n );\n throw interrupted ?? error;\n }\n currentOperation = await this.markVerified(\n currentOperation,\n runtime,\n result,\n archiveSize\n );\n return currentOperation;\n }\n };\n\n try {\n return await attempt(context);\n } catch (error) {\n if (error instanceof OperationInterruptedError) {\n throw await this.translateOperationInterrupted(error, currentOperation);\n }\n const translated = await this.translateRPCError(error, currentOperation);\n if (translated) {\n throw translated;\n }\n await this.markFailed(currentOperation, error);\n throw error;\n }\n }\n\n async markRuntimeReady(\n operation: BackupRestoreOperationRecord,\n runtime: RuntimeIdentity,\n archiveSize?: number\n ): Promise<BackupRestoreOperationRecord> {\n const next = {\n ...operation,\n phase: 'runtime_ready' as const,\n runtimeIdentityID: runtime.id,\n runtimeIncarnationID: runtime.runtimeIncarnationID,\n payload: {\n ...operation.payload,\n ...(archiveSize !== undefined && { archiveSize })\n },\n updatedAt: new Date().toISOString()\n };\n await this.operationRecords.put(next);\n return next;\n }\n\n async markArchiveReady(\n operation: BackupRestoreOperationRecord,\n runtime: RuntimeIdentity,\n archiveSize?: number\n ): Promise<BackupRestoreOperationRecord> {\n const next = {\n ...operation,\n phase: 'archive_ready' as const,\n runtimeIdentityID: runtime.id,\n runtimeIncarnationID: runtime.runtimeIncarnationID,\n payload: {\n ...operation.payload,\n ...(archiveSize !== undefined && { archiveSize })\n },\n updatedAt: new Date().toISOString()\n };\n await this.operationRecords.put(next);\n return next;\n }\n\n async assertFences(\n runtime: RuntimeIdentity,\n lifetime: SandboxLifetime\n ): Promise<void> {\n await this.deps.runtimeReader.assertActive(runtime);\n await this.deps.currentLifetime.assertCurrent(lifetime);\n }\n\n async markVerified(\n operation: BackupRestoreOperationRecord,\n runtime: RuntimeIdentity,\n result: BackupRestoreOperationResult,\n archiveSize?: number\n ): Promise<BackupRestoreOperationRecord> {\n const completedAt = new Date().toISOString();\n const next = {\n ...operation,\n phase: 'verified' as const,\n status: 'committed' as const,\n runtimeIdentityID: runtime.id,\n runtimeIncarnationID: runtime.runtimeIncarnationID,\n payload: {\n ...operation.payload,\n ...(archiveSize !== undefined && { archiveSize })\n },\n result,\n completedAt,\n updatedAt: completedAt\n };\n await this.operationRecords.put(next);\n return next;\n }\n\n async translateFenceError(\n error: unknown,\n operation: BackupRestoreOperationRecord,\n phase: BackupRestoreOperationPhase,\n admitted: true | 'unknown' = true\n ): Promise<OperationInterruptedError | null> {\n if (\n !(\n error instanceof RuntimeIdentityInactiveError ||\n error instanceof SandboxLifetimeChangedError\n )\n ) {\n return null;\n }\n\n const reason =\n error instanceof RuntimeIdentityInactiveError\n ? 'runtime_replaced'\n : 'sandbox_lifetime_changed';\n const message =\n 'Backup restore was interrupted before completion could be verified';\n const interrupted = await this.markInterrupted(\n operation,\n message,\n reason !== 'sandbox_lifetime_changed'\n );\n return this.createInterruptedError({\n operation: interrupted,\n reason,\n phase,\n admitted,\n message\n });\n }\n\n private async translateOperationInterrupted(\n error: OperationInterruptedError,\n operation: BackupRestoreOperationRecord\n ): Promise<OperationInterruptedError> {\n if (error.context.operationId === operation.operationId) {\n return error;\n }\n\n const phase = operation.phase;\n const message =\n 'Backup restore was interrupted before completion could be verified';\n const retryable = this.isRestoreRetryable(error.context.reason);\n const interrupted = await this.markInterrupted(\n operation,\n message,\n retryable\n );\n return this.createInterruptedError({\n operation: interrupted,\n reason: error.context.reason,\n phase,\n admitted: error.context.admitted,\n message\n });\n }\n\n async translateRPCError(\n error: unknown,\n operation: BackupRestoreOperationRecord\n ): Promise<OperationInterruptedError | null> {\n if (!(error instanceof RPCTransportError)) {\n return null;\n }\n\n const message =\n 'Backup restore was interrupted before completion could be verified';\n const interruptedPhase = operation.phase;\n const interrupted = await this.markInterrupted(operation, message);\n return this.createInterruptedError({\n operation: interrupted,\n reason: 'transport_disposed',\n phase: interruptedPhase,\n admitted: 'unknown',\n message\n });\n }\n\n private async markFailed(\n operation: BackupRestoreOperationRecord,\n error: unknown\n ): Promise<BackupRestoreOperationRecord> {\n const completedAt = new Date().toISOString();\n const message = error instanceof Error ? error.message : String(error);\n const code =\n error instanceof Error &&\n 'code' in error &&\n typeof error.code === 'string'\n ? error.code\n : 'UNKNOWN';\n const next = {\n ...operation,\n phase: 'failed' as const,\n status: 'failed' as const,\n error: {\n code,\n message,\n retryable: false\n },\n completedAt,\n updatedAt: completedAt\n };\n await this.operationRecords.put(next);\n return next;\n }\n\n private async markInterrupted(\n operation: BackupRestoreOperationRecord,\n message: string,\n retryable = true\n ): Promise<BackupRestoreOperationRecord> {\n const now = new Date().toISOString();\n const next = {\n ...operation,\n phase: 'interrupted' as const,\n status: 'interrupted' as const,\n error: {\n code: ErrorCode.OPERATION_INTERRUPTED,\n message,\n retryable\n },\n lastInterruptedAt: now,\n updatedAt: now\n };\n await this.operationRecords.put(next);\n return next;\n }\n\n private isRestoreRetryable(\n reason: OperationInterruptedError['context']['reason']\n ): boolean {\n return ![\n 'sandbox_destroyed',\n 'sandbox_lifetime_changed',\n 'recovery_exhausted'\n ].includes(reason);\n }\n\n private createInterruptedError(params: {\n operation: BackupRestoreOperationRecord;\n reason: OperationInterruptedError['context']['reason'];\n phase: BackupRestoreOperationPhase;\n admitted: true | 'unknown';\n message: string;\n }): OperationInterruptedError {\n const retryable = this.isRestoreRetryable(params.reason);\n const suggestion = retryable\n ? 'Retry restoreBackup() with the same backup handle so the SDK can reconcile the restore operation.'\n : 'Start a new restoreBackup() call only if restoring this backup is still desired for the current sandbox.';\n\n return new OperationInterruptedError({\n message: params.message,\n code: ErrorCode.OPERATION_INTERRUPTED,\n httpStatus: 409,\n context: {\n reason: params.reason,\n operation: 'backup.restore',\n operationId: params.operation.operationId,\n operationKey: params.operation.operationKey,\n idempotencyKey: params.operation.operationKey,\n backupId: params.operation.payload.backupId,\n dir: params.operation.payload.dir,\n phase: params.phase,\n admitted: params.admitted,\n retryable\n },\n timestamp: new Date().toISOString(),\n suggestion\n });\n }\n}\n","import {\n type createLogger,\n getEnvString,\n type UploadPartsResponse\n} from '@repo/shared';\nimport { AwsClient } from 'aws4fetch';\nimport type { ContainerControlClient } from '../container-control';\nimport {\n BackupCreateError,\n BackupRestoreError,\n ErrorCode,\n InvalidBackupConfigError,\n OperationInterruptedError,\n RPCTransportError,\n SandboxError\n} from '../errors';\nimport { isR2Bucket } from '../storage-mount';\nimport {\n BACKUP_DOWNLOAD_MAX_PARTS,\n BACKUP_DOWNLOAD_PARALLEL_MIN_SIZE,\n BACKUP_DOWNLOAD_PARALLEL_PARTS,\n BACKUP_MULTIPART_MAX_PARTS,\n BACKUP_MULTIPART_MIN_PART_SIZE,\n BACKUP_MULTIPART_TARGET_PARTS,\n calculatePartCount\n} from './constants';\n\ntype BackupTransferDeps = {\n getEnv: () => unknown;\n logger: ReturnType<typeof createLogger>;\n};\n\nexport class BackupTransfer {\n private static readonly PRESIGNED_URL_EXPIRY_SECONDS = 3600;\n\n constructor(private readonly deps: BackupTransferDeps) {\n this.parseBackupBucketEndpoint(this.env as Record<string, unknown>);\n }\n\n private get env(): unknown {\n return this.deps.getEnv();\n }\n\n private parseBackupBucketEndpoint(\n envObj: Record<string, unknown>\n ): string | null {\n const rawEndpoint = getEnvString(envObj, 'BACKUP_BUCKET_ENDPOINT') ?? null;\n if (rawEndpoint === null) {\n return null;\n }\n\n let parsed: URL;\n try {\n parsed = new URL(rawEndpoint);\n } catch {\n const msg = `BACKUP_BUCKET_ENDPOINT is not a valid URL: \"${rawEndpoint}\". Expected format: https://<account_id>.eu.r2.cloudflarestorage.com`;\n throw new InvalidBackupConfigError({\n message: msg,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: msg },\n timestamp: new Date().toISOString()\n });\n }\n if (parsed.protocol !== 'https:') {\n const msg = `BACKUP_BUCKET_ENDPOINT must use https://, got \"${parsed.protocol.slice(0, -1)}://\"`;\n throw new InvalidBackupConfigError({\n message: msg,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: msg },\n timestamp: new Date().toISOString()\n });\n }\n if (parsed.pathname !== '/') {\n const msg = `BACKUP_BUCKET_ENDPOINT must not include a path (got \"${parsed.pathname}\"). Provide only the origin, e.g. https://<account_id>.eu.r2.cloudflarestorage.com`;\n throw new InvalidBackupConfigError({\n message: msg,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: msg },\n timestamp: new Date().toISOString()\n });\n }\n if (parsed.search !== '' || parsed.hash !== '') {\n const msg =\n 'BACKUP_BUCKET_ENDPOINT must not include query parameters or fragments. Provide only the origin, e.g. https://<account_id>.eu.r2.cloudflarestorage.com';\n throw new InvalidBackupConfigError({\n message: msg,\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: msg },\n timestamp: new Date().toISOString()\n });\n }\n return parsed.origin;\n }\n\n /** Validate that the BACKUP_BUCKET R2 binding is present and return it. */\n requireBackupBucket(): R2Bucket {\n const bucket = (this.env as Record<string, unknown>).BACKUP_BUCKET;\n if (!isR2Bucket(bucket)) {\n throw new InvalidBackupConfigError({\n message:\n 'Backup not configured. Add a BACKUP_BUCKET R2 binding to your wrangler.jsonc.',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'Missing BACKUP_BUCKET R2 binding' },\n timestamp: new Date().toISOString()\n });\n }\n return bucket;\n }\n\n requirePresignedURLSupport(): {\n client: AwsClient;\n accountId: string;\n bucketName: string;\n } {\n const envObj = this.env as Record<string, unknown>;\n const r2AccountId =\n getEnvString(envObj, 'CLOUDFLARE_R2_ACCOUNT_ID') ??\n getEnvString(envObj, 'CLOUDFLARE_ACCOUNT_ID') ??\n null;\n const r2AccessKeyId = getEnvString(envObj, 'R2_ACCESS_KEY_ID') ?? null;\n const r2SecretAccessKey =\n getEnvString(envObj, 'R2_SECRET_ACCESS_KEY') ?? null;\n const backupBucketName = getEnvString(envObj, 'BACKUP_BUCKET_NAME') ?? null;\n\n if (\n !r2AccessKeyId ||\n !r2SecretAccessKey ||\n !r2AccountId ||\n !backupBucketName\n ) {\n const missing: string[] = [];\n if (!r2AccountId)\n missing.push('CLOUDFLARE_R2_ACCOUNT_ID or CLOUDFLARE_ACCOUNT_ID');\n if (!r2AccessKeyId) missing.push('R2_ACCESS_KEY_ID');\n if (!r2SecretAccessKey) missing.push('R2_SECRET_ACCESS_KEY');\n if (!backupBucketName) missing.push('BACKUP_BUCKET_NAME');\n\n throw new InvalidBackupConfigError({\n message:\n `Backup requires R2 presigned URL credentials. ` +\n `Missing: ${missing.join(', ')}. ` +\n 'Set these as environment variables or secrets in your wrangler.jsonc.',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: `Missing env vars: ${missing.join(', ')}` },\n timestamp: new Date().toISOString()\n });\n }\n\n return {\n client: new AwsClient({\n accessKeyId: r2AccessKeyId,\n secretAccessKey: r2SecretAccessKey\n }),\n accountId: r2AccountId,\n bucketName: backupBucketName\n };\n }\n\n private getBackupBucketEndpoint(accountId: string): string {\n return (\n this.parseBackupBucketEndpoint(this.env as Record<string, unknown>) ??\n `https://${accountId}.r2.cloudflarestorage.com`\n );\n }\n\n getBackupObjectURL(\n accountId: string,\n bucketName: string,\n r2Key: string\n ): URL {\n const encodedBucket = encodeURIComponent(bucketName);\n const encodedKey = r2Key\n .split('/')\n .map((seg) => encodeURIComponent(seg))\n .join('/');\n\n return new URL(\n `${this.getBackupBucketEndpoint(accountId)}/${encodedBucket}/${encodedKey}`\n );\n }\n\n /**\n * Generate a presigned GET URL for downloading an object from R2.\n * The container can curl this URL directly without credentials.\n */\n async generatePresignedGetURL(r2Key: string): Promise<string> {\n const { client, accountId, bucketName } = this.requirePresignedURLSupport();\n\n const url = this.getBackupObjectURL(accountId, bucketName, r2Key);\n url.searchParams.set(\n 'X-Amz-Expires',\n String(BackupTransfer.PRESIGNED_URL_EXPIRY_SECONDS)\n );\n\n const signed = await client.sign(new Request(url), {\n aws: { signQuery: true }\n });\n\n return signed.url;\n }\n\n /**\n * Generate a presigned PUT URL for uploading an object to R2.\n * The container can curl PUT to this URL directly without credentials.\n */\n private async generatePresignedPutURL(r2Key: string): Promise<string> {\n const { client, accountId, bucketName } = this.requirePresignedURLSupport();\n\n const url = this.getBackupObjectURL(accountId, bucketName, r2Key);\n url.searchParams.set(\n 'X-Amz-Expires',\n String(BackupTransfer.PRESIGNED_URL_EXPIRY_SECONDS)\n );\n\n const signed = await client.sign(new Request(url, { method: 'PUT' }), {\n aws: { signQuery: true }\n });\n\n return signed.url;\n }\n\n /**\n * Upload a backup archive via presigned PUT URL.\n * The container curls the archive directly to R2, bypassing the DO.\n * ~24 MB/s throughput vs ~0.6 MB/s for base64 readFile.\n */\n async uploadBackupPresigned(\n archivePath: string,\n r2Key: string,\n archiveSize: number,\n backupId: string,\n dir: string,\n control: ContainerControlClient\n ): Promise<void> {\n const presignedURL = await this.generatePresignedPutURL(r2Key);\n\n await control.backup.uploadArchive({\n archivePath,\n url: presignedURL,\n timeoutMs: 1_810_000\n });\n\n // Verify the upload landed correctly in R2\n const bucket = this.requireBackupBucket();\n const head = await bucket.head(r2Key);\n if (!head || head.size !== archiveSize) {\n const actualSize = head?.size ?? 0;\n // curl succeeded but R2 binding sees nothing — almost certainly a\n // local-dev mismatch where presigned URLs target real R2 while the\n // BACKUP_BUCKET binding points to local (miniflare) storage.\n const localDevHint =\n actualSize === 0\n ? ' This usually means the BACKUP_BUCKET R2 binding is using local storage ' +\n 'while presigned URLs upload to remote R2. Add `\"remote\": true` to your ' +\n 'BACKUP_BUCKET R2 binding in wrangler.jsonc to fix this.'\n : '';\n throw new BackupCreateError({\n message: `Upload verification failed: expected ${archiveSize} bytes, got ${actualSize}.${localDevHint}`,\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n }\n\n /**\n * Generate a presigned PUT URL for a single part in a multipart upload.\n */\n private async generatePresignedPartURL(\n r2Key: string,\n uploadId: string,\n partNumber: number\n ): Promise<string> {\n const { client, accountId, bucketName } = this.requirePresignedURLSupport();\n\n const url = this.getBackupObjectURL(accountId, bucketName, r2Key);\n url.searchParams.set(\n 'X-Amz-Expires',\n String(BackupTransfer.PRESIGNED_URL_EXPIRY_SECONDS)\n );\n url.searchParams.set('partNumber', String(partNumber));\n url.searchParams.set('uploadId', uploadId);\n\n const signed = await client.sign(new Request(url, { method: 'PUT' }), {\n aws: { signQuery: true }\n });\n\n return signed.url;\n }\n\n /**\n * Upload a backup archive to R2 using parallel multipart upload.\n * Uses the S3-compatible API exclusively for create/complete/abort so that\n * the uploadId is in the same namespace as the presigned part PUT URLs.\n */\n async uploadBackupMultipart(\n archivePath: string,\n r2Key: string,\n sizeBytes: number,\n backupId: string,\n dir: string,\n control: ContainerControlClient\n ): Promise<void> {\n const targetParts = calculatePartCount(\n sizeBytes,\n BACKUP_MULTIPART_TARGET_PARTS,\n BACKUP_MULTIPART_MAX_PARTS\n );\n const numParts = Math.min(\n targetParts,\n Math.floor(sizeBytes / BACKUP_MULTIPART_MIN_PART_SIZE)\n );\n\n if (numParts <= 1) {\n return this.uploadBackupPresigned(\n archivePath,\n r2Key,\n sizeBytes,\n backupId,\n dir,\n control\n );\n }\n\n const { client, accountId, bucketName } = this.requirePresignedURLSupport();\n const objectURL = this.getBackupObjectURL(\n accountId,\n bucketName,\n r2Key\n ).toString();\n\n const createResp = await client.fetch(`${objectURL}?uploads`, {\n method: 'POST'\n });\n if (!createResp.ok) {\n throw new BackupCreateError({\n message: `Failed to initiate multipart upload: HTTP ${createResp.status}`,\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n\n const createXml = await createResp.text();\n const uploadIdMatch = createXml.match(/<UploadId>([^<]+)<\\/UploadId>/);\n const uploadId = uploadIdMatch?.[1];\n if (!uploadId) {\n throw new BackupCreateError({\n message: 'Multipart upload response did not contain an UploadId',\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n\n const abortMultipart = async () => {\n await client\n .fetch(`${objectURL}?uploadId=${encodeURIComponent(uploadId)}`, {\n method: 'DELETE'\n })\n .catch(() => {});\n };\n\n try {\n const partSize = Math.ceil(sizeBytes / numParts);\n const parts = await Promise.all(\n Array.from({ length: numParts }, (_, i) => ({\n partNumber: i + 1,\n url: '',\n offset: i * partSize,\n size: i === numParts - 1 ? sizeBytes - i * partSize : partSize\n })).map(async (part) => ({\n ...part,\n url: await this.generatePresignedPartURL(\n r2Key,\n uploadId,\n part.partNumber\n )\n }))\n );\n\n let uploadResult: UploadPartsResponse;\n try {\n uploadResult = await control.backup.uploadParts({\n archivePath,\n parts\n });\n } catch (err) {\n if (\n err instanceof SandboxError &&\n err.errorResponse.httpStatus === 404\n ) {\n await abortMultipart();\n return this.uploadBackupPresigned(\n archivePath,\n r2Key,\n sizeBytes,\n backupId,\n dir,\n control\n );\n }\n throw err;\n }\n\n if (!uploadResult.success || uploadResult.parts.length !== numParts) {\n throw new BackupCreateError({\n message: `Multipart upload returned ${uploadResult.parts.length} of ${numParts} parts`,\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n\n const completeXml = [\n '<CompleteMultipartUpload>',\n ...uploadResult.parts.map(\n (p: { partNumber: number; etag: string }) =>\n `<Part><PartNumber>${p.partNumber}</PartNumber><ETag>${p.etag}</ETag></Part>`\n ),\n '</CompleteMultipartUpload>'\n ].join('');\n\n const completeResp = await client.fetch(\n `${objectURL}?uploadId=${encodeURIComponent(uploadId)}`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/xml' },\n body: completeXml\n }\n );\n\n if (!completeResp.ok) {\n const body = await completeResp.text().catch(() => '');\n throw new BackupCreateError({\n message: `Multipart upload completion failed: HTTP ${completeResp.status} ${body}`,\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n\n const head = await this.requireBackupBucket().head(r2Key);\n if (!head || head.size !== sizeBytes) {\n throw new BackupCreateError({\n message: `Multipart upload verification failed: expected ${sizeBytes} bytes, got ${head?.size ?? 0}`,\n code: ErrorCode.BACKUP_CREATE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n } catch (error) {\n await abortMultipart();\n throw error;\n }\n }\n\n /**\n * Download a backup archive from R2 via presigned GET URL.\n * For archives >= BACKUP_DOWNLOAD_PARALLEL_MIN_SIZE, uses BACKUP_DOWNLOAD_PARALLEL_PARTS\n * concurrent curl processes (each downloading a byte-range) to maximise both\n * network and disk-write throughput. Parts are written into a pre-sized file\n * with dd using byte offsets, then atomically moved to the final path.\n */\n async downloadBackupParallel(\n archivePath: string,\n r2Key: string,\n expectedSize: number,\n backupId: string,\n dir: string,\n control: ContainerControlClient\n ): Promise<void> {\n const presignedURL = await this.generatePresignedGetURL(r2Key);\n const parts =\n expectedSize < BACKUP_DOWNLOAD_PARALLEL_MIN_SIZE\n ? [{ url: presignedURL, offset: 0 }]\n : (() => {\n const partCount = calculatePartCount(\n expectedSize,\n BACKUP_DOWNLOAD_PARALLEL_PARTS,\n BACKUP_DOWNLOAD_MAX_PARTS\n );\n const partSize = Math.floor(expectedSize / partCount);\n return Array.from({ length: partCount }, (_, i) => {\n const start = i * partSize;\n const end =\n i < partCount - 1 ? start + partSize - 1 : expectedSize - 1;\n return {\n url: presignedURL,\n offset: start,\n range: `bytes=${start}-${end}`\n };\n });\n })();\n\n try {\n await control.backup.downloadArchive({\n archivePath,\n expectedSize,\n parts,\n timeoutMs: 1_810_000\n });\n } catch (error) {\n if (\n error instanceof OperationInterruptedError ||\n error instanceof RPCTransportError\n ) {\n throw error;\n }\n throw new BackupRestoreError({\n message: `Presigned URL download failed: ${error instanceof Error ? error.message : String(error)}`,\n code: ErrorCode.BACKUP_RESTORE_FAILED,\n httpStatus: 500,\n context: { dir, backupId },\n timestamp: new Date().toISOString()\n });\n }\n }\n\n /**\n * Serialize backup operations on this sandbox instance.\n * Concurrent backup/restore calls are queued so the multi-step\n * create-archive → read → upload (or mount → extract) flow\n * is not interleaved with another backup operation on the same directory.\n */\n}\n","import type {\n BackupOptions,\n DirectoryBackup,\n RestoreBackupResult\n} from '@repo/shared';\nimport { type createLogger, logCanonicalEvent } from '@repo/shared';\nimport type { ContainerControlClient } from '../container-control';\nimport {\n BackupCreateError,\n BackupExpiredError,\n BackupNotFoundError,\n BackupRestoreError,\n ErrorCode,\n InvalidBackupConfigError\n} from '../errors';\nimport type { RuntimeIdentity, RuntimeIdentityReader } from '../runtime';\nimport type { CurrentSandboxLifetime } from '../sandbox-lifetime';\nimport { isR2Bucket } from '../storage-mount';\nimport {\n BACKUP_ARCHIVE_OBJECT_NAME,\n BACKUP_CONTAINER_DIR,\n BACKUP_METADATA_OBJECT_NAME,\n BACKUP_STORAGE_PREFIX\n} from './constants';\nimport { BackupCreator } from './create';\nimport {\n type BackupRestoreTestFault,\n StorageBackedBackupRestoreFaultInjector\n} from './restore-fault-injection';\nimport {\n type RestoreLifecycleContext,\n RestoreLifecycleRunner\n} from './restore-lifecycle';\nimport type { BackupRestoreOperationResult } from './restore-operation-store';\nimport { BackupTransfer } from './transfer';\nimport { validateBackupDir } from './validation';\n\nexport type { BackupRestoreTestFault } from './restore-fault-injection';\n\nexport type BackupAttemptLease = {\n runtime: RuntimeIdentity;\n control: ContainerControlClient;\n retain(onInterrupt?: () => void): { release(): void };\n};\n\ntype BackupAttemptCall = <T>(\n operation: string,\n call: (lease: BackupAttemptLease) => Promise<T>\n) => Promise<T>;\n\ntype BackupServiceDeps = {\n ctx: DurableObjectState<{}>;\n getEnv: () => unknown;\n logger: ReturnType<typeof createLogger>;\n runBackupAttempt: BackupAttemptCall;\n runtimeReader: RuntimeIdentityReader;\n currentLifetime: CurrentSandboxLifetime;\n};\n\nexport class BackupService {\n private readonly ctx: DurableObjectState<{}>;\n private readonly logger: ReturnType<typeof createLogger>;\n private readonly creator: BackupCreator;\n private readonly restoreFaults: StorageBackedBackupRestoreFaultInjector;\n private readonly restoreLifecycle: RestoreLifecycleRunner;\n private readonly transfer: BackupTransfer;\n private backupInProgress: Promise<unknown> = Promise.resolve();\n\n constructor(private readonly deps: BackupServiceDeps) {\n this.ctx = deps.ctx;\n this.logger = deps.logger;\n this.restoreFaults = new StorageBackedBackupRestoreFaultInjector(\n this.ctx.storage,\n deps.getEnv\n );\n this.restoreLifecycle = new RestoreLifecycleRunner({\n storage: this.ctx.storage,\n runtimeReader: deps.runtimeReader,\n currentLifetime: deps.currentLifetime,\n faultInjector: this.restoreFaults\n });\n this.transfer = new BackupTransfer({\n getEnv: deps.getEnv,\n logger: deps.logger\n });\n this.creator = new BackupCreator({\n getEnv: deps.getEnv,\n runBackupAttempt: deps.runBackupAttempt,\n logger: deps.logger,\n transfer: this.transfer\n });\n }\n\n private get env(): unknown {\n return this.deps.getEnv();\n }\n\n private static readonly UUID_REGEX =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n /**\n * Serialize backup operations so concurrent calls run one at a time.\n */\n private async enqueueBackupOp<T>(fn: () => Promise<T>): Promise<T> {\n try {\n await this.backupInProgress;\n } catch {\n // Previous backup/restore failure should not poison later operations.\n }\n\n const next = fn();\n this.backupInProgress = next.catch(() => {});\n return await next;\n }\n\n /**\n * Create a backup of a directory and upload it to R2.\n *\n * Flow:\n * 1. Container creates squashfs archive from the directory\n * 2. Container uploads the archive directly to R2 via presigned URL\n * 3. DO writes metadata to R2\n * 4. Container cleans up the local archive\n *\n * The returned DirectoryBackup handle is serializable. Store it anywhere\n * (KV, D1, DO storage) and pass it to restoreBackup() later.\n *\n * Concurrent backup/restore calls on the same sandbox are serialized.\n *\n * Partially-written files in the target directory may not be captured\n * consistently. Completed writes are captured.\n *\n * NOTE: Expired backups are not automatically deleted from R2. Configure\n * R2 lifecycle rules on the BACKUP_BUCKET to garbage-collect objects\n * under the `backups/` prefix after the desired retention period.\n */\n async createBackup(options: BackupOptions): Promise<DirectoryBackup> {\n return await this.enqueueBackupOp(() => this.creator.createBackup(options));\n }\n\n async restoreBackup(backup: DirectoryBackup): Promise<RestoreBackupResult> {\n const { id, dir } = this.validateRestoreBackup(backup);\n if (backup.localBucket) {\n return await this.enqueueBackupOp(() =>\n this.restoreLifecycle.execute({\n backupId: id,\n dir,\n attempt: (context) => this.doRestoreBackupLocal(backup, context)\n })\n );\n }\n this.transfer.requireBackupBucket();\n return await this.enqueueBackupOp(() =>\n this.restoreLifecycle.execute({\n backupId: id,\n dir,\n attempt: (context) => this.doRestoreBackup(backup, context)\n })\n );\n }\n\n private validateRestoreBackup(backup: DirectoryBackup): {\n id: string;\n dir: string;\n } {\n const { id, dir } = backup;\n if (!id || typeof id !== 'string') {\n throw new InvalidBackupConfigError({\n message: 'Invalid backup: missing or invalid id',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'missing or invalid id' },\n timestamp: new Date().toISOString()\n });\n }\n if (!BackupService.UUID_REGEX.test(id)) {\n throw new InvalidBackupConfigError({\n message:\n 'Invalid backup: id must be a valid UUID (e.g. from createBackup)',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'id must be a valid UUID' },\n timestamp: new Date().toISOString()\n });\n }\n validateBackupDir(dir, 'Invalid backup: dir');\n return { id, dir };\n }\n\n async setRestoreFaultForTesting(\n fault: BackupRestoreTestFault | null\n ): Promise<void> {\n await this.restoreFaults.setFaultForTesting(fault);\n }\n\n private async doRestoreBackup(\n backup: DirectoryBackup,\n lifecycle: RestoreLifecycleContext\n ): Promise<BackupRestoreOperationResult> {\n const restoreStartTime = Date.now();\n const bucket = this.transfer.requireBackupBucket();\n this.transfer.requirePresignedURLSupport();\n const { id, dir } = backup;\n\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n\n try {\n // Step 1: Read metadata to check TTL\n const metaKey = `${BACKUP_STORAGE_PREFIX}/${id}/${BACKUP_METADATA_OBJECT_NAME}`;\n const metaObject = await bucket.get(metaKey);\n if (!metaObject) {\n throw new BackupNotFoundError({\n message:\n `Backup not found: ${id}. ` +\n 'Verify the backup ID is correct and the backup has not been deleted.',\n code: ErrorCode.BACKUP_NOT_FOUND,\n httpStatus: 404,\n context: { backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n\n const metadata = await metaObject.json<{\n ttl: number;\n createdAt: string;\n dir: string;\n }>();\n\n // Check TTL with 60-second buffer to prevent race between check and restore completion\n const TTL_BUFFER_MS = 60 * 1000;\n const createdAt = new Date(metadata.createdAt).getTime();\n if (Number.isNaN(createdAt)) {\n throw new BackupRestoreError({\n message: `Backup metadata has invalid createdAt timestamp: ${metadata.createdAt}`,\n code: ErrorCode.BACKUP_RESTORE_FAILED,\n httpStatus: 500,\n context: { dir, backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n const expiresAt = createdAt + metadata.ttl * 1000;\n if (Date.now() + TTL_BUFFER_MS > expiresAt) {\n throw new BackupExpiredError({\n message:\n `Backup ${id} has expired ` +\n `(created: ${metadata.createdAt}, TTL: ${metadata.ttl}s). ` +\n 'Create a new backup.',\n code: ErrorCode.BACKUP_EXPIRED,\n httpStatus: 400,\n context: {\n backupId: id,\n expiredAt: new Date(expiresAt).toISOString()\n },\n timestamp: new Date().toISOString()\n });\n }\n\n // Step 2: Check archive exists and get its size via HEAD (no body stream)\n const r2Key = `${BACKUP_STORAGE_PREFIX}/${id}/${BACKUP_ARCHIVE_OBJECT_NAME}`;\n const archiveHead = await bucket.head(r2Key);\n if (!archiveHead) {\n throw new BackupNotFoundError({\n message:\n `Backup archive not found in R2: ${id}. ` +\n 'The archive may have been deleted by R2 lifecycle rules.',\n code: ErrorCode.BACKUP_NOT_FOUND,\n httpStatus: 404,\n context: { backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n\n const archivePath = `${BACKUP_CONTAINER_DIR}/${id}.sqsh`;\n const restoreResult = await this.deps.runBackupAttempt(\n 'backup.restore',\n async ({ runtime, control }) => {\n await lifecycle.runtimeReady(runtime, archiveHead.size);\n try {\n const prepareResult = await control.backup.prepareRestore({\n dir,\n backupId: id,\n archivePath\n });\n\n if (prepareResult.existingSize !== archiveHead.size) {\n await this.transfer.downloadBackupParallel(\n archivePath,\n r2Key,\n archiveHead.size,\n id,\n dir,\n control\n );\n }\n\n await lifecycle.archiveReady(archiveHead.size);\n return await control.backup.restoreArchive(dir, archivePath);\n } catch (error) {\n await control.backup.cleanupArchive(archivePath).catch(() => {});\n throw error;\n }\n }\n );\n\n if (!restoreResult.success) {\n throw new BackupRestoreError({\n message: 'Container failed to restore backup archive',\n code: ErrorCode.BACKUP_RESTORE_FAILED,\n httpStatus: 500,\n context: { dir, backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n\n const result = {\n success: true as const,\n dir,\n id\n };\n await lifecycle.verify(result, archiveHead.size);\n\n outcome = 'success';\n\n return result;\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.logger, {\n event: 'backup.restore',\n outcome,\n durationMs: Date.now() - restoreStartTime,\n backupId: id,\n dir,\n error: caughtError\n });\n }\n }\n\n /**\n * Local-dev implementation of restoreBackup.\n * Uses the R2 binding directly instead of presigned URLs, and\n * unsquashfs for extraction instead of squashfuse + fuse-overlayfs.\n */\n private async doRestoreBackupLocal(\n backup: DirectoryBackup,\n lifecycle: RestoreLifecycleContext\n ): Promise<BackupRestoreOperationResult> {\n const restoreStartTime = Date.now();\n const { id, dir } = backup;\n\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n\n // Resolve backup bucket from env as an R2 binding\n const envObj = this.env as Record<string, unknown>;\n const bucket = envObj.BACKUP_BUCKET;\n if (!bucket || !isR2Bucket(bucket)) {\n throw new InvalidBackupConfigError({\n message:\n 'BACKUP_BUCKET R2 binding not found in env. ' +\n 'Add a BACKUP_BUCKET R2 binding to your wrangler.jsonc for local backup support.',\n code: ErrorCode.INVALID_BACKUP_CONFIG,\n httpStatus: 400,\n context: { reason: 'Missing BACKUP_BUCKET R2 binding' },\n timestamp: new Date().toISOString()\n });\n }\n\n try {\n // Step 1: Read metadata to check TTL\n const metaKey = `${BACKUP_STORAGE_PREFIX}/${id}/${BACKUP_METADATA_OBJECT_NAME}`;\n const metaObject = await bucket.get(metaKey);\n if (!metaObject) {\n throw new BackupNotFoundError({\n message:\n `Backup not found: ${id}. ` +\n 'Verify the backup ID is correct and the backup has not been deleted.',\n code: ErrorCode.BACKUP_NOT_FOUND,\n httpStatus: 404,\n context: { backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n\n const metadata = await metaObject.json<{\n ttl: number;\n createdAt: string;\n dir: string;\n sizeBytes?: number;\n }>();\n\n // Check TTL with 60-second buffer\n const TTL_BUFFER_MS = 60 * 1000;\n const createdAt = new Date(metadata.createdAt).getTime();\n if (Number.isNaN(createdAt)) {\n throw new BackupRestoreError({\n message: `Backup metadata has invalid createdAt timestamp: ${metadata.createdAt}`,\n code: ErrorCode.BACKUP_RESTORE_FAILED,\n httpStatus: 500,\n context: { dir, backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n const expiresAt = createdAt + metadata.ttl * 1000;\n if (Date.now() + TTL_BUFFER_MS > expiresAt) {\n throw new BackupExpiredError({\n message:\n `Backup ${id} has expired ` +\n `(created: ${metadata.createdAt}, TTL: ${metadata.ttl}s). ` +\n 'Create a new backup.',\n code: ErrorCode.BACKUP_EXPIRED,\n httpStatus: 400,\n context: {\n backupId: id,\n expiredAt: new Date(expiresAt).toISOString()\n },\n timestamp: new Date().toISOString()\n });\n }\n\n // Step 2: Download archive from R2 via binding and write to container\n const r2Key = `${BACKUP_STORAGE_PREFIX}/${id}/${BACKUP_ARCHIVE_OBJECT_NAME}`;\n const archiveObject = await bucket.get(r2Key);\n if (!archiveObject) {\n throw new BackupNotFoundError({\n message:\n `Backup archive not found in R2: ${id}. ` +\n 'The archive may have been deleted by R2 lifecycle rules.',\n code: ErrorCode.BACKUP_NOT_FOUND,\n httpStatus: 404,\n context: { backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n\n const body = archiveObject.body;\n if (!body) {\n throw new BackupRestoreError({\n message: `R2 archive object has no body stream for backup ${id}`,\n code: ErrorCode.BACKUP_RESTORE_FAILED,\n httpStatus: 500,\n context: { dir, backupId: id },\n timestamp: new Date().toISOString()\n });\n }\n const archivePath = `${BACKUP_CONTAINER_DIR}/${id}.sqsh`;\n\n await this.deps.runBackupAttempt(\n 'backup.restore',\n async ({ runtime, control }) => {\n await lifecycle.runtimeReady(runtime, metadata.sizeBytes);\n try {\n await control.backup.prepareRestore({\n dir,\n backupId: id,\n archivePath\n });\n\n // Stream the archive into the container to avoid base64-encoding the\n // whole archive in Worker memory and hitting workerd's 32 MiB RPC\n // payload cap.\n await control.files.writeFileStream(archivePath, body);\n\n await lifecycle.archiveReady(metadata.sizeBytes);\n await control.backup.extractArchive(dir, archivePath);\n\n // Clean up archive after extraction (no FUSE mount holds it open)\n await control.backup.cleanupArchive(archivePath).catch(() => {});\n } catch (error) {\n await control.backup.cleanupArchive(archivePath).catch(() => {});\n throw error;\n }\n }\n );\n\n const result = {\n success: true as const,\n dir,\n id\n };\n await lifecycle.verify(result, metadata.sizeBytes);\n\n outcome = 'success';\n\n return result;\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.logger, {\n event: 'backup.restore',\n outcome,\n durationMs: Date.now() - restoreStartTime,\n backupId: id,\n dir,\n provider: 'local-binding',\n error: caughtError\n });\n }\n }\n}\n","import { ErrorCode, OperationInterruptedError } from '../errors';\nimport type { RuntimeLease } from '../runtime';\n\nexport type PreviewTCPPort = {\n fetch(\n input: Request | string,\n init?: Request | RequestInit\n ): Promise<Response>;\n};\n\nexport type PreviewForwardingLease = Pick<RuntimeLease, 'retain'>;\n\nexport type PreviewForwardingResult =\n | { status: 'response'; response: Response }\n | { status: 'network-lost' };\n\nexport async function forwardPreviewRequest(\n tcpPort: PreviewTCPPort,\n request: Request,\n lease: PreviewForwardingLease\n): Promise<PreviewForwardingResult> {\n const containerURL = request.url.replace('https:', 'http:');\n let interruptedError: OperationInterruptedError | undefined;\n let closeAssignedResponse:\n | ((error: OperationInterruptedError) => void)\n | undefined;\n const hold = lease.retain(() => {\n interruptedError = previewForwardInterrupted();\n closeAssignedResponse?.(interruptedError);\n });\n const release = once(() => hold.release());\n if (interruptedError) {\n release();\n throw interruptedError;\n }\n\n try {\n const response = await tcpPort.fetch(containerURL, request);\n if (interruptedError) {\n closeLateResponse(response, interruptedError);\n throw interruptedError;\n }\n\n if (response.webSocket !== null) {\n return {\n status: 'response',\n response: bridgePreviewWebSocket(response, release, (close) => {\n closeAssignedResponse = close;\n if (interruptedError) close(interruptedError);\n })\n };\n }\n\n if (response.body !== null) {\n const retained = retainPreviewBody(response.body, release, (close) => {\n closeAssignedResponse = close;\n if (interruptedError) close(interruptedError);\n });\n return {\n status: 'response',\n response: new Response(retained, response)\n };\n }\n\n release();\n return { status: 'response', response };\n } catch (error) {\n release();\n if (\n error instanceof Error &&\n error.message.includes('Network connection lost.')\n ) {\n return { status: 'network-lost' };\n }\n throw error;\n }\n}\n\nfunction retainPreviewBody(\n body: ReadableStream<Uint8Array>,\n release: () => void,\n bindInterrupt: (close: (error: OperationInterruptedError) => void) => void\n): ReadableStream<Uint8Array> {\n const reader = body.getReader();\n let controller: ReadableStreamDefaultController<Uint8Array> | undefined;\n let interruptedError: OperationInterruptedError | undefined;\n let sourceCancelled = false;\n const cancelSource = (reason?: unknown) => {\n if (sourceCancelled) return;\n sourceCancelled = true;\n release();\n void reader.cancel(reason).catch(() => undefined);\n };\n const fail = once((error: OperationInterruptedError) => {\n interruptedError = error;\n controller?.error(error);\n cancelSource(error);\n });\n bindInterrupt(fail);\n\n return new ReadableStream<Uint8Array>({\n start(streamController) {\n controller = streamController;\n if (interruptedError) streamController.error(interruptedError);\n },\n async pull(streamController) {\n try {\n if (interruptedError) throw interruptedError;\n const result = await reader.read();\n if (interruptedError) throw interruptedError;\n if (result.done) {\n release();\n streamController.close();\n return;\n }\n streamController.enqueue(result.value);\n } catch (error) {\n release();\n if (error !== interruptedError) {\n streamController.error(error);\n }\n }\n },\n cancel(reason) {\n cancelSource(reason);\n }\n });\n}\n\nfunction bridgePreviewWebSocket(\n response: Response,\n release: () => void,\n bindInterrupt: (close: (error: OperationInterruptedError) => void) => void\n): Response {\n const containerWebSocket = response.webSocket;\n if (containerWebSocket === null) {\n release();\n return response;\n }\n\n const [client, server] = Object.values(new WebSocketPair());\n let settled = false;\n const settle = once(() => {\n settled = true;\n release();\n });\n const closeInterrupted = once(() => {\n try {\n containerWebSocket.close(1012, 'Runtime replaced');\n server.close(1012, 'Runtime replaced');\n } finally {\n settle();\n }\n });\n bindInterrupt(closeInterrupted);\n\n containerWebSocket.accept();\n server.accept();\n\n server.addEventListener('message', async (event) => {\n if (settled) return;\n try {\n const data =\n event.data instanceof Blob\n ? await event.data.arrayBuffer()\n : event.data;\n containerWebSocket.send(data);\n } catch {\n server.close(1011, 'Failed to forward message to container');\n }\n });\n\n containerWebSocket.addEventListener('message', async (event) => {\n if (settled) return;\n try {\n const data =\n event.data instanceof Blob\n ? await event.data.arrayBuffer()\n : event.data;\n server.send(data);\n } catch {\n containerWebSocket.close(1011, 'Failed to forward message to client');\n }\n });\n\n server.addEventListener('close', (event) => {\n settle();\n const code = event.code === 1005 || event.code === 1006 ? 1000 : event.code;\n containerWebSocket.close(code, event.reason);\n });\n\n containerWebSocket.addEventListener('close', (event) => {\n settle();\n const code = event.code === 1005 || event.code === 1006 ? 1000 : event.code;\n server.close(code, event.reason);\n });\n\n server.addEventListener('error', () => {\n settle();\n containerWebSocket.close(1011, 'Client WebSocket error');\n });\n\n containerWebSocket.addEventListener('error', () => {\n settle();\n server.close(1011, 'Container WebSocket error');\n });\n\n return new Response(null, {\n status: response.status,\n webSocket: client,\n headers: response.headers\n });\n}\n\nfunction closeLateResponse(\n response: Response,\n error: OperationInterruptedError\n): void {\n if (response.webSocket !== null) {\n try {\n response.webSocket.accept();\n } catch {\n // The socket may already be accepted by the platform.\n }\n response.webSocket.close(1012, 'Runtime replaced');\n return;\n }\n void response.body?.cancel(error).catch(() => undefined);\n}\n\nfunction previewForwardInterrupted(): OperationInterruptedError {\n return new OperationInterruptedError({\n code: ErrorCode.OPERATION_INTERRUPTED,\n message:\n 'Sandbox operation preview.forward was interrupted because the runtime changed',\n context: {\n reason: 'runtime_replaced',\n operation: 'preview.forward',\n admitted: true,\n retryable: false\n },\n httpStatus: 409,\n timestamp: new Date().toISOString()\n });\n}\n\nfunction once<T extends (...args: never[]) => void>(fn: T): T {\n let called = false;\n return ((...args: Parameters<T>) => {\n if (called) return;\n called = true;\n fn(...(args as never[]));\n }) as T;\n}\n","import { PREVIEW_PROXY_HEADERS } from './protocol';\n\nexport interface BuildPreviewProxyRequestOptions {\n port: number;\n sandboxId: string;\n sandboxName: string | null;\n}\n\nexport function buildPreviewProxyRequest(\n request: Request,\n { port, sandboxId, sandboxName }: BuildPreviewProxyRequestOptions\n): Request {\n const url = new URL(request.url);\n const proxyURL = `http://localhost:${port}${url.pathname}${url.search}`;\n const headers = stripPreviewProxyHeaders(request.headers);\n\n headers.set('X-Original-URL', request.url);\n headers.set('X-Forwarded-Host', url.hostname);\n headers.set('X-Forwarded-Proto', url.protocol.replace(':', ''));\n headers.set('X-Sandbox-Name', sandboxName ?? sandboxId);\n\n const upgradeHeader = request.headers.get('Upgrade');\n if (upgradeHeader?.toLowerCase() === 'websocket') {\n return new Request(request, {\n headers,\n redirect: 'manual'\n });\n }\n\n return new Request(proxyURL, {\n method: request.method,\n headers,\n body: request.body,\n // @ts-expect-error - duplex required for body streaming in modern runtimes\n duplex: 'half',\n redirect: 'manual'\n });\n}\n\nfunction stripPreviewProxyHeaders(source: Headers): Headers {\n const headers = new Headers(source);\n for (const header of PREVIEW_PROXY_HEADERS) {\n headers.delete(header);\n }\n headers.delete('cf-container-target-port');\n headers.delete('x-sandbox-port-route-token');\n return headers;\n}\n","import type { RuntimeIdentityID, RuntimeIncarnationID } from '../runtime/types';\n\n/**\n * Persisted record for a single exposed port. `token` authorizes preview\n * URL requests; `name` is the optional friendly name the caller passed to\n * `exposePort()` and is preserved across container restarts.\n */\nexport type PortTokenEntry = {\n token: string;\n name?: string;\n};\n\nexport type PreviewPortActivation = {\n runtimeIdentityID: RuntimeIdentityID;\n runtimeIncarnationID: RuntimeIncarnationID;\n token: string;\n};\n\nexport type PreviewPortActivations = Record<string, PreviewPortActivation>;\n\nexport type CurrentPreviewPort = {\n port: number;\n entry: PortTokenEntry;\n};\n\nexport type PreviewStateStorage = Pick<\n DurableObjectStorage | DurableObjectTransaction,\n 'get' | 'put' | 'delete'\n>;\n\nexport const PORT_TOKENS_STORAGE_KEY = 'portTokens';\nexport const ACTIVE_PREVIEW_PORTS_STORAGE_KEY = 'activePreviewPorts';\n\n/**\n * Read the `portTokens` map from DO storage, normalizing the legacy\n * string-valued format (just a token) to the current object format\n * ({ token, name? }). The legacy format predates port-name persistence and\n * can appear on any DO whose storage was written before that change.\n */\nexport async function readPortTokens(\n storage: PreviewStateStorage\n): Promise<Record<string, PortTokenEntry>> {\n const raw =\n (await storage.get<Record<string, string | PortTokenEntry>>(\n PORT_TOKENS_STORAGE_KEY\n )) ?? {};\n const normalized: Record<string, PortTokenEntry> = {};\n for (const [port, value] of Object.entries(raw)) {\n normalized[port] = typeof value === 'string' ? { token: value } : value;\n }\n return normalized;\n}\n\nexport async function readActivePreviewPorts(\n storage: PreviewStateStorage\n): Promise<PreviewPortActivations> {\n return (\n (await storage.get<PreviewPortActivations>(\n ACTIVE_PREVIEW_PORTS_STORAGE_KEY\n )) ?? {}\n );\n}\n\nexport async function writeActivePreviewPorts(\n activations: PreviewPortActivations,\n storage: PreviewStateStorage\n): Promise<void> {\n if (Object.keys(activations).length === 0) {\n await storage.delete(ACTIVE_PREVIEW_PORTS_STORAGE_KEY);\n return;\n }\n\n await storage.put(ACTIVE_PREVIEW_PORTS_STORAGE_KEY, activations);\n}\n\nexport async function readPreviewState(storage: PreviewStateStorage): Promise<{\n tokens: Record<string, PortTokenEntry>;\n activations: PreviewPortActivations;\n}> {\n const [tokens, activations] = await Promise.all([\n readPortTokens(storage),\n readActivePreviewPorts(storage)\n ]);\n return { tokens, activations };\n}\n\nexport async function clearActivePreviewPorts(\n storage: PreviewStateStorage\n): Promise<void> {\n await storage.delete(ACTIVE_PREVIEW_PORTS_STORAGE_KEY);\n}\n","import { SandboxSecurityError } from '../security';\n\nconst CUSTOM_TOKEN_PATTERN = /^[a-z0-9_]+$/;\n\nexport function assertValidCustomPreviewToken(token: string): void {\n if (token.length === 0) {\n throw new SandboxSecurityError(`Custom token cannot be empty.`);\n }\n\n if (token.length > 16) {\n throw new SandboxSecurityError(\n `Custom token too long. Maximum 16 characters allowed. Received: ${token.length} characters.`\n );\n }\n\n if (!CUSTOM_TOKEN_PATTERN.test(token)) {\n throw new SandboxSecurityError(\n `Custom token must contain only lowercase letters (a-z), numbers (0-9), and underscores (_). Invalid token provided.`\n );\n }\n}\n\nexport function generatePreviewToken(): string {\n const array = new Uint8Array(12);\n crypto.getRandomValues(array);\n\n const base64 = btoa(String.fromCharCode(...array));\n return base64\n .replace(/\\+/g, '_')\n .replace(/\\//g, '_')\n .replace(/=/g, '')\n .toLowerCase();\n}\n\nexport function previewTokensMatch(expected: string, actual: string): boolean {\n const encoder = new TextEncoder();\n const a = encoder.encode(expected);\n const b = encoder.encode(actual);\n\n try {\n return (\n crypto.subtle as SubtleCrypto & {\n timingSafeEqual(a: ArrayBufferView, b: ArrayBufferView): boolean;\n }\n ).timingSafeEqual(a, b);\n } catch {\n return false;\n }\n}\n","import { type Logger, logCanonicalEvent } from '@repo/shared';\nimport type { ErrorResponse } from '../errors';\nimport {\n CustomDomainRequiredError,\n ErrorCode,\n OperationInterruptedError,\n RuntimeControlProtocolError\n} from '../errors';\nimport type { RuntimeLease } from '../runtime';\nimport type { RuntimeIdentity } from '../runtime/types';\nimport { RuntimeIdentityInactiveError } from '../runtime/types';\nimport { SandboxSecurityError, validatePort } from '../security';\nimport {\n forwardPreviewRequest,\n type PreviewForwardingLease,\n type PreviewTCPPort\n} from './forwarding';\nimport { readPreviewProxyMetadata } from './protocol';\nimport { buildPreviewProxyRequest } from './proxy-request';\nimport { constructPreviewURL } from './route';\nimport {\n type CurrentPreviewPort,\n clearActivePreviewPorts,\n PORT_TOKENS_STORAGE_KEY,\n type PortTokenEntry,\n type PreviewPortActivation,\n type PreviewPortActivations,\n readActivePreviewPorts,\n readPortTokens,\n readPreviewState,\n writeActivePreviewPorts\n} from './state';\nimport {\n assertValidCustomPreviewToken,\n generatePreviewToken,\n previewTokensMatch\n} from './token';\n\nexport type PreviewForwardingContainer = {\n running?: boolean;\n getTcpPort(port: number): PreviewTCPPort;\n};\n\ntype StalePreviewRuntime = {\n status: 'stale';\n reason:\n | 'runtime-not-healthy'\n | 'runtime-not-running'\n | 'missing-runtime-id'\n | 'missing-activation'\n | 'runtime-mismatch'\n | 'token-mismatch';\n containerStatus?: string;\n};\n\ntype PreviewURLRuntimeValidation =\n | { status: 'invalid' }\n | StalePreviewRuntime\n | { status: 'active'; activation: PreviewPortActivation };\n\ntype PreviewRuntimeAvailability =\n | StalePreviewRuntime\n | { status: 'active'; runtime: RuntimeIdentity };\n\ntype PreviewRuntimeSnapshot = {\n containerStatus: string;\n containerRunning: boolean;\n tokens: Record<string, PortTokenEntry>;\n activations: PreviewPortActivations;\n runtime: RuntimeIdentity | null;\n};\n\ntype PreviewExposureLease = Pick<RuntimeLease, 'runtime' | 'retain'>;\n\ntype PreviewExposureCommit = {\n portKey: string;\n previousEntry: PortTokenEntry | undefined;\n previousActivation: PreviewPortActivation | undefined;\n entry: PortTokenEntry;\n activation: PreviewPortActivation;\n};\n\nexport interface PreviewServiceDeps {\n storage: DurableObjectStorage;\n logger: Logger;\n getStoredRuntime(\n storage: DurableObjectTransaction\n ): Promise<RuntimeIdentity | null>;\n assertRuntimeActive(runtime: RuntimeIdentity): Promise<void>;\n getContainerState(): Promise<{ status: string }>;\n getForwardingContainer(): PreviewForwardingContainer | undefined;\n runWaking<T>(\n operation: string,\n call: (lease: PreviewExposureLease) => Promise<T>\n ): Promise<T>;\n runExisting<T>(\n operation: string,\n call: (\n lease: PreviewForwardingLease & { runtime: RuntimeIdentity }\n ) => Promise<T>\n ): Promise<T | null>;\n getSandboxName(): string | null;\n getNormalizeID(): boolean;\n}\n\nexport class PreviewService {\n constructor(private readonly deps: PreviewServiceDeps) {}\n\n async clearActivePreviewPorts(): Promise<void> {\n await clearActivePreviewPorts(this.deps.storage);\n }\n\n async clearPreviewState(): Promise<void> {\n await this.deps.storage.transaction(async (txn) => {\n await txn.delete(PORT_TOKENS_STORAGE_KEY);\n await clearActivePreviewPorts(txn);\n });\n }\n\n async exposePort(\n port: number,\n options: { name?: string; hostname: string; token?: string }\n ): Promise<{ url: string; port: number; name?: string }> {\n const exposeStartTime = Date.now();\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n let committed: PreviewExposureCommit | undefined;\n try {\n if (!validatePort(port)) {\n throw new SandboxSecurityError(\n `Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`\n );\n }\n\n if (options.hostname.endsWith('.workers.dev')) {\n const errorResponse: ErrorResponse = {\n code: ErrorCode.CUSTOM_DOMAIN_REQUIRED,\n message: `Port exposure requires a custom domain. .workers.dev domains do not support wildcard subdomains required for port proxying.`,\n context: { originalError: options.hostname },\n httpStatus: 400,\n timestamp: new Date().toISOString()\n };\n throw new CustomDomainRequiredError(errorResponse);\n }\n\n const sandboxName = this.deps.getSandboxName();\n if (!sandboxName) {\n throw new Error(\n 'Sandbox name not available. Ensure sandbox is accessed through getSandbox()'\n );\n }\n\n if (options.token !== undefined) {\n assertValidCustomPreviewToken(options.token);\n }\n\n await this.preflightTokenCollision(port, options.token);\n\n const result = await this.deps.runWaking(\n 'preview.expose',\n async (lease) => {\n let interrupted = false;\n const hold = lease.retain(() => {\n interrupted = true;\n });\n const assertLeaseActive = () => {\n if (interrupted) throw new RuntimeIdentityInactiveError();\n };\n const runtime = lease.runtime;\n try {\n assertLeaseActive();\n await this.deps.assertRuntimeActive(runtime);\n\n const exposure = await this.deps.storage.transaction(\n async (txn) => {\n const portKey = port.toString();\n const tokens = await readPortTokens(txn);\n const previousEntry = tokens[portKey];\n const nextToken =\n options.token ??\n previousEntry?.token ??\n generatePreviewToken();\n\n const existingPort = Object.entries(tokens).find(\n ([p, entry]) => entry.token === nextToken && p !== portKey\n );\n if (existingPort) {\n throw new SandboxSecurityError(\n `Token '${nextToken}' is already in use by port ${existingPort[0]}. Please use a different token.`\n );\n }\n\n const activations = await readActivePreviewPorts(txn);\n const previousActivation = activations[portKey];\n const storedRuntime = await this.deps.getStoredRuntime(txn);\n assertLeaseActive();\n if (!storedRuntime || !sameRuntime(storedRuntime, runtime)) {\n throw new RuntimeIdentityInactiveError();\n }\n\n const entry = { token: nextToken, name: options.name };\n const activation = {\n runtimeIdentityID: runtime.id,\n runtimeIncarnationID: runtime.runtimeIncarnationID,\n token: nextToken\n };\n tokens[portKey] = entry;\n activations[portKey] = activation;\n await Promise.all([\n txn.put(PORT_TOKENS_STORAGE_KEY, tokens),\n writeActivePreviewPorts(activations, txn)\n ]);\n\n return {\n token: nextToken,\n commit: {\n portKey,\n previousEntry,\n previousActivation,\n entry,\n activation\n }\n };\n }\n );\n committed = exposure.commit;\n\n assertLeaseActive();\n await this.deps.assertRuntimeActive(runtime);\n assertLeaseActive();\n const token = exposure.token;\n\n const url = constructPreviewURL({\n port,\n sandboxId: sandboxName,\n effectiveId: sandboxName,\n hostname: options.hostname,\n token,\n normalizeId: this.deps.getNormalizeID()\n });\n\n return {\n url,\n port,\n name: options.name\n };\n } finally {\n hold.release();\n }\n }\n );\n\n outcome = 'success';\n\n return result;\n } catch (error) {\n if (committed) await this.rollbackExposureIfUnchanged(committed);\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.deps.logger, {\n event: 'port.expose',\n outcome,\n port,\n durationMs: Date.now() - exposeStartTime,\n name: options.name,\n hostname: options.hostname,\n error: caughtError\n });\n }\n }\n\n private async rollbackExposureIfUnchanged(\n commit: PreviewExposureCommit\n ): Promise<void> {\n await this.deps.storage.transaction(async (txn) => {\n const [tokens, activations] = await Promise.all([\n readPortTokens(txn),\n readActivePreviewPorts(txn)\n ]);\n if (\n !samePortTokenEntry(tokens[commit.portKey], commit.entry) ||\n !isSameActivation(activations[commit.portKey], commit.activation)\n ) {\n return;\n }\n\n if (commit.previousEntry) tokens[commit.portKey] = commit.previousEntry;\n else delete tokens[commit.portKey];\n if (commit.previousActivation) {\n activations[commit.portKey] = commit.previousActivation;\n } else {\n delete activations[commit.portKey];\n }\n await Promise.all([\n txn.put(PORT_TOKENS_STORAGE_KEY, tokens),\n writeActivePreviewPorts(activations, txn)\n ]);\n });\n }\n\n private async preflightTokenCollision(\n port: number,\n requestedToken: string | undefined\n ): Promise<void> {\n const tokens = await readPortTokens(this.deps.storage);\n const existingEntry = tokens[port.toString()];\n const candidate = requestedToken ?? existingEntry?.token;\n if (!candidate) return;\n const existingPort = Object.entries(tokens).find(\n ([p, entry]) => entry.token === candidate && p !== port.toString()\n );\n if (existingPort) {\n throw new SandboxSecurityError(\n `Token '${candidate}' is already in use by port ${existingPort[0]}. Please use a different token.`\n );\n }\n }\n\n async unexposePort(port: number): Promise<void> {\n const unexposeStartTime = Date.now();\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n try {\n if (!validatePort(port)) {\n throw new SandboxSecurityError(\n `Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`\n );\n }\n\n await this.deps.storage.transaction(async (txn) => {\n const tokens = await readPortTokens(txn);\n if (tokens[port.toString()]) {\n delete tokens[port.toString()];\n await txn.put(PORT_TOKENS_STORAGE_KEY, tokens);\n }\n\n const activations = await readActivePreviewPorts(txn);\n if (activations[port.toString()]) {\n delete activations[port.toString()];\n await writeActivePreviewPorts(activations, txn);\n }\n });\n\n outcome = 'success';\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.deps.logger, {\n event: 'port.unexpose',\n outcome,\n port,\n durationMs: Date.now() - unexposeStartTime,\n error: caughtError\n });\n }\n }\n\n async getExposedPorts(\n hostname: string\n ): Promise<Array<{ url: string; port: number; status: 'active' }>> {\n const sandboxName = this.deps.getSandboxName();\n if (!sandboxName) {\n throw new Error(\n 'Sandbox name not available. Ensure sandbox is accessed through getSandbox()'\n );\n }\n\n const activePorts = await this.getCurrentPreviewPorts();\n return activePorts.map(({ port, entry }) => ({\n url: constructPreviewURL({\n port,\n sandboxId: sandboxName,\n effectiveId: sandboxName,\n hostname,\n token: entry.token,\n normalizeId: this.deps.getNormalizeID()\n }),\n port,\n status: 'active' as const\n }));\n }\n\n async isPortExposed(port: number): Promise<boolean> {\n if (!validatePort(port)) {\n return false;\n }\n\n const activePorts = await this.getCurrentPreviewPorts();\n return activePorts.some((activePort) => activePort.port === port);\n }\n\n async validatePortToken(port: number, token: string): Promise<boolean> {\n const tokens = await readPortTokens(this.deps.storage);\n const entry = tokens[port.toString()];\n if (!entry) {\n return false;\n }\n\n return previewTokensMatch(entry.token, token);\n }\n\n async proxyPreviewRequest(request: Request): Promise<Response> {\n const target = readPreviewProxyMetadata(request);\n if (!target) {\n return this.invalidPreviewTokenResponse();\n }\n\n const { port, token, sandboxId } = target;\n const proxyRequest = buildPreviewProxyRequest(request, {\n port,\n sandboxId,\n sandboxName: this.deps.getSandboxName()\n });\n\n const validation = await this.validatePreviewURLForRuntime(port, token);\n if (validation.status === 'invalid') {\n return this.invalidPreviewTokenResponse();\n }\n\n if (validation.status === 'stale') {\n this.deps.logger.warn('Stale preview URL blocked', {\n port,\n sandboxId,\n containerStatus: validation.containerStatus,\n reason: validation.reason,\n method: request.method\n });\n return this.stalePreviewURLResponse();\n }\n\n try {\n const response = await this.deps.runExisting(\n 'preview.forward',\n async (lease) =>\n await this.fetchPreviewIfRunning(\n proxyRequest,\n port,\n validation.activation,\n lease\n )\n );\n\n if (response) return response;\n await this.clearActivationIfUnchanged(port, validation.activation);\n return this.stalePreviewURLResponse();\n } catch (error) {\n if (\n error instanceof OperationInterruptedError ||\n isActivationMismatch(error)\n ) {\n await this.clearActivationIfUnchanged(port, validation.activation);\n return this.stalePreviewURLResponse();\n }\n throw error;\n }\n }\n\n private invalidPreviewTokenResponse(): Response {\n return new Response(\n JSON.stringify({\n error: 'Access denied: Invalid token or port not exposed',\n code: 'INVALID_TOKEN'\n }),\n {\n status: 404,\n headers: {\n 'Content-Type': 'application/json'\n }\n }\n );\n }\n\n private stalePreviewURLResponse(): Response {\n return new Response(\n JSON.stringify({\n error: 'Preview URL is stale because the sandbox runtime is not active',\n code: 'STALE_PREVIEW_URL'\n }),\n {\n status: 410,\n headers: {\n 'Content-Type': 'application/json'\n }\n }\n );\n }\n\n private async fetchPreviewIfRunning(\n request: Request,\n port: number,\n activation: PreviewPortActivation,\n lease: PreviewForwardingLease & { runtime: RuntimeIdentity }\n ): Promise<Response> {\n if (!samePreviewRuntime(lease.runtime, activation)) {\n await this.clearActivationIfUnchanged(port, activation);\n return this.stalePreviewURLResponse();\n }\n\n const container = this.deps.getForwardingContainer();\n if (!container?.running) {\n return this.stalePreviewURLResponse();\n }\n\n const tcpPort = container.getTcpPort(port);\n\n const result = await forwardPreviewRequest(tcpPort, request, lease);\n\n if (result.status === 'network-lost') {\n return this.stalePreviewURLResponse();\n }\n\n return result.response;\n }\n\n private async validatePreviewURLForRuntime(\n port: number,\n token: string\n ): Promise<PreviewURLRuntimeValidation> {\n const { tokens, activations } = await this.deps.storage.transaction(\n async (txn) => readPreviewState(txn)\n );\n const entry = tokens[port.toString()];\n if (!entry) {\n return { status: 'invalid' };\n }\n\n const tokenMatches = previewTokensMatch(entry.token, token);\n if (!tokenMatches) {\n return { status: 'invalid' };\n }\n\n const activation = activations[port.toString()];\n if (!isPreviewActivation(activation)) {\n if (activation) await this.clearActivationIfUnchanged(port, activation);\n return {\n status: 'stale',\n reason: 'missing-activation'\n };\n }\n\n const activationTokenMatches = previewTokensMatch(activation.token, token);\n if (!activationTokenMatches) {\n this.deps.logger.warn('Preview URL activation token mismatch', {\n port,\n runtimeIdentityID: activation.runtimeIdentityID\n });\n await this.clearActivationIfUnchanged(port, activation);\n return {\n status: 'stale',\n reason: 'token-mismatch'\n };\n }\n\n return { status: 'active', activation };\n }\n\n private async clearActivationIfUnchanged(\n port: number,\n expected: PreviewPortActivation\n ): Promise<void> {\n await this.deps.storage.transaction(async (txn) => {\n const activations = await readActivePreviewPorts(txn);\n const current = activations[port.toString()];\n if (!isSameActivation(current, expected)) return;\n delete activations[port.toString()];\n await writeActivePreviewPorts(activations, txn);\n });\n }\n\n private async getCurrentPreviewPorts(): Promise<CurrentPreviewPort[]> {\n const snapshot = await this.readRuntimeSnapshot();\n const availability = this.getRuntimeAvailability(snapshot);\n if (availability.status === 'stale') {\n return [];\n }\n\n const activePorts: CurrentPreviewPort[] = [];\n\n for (const [portKey, activation] of Object.entries(snapshot.activations)) {\n const port = Number.parseInt(portKey, 10);\n const entry = snapshot.tokens[portKey];\n if (!entry || !Number.isInteger(port) || !validatePort(port)) {\n continue;\n }\n\n if (\n !isPreviewActivation(activation) ||\n !samePreviewRuntime(availability.runtime, activation)\n ) {\n continue;\n }\n\n if (!previewTokensMatch(entry.token, activation.token)) {\n continue;\n }\n\n activePorts.push({ port, entry });\n }\n\n return activePorts.sort((a, b) => a.port - b.port);\n }\n\n private async readRuntimeSnapshot(): Promise<PreviewRuntimeSnapshot> {\n const containerState = await this.deps.getContainerState();\n const containerRunning =\n this.deps.getForwardingContainer()?.running === true;\n const { tokens, activations, runtime } =\n await this.deps.storage.transaction(async (txn) => {\n const [previewState, runtime] = await Promise.all([\n readPreviewState(txn),\n this.deps.getStoredRuntime(txn)\n ]);\n return { ...previewState, runtime };\n });\n\n return {\n containerStatus: containerState.status,\n containerRunning,\n tokens,\n activations,\n runtime\n };\n }\n\n private getRuntimeAvailability(\n snapshot: PreviewRuntimeSnapshot\n ): PreviewRuntimeAvailability {\n if (snapshot.containerStatus !== 'healthy') {\n return {\n status: 'stale',\n reason: 'runtime-not-healthy',\n containerStatus: snapshot.containerStatus\n };\n }\n\n if (!snapshot.containerRunning) {\n return {\n status: 'stale',\n reason: 'runtime-not-running',\n containerStatus: snapshot.containerStatus\n };\n }\n\n if (!snapshot.runtime) {\n return {\n status: 'stale',\n reason: 'missing-runtime-id',\n containerStatus: snapshot.containerStatus\n };\n }\n\n return { status: 'active', runtime: snapshot.runtime };\n }\n}\n\nfunction sameRuntime(left: RuntimeIdentity, right: RuntimeIdentity): boolean {\n return (\n left.id === right.id &&\n left.runtimeIncarnationID === right.runtimeIncarnationID\n );\n}\n\nfunction samePreviewRuntime(\n runtime: RuntimeIdentity,\n activation: PreviewPortActivation\n): boolean {\n return (\n runtime.id === activation.runtimeIdentityID &&\n runtime.runtimeIncarnationID === activation.runtimeIncarnationID\n );\n}\n\nfunction samePortTokenEntry(\n left: PortTokenEntry | undefined,\n right: PortTokenEntry\n): boolean {\n return Boolean(\n left && left.token === right.token && left.name === right.name\n );\n}\n\nfunction isPreviewActivation(value: unknown): value is PreviewPortActivation {\n if (!value || typeof value !== 'object') return false;\n const activation = value as Partial<\n Record<keyof PreviewPortActivation, unknown>\n >;\n return (\n typeof activation.runtimeIdentityID === 'string' &&\n activation.runtimeIdentityID.length > 0 &&\n typeof activation.runtimeIncarnationID === 'string' &&\n activation.runtimeIncarnationID.length > 0 &&\n typeof activation.token === 'string'\n );\n}\n\nfunction isSameActivation(\n left: PreviewPortActivation | undefined,\n right: PreviewPortActivation\n): boolean {\n return Boolean(\n left &&\n left.runtimeIdentityID === right.runtimeIdentityID &&\n left.runtimeIncarnationID === right.runtimeIncarnationID &&\n left.token === right.token\n );\n}\n\nfunction isActivationMismatch(error: unknown): boolean {\n return (\n error instanceof RuntimeControlProtocolError &&\n error.context.reason === 'activation-mismatch'\n );\n}\n","import { RpcTarget } from 'cloudflare:workers';\nimport type {\n PortWatchEvent,\n PortWatchRPCOptions,\n PortWatchSubscriptionAPI,\n ProcessLogEvent,\n ProcessLogSubscriptionAPI,\n ProcessLogsRPCOptions,\n ProcessStatus\n} from '@repo/shared';\nimport { ErrorCode } from '@repo/shared/errors';\nimport { translateRPCError } from '../container-control/rpc-error';\nimport { ProcessNotFoundError, StaleProcessHandleError } from '../errors';\nimport type {\n ProcessLogSubscriptionRPC,\n ProcessPortSubscriptionRPC,\n ProcessPullSubscriptionRPC,\n ProcessSubscriptionRPC\n} from './rpc-types';\n\nexport interface ProcessCapabilityRuntime {\n readonly id: string;\n readonly runtimeIncarnationID: string;\n}\n\nexport interface ProcessCapabilityControl {\n retainRuntimeHold(): () => void;\n getProcess(id: string): Promise<ProcessStatus | null>;\n openLogs(\n id: string,\n options?: ProcessLogsRPCOptions\n ): Promise<ProcessLogSubscriptionAPI>;\n openPortWatch(\n port: number,\n options?: PortWatchRPCOptions\n ): Promise<PortWatchSubscriptionAPI>;\n kill(id: string, signal: number): Promise<void>;\n}\n\nexport interface ProcessCapabilityLifecycle {\n runRead<T>(\n runtime: ProcessCapabilityRuntime,\n operation: string,\n call: (control: ProcessCapabilityControl) => Promise<T>\n ): Promise<T>;\n runControl<T>(\n runtime: ProcessCapabilityRuntime,\n operation: string,\n call: (control: ProcessCapabilityControl) => Promise<T>\n ): Promise<T>;\n}\n\ntype ProcessCapabilityTargetOptions = {\n id: string;\n pid: number;\n runtime: ProcessCapabilityRuntime;\n lifecycle: ProcessCapabilityLifecycle;\n};\n\nexport class ProcessCapabilityTarget extends RpcTarget {\n readonly #id: string;\n readonly #pid: number;\n readonly #runtime: ProcessCapabilityRuntime;\n readonly #lifecycle: ProcessCapabilityLifecycle;\n\n constructor(options: ProcessCapabilityTargetOptions) {\n super();\n this.#id = options.id;\n this.#pid = options.pid;\n this.#runtime = options.runtime;\n this.#lifecycle = options.lifecycle;\n }\n\n status(): Promise<ProcessStatus> {\n return this.#lifecycle.runRead(\n this.#runtime,\n 'process.status',\n async (control) =>\n this.#verifiedStatus(await control.getProcess(this.#id))\n );\n }\n\n async openLogs(\n options?: ProcessLogsRPCOptions\n ): Promise<ProcessPullSubscriptionRPC<ProcessLogEvent>> {\n const retained = await this.#lifecycle.runRead(\n this.#runtime,\n 'process.logs.open',\n async (control) => {\n const releaseConnection = control.retainRuntimeHold();\n try {\n this.#verifiedStatus(await control.getProcess(this.#id));\n return {\n subscription: await control.openLogs(this.#id, options),\n releaseConnection\n };\n } catch (error) {\n releaseConnection();\n throw error;\n }\n }\n );\n return new FencedSubscriptionTarget({\n subscription: retained.subscription,\n runtime: this.#runtime,\n lifecycle: this.#lifecycle,\n operation: 'process.logs.forward',\n isTerminal: (event) => event.type === 'terminal',\n allowCleanClose: true,\n releaseConnection: retained.releaseConnection\n });\n }\n\n async openPortWatch(\n port: number,\n options?: PortWatchRPCOptions\n ): Promise<ProcessPullSubscriptionRPC<PortWatchEvent>> {\n const retained = await this.#lifecycle.runRead(\n this.#runtime,\n 'process.port.open',\n async (control) => {\n const releaseConnection = control.retainRuntimeHold();\n try {\n this.#verifiedStatus(await control.getProcess(this.#id));\n return {\n subscription: await control.openPortWatch(port, options),\n releaseConnection\n };\n } catch (error) {\n releaseConnection();\n throw error;\n }\n }\n );\n return new FencedSubscriptionTarget({\n subscription: retained.subscription,\n runtime: this.#runtime,\n lifecycle: this.#lifecycle,\n operation: 'process.port.forward',\n isTerminal: (event) => event.type === 'ready' || event.type === 'error',\n releaseConnection: retained.releaseConnection\n });\n }\n\n kill(signal: number): Promise<void> {\n return this.#lifecycle.runControl(\n this.#runtime,\n 'process.kill',\n async (control) => {\n this.#verifiedStatus(await control.getProcess(this.#id));\n await control.kill(this.#id, signal);\n }\n );\n }\n\n #verifiedStatus(status: ProcessStatus | null): ProcessStatus {\n if (!status) {\n throw new ProcessNotFoundError({\n code: ErrorCode.PROCESS_NOT_FOUND,\n message: `Process not found: ${this.#id}`,\n context: { processId: this.#id },\n httpStatus: 404,\n timestamp: new Date().toISOString()\n });\n }\n if (status.id !== this.#id || status.pid !== this.#pid) {\n throw staleHandle(this.#id, this.#pid, 'process identity');\n }\n return status;\n }\n}\n\ntype FencedSubscriptionOptions<T> = {\n subscription: ProcessSubscriptionRPC<T>;\n runtime: ProcessCapabilityRuntime;\n lifecycle: ProcessCapabilityLifecycle;\n operation: string;\n isTerminal: (event: T) => boolean;\n allowCleanClose?: boolean;\n releaseConnection?: () => void;\n};\n\nclass FencedSubscriptionTarget<T>\n extends RpcTarget\n implements ProcessPullSubscriptionRPC<T>\n{\n readonly #subscription: ProcessSubscriptionRPC<T>;\n readonly #runtime: ProcessCapabilityRuntime;\n readonly #lifecycle: ProcessCapabilityLifecycle;\n readonly #operation: string;\n readonly #isTerminal: (event: T) => boolean;\n readonly #allowCleanClose: boolean;\n readonly #releaseConnection: (() => void) | undefined;\n #reader: ReadableStreamDefaultReader<T> | undefined;\n #released = false;\n\n constructor(options: FencedSubscriptionOptions<T>) {\n super();\n this.#subscription = options.subscription;\n this.#runtime = options.runtime;\n this.#lifecycle = options.lifecycle;\n this.#operation = options.operation;\n this.#isTerminal = options.isTerminal;\n this.#allowCleanClose = options.allowCleanClose ?? false;\n this.#releaseConnection = options.releaseConnection;\n }\n\n async next(): Promise<ReadableStreamReadResult<T>> {\n if (this.#released) return { done: true, value: undefined };\n\n if (!this.#reader) {\n try {\n const source = await this.#lifecycle.runRead(\n this.#runtime,\n this.#operation,\n () => this.#subscription.stream()\n );\n this.#reader = source.getReader();\n } catch (error) {\n this.#release();\n throw error;\n }\n }\n\n let result: ReadableStreamReadResult<T>;\n try {\n result = await this.#lifecycle.runRead(\n this.#runtime,\n this.#operation,\n () => this.#reader!.read()\n );\n } catch (error) {\n this.#release();\n translateRPCError(error, {\n operation: this.#operation,\n translateTransportErrorsAsInterruptions: false\n });\n }\n\n if (result.done) {\n this.#release();\n if (this.#allowCleanClose) return result;\n translateRPCError(\n new Error('Process subscription closed before a terminal event'),\n {\n operation: this.#operation,\n translateTransportErrorsAsInterruptions: false\n }\n );\n }\n\n if (this.#isTerminal(result.value)) this.#release();\n return result;\n }\n\n async cancel(): Promise<void> {\n this.#release();\n }\n\n [Symbol.dispose](): void {\n void this.#release();\n }\n\n #release(): void {\n if (this.#released) return;\n this.#released = true;\n this.#releaseConnection?.();\n try {\n void this.#reader?.cancel().catch(() => undefined);\n } catch {\n // Remote subscription cleanup below remains authoritative.\n }\n try {\n void this.#subscription.cancel().catch(() => undefined);\n } catch {\n // Local disposal still runs after a synchronous RPC failure.\n }\n try {\n this.#subscription[Symbol.dispose]();\n } catch {\n // Caller-visible stream settlement remains authoritative.\n }\n }\n}\n\nfunction staleHandle(\n processId: string,\n pid: number,\n operation: string\n): StaleProcessHandleError {\n return new StaleProcessHandleError({\n code: ErrorCode.STALE_PROCESS_HANDLE,\n message: `Process handle ${processId} no longer identifies PID ${pid}`,\n context: { processId, pid, operation },\n httpStatus: 409,\n timestamp: new Date().toISOString()\n });\n}\n","import { RpcTarget } from 'cloudflare:workers';\nimport type {\n TerminalOutputEvent,\n TerminalOutputOptions,\n TerminalSnapshot\n} from '@repo/shared';\nimport type { ProcessPullSubscriptionRPC } from '../processes/rpc-types';\nimport type { TerminalCapabilityRPC } from './rpc-types';\n\nexport interface TerminalCapabilityControl {\n get(id: string): Promise<TerminalSnapshot | null>;\n openOutput(\n id: string,\n options?: Omit<TerminalOutputOptions, 'signal'>\n ): Promise<ProcessPullSubscriptionRPC<TerminalOutputEvent>>;\n write(id: string, data: Uint8Array): Promise<void>;\n resize(id: string, cols: number, rows: number): Promise<void>;\n interrupt(id: string): Promise<void>;\n terminate(id: string): Promise<void>;\n authorizeConnection(): Promise<string>;\n}\n\nexport class TerminalCapabilityTarget\n extends RpcTarget\n implements TerminalCapabilityRPC\n{\n readonly #id: string;\n readonly #control: TerminalCapabilityControl;\n\n constructor(id: string, control: TerminalCapabilityControl) {\n super();\n this.#id = id;\n this.#control = control;\n }\n\n getSnapshot(): Promise<TerminalSnapshot | null> {\n return this.#control.get(this.#id);\n }\n\n openOutput(\n options?: Omit<TerminalOutputOptions, 'signal'>\n ): Promise<ProcessPullSubscriptionRPC<TerminalOutputEvent>> {\n return this.#control.openOutput(this.#id, options);\n }\n\n write(data: Uint8Array): Promise<void> {\n return this.#control.write(this.#id, data);\n }\n\n resize(cols: number, rows: number): Promise<void> {\n return this.#control.resize(this.#id, cols, rows);\n }\n\n interrupt(): Promise<void> {\n return this.#control.interrupt(this.#id);\n }\n\n terminate(): Promise<void> {\n return this.#control.terminate(this.#id);\n }\n\n authorizeConnection(): Promise<string> {\n return this.#control.authorizeConnection();\n }\n}\n","export type ResourceActivityAvailability = 'available' | 'absent' | 'unknown';\n\nexport type ResourceActivityProbe = {\n availability: () => Promise<ResourceActivityAvailability>;\n processesHasActive: () => Promise<boolean>;\n terminalsHasActive: () => Promise<boolean>;\n};\n\nexport type ResourceActivityOperation = {\n beforeCall: Promise<void>;\n finish: () => void;\n};\n\nexport class ResourceActivityGate {\n private generation = 0;\n private activityInFlight = 0;\n private nonWakingInFlight = 0;\n private committedStop: Promise<void> | null = null;\n\n constructor(\n private readonly renewActivity: () => void,\n private readonly stopInactive: () => Promise<void>\n ) {}\n\n recordActivity(): void {\n this.generation += 1;\n this.renewActivity();\n }\n\n beginActivity(): ResourceActivityOperation {\n return this.beginTrackedOperation('activity');\n }\n\n /**\n * Admits already-live work without renewing activity. The hold blocks an\n * expiry decision while the operation is in flight.\n */\n beginExistingHold(): ResourceActivityOperation {\n return this.beginTrackedOperation('hold');\n }\n\n /**\n * Admits observation of an already-live runtime without renewing activity or\n * blocking the expiry decision that the observation may inform.\n */\n beginProbe(): ResourceActivityOperation {\n const stopToAwait = this.committedStop;\n return {\n beforeCall: stopToAwait\n ? this.awaitCommittedTeardowns(stopToAwait)\n : Promise.resolve(),\n finish: () => {}\n };\n }\n\n private beginTrackedOperation(\n mode: 'activity' | 'hold'\n ): ResourceActivityOperation {\n const renew = mode === 'activity';\n if (renew) {\n this.recordActivity();\n this.activityInFlight += 1;\n } else {\n this.nonWakingInFlight += 1;\n }\n let finished = false;\n const stopToAwait = this.committedStop;\n\n return {\n beforeCall: stopToAwait\n ? this.awaitCommittedTeardowns(stopToAwait).then(() => {\n if (renew) this.recordActivity();\n })\n : Promise.resolve(),\n finish: () => {\n if (finished) return;\n finished = true;\n if (renew) {\n this.activityInFlight -= 1;\n if (this.activityInFlight === 0) this.recordActivity();\n } else {\n this.nonWakingInFlight -= 1;\n }\n }\n };\n }\n\n private async awaitCommittedTeardowns(initial: Promise<void>): Promise<void> {\n let current = initial;\n while (true) {\n let failed = false;\n let failure: unknown;\n try {\n await current;\n } catch (error) {\n failed = true;\n failure = error;\n }\n const latest = this.committedStop;\n if (latest && latest !== current) {\n current = latest;\n continue;\n }\n if (failed) throw failure;\n return;\n }\n }\n\n async runStopTeardown(teardown: () => Promise<void>): Promise<void> {\n if (!this.committedStop) {\n this.committedStop = this.wrapCommittedStop(teardown());\n }\n await this.committedStop;\n }\n\n runDestroyTeardown(teardown: () => Promise<void>): Promise<void> {\n const previous = this.committedStop;\n const work = previous ? previous.then(teardown, teardown) : teardown();\n this.committedStop = this.wrapCommittedStop(work);\n return this.committedStop;\n }\n\n async runExpiry(\n probe: ResourceActivityProbe,\n keepAlive: boolean\n ): Promise<void> {\n if (keepAlive) return;\n if (this.committedStop) {\n await this.commitStop();\n return;\n }\n const generation = this.generation;\n const activityInFlight = this.activityInFlight;\n const nonWakingInFlight = this.nonWakingInFlight;\n if (activityInFlight > 0) {\n this.recordActivity();\n return;\n }\n if (nonWakingInFlight > 0) return;\n\n let availability: ResourceActivityAvailability;\n try {\n availability = await probe.availability();\n } catch {\n this.recordActivity();\n return;\n }\n if (availability === 'unknown') {\n this.recordActivity();\n return;\n }\n if (\n this.expiryInvalidated(generation, activityInFlight, nonWakingInFlight)\n ) {\n return;\n }\n\n if (availability === 'absent') {\n await this.commitStop();\n return;\n }\n\n let processesActive: boolean;\n try {\n processesActive = await probe.processesHasActive();\n } catch {\n this.recordActivity();\n return;\n }\n if (processesActive) {\n this.recordActivity();\n return;\n }\n if (\n this.expiryInvalidated(generation, activityInFlight, nonWakingInFlight)\n ) {\n return;\n }\n\n let terminalsActive: boolean;\n try {\n terminalsActive = await probe.terminalsHasActive();\n } catch {\n this.recordActivity();\n return;\n }\n if (terminalsActive) {\n this.recordActivity();\n return;\n }\n if (\n this.expiryInvalidated(generation, activityInFlight, nonWakingInFlight)\n ) {\n return;\n }\n\n await this.commitStop();\n }\n\n private expiryInvalidated(\n generation: number,\n activityInFlight: number,\n nonWakingInFlight: number\n ): boolean {\n if (\n this.generation !== generation ||\n this.activityInFlight !== activityInFlight\n ) {\n this.recordActivity();\n return true;\n }\n return this.nonWakingInFlight !== nonWakingInFlight;\n }\n\n private async commitStop(): Promise<void> {\n if (!this.committedStop) {\n this.committedStop = this.wrapCommittedStop(this.stopInactive());\n }\n await this.committedStop;\n }\n\n private wrapCommittedStop(work: Promise<void>): Promise<void> {\n const committed = work.finally(() => {\n if (this.committedStop === committed) {\n this.committedStop = null;\n }\n this.recordActivity();\n });\n return committed;\n }\n}\n","/**\n * Capnweb RPC connection to the container.\n *\n * Manages a single WebSocket session and exposes typed methods that map\n * 1:1 to the container's SandboxAPI. The Sandbox DO calls these directly\n * over the control channel.\n */\n\nimport type {\n Logger,\n RuntimeMetadata,\n SandboxAPI,\n SandboxBackupAPI,\n SandboxControlCallback,\n SandboxFilesAPI,\n SandboxPortsAPI,\n SandboxUtilsAPI,\n SandboxWatchAPI\n} from '@repo/shared';\nimport { createNoOpLogger } from '@repo/shared';\nimport {\n ErrorCode,\n type ErrorResponse,\n getHttpStatus,\n getSuggestion\n} from '@repo/shared/errors';\nimport {\n RpcSession,\n type RpcStub,\n type RpcTarget,\n type RpcTransport\n} from 'capnweb';\nimport { createErrorFromResponse } from '../errors/adapter';\n\n// ---------------------------------------------------------------------------\n// Connection manager\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_CONNECT_TIMEOUT_MS = 30_000;\n\n/** Stub that can issue a WebSocket-upgrade fetch through the DO's Container base class. */\nexport interface ContainerFetchStub {\n fetch(request: Request): Promise<Response>;\n}\n\ninterface StructuredErrorBody {\n code?: string;\n message?: string;\n context?: Record<string, unknown>;\n httpStatus?: number;\n timestamp?: string;\n suggestion?: string;\n}\n\nfunction isErrorResponse(value: unknown): value is ErrorResponse {\n if (typeof value !== 'object' || value === null) return false;\n const candidate = value as Partial<ErrorResponse>;\n return (\n typeof candidate.code === 'string' &&\n Object.values(ErrorCode).includes(candidate.code as ErrorCode) &&\n typeof candidate.message === 'string' &&\n typeof candidate.httpStatus === 'number' &&\n typeof candidate.timestamp === 'string' &&\n typeof candidate.context === 'object' &&\n candidate.context !== null\n );\n}\n\nfunction normalizeContainerUnavailableReason(\n reason: unknown\n):\n | 'container_starting'\n | 'container_unhealthy'\n | 'container_replaced'\n | 'rpc_upgrade_failed' {\n switch (reason) {\n case 'container_starting':\n case 'container_unhealthy':\n case 'container_replaced':\n case 'rpc_upgrade_failed':\n return reason;\n case 'provisioning':\n case 'startup':\n return 'container_starting';\n case 'container_restarted':\n return 'container_replaced';\n default:\n return 'container_replaced';\n }\n}\n\nexport interface ContainerControlConnectionOptions {\n stub: ContainerFetchStub;\n port?: number;\n logger?: Logger;\n /**\n * Optional `localMain` exposed to the container side of the capnweb\n * session. The container reaches it via\n * `session.getRemoteMain()` and uses it for control-plane callbacks\n * (e.g. notifying the DO when a tunnel's cloudflared process has\n * exited). When omitted, the container sees an empty remote main.\n */\n localMain?: SandboxControlCallback & RpcTarget;\n /**\n * Invoked when an active WebSocket transitions to closed/errored.\n * Fired at most once per successful connection from the WS event\n * handlers in `doConnect`. Gives owners a synchronous teardown\n * signal so recovery doesn't depend on a periodic poller running\n * inside what may be an idle isolate.\n *\n * Also fired for `doConnect` failures after the deferred transport is\n * aborted. A failed upgrade poisons the transport, so owners must discard\n * the connection and create a fresh one for subsequent calls. Not fired for\n * `disconnect()`.\n */\n onClose?: () => void;\n}\n\n/**\n * Manages a capnweb WebSocket RPC session to the container.\n *\n * The RPC stub is created eagerly in the constructor using a deferred\n * transport. Calls made before `connect()` completes are queued in the\n * transport and flushed once the WebSocket is established.\n */\nexport class ContainerControlConnection {\n private readonly stub: RpcStub<SandboxAPI>;\n private readonly session: RpcSession<SandboxAPI>;\n private readonly transport: DeferredTransport;\n private ws: WebSocket | null = null;\n private state:\n | 'disconnected'\n | 'connecting'\n | 'connected'\n | 'activating'\n | 'active'\n | 'disposed' = 'disconnected';\n private connectPromise: Promise<void> | null = null;\n private activeUpgradeAbortController: AbortController | null = null;\n private readonly containerStub: ContainerFetchStub;\n private readonly port: number;\n private readonly logger: Logger;\n private readonly onClose: (() => void) | undefined;\n private readonly disposalError = new Error(\n 'Container control connection was disconnected'\n );\n private disposed = false;\n\n constructor(options: ContainerControlConnectionOptions) {\n this.containerStub = options.stub;\n this.port = options.port ?? 3000;\n this.logger = options.logger ?? createNoOpLogger();\n this.onClose = options.onClose;\n\n this.transport = new DeferredTransport();\n this.session = new RpcSession<SandboxAPI>(\n this.transport,\n options.localMain\n );\n this.stub = this.session.getRemoteMain();\n }\n\n /**\n * Get the typed RPC stub.\n *\n * The stub is available immediately — calls made before connect()\n * completes are queued in the deferred transport and flushed once\n * the WebSocket is established.\n */\n rpc(): RpcStub<SandboxAPI> {\n if (this.state !== 'active') {\n throw new Error('Container control connection is not activated');\n }\n return this.stub;\n }\n\n async getRuntimeMetadata(): Promise<RuntimeMetadata> {\n await this.connect();\n return this.stub.utils.getRuntimeMetadata();\n }\n\n async activateControlSession(\n expectedRuntimeIncarnationID: string\n ): Promise<RuntimeMetadata> {\n await this.connect();\n if (this.state === 'active') return this.stub.utils.getRuntimeMetadata();\n if (this.state !== 'connected') {\n throw new Error(\n 'Container control connection cannot activate from current state'\n );\n }\n this.state = 'activating';\n try {\n const metadata = await this.stub.utils.activateControlSession(\n expectedRuntimeIncarnationID\n );\n this.state = 'active';\n return metadata;\n } catch (error) {\n if (this.state === 'activating') this.state = 'connected';\n throw error;\n }\n }\n\n /**\n * Return capnweb session statistics. The `imports` and `exports` counts\n * reflect all in-flight RPC calls, streams, and peer-held references.\n * An idle session has imports <= 1 && exports <= 1 (the bootstrap stubs).\n */\n getStats(): { imports: number; exports: number } {\n return this.session.getStats();\n }\n\n isConnected(): boolean {\n return (\n this.state === 'connected' ||\n this.state === 'activating' ||\n this.state === 'active'\n );\n }\n\n async connect(): Promise<void> {\n if (this.state === 'disposed') throw this.disposalError;\n if (this.isConnected()) return;\n\n if (this.connectPromise) {\n return this.connectPromise;\n }\n\n this.connectPromise = this.doConnect();\n try {\n await this.connectPromise;\n } finally {\n this.connectPromise = null;\n }\n }\n\n disconnect(): void {\n if (this.state === 'disposed') return;\n this.state = 'disposed';\n this.disposed = true;\n this.activeUpgradeAbortController?.abort();\n this.activeUpgradeAbortController = null;\n if (this.ws) {\n // Unbind first so close/error delivery from transport abortion cannot\n // invoke owner recovery for an explicitly disposed connection.\n this.ws.removeEventListener('close', this.onWebSocketClose);\n this.ws.removeEventListener('error', this.onWebSocketError);\n }\n this.transport.abort(this.disposalError);\n try {\n (this.stub as unknown as Disposable)[Symbol.dispose]?.();\n } catch {\n // Stub may already be disposed\n }\n this.ws = null;\n }\n\n // -----------------------------------------------------------------------\n // Internal\n // -----------------------------------------------------------------------\n\n /**\n * Run the owner-provided `onClose` callback exactly once per call,\n * swallowing any errors so a buggy listener can't keep the connection\n * object in a half-torn-down state.\n */\n private fireOnClose(): void {\n if (!this.onClose) return;\n try {\n this.onClose();\n } catch (err) {\n this.logger.warn('ContainerControlConnection onClose handler threw', {\n error: err instanceof Error ? err.message : String(err)\n });\n }\n }\n\n /**\n * WebSocket `close` listener. Defined as a bound arrow field so the\n * same reference can be passed to both `addEventListener` and\n * `removeEventListener` — a fresh anonymous lambda would silently\n * fail to unbind.\n */\n private onWebSocketClose = (): void => {\n const wasConnected = this.isConnected();\n if (this.state !== 'disposed') this.state = 'disconnected';\n this.ws = null;\n this.logger.debug('ContainerControlConnection WebSocket closed');\n if (wasConnected) this.fireOnClose();\n };\n\n /**\n * WebSocket `error` listener. Same field-form rationale as\n * {@link onWebSocketClose}.\n */\n private onWebSocketError = (): void => {\n const wasConnected = this.isConnected();\n if (this.state !== 'disposed') this.state = 'disconnected';\n this.ws = null;\n if (wasConnected) this.fireOnClose();\n };\n\n private async doConnect(): Promise<void> {\n this.state = 'connecting';\n try {\n const response = await this.fetchUpgradeAttempt();\n\n if (this.disposed) {\n this.closeUpgradeWebSocket(response);\n throw this.disposalError;\n }\n\n if (response.status !== 101) {\n const containerUnavailable =\n await this.parseContainerUnavailableUpgradeError(response);\n if (containerUnavailable) {\n throw createErrorFromResponse(containerUnavailable);\n }\n const structuredError =\n await this.parseStructuredUpgradeError(response);\n if (structuredError) {\n throw createErrorFromResponse(structuredError);\n }\n throw new Error(\n `WebSocket upgrade failed: ${response.status} ${response.statusText}`\n );\n }\n\n // The Container base class returns the WebSocket on the response object\n // (Cloudflare Workers runtime convention, not standard fetch)\n const ws = (response as unknown as { webSocket?: WebSocket }).webSocket;\n if (!ws) {\n throw new Error('No WebSocket in upgrade response');\n }\n\n // Workers WebSockets require explicit accept() before use\n (ws as unknown as { accept: () => void }).accept();\n\n ws.addEventListener('close', this.onWebSocketClose);\n ws.addEventListener('error', this.onWebSocketError);\n\n this.ws = ws;\n this.transport.activate(ws);\n this.state = 'connected';\n\n this.logger.debug('ContainerControlConnection established', {\n port: this.port\n });\n } catch (error) {\n if (!this.disposed) this.state = 'disconnected';\n this.transport.abort(error);\n if (this.disposed) throw this.disposalError;\n this.logger.error(\n 'ContainerControlConnection failed',\n error instanceof Error ? error : new Error(String(error))\n );\n this.fireOnClose();\n throw error;\n }\n }\n\n private closeUpgradeWebSocket(response: Response): void {\n const ws = (response as unknown as { webSocket?: WebSocket }).webSocket;\n if (!ws) return;\n try {\n ws.close(3000, this.disposalError.message);\n } catch {\n // A late upgrade may already have closed its WebSocket.\n }\n }\n\n private async parseStructuredUpgradeError(\n response: Response\n ): Promise<ErrorResponse | null> {\n const body = await this.parseJSONBody(response);\n if (!isErrorResponse(body)) return null;\n return body;\n }\n\n private async parseContainerUnavailableUpgradeError(\n response: Response\n ): Promise<ErrorResponse | null> {\n const body = await this.parseJSONBody(response);\n if (!body || body.code !== ErrorCode.CONTAINER_UNAVAILABLE) return null;\n\n const reason = normalizeContainerUnavailableReason(body.context?.reason);\n const context = {\n reason,\n retryable: true as const,\n ...(typeof body.context?.retryAfterMs === 'number' && {\n retryAfterMs: body.context.retryAfterMs\n })\n };\n\n return {\n code: ErrorCode.CONTAINER_UNAVAILABLE,\n message: body.message ?? 'Container is unavailable',\n context,\n httpStatus:\n typeof body.httpStatus === 'number'\n ? body.httpStatus\n : getHttpStatus(ErrorCode.CONTAINER_UNAVAILABLE),\n timestamp:\n typeof body.timestamp === 'string'\n ? body.timestamp\n : new Date().toISOString(),\n suggestion:\n typeof body.suggestion === 'string'\n ? body.suggestion\n : getSuggestion(ErrorCode.CONTAINER_UNAVAILABLE, context)\n };\n }\n\n private async parseJSONBody(\n response: Response\n ): Promise<StructuredErrorBody | null> {\n const contentType = response.headers.get('Content-Type') ?? '';\n if (!contentType.includes('application/json')) return null;\n\n try {\n const body = (await response.clone().json()) as StructuredErrorBody;\n return body && typeof body === 'object' ? body : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Single WebSocket-upgrade fetch attempt. Owns its own AbortController for\n * the connect timeout.\n */\n private async fetchUpgradeAttempt(): Promise<Response> {\n const controller = new AbortController();\n this.activeUpgradeAbortController = controller;\n const timeout = setTimeout(\n () => controller.abort(),\n DEFAULT_CONNECT_TIMEOUT_MS\n );\n\n try {\n const url = `http://localhost:${this.port}/rpc`;\n const request = new Request(url, {\n headers: {\n Upgrade: 'websocket',\n Connection: 'Upgrade'\n },\n signal: controller.signal\n });\n const response = await this.containerStub.fetch(request);\n if (this.disposed) {\n this.closeUpgradeWebSocket(response);\n throw this.disposalError;\n }\n return response;\n } finally {\n clearTimeout(timeout);\n if (this.activeUpgradeAbortController === controller) {\n this.activeUpgradeAbortController = null;\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Deferred WebSocket transport\n// ---------------------------------------------------------------------------\n\n/**\n * RPC transport that queues sends and blocks receives until a WebSocket\n * is provided via `activate()`. Allows the RPC stub to be created before\n * the connection is established — queued calls flush automatically.\n */\nexport class DeferredTransport implements RpcTransport {\n #ws: WebSocket | null = null;\n #sendQueue: string[] = [];\n #receiveQueue: string[] = [];\n #receiveResolver?: (msg: string) => void;\n #receiveRejecter?: (err: unknown) => void;\n #error?: unknown;\n\n activate(ws: WebSocket): void {\n if (this.#error) {\n const message =\n this.#error instanceof Error\n ? this.#error.message\n : String(this.#error);\n ws.close(3000, message);\n throw this.#error;\n }\n this.#ws = ws;\n\n ws.addEventListener('message', (event: MessageEvent) => {\n if (this.#error) return;\n if (typeof event.data === 'string') {\n if (this.#receiveResolver) {\n this.#receiveResolver(event.data);\n this.#receiveResolver = undefined;\n this.#receiveRejecter = undefined;\n } else {\n this.#receiveQueue.push(event.data);\n }\n } else {\n // Capnweb's wire format is strictly text (JSON), so a binary\n // frame indicates a misbehaving\n // peer. Failing the transport here surfaces the problem to in-flight\n // RPC calls; without it `receive()` would hang forever waiting for\n // a string that is never going to arrive.\n this.#fail(\n new TypeError('Received non-string message from WebSocket.')\n );\n }\n });\n ws.addEventListener('close', (event: CloseEvent) => {\n this.#fail(\n new Error(`Peer closed WebSocket: ${event.code} ${event.reason}`)\n );\n });\n ws.addEventListener('error', () => {\n this.#fail(new Error('WebSocket connection failed.'));\n });\n\n // Flush queued sends\n for (const msg of this.#sendQueue) {\n ws.send(msg);\n }\n this.#sendQueue = [];\n }\n\n async send(message: string): Promise<void> {\n if (this.#error) throw this.#error;\n if (this.#ws) {\n this.#ws.send(message);\n } else {\n this.#sendQueue.push(message);\n }\n }\n\n async receive(): Promise<string> {\n if (this.#receiveQueue.length > 0) return this.#receiveQueue.shift()!;\n if (this.#error) throw this.#error;\n return new Promise<string>((resolve, reject) => {\n this.#receiveResolver = resolve;\n this.#receiveRejecter = reject;\n });\n }\n\n abort(reason: unknown): void {\n this.#fail(reason instanceof Error ? reason : new Error(String(reason)));\n if (this.#ws) {\n const message = reason instanceof Error ? reason.message : String(reason);\n try {\n this.#ws.close(3000, message);\n } catch {\n // The transport remains failed even if its WebSocket is already closed.\n }\n }\n }\n\n #fail(err: unknown): void {\n if (this.#error) return;\n this.#error = err;\n this.#sendQueue = [];\n this.#receiveQueue = [];\n this.#receiveRejecter?.(err);\n this.#receiveResolver = undefined;\n this.#receiveRejecter = undefined;\n }\n}\n","import type { RuntimeMetadata } from '@repo/shared';\nimport {\n ContainerControlConnection,\n type ContainerFetchStub\n} from '../container-control/connection';\nimport { RuntimeControlProtocolError } from '../errors';\nimport type { RuntimeBootstrapProbe as RuntimeBootstrapProbeContract } from './types';\n\nconst CONTROL_PROTOCOL_VERSION = 1;\n\nexport type RuntimeBootstrapProbeOptions = {\n getTcpPort: (port: number) => ContainerFetchStub;\n};\n\nexport function validateRuntimeMetadata(\n value: unknown,\n operation: string\n): RuntimeMetadata {\n if (value == null) {\n throw new RuntimeControlProtocolError('Runtime metadata is missing', {\n reason: 'missing-metadata',\n operation\n });\n }\n if (typeof value !== 'object') {\n throw new RuntimeControlProtocolError('Runtime metadata is malformed', {\n reason: 'malformed-metadata',\n operation\n });\n }\n const metadata = value as Partial<RuntimeMetadata>;\n if (\n typeof metadata.runtimeIncarnationID !== 'string' ||\n metadata.runtimeIncarnationID.length === 0 ||\n typeof metadata.sandboxVersion !== 'string' ||\n metadata.sandboxVersion.length === 0\n ) {\n throw new RuntimeControlProtocolError('Runtime metadata is malformed', {\n reason: 'malformed-metadata',\n operation\n });\n }\n if (metadata.controlProtocolVersion !== CONTROL_PROTOCOL_VERSION) {\n throw new RuntimeControlProtocolError(\n 'Runtime control protocol version is unsupported',\n {\n reason: 'unsupported-protocol-version',\n operation\n }\n );\n }\n return metadata as RuntimeMetadata;\n}\n\nexport class RuntimeBootstrapProbe implements RuntimeBootstrapProbeContract {\n constructor(private readonly options: RuntimeBootstrapProbeOptions) {}\n\n async probe(): Promise<RuntimeMetadata> {\n const connection = new ContainerControlConnection({\n stub: this.options.getTcpPort(3000)\n });\n try {\n await connection.connect();\n return validateRuntimeMetadata(\n await connection.getRuntimeMetadata(),\n 'utils.getRuntimeMetadata'\n );\n } finally {\n connection.disconnect();\n }\n }\n}\n","import type { ContainerStartConfigOptions } from '@cloudflare/containers';\nimport type { ContainerControlClient } from '../container-control/client';\nimport {\n type RuntimeIdentityID,\n RuntimeIdentityInactiveError\n} from '../runtime/types';\nimport { validateRuntimeMetadata } from './bootstrap-probe';\nimport type { RuntimeBootstrapProbe, RuntimeSessionManager } from './types';\nimport {\n RuntimeIdentity,\n type RuntimeIdentityReader,\n type RuntimeIncarnationID,\n type RuntimeRecord,\n type RuntimeRecordStorage\n} from './types';\n\nconst RUNTIME_RECORD_KEY = 'currentRuntimeIdentity';\n\ntype LifecycleStorage = RuntimeRecordStorage &\n Pick<DurableObjectStorage, 'put' | 'delete'>;\n\ntype ReplacementStartTransition = {\n phase: 'reconciling-previous-stop' | 'replacement-started';\n};\n\ntype RuntimeStopDisposition = 'reconciled-previous-stop' | 'hard-invalidation';\n\nexport type RuntimeEstablishOptions = {\n signal?: AbortSignal;\n startOptions?: ContainerStartConfigOptions;\n};\n\nexport type SandboxRuntimeLifecycleOptions = {\n readonly storage: LifecycleStorage;\n readonly isRuntimeRunning: () => boolean;\n readonly startControlPort: (\n port: 3000,\n options?: RuntimeEstablishOptions\n ) => Promise<void>;\n readonly waitForControlPort: (\n port: 3000,\n options?: { signal?: AbortSignal }\n ) => Promise<void>;\n readonly stopControlPort?: (port: 3000) => Promise<void>;\n readonly probe: RuntimeBootstrapProbe;\n readonly sessions: RuntimeSessionManager;\n readonly observeVersionCompatibility: (\n client: ContainerControlClient,\n runtime: RuntimeIdentity\n ) => Promise<void>;\n readonly reconcileReplacement: (runtime: RuntimeIdentity) => Promise<void>;\n};\n\nexport class SandboxRuntimeLifecycle implements RuntimeIdentityReader {\n private readonly changeListeners = new Set<() => void>();\n private generation = 0;\n private establishing: Promise<RuntimeIdentity> | null = null;\n private mutationGate: Promise<void> = Promise.resolve();\n private replacementStart: ReplacementStartTransition | null = null;\n\n constructor(private readonly options: SandboxRuntimeLifecycleOptions) {}\n\n get sessions(): RuntimeSessionManager {\n return this.options.sessions;\n }\n\n establish(options?: RuntimeEstablishOptions): Promise<RuntimeIdentity> {\n if (!this.establishing) {\n this.establishing = this.doEstablish(options).finally(() => {\n this.establishing = null;\n });\n }\n return this.establishing;\n }\n\n observeStoredActive(): Promise<RuntimeIdentity | null> {\n return this.getStored();\n }\n\n async get(): Promise<RuntimeIdentity | null> {\n if (!this.options.isRuntimeRunning()) return null;\n return this.getStored();\n }\n\n async getStored(\n storage: RuntimeRecordStorage = this.options.storage\n ): Promise<RuntimeIdentity | null> {\n const record = (await storage.get<unknown>(RUNTIME_RECORD_KEY)) ?? null;\n if (!isRuntimeRecord(record)) return null;\n return new RuntimeIdentity(record);\n }\n\n async isActive(runtime: RuntimeIdentity): Promise<boolean> {\n const active = await this.get();\n return (\n active?.id === runtime.id &&\n active.runtimeIncarnationID === runtime.runtimeIncarnationID\n );\n }\n\n async assertActive(runtime: RuntimeIdentity): Promise<void> {\n if (!(await this.isActive(runtime)))\n throw new RuntimeIdentityInactiveError();\n }\n\n async invalidate(expected?: RuntimeIdentity): Promise<void> {\n if (!expected) {\n await this.invalidateAndObserveStoredActive();\n return;\n }\n\n await this.withMutationGate(async () => {\n const stored = await this.getStored();\n if (!stored || !sameIdentity(stored, expected)) return;\n\n this.generation++;\n this.notifyChanged();\n this.options.sessions.closeActive();\n await this.options.storage.delete(RUNTIME_RECORD_KEY);\n await this.options.stopControlPort?.(3000);\n });\n }\n\n invalidateAndObserveStoredActive(): Promise<RuntimeIdentity | null> {\n return this.invalidateCurrent(true);\n }\n\n markRuntimeStarted(): boolean {\n if (this.replacementStart?.phase === 'reconciling-previous-stop') {\n this.replacementStart.phase = 'replacement-started';\n return true;\n }\n return false;\n }\n\n async reconcileObservedStop(): Promise<RuntimeStopDisposition> {\n const invalidateEstablishment =\n this.replacementStart?.phase !== 'reconciling-previous-stop';\n await this.invalidateCurrent(invalidateEstablishment);\n return invalidateEstablishment\n ? 'hard-invalidation'\n : 'reconciled-previous-stop';\n }\n\n onChange(listener: () => void): () => void {\n this.changeListeners.add(listener);\n return () => this.changeListeners.delete(listener);\n }\n\n private async doEstablish(\n options?: RuntimeEstablishOptions\n ): Promise<RuntimeIdentity> {\n const needsPhysicalStart = !this.options.isRuntimeRunning();\n const replacementStart = needsPhysicalStart\n ? this.beginReplacementStart()\n : null;\n const generation = this.generation;\n\n try {\n const stored = await this.getStored();\n\n if (needsPhysicalStart) {\n await this.options.startControlPort(3000, options);\n if (replacementStart?.phase !== 'replacement-started') {\n throw new RuntimeIdentityInactiveError();\n }\n this.assertGeneration(generation);\n await this.options.waitForControlPort(3000, options);\n this.assertGeneration(generation);\n }\n\n const metadata = validateRuntimeMetadata(\n await this.options.probe.probe(),\n 'runtime.lifecycle.probe'\n );\n this.assertGeneration(generation);\n\n const runtimeIncarnationID =\n metadata.runtimeIncarnationID as RuntimeIncarnationID;\n const runtime =\n stored?.runtimeIncarnationID === runtimeIncarnationID\n ? stored\n : new RuntimeIdentity({\n id: crypto.randomUUID() as RuntimeIdentityID,\n runtimeIncarnationID\n });\n const client = await this.options.sessions.acquire(runtime);\n this.assertGeneration(generation);\n await this.options.observeVersionCompatibility(client, runtime);\n this.assertGeneration(generation);\n\n if (!stored || !sameIdentity(stored, runtime)) {\n await this.options.reconcileReplacement(runtime);\n this.assertGeneration(generation);\n }\n\n if (!this.options.isRuntimeRunning())\n throw new RuntimeIdentityInactiveError();\n return await this.withMutationGate(async () => {\n this.assertGeneration(generation);\n await this.options.storage.put(RUNTIME_RECORD_KEY, toRecord(runtime));\n if (this.generation !== generation) {\n await this.deleteIfStored(runtime);\n throw new RuntimeIdentityInactiveError();\n }\n this.notifyChanged();\n return runtime;\n });\n } finally {\n this.endReplacementStart(replacementStart);\n }\n }\n\n private beginReplacementStart(): ReplacementStartTransition {\n const transition: ReplacementStartTransition = {\n phase: 'reconciling-previous-stop'\n };\n this.replacementStart = transition;\n return transition;\n }\n\n private endReplacementStart(\n transition: ReplacementStartTransition | null\n ): void {\n if (transition && this.replacementStart === transition) {\n this.replacementStart = null;\n }\n }\n\n private invalidateCurrent(\n invalidateEstablishment: boolean\n ): Promise<RuntimeIdentity | null> {\n if (invalidateEstablishment) this.generation++;\n this.notifyChanged();\n this.options.sessions.closeActive();\n return this.withMutationGate(async () => {\n const stored = await this.getStored();\n if (stored) await this.options.storage.delete(RUNTIME_RECORD_KEY);\n await this.options.stopControlPort?.(3000);\n return stored;\n });\n }\n\n private assertGeneration(generation: number): void {\n if (this.generation !== generation)\n throw new RuntimeIdentityInactiveError();\n }\n\n private async withMutationGate<T>(operation: () => Promise<T>): Promise<T> {\n const previous = this.mutationGate;\n let release!: () => void;\n this.mutationGate = new Promise<void>((resolve) => {\n release = resolve;\n });\n await previous;\n try {\n return await operation();\n } finally {\n release();\n }\n }\n\n private async deleteIfStored(runtime: RuntimeIdentity): Promise<void> {\n const stored = await this.getStored();\n if (stored && sameIdentity(stored, runtime)) {\n await this.options.storage.delete(RUNTIME_RECORD_KEY);\n }\n }\n\n private notifyChanged(): void {\n for (const listener of this.changeListeners) {\n try {\n listener();\n } catch {\n // Listener failures must not interrupt lifecycle state transitions.\n }\n }\n }\n}\n\nfunction toRecord(runtime: RuntimeIdentity): RuntimeRecord {\n return {\n schemaVersion: 1,\n id: runtime.id,\n runtimeIncarnationID: runtime.runtimeIncarnationID\n };\n}\n\nfunction sameIdentity(left: RuntimeIdentity, right: RuntimeIdentity): boolean {\n return (\n left.id === right.id &&\n left.runtimeIncarnationID === right.runtimeIncarnationID\n );\n}\n\nfunction isRuntimeRecord(value: unknown): value is RuntimeRecord {\n if (!value || typeof value !== 'object') return false;\n const record = value as Partial<RuntimeRecord>;\n return (\n record.schemaVersion === 1 &&\n typeof record.id === 'string' &&\n record.id.length > 0 &&\n typeof record.runtimeIncarnationID === 'string' &&\n record.runtimeIncarnationID.length > 0\n );\n}\n","import { ErrorCode } from '@repo/shared';\nimport { getHttpStatus } from '@repo/shared/errors';\nimport type { ContainerControlClient } from '../container-control/client';\nimport { OperationInterruptedError } from '../errors';\nimport type { ResourceActivityGate } from '../resource-activity-gate';\nimport { RuntimeIdentityInactiveError } from '../runtime/types';\nimport type {\n RuntimeEstablishOptions,\n SandboxRuntimeLifecycle\n} from './lifecycle';\nimport type {\n RuntimeConnectionHold,\n RuntimeIdentity,\n RuntimeSession\n} from './types';\n\nexport type RuntimeAbsent = { status: 'absent' };\nexport const RUNTIME_ABSENT: RuntimeAbsent = { status: 'absent' };\n\nexport type RuntimeLease = {\n runtime: RuntimeIdentity;\n control: ContainerControlClient;\n retain(onInterrupt?: () => void): RuntimeConnectionHold;\n};\n\nexport type RuntimeOperationTarget =\n | { kind: 'current' }\n | { kind: 'runtime'; runtime: RuntimeIdentity };\n\nexport type RuntimeOperationRunnerOptions = {\n lifecycle: SandboxRuntimeLifecycle;\n activityGate: ResourceActivityGate;\n};\n\ntype ActivityAdmission = ReturnType<ResourceActivityGate['beginActivity']>;\n\nexport class RuntimeOperationRunner {\n constructor(private readonly options: RuntimeOperationRunnerOptions) {}\n\n async runWaking<T>(\n operation: string,\n call: (lease: RuntimeLease) => Promise<T>,\n establishOptions?: RuntimeEstablishOptions\n ): Promise<T> {\n const owner = new OperationActivityOwner(\n this.options.activityGate.beginActivity()\n );\n try {\n await owner.beforeCall;\n let runtime: RuntimeIdentity;\n try {\n runtime = await this.options.lifecycle.establish(establishOptions);\n } catch (error) {\n if (\n error instanceof RuntimeIdentityInactiveError ||\n error instanceof OperationInterruptedError\n ) {\n throw interrupted(operation);\n }\n throw error;\n }\n return await this.runWithLease(runtime, operation, owner, call);\n } finally {\n owner.releaseBase();\n }\n }\n\n async runExisting<T>(\n target: RuntimeOperationTarget,\n operation: string,\n call: (lease: RuntimeLease) => Promise<T>\n ): Promise<T | RuntimeAbsent> {\n const owner = new OperationActivityOwner(\n this.options.activityGate.beginExistingHold()\n );\n try {\n await owner.beforeCall;\n const runtime = await this.resolveExisting(target);\n if (!runtime) return RUNTIME_ABSENT;\n return await this.runWithLease(runtime, operation, owner, call);\n } finally {\n owner.releaseBase();\n }\n }\n\n async probeExisting<T>(\n target: RuntimeOperationTarget,\n operation: string,\n call: (lease: RuntimeLease) => Promise<T>\n ): Promise<T | RuntimeAbsent> {\n const owner = new OperationActivityOwner(\n this.options.activityGate.beginProbe()\n );\n try {\n await owner.beforeCall;\n const runtime = await this.resolveExisting(target);\n if (!runtime) return RUNTIME_ABSENT;\n return await this.runWithLease(runtime, operation, owner, call);\n } finally {\n owner.releaseBase();\n }\n }\n\n private async resolveExisting(\n target: RuntimeOperationTarget\n ): Promise<RuntimeIdentity | null> {\n const current = await this.options.lifecycle.get();\n if (!current) return null;\n if (target.kind === 'current') return current;\n if (sameIdentity(current, target.runtime)) return target.runtime;\n return null;\n }\n\n private async runWithLease<T>(\n runtime: RuntimeIdentity,\n operation: string,\n owner: OperationActivityOwner,\n call: (lease: RuntimeLease) => Promise<T>\n ): Promise<T> {\n const session =\n await this.options.lifecycle.sessions.acquireSession(runtime);\n const sessionHold = session.retain();\n const releaseChangeListener = this.options.lifecycle.onChange(() => {\n owner.interruptRetainedHolds();\n });\n try {\n await this.assertActive(runtime, operation);\n if (session.isInterrupted()) throw interrupted(operation);\n const lease: RuntimeLease = {\n runtime,\n control: session.client,\n retain: (onInterrupt) => {\n if (!owner.canRetain() || session.isInterrupted()) {\n onInterrupt?.();\n return { release: () => {} };\n }\n const activityHold = owner.retain();\n const retainedSessionHold = session.retain(() => {\n activityHold.release();\n onInterrupt?.();\n });\n return {\n release: once(() => {\n retainedSessionHold.release();\n activityHold.release();\n })\n };\n }\n };\n const result = await this.raceSession(session, operation, call(lease));\n await this.assertActive(runtime, operation);\n if (session.isInterrupted()) throw interrupted(operation);\n return result;\n } catch (error) {\n if (await this.shouldTranslateInterruption(runtime, session, error)) {\n throw interrupted(operation);\n }\n throw error;\n } finally {\n releaseChangeListener();\n sessionHold.release();\n }\n }\n\n private async raceSession<T>(\n session: RuntimeSession,\n operation: string,\n promise: Promise<T>\n ): Promise<T> {\n try {\n return await Promise.race([promise, session.interrupted]);\n } catch (error) {\n if (session.isInterrupted()) throw interrupted(operation);\n throw error;\n }\n }\n\n private async shouldTranslateInterruption(\n runtime: RuntimeIdentity,\n session: RuntimeSession,\n error: unknown\n ): Promise<boolean> {\n if (error instanceof RuntimeIdentityInactiveError) return true;\n if (session.isInterrupted()) return true;\n return !(await this.options.lifecycle.isActive(runtime));\n }\n\n private async assertActive(\n runtime: RuntimeIdentity,\n operation: string\n ): Promise<void> {\n try {\n await this.options.lifecycle.assertActive(runtime);\n } catch {\n throw interrupted(operation);\n }\n }\n}\n\nclass OperationActivityOwner {\n private references = 1;\n private finished = false;\n private readonly retained = new Set<{ release(): void }>();\n\n constructor(private readonly activity: ActivityAdmission) {}\n\n get beforeCall(): Promise<void> {\n return this.activity.beforeCall;\n }\n\n canRetain(): boolean {\n return !this.finished;\n }\n\n retain(): RuntimeConnectionHold {\n if (!this.canRetain()) return { release: () => {} };\n this.references += 1;\n const hold = { release: once(() => this.releaseRetained(hold)) };\n this.retained.add(hold);\n return hold;\n }\n\n releaseBase(): void {\n this.releaseReference();\n }\n\n interruptRetainedHolds(): void {\n for (const hold of [...this.retained]) hold.release();\n }\n\n private releaseRetained(hold: { release(): void }): void {\n this.retained.delete(hold);\n this.releaseReference();\n }\n\n private releaseReference(): void {\n if (this.finished) return;\n this.references -= 1;\n if (this.references === 0) {\n this.finished = true;\n this.activity.finish();\n }\n }\n}\n\nexport function interrupted(operation: string): OperationInterruptedError {\n return new OperationInterruptedError({\n code: ErrorCode.OPERATION_INTERRUPTED,\n message: `Sandbox operation ${operation} was interrupted because the runtime changed`,\n context: {\n reason: 'runtime_replaced',\n operation,\n admitted: true,\n retryable: false\n },\n httpStatus: getHttpStatus(ErrorCode.OPERATION_INTERRUPTED),\n timestamp: new Date().toISOString()\n });\n}\n\nfunction sameIdentity(left: RuntimeIdentity, right: RuntimeIdentity): boolean {\n return (\n left.id === right.id &&\n left.runtimeIncarnationID === right.runtimeIncarnationID\n );\n}\n\nfunction once(fn: () => void): () => void {\n let called = false;\n return () => {\n if (called) return;\n called = true;\n fn();\n };\n}\n","import { translateRPCError } from './rpc-error';\n\nexport function createControlDomainProxy<T extends object>(\n getStub: () => T,\n domain: string,\n translateTransportErrorsAsInterruptions = true\n): T {\n return new Proxy(Object.create(null) as T, {\n get(_target, prop) {\n return (...args: unknown[]) => {\n const operation =\n typeof prop === 'string' ? `${domain}.${prop}` : domain;\n try {\n const target = getStub();\n const value = Reflect.get(target, prop, target);\n if (typeof value !== 'function') return value;\n const result = Reflect.apply(\n value as (...a: unknown[]) => unknown,\n target,\n args\n );\n if (\n result != null &&\n typeof (result as { then?: unknown }).then === 'function'\n ) {\n return (result as Promise<unknown>).catch((err: unknown) =>\n translateRPCError(err, {\n operation,\n translateTransportErrorsAsInterruptions\n })\n );\n }\n return result;\n } catch (err) {\n translateRPCError(err, {\n operation,\n translateTransportErrorsAsInterruptions\n });\n }\n };\n }\n });\n}\n","/** Sandbox control client implementation backed by direct capnweb RPC calls. */\n\nimport type {\n Logger,\n SandboxBackupAPI,\n SandboxExtensionsAPI,\n SandboxFilesAPI,\n SandboxMountsAPI,\n SandboxPortsAPI,\n SandboxProcessesAPI,\n SandboxTerminalsAPI,\n SandboxTunnelsAPI,\n SandboxUtilsAPI,\n SandboxWatchAPI,\n SandboxWorkspaceAPI\n} from '@repo/shared';\nimport { createNoOpLogger } from '@repo/shared';\nimport {\n ContainerControlConnection,\n type ContainerControlConnectionOptions\n} from './connection';\nimport { createControlDomainProxy } from './rpc-proxy';\n\nexport { translateRPCError } from './rpc-error';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Close the idle capnweb WebSocket promptly so the DO can sleep. */\nconst DEFAULT_IDLE_DISCONNECT_MS = 1_000;\n\n/**\n * How often the busy/idle poller samples `getStats()`.\n *\n * Sets two worst-case bounds:\n *\n * 1. **Idle-detection lag.** Time between the session going idle on\n * the wire and the DO observing it (and arming the disconnect).\n * Bounded by `pollInterval`.\n * 2. **Activity-renewal lag while busy.** While a stream is active we\n * renew the DO's activity timeout once per tick. The alarm could\n * fire as late as `sleepAfter` after the last renew, so the\n * effective margin against a mid-stream sleep is\n * `sleepAfter - pollInterval`.\n *\n * **Invariant: `pollInterval` must be comfortably less than the\n * smallest configurable `sleepAfter`.** Aim for at least 2-3× headroom.\n * The minimum `sleepAfter` exercised by the E2E suite is 3s, so 1s gives\n * 3× margin and at least two renewals during a 3s window. If a smaller\n * `sleepAfter` is ever supported, drop this proportionally.\n */\nconst BUSY_POLL_INTERVAL_MS = 1_000;\n\n/**\n * Baseline getStats() values for an idle session. The bootstrap stub on each\n * side accounts for 1 import and 1 export.\n */\nconst IDLE_IMPORT_THRESHOLD = 1;\nconst IDLE_EXPORT_THRESHOLD = 1;\n\n// ---------------------------------------------------------------------------\n// Public client\n// ---------------------------------------------------------------------------\n\nexport interface ContainerControlClientOptions extends ContainerControlConnectionOptions {\n /** Idle timeout before disconnecting the WebSocket (ms). Defaults to 1 000. */\n idleDisconnectMs?: number;\n /** Busy/idle poll interval (ms). Defaults to 1 000. */\n busyPollIntervalMs?: number;\n /**\n * Renew the DO's activity timeout. Fires at the start of every RPC call\n * and on every busy-poll tick while the session has work in flight.\n * Mirrors what `containerFetch()` does at the top of each HTTP request.\n */\n onActivity?: () => void;\n /** Verifies that the owning runtime lease still permits RPC dispatch. */\n onDispatch?: () => void;\n /** Fires when the underlying connection closes or fails. */\n onConnectionClose?: () => void;\n /** Maps transport loss to operation interruption for the waking facade. */\n translateTransportErrorsAsInterruptions?: boolean;\n connection?: ContainerControlConnection;\n externallyOwnedConnection?: boolean;\n /**\n * Fires once when the capnweb session transitions from idle to busy\n * (an RPC call was started or a stream return is now in flight). The\n * Sandbox DO wires this to the resource activity gate so current\n * session ownership blocks an inactivity stop.\n */\n onSessionBusy?: () => void;\n /**\n * Fires once when the session transitions from busy back to idle\n * (all RPC promises settled and all stream exports released). The\n * Sandbox DO releases the gate operation for the current control\n * session and renews activity for the idle transition.\n */\n onSessionIdle?: () => void;\n}\n\n/**\n * Sandbox control facade backed by direct capnweb RPC.\n *\n * All operations call the container's SandboxAPI control interface directly\n * over capnweb, bypassing the HTTP handler/router layer entirely.\n *\n * Manages its own WebSocket lifecycle: a fresh `ContainerControlConnection` is\n * created on demand and torn down after `idleDisconnectMs` of inactivity.\n * Busy/idle detection relies on `RpcSession.getStats()` which tracks all\n * in-flight RPC calls and stream exports — including long-lived streaming\n * RPCs that would be invisible to a simple per-call request counter (see\n * the file-level comment for the full rationale).\n */\nexport class ContainerControlClient {\n private readonly connOptions: ContainerControlConnectionOptions & {\n connection?: ContainerControlConnection;\n externallyOwnedConnection?: boolean;\n };\n private readonly idleDisconnectMs: number;\n private readonly busyPollIntervalMs: number;\n private readonly logger: Logger;\n private readonly onActivity: (() => void) | undefined;\n private readonly onDispatch: (() => void) | undefined;\n private readonly onConnectionClose: (() => void) | undefined;\n private readonly translateTransportErrorsAsInterruptions: boolean;\n private readonly onSessionBusy: (() => void) | undefined;\n private readonly onSessionIdle: (() => void) | undefined;\n\n private conn: ContainerControlConnection | null = null;\n private idleTimer: ReturnType<typeof setTimeout> | null = null;\n private busyPollTimer: ReturnType<typeof setInterval> | null = null;\n /** Tracks whether we currently believe the session is busy. */\n private busy = false;\n private connectionRetainers = 0;\n private connectionGeneration = 0;\n\n constructor(options: ContainerControlClientOptions) {\n this.connOptions = {\n stub: options.stub,\n port: options.port,\n localMain: options.localMain,\n logger: options.logger,\n connection: options.connection,\n externallyOwnedConnection: options.externallyOwnedConnection,\n // Event-driven failure recovery: when the live WebSocket closes\n // or errors, tear the connection down inside the same turn of\n // the event loop so the next RPC call builds a fresh one. The\n // 1Hz busy-poll fallback can't be relied on here — `setInterval`\n // callbacks don't fire while the DO isolate sits idle between\n // requests, which is exactly the state the isolate enters after\n // every in-flight RPC rejects with a peer-closed error.\n onClose: () => {\n if (this.conn) this.destroyConnection();\n this.onConnectionClose?.();\n }\n };\n this.idleDisconnectMs =\n options.idleDisconnectMs ?? DEFAULT_IDLE_DISCONNECT_MS;\n this.busyPollIntervalMs =\n options.busyPollIntervalMs ?? BUSY_POLL_INTERVAL_MS;\n this.logger = options.logger ?? createNoOpLogger();\n this.onActivity = options.onActivity;\n this.onDispatch = options.onDispatch;\n this.onConnectionClose = options.onConnectionClose;\n this.translateTransportErrorsAsInterruptions =\n options.translateTransportErrorsAsInterruptions ?? true;\n this.onSessionBusy = options.onSessionBusy;\n this.onSessionIdle = options.onSessionIdle;\n }\n\n // -------------------------------------------------------------------------\n // Connection factory\n // -------------------------------------------------------------------------\n\n /**\n * Return the current connection, creating one when the client is disconnected.\n * Starts the busy-poll timer the first time a connection is materialized.\n */\n private getConnection(): ContainerControlConnection {\n this.onDispatch?.();\n if (!this.conn) {\n this.conn =\n this.connOptions.connection ??\n new ContainerControlConnection(this.connOptions);\n if (!this.connOptions.externallyOwnedConnection) this.startBusyPoll();\n }\n return this.conn;\n }\n\n // -------------------------------------------------------------------------\n // Activity & busy/idle tracking\n // -------------------------------------------------------------------------\n\n /**\n * Sample `getStats()` and update busy/idle state. While busy, renews the\n * activity timeout each tick so an in-flight stream keeps pushing the\n * sleepAfter deadline forward. On the busy → idle edge, fires\n * `onSessionIdle` and schedules the WebSocket disconnect.\n *\n * If the WebSocket has dropped underneath us (container crash, network\n * blip) we tear the connection down here. `destroyConnection()` fires\n * `onSessionIdle` if we were busy, so the DO's inflight counter doesn't\n * stay pinned forever waiting for a peer that's never going to reply.\n */\n private pollBusyState = (): void => {\n const conn = this.conn;\n if (!conn) return;\n if (!conn.isConnected()) {\n // The WebSocket upgrade is still in progress (or a freshly\n // recreated connection hasn't finished doConnect() yet). Sends\n // are queued in the deferred transport and will flush once the\n // upgrade resolves — don't tear down here.\n //\n // Failure recovery (a peer that disconnected after we connected)\n // is handled synchronously by `ContainerControlConnection`'s\n // `onClose` callback firing `destroyConnection()`, which nulls\n // `this.conn` before the poller could observe it.\n return;\n }\n\n const { imports, exports } = conn.getStats();\n const isBusy =\n this.connectionRetainers > 0 ||\n imports > IDLE_IMPORT_THRESHOLD ||\n exports > IDLE_EXPORT_THRESHOLD;\n\n if (isBusy) {\n if (!this.busy) {\n this.busy = true;\n this.onSessionBusy?.();\n }\n // Renew on every busy tick — this is what keeps a long-lived stream\n // alive past sleepAfter.\n this.onActivity?.();\n this.clearIdleTimer();\n } else if (this.busy) {\n this.busy = false;\n this.onSessionIdle?.();\n this.scheduleIdleDisconnect();\n } else {\n // Already idle, no state change. Still ensure the disconnect timer\n // is armed (covers the case where we connected but never observed\n // any activity).\n if (!this.idleTimer) this.scheduleIdleDisconnect();\n }\n };\n\n private startBusyPoll(): void {\n if (this.busyPollTimer) return;\n this.busyPollTimer = setInterval(\n this.pollBusyState,\n this.busyPollIntervalMs\n );\n }\n\n private stopBusyPoll(): void {\n if (this.busyPollTimer) {\n clearInterval(this.busyPollTimer);\n this.busyPollTimer = null;\n }\n }\n\n private scheduleIdleDisconnect(): void {\n this.clearIdleTimer();\n this.idleTimer = setTimeout(() => {\n this.idleTimer = null;\n const conn = this.conn;\n if (!conn || !conn.isConnected()) return;\n\n // Re-check before disconnecting — a new call may have started.\n const { imports, exports } = conn.getStats();\n if (\n this.connectionRetainers === 0 &&\n imports <= IDLE_IMPORT_THRESHOLD &&\n exports <= IDLE_EXPORT_THRESHOLD\n ) {\n this.logger.debug('Disconnecting idle RPC connection');\n this.destroyConnection();\n }\n }, this.idleDisconnectMs);\n }\n\n private clearIdleTimer(): void {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer);\n this.idleTimer = null;\n }\n }\n\n private destroyConnection(): void {\n this.stopBusyPoll();\n this.clearIdleTimer();\n this.connectionRetainers = 0;\n this.connectionGeneration += 1;\n // If we tear down while still believing the session is busy, fire the\n // idle transition so the DO's inflight counter doesn't leak.\n if (this.busy) {\n this.busy = false;\n this.onSessionIdle?.();\n }\n if (this.conn) {\n if (!this.connOptions.externallyOwnedConnection) {\n this.conn.disconnect();\n }\n this.conn = null;\n }\n }\n\n // -------------------------------------------------------------------------\n // Sub-client getters\n // -------------------------------------------------------------------------\n\n // Each getter returns the corresponding nested RpcTarget stub\n // wrapped in a Proxy that translates RPC errors into SandboxError\n // subclasses. Explicit return types keep capnweb's recursive\n // type machinery out of .d.ts output.\n\n private createDomainProxy<T extends object>(\n getStub: () => T,\n domain: string\n ): T {\n return createControlDomainProxy(\n getStub,\n domain,\n this.translateTransportErrorsAsInterruptions\n );\n }\n\n get files(): SandboxFilesAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().files,\n 'files'\n ) as unknown as SandboxFilesAPI;\n }\n get ports(): SandboxPortsAPI {\n // capnweb's recursive stream stub type cannot express the shared stream\n // subscription contract at this external RPC boundary.\n return this.createDomainProxy(\n () => this.getConnection().rpc().ports,\n 'ports'\n ) as unknown as SandboxPortsAPI;\n }\n get processes(): SandboxProcessesAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().processes,\n 'processes'\n ) as unknown as SandboxProcessesAPI;\n }\n get mounts(): SandboxMountsAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().mounts,\n 'mounts'\n ) as unknown as SandboxMountsAPI;\n }\n get workspace(): SandboxWorkspaceAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().workspace,\n 'workspace'\n ) as unknown as SandboxWorkspaceAPI;\n }\n get utils(): SandboxUtilsAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().utils,\n 'utils'\n );\n }\n get backup(): SandboxBackupAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().backup,\n 'backup'\n );\n }\n get watch(): SandboxWatchAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().watch,\n 'watch'\n );\n }\n get tunnels(): SandboxTunnelsAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().tunnels,\n 'tunnels'\n );\n }\n get terminals(): SandboxTerminalsAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().terminals,\n 'terminals'\n ) as unknown as SandboxTerminalsAPI;\n }\n get extensions(): SandboxExtensionsAPI {\n return this.createDomainProxy(\n () => this.getConnection().rpc().extensions,\n 'extensions'\n );\n }\n\n isWebSocketConnected(): boolean {\n return this.conn?.isConnected() ?? false;\n }\n\n async connect(): Promise<void> {\n await this.getConnection().connect();\n }\n\n retainRuntimeHold(): () => void {\n this.getConnection();\n const generation = this.connectionGeneration;\n this.connectionRetainers += 1;\n this.clearIdleTimer();\n let released = false;\n\n return () => {\n if (released || generation !== this.connectionGeneration) return;\n released = true;\n this.connectionRetainers -= 1;\n if (this.connectionRetainers === 0 && !this.busy) {\n this.scheduleIdleDisconnect();\n }\n };\n }\n\n disconnect(): void {\n this.destroyConnection();\n }\n}\n","import type { Logger, SandboxControlCallback } from '@repo/shared';\nimport type { RpcTarget } from 'capnweb';\nimport { ContainerControlClient } from '../container-control/client';\nimport {\n ContainerControlConnection,\n type ContainerFetchStub\n} from '../container-control/connection';\nimport { translateRPCError } from '../container-control/rpc-error';\nimport { RuntimeControlProtocolError } from '../errors';\nimport { RuntimeIdentityInactiveError } from '../runtime/types';\nimport { validateRuntimeMetadata } from './bootstrap-probe';\nimport { interrupted } from './operation-runner';\nimport type {\n RuntimeConnectionHold,\n RuntimeIdentity,\n RuntimeSession,\n RuntimeSessionManager as RuntimeSessionManagerContract\n} from './types';\n\nexport type RuntimeControlCallbackBinder = (\n runtime: RuntimeIdentity,\n isSessionCurrent: () => boolean\n) => (SandboxControlCallback & RpcTarget) | undefined;\n\nexport type RuntimeSessionManagerOptions = {\n getTcpPort: (port: number) => ContainerFetchStub;\n logger?: Logger;\n callbackBinder?: RuntimeControlCallbackBinder;\n onConnectionClose?: () => void;\n};\n\ntype CachedSession = RuntimeSession & {\n generation: number;\n key: string;\n connection: ContainerControlConnection;\n client: ContainerControlClient;\n holds: Set<ManagedRuntimeConnectionHold>;\n interrupt(operation: string): void;\n};\n\ntype OpeningSession = {\n key: string;\n generation: number;\n connection: ContainerControlConnection;\n promise: Promise<CachedSession>;\n};\n\nexport class RuntimeSessionManager implements RuntimeSessionManagerContract {\n private cached: CachedSession | null = null;\n private opening: OpeningSession | null = null;\n private generation = 0;\n private disposed = false;\n\n constructor(private readonly options: RuntimeSessionManagerOptions) {}\n\n async acquire(runtime: RuntimeIdentity): Promise<ContainerControlClient> {\n return (await this.acquireSession(runtime)).client;\n }\n\n async acquireSession(runtime: RuntimeIdentity): Promise<RuntimeSession> {\n const key = this.cacheKey(runtime);\n if (this.disposed) throw new Error('Runtime session manager is disposed');\n if (this.cached?.key === key) return this.cached;\n if (this.opening?.key === key) return await this.opening.promise;\n\n this.supersedeCurrentSession();\n const generation = ++this.generation;\n const connection = this.createConnection(runtime, generation);\n const promise = this.open(runtime, key, generation, connection);\n this.opening = { key, generation, connection, promise };\n\n try {\n return await promise;\n } finally {\n if (this.opening?.promise === promise) this.opening = null;\n }\n }\n\n closeActive(): void {\n this.generation += 1;\n this.opening?.connection.disconnect();\n this.opening = null;\n const cached = this.cached;\n this.cached = null;\n this.interruptSession(cached, 'runtime.session.closeActive');\n cached?.connection.disconnect();\n }\n\n dispose(): void {\n this.disposed = true;\n this.closeActive();\n }\n\n private supersedeCurrentSession(): void {\n this.opening?.connection.disconnect();\n this.opening = null;\n const previous = this.cached;\n this.cached = null;\n this.interruptSession(previous, 'runtime.session.superseded');\n previous?.connection.disconnect();\n }\n\n private retainSession(\n session: CachedSession,\n onInterrupt?: () => void\n ): RuntimeConnectionHold {\n if (session.isInterrupted()) {\n onInterrupt?.();\n return { release: () => {} };\n }\n const hold = new ManagedRuntimeConnectionHold(onInterrupt, () => {\n session.holds.delete(hold);\n });\n session.holds.add(hold);\n if (session.isInterrupted()) hold.forceRelease();\n return hold;\n }\n\n private interruptSession(\n session: CachedSession | null,\n operation: string\n ): void {\n if (!session) return;\n session.interrupt(operation);\n for (const hold of [...session.holds]) hold.forceRelease();\n }\n\n private createConnection(\n runtime: RuntimeIdentity,\n generation: number\n ): ContainerControlConnection {\n let connection: ContainerControlConnection;\n connection = new ContainerControlConnection({\n stub: this.options.getTcpPort(3000),\n logger: this.options.logger,\n localMain: this.bindLocalMain(runtime, generation),\n onClose: () => {\n const cached =\n this.cached?.connection === connection ? this.cached : null;\n if (cached) this.cached = null;\n this.interruptSession(cached, 'runtime.session.transportClosed');\n this.options.onConnectionClose?.();\n }\n });\n return connection;\n }\n\n private async open(\n runtime: RuntimeIdentity,\n key: string,\n generation: number,\n connection: ContainerControlConnection\n ): Promise<CachedSession> {\n try {\n const metadata = validateRuntimeMetadata(\n await this.activate(connection, runtime),\n 'utils.activateControlSession'\n );\n if (metadata.runtimeIncarnationID !== runtime.runtimeIncarnationID) {\n throw this.activationMismatch();\n }\n this.assertCurrentOpening(generation, connection);\n\n const client = new ContainerControlClient({\n stub: this.options.getTcpPort(3000),\n logger: this.options.logger,\n translateTransportErrorsAsInterruptions: false,\n connection,\n externallyOwnedConnection: true,\n onConnectionClose: () => {\n const cached =\n this.cached?.connection === connection ? this.cached : null;\n if (cached) this.cached = null;\n this.interruptSession(cached, 'runtime.session.transportClosed');\n this.options.onConnectionClose?.();\n }\n });\n let rejectInterrupted!: (error: Error) => void;\n let poisoned = false;\n const session: CachedSession = {\n generation,\n key,\n connection,\n client,\n holds: new Set(),\n interrupted: new Promise<never>((_, reject) => {\n rejectInterrupted = reject;\n }),\n isInterrupted: () => poisoned,\n interrupt: (operation) => {\n if (poisoned) return;\n poisoned = true;\n rejectInterrupted(interrupted(operation));\n },\n retain: (onInterrupt) => this.retainSession(session, onInterrupt)\n };\n session.interrupted.catch(() => undefined);\n this.assertCurrentOpening(generation, connection);\n this.cached = session;\n return session;\n } catch (error) {\n connection.disconnect();\n throw error;\n }\n }\n\n private assertCurrentOpening(\n generation: number,\n connection: ContainerControlConnection\n ): void {\n if (\n this.disposed ||\n generation !== this.generation ||\n this.opening?.connection !== connection\n ) {\n connection.disconnect();\n throw new RuntimeIdentityInactiveError();\n }\n }\n\n private async activate(\n connection: ContainerControlConnection,\n runtime: RuntimeIdentity\n ) {\n try {\n return await connection.activateControlSession(\n runtime.runtimeIncarnationID\n );\n } catch (error) {\n if (this.isActivationMismatchError(error))\n throw this.activationMismatch(error);\n translateRPCError(error, {\n operation: 'utils.activateControlSession',\n translateTransportErrorsAsInterruptions: false\n });\n }\n }\n\n private isActivationMismatchError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const code = (error as { code?: unknown }).code;\n return (\n code === 'CONTROL_PROTOCOL_INCOMPATIBLE' ||\n /runtime incarnation does not match|control protocol incompatible/i.test(\n error.message\n )\n );\n }\n\n private activationMismatch(cause?: unknown): RuntimeControlProtocolError {\n return new RuntimeControlProtocolError(\n 'Activated runtime incarnation does not match expected runtime',\n {\n reason: 'activation-mismatch',\n operation: 'utils.activateControlSession'\n },\n { cause }\n );\n }\n\n private bindLocalMain(\n runtime: RuntimeIdentity,\n generation: number\n ): (SandboxControlCallback & RpcTarget) | undefined {\n return this.options.callbackBinder?.(runtime, () =>\n this.isSessionGenerationCurrent(generation)\n );\n }\n\n private isSessionGenerationCurrent(generation: number): boolean {\n if (this.disposed || this.generation !== generation) return false;\n return (\n this.cached?.generation === generation ||\n this.opening?.generation === generation\n );\n }\n\n private cacheKey(runtime: RuntimeIdentity): string {\n return `${runtime.id}\\0${runtime.runtimeIncarnationID}`;\n }\n}\n\nclass ManagedRuntimeConnectionHold implements RuntimeConnectionHold {\n private active = true;\n\n constructor(\n private readonly onInterrupt: (() => void) | undefined,\n private readonly onRelease: () => void\n ) {}\n\n release(): void {\n this.releaseInternal(false);\n }\n\n forceRelease(): void {\n this.releaseInternal(true);\n }\n\n private releaseInternal(interrupted: boolean): void {\n if (!this.active) return;\n this.active = false;\n this.onRelease();\n if (interrupted) this.onInterrupt?.();\n }\n}\n","import type {\n PortWatchEvent,\n PortWatchSubscriptionAPI,\n WaitForPortOptions\n} from '@repo/shared';\nimport type { RuntimeLease } from './operation-runner';\n\nexport async function waitForRuntimePort(\n lease: RuntimeLease,\n port: number,\n options: WaitForPortOptions = {}\n): Promise<void> {\n const hold = lease.retain();\n const abort = createCombinedAbort(options.signal, options.timeout);\n let subscription: PortWatchSubscriptionAPI | undefined;\n let reader: ReadableStreamDefaultReader<PortWatchEvent> | undefined;\n\n try {\n subscription = await abort.race(\n lease.control.ports.openWatch(port, {\n mode: options.mode,\n path: options.path,\n status: options.status,\n interval: options.interval\n })\n );\n const stream = await abort.race(subscription.stream());\n reader = stream.getReader();\n while (true) {\n const { done, value } = await abort.race(reader.read());\n if (done) break;\n if (value.type === 'ready') return;\n if (value.type === 'error') throw new Error(value.error);\n }\n throw new Error(`Port ${port} readiness watch ended before ready`);\n } finally {\n abort.cleanup();\n observe(reader?.cancel());\n observe(subscription?.cancel());\n subscription?.[Symbol.dispose]();\n hold.release();\n }\n}\n\nfunction observe(promise: Promise<unknown> | undefined): void {\n promise?.catch(() => undefined);\n}\n\ntype CombinedAbort = {\n race<T>(promise: Promise<T>): Promise<T>;\n cleanup(): void;\n};\n\nfunction createCombinedAbort(\n externalSignal: AbortSignal | undefined,\n timeoutMs: number | undefined\n): CombinedAbort {\n const controller = new AbortController();\n const listeners: Array<() => void> = [];\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const abort = (reason: unknown) => {\n if (!controller.signal.aborted) controller.abort(reason);\n };\n\n if (externalSignal?.aborted) {\n abort(externalSignal.reason);\n } else if (externalSignal) {\n const listener = () => abort(externalSignal.reason);\n externalSignal.addEventListener('abort', listener, { once: true });\n listeners.push(() => externalSignal.removeEventListener('abort', listener));\n }\n\n if (timeoutMs !== undefined) {\n timer = setTimeout(() => {\n abort(new Error('Timed out waiting for runtime port'));\n }, timeoutMs);\n }\n\n return {\n race: <T>(promise: Promise<T>) => {\n if (controller.signal.aborted)\n return Promise.reject(controller.signal.reason);\n return Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n const listener = () => reject(controller.signal.reason);\n controller.signal.addEventListener('abort', listener, { once: true });\n promise.then(\n () => controller.signal.removeEventListener('abort', listener),\n () => controller.signal.removeEventListener('abort', listener)\n );\n })\n ]);\n },\n cleanup: () => {\n if (timer !== undefined) clearTimeout(timer);\n for (const remove of listeners) remove();\n }\n };\n}\n","/**\n * Resolve a Cloudflare account id from environment with documented\n * precedence. Used by features that need to address a specific account\n * (Cloudflare Tunnel, R2 backup) to find their account id without\n * forcing every caller to set the same env var.\n *\n * Precedence (first non-empty wins):\n * 1. The feature-specific override env var (e.g. `CLOUDFLARE_TUNNEL_ACCOUNT_ID`).\n * 2. `CLOUDFLARE_ACCOUNT_ID`.\n * 3. The single account `CLOUDFLARE_API_TOKEN` is scoped to, via\n * `GET /user/tokens/verify`. Multi-account tokens are rejected.\n *\n * The resolver is feature-agnostic; only the `overrideKey` differs per\n * caller. Throws on any failure with a message that names the env vars\n * the caller can set for an explicit configuration.\n */\n\nimport { getEnvString } from '@repo/shared';\n\nconst TOKEN_VERIFY_URL =\n 'https://api.cloudflare.com/client/v4/user/tokens/verify';\nconst ACCOUNTS_LIST_URL = 'https://api.cloudflare.com/client/v4/accounts';\n\n/**\n * Per-request timeout for the credential introspection calls below.\n * Without one a hung Cloudflare control-plane call wedges every\n * first-time named-tunnel `get()` on the DO (the resolver promises are\n * memoised on `Sandbox`, so the first caller's hang is everyone's hang).\n */\nconst CREDENTIALS_TIMEOUT_MS = 10_000;\n\n/**\n * Fetch wrapper that adds an `AbortSignal.timeout` and surfaces a\n * timeout as a labelled `Error` so the caller can blame the right URL.\n */\nasync function fetchWithTimeout(\n fetcher: typeof fetch,\n url: string,\n init: RequestInit,\n timeoutMs: number = CREDENTIALS_TIMEOUT_MS\n): Promise<Response> {\n try {\n return await fetcher(url, {\n ...init,\n signal: AbortSignal.timeout(timeoutMs)\n });\n } catch (err) {\n if (err instanceof Error && err.name === 'TimeoutError') {\n throw new Error(\n `Cloudflare API request to ${url} timed out after ${timeoutMs}ms`\n );\n }\n throw err;\n }\n}\n\n/**\n * Cloudflare error code returned by `GET /user/tokens/verify` when the\n * presented token is an account-owned (`cfat-`) token rather than a\n * user-owned one. Matches the heuristic wrangler uses in\n * `src/user/whoami.ts` (`getTokenType`).\n */\nconst ACCOUNT_OWNED_TOKEN_CODE = 1000;\n\nexport interface ResolveAccountIdOptions {\n /**\n * The feature-specific override env var name. The caller's \"preferred\"\n * source; checked first.\n */\n overrideKey: 'CLOUDFLARE_TUNNEL_ACCOUNT_ID' | 'CLOUDFLARE_R2_ACCOUNT_ID';\n /**\n * Override the `fetch` implementation for the token-verify call.\n * Defaults to the global `fetch`. Tests inject a mock here.\n */\n fetcher?: typeof fetch;\n}\n\n/**\n * Cloudflare's `result_info` shape for `/user/tokens/verify`. The token\n * is \"account-scoped\" iff `result_info.account.id` is present.\n */\ninterface TokenVerifyResponse {\n success?: boolean;\n errors?: Array<{ code?: number; message?: string }>;\n result_info?: {\n account?: { id?: string };\n };\n}\n\ninterface AccountsListResponse {\n success?: boolean;\n errors?: Array<{ code?: number; message?: string }>;\n result?: Array<{ id?: string; name?: string }>;\n}\n\ninterface AccountTokenVerifyResponse {\n success?: boolean;\n errors?: Array<{ code?: number; message?: string }>;\n result?: { id?: string; status?: string };\n}\n\nexport async function resolveAccountId(\n env: Record<string, unknown>,\n options: ResolveAccountIdOptions\n): Promise<string> {\n // Step 1: feature-specific override.\n const override = getEnvString(env, options.overrideKey);\n if (override) return override;\n\n // Step 2: generic account fallback.\n const generic = getEnvString(env, 'CLOUDFLARE_ACCOUNT_ID');\n if (generic) return generic;\n\n // Step 3: derive from the API token. Requires a token to be present.\n const token = getEnvString(env, 'CLOUDFLARE_API_TOKEN');\n if (!token) {\n throw new Error(\n `Cloudflare account id could not be resolved. Set one of: ` +\n `${options.overrideKey}, CLOUDFLARE_ACCOUNT_ID, or ` +\n `CLOUDFLARE_API_TOKEN (a token scoped to a single account).`\n );\n }\n\n const fetcher = options.fetcher ?? fetch;\n const response = await fetchWithTimeout(fetcher, TOKEN_VERIFY_URL, {\n method: 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json'\n }\n });\n\n // Drain the body whether we succeed or not so the caller-facing error\n // can report the API error code (e.g. 1000 = account-owned token).\n let body: TokenVerifyResponse;\n try {\n body = (await response.json()) as TokenVerifyResponse;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Cloudflare token verification returned malformed JSON: ${message}`\n );\n }\n\n if (response.ok && body?.success) {\n const derived = body.result_info?.account?.id;\n if (!derived) {\n throw new Error(\n 'Cloudflare token is not scoped to a single account (ambiguous). ' +\n `Set ${options.overrideKey} or CLOUDFLARE_ACCOUNT_ID explicitly.`\n );\n }\n return derived;\n }\n\n // Wrangler's `getTokenType` uses the same heuristic: code 1000 on\n // `/user/tokens/verify` means the token is account-owned (cfat-...)\n // and can't be introspected via the user-scoped endpoint. Fall through\n // to `/accounts` + `/accounts/:id/tokens/verify` to derive the id.\n const isAccountOwned = body?.errors?.some(\n (e) => e.code === ACCOUNT_OWNED_TOKEN_CODE\n );\n if (isAccountOwned) {\n return await deriveAccountIdViaAccountToken(token, fetcher, options);\n }\n\n throw new Error(\n `Cloudflare token verification failed with status ${response.status}. ` +\n `Check that CLOUDFLARE_API_TOKEN is valid or set ${options.overrideKey} ` +\n `/ CLOUDFLARE_ACCOUNT_ID explicitly.`\n );\n}\n\n/**\n * Account-owned token (cfat-) fallback: list the accounts the token can\n * see, and — if there's exactly one — confirm with the account-scoped\n * verify endpoint before returning the id.\n *\n * Common failure modes get specific, actionable error messages:\n * - `/accounts` 403 (token lacks `account:read`): tell the caller to\n * set `CLOUDFLARE_ACCOUNT_ID` explicitly.\n * - multiple accounts: same.\n * - zero accounts: same.\n * - confirm step fails: surface the API error code verbatim.\n */\nasync function deriveAccountIdViaAccountToken(\n token: string,\n fetcher: typeof fetch,\n options: ResolveAccountIdOptions\n): Promise<string> {\n // per_page=2 is the cheapest probe that still distinguishes one-vs-many.\n const listResponse = await fetchWithTimeout(\n fetcher,\n `${ACCOUNTS_LIST_URL}?per_page=2`,\n {\n method: 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json'\n }\n }\n );\n\n let listBody: AccountsListResponse;\n try {\n listBody = (await listResponse.json()) as AccountsListResponse;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Cloudflare account-owned token: /accounts returned malformed JSON: ${message}`\n );\n }\n\n if (!listResponse.ok || !listBody?.success) {\n throw new Error(\n 'Cloudflare account-owned token (cfat-...) detected, but ' +\n `/accounts returned status ${listResponse.status}. The token may ` +\n 'lack account:read scope. Set CLOUDFLARE_ACCOUNT_ID explicitly to ' +\n 'skip introspection.'\n );\n }\n\n const accounts = listBody.result ?? [];\n if (accounts.length === 0) {\n throw new Error(\n 'Cloudflare account-owned token has access to no accounts. ' +\n 'Set CLOUDFLARE_ACCOUNT_ID explicitly.'\n );\n }\n if (accounts.length > 1) {\n throw new Error(\n 'Cloudflare account-owned token has access to multiple accounts ' +\n '(ambiguous). Set CLOUDFLARE_ACCOUNT_ID explicitly to disambiguate.'\n );\n }\n const accountId = accounts[0]?.id;\n if (!accountId) {\n throw new Error(\n 'Cloudflare /accounts returned a result without an id field.'\n );\n }\n\n // Confirm via the account-scoped verify endpoint. This is the canonical\n // check for account-owned tokens, and it doubles as proof that the token\n // is actually valid for the account we picked.\n const verifyResponse = await fetchWithTimeout(\n fetcher,\n `${ACCOUNTS_LIST_URL}/${encodeURIComponent(accountId)}/tokens/verify`,\n {\n method: 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json'\n }\n }\n );\n let verifyBody: AccountTokenVerifyResponse;\n try {\n verifyBody = (await verifyResponse.json()) as AccountTokenVerifyResponse;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Cloudflare account token verify returned malformed JSON: ${message}`\n );\n }\n if (!verifyResponse.ok || !verifyBody?.success) {\n const detail =\n verifyBody?.errors?.map((e) => `${e.code}: ${e.message}`).join('; ') ??\n `HTTP ${verifyResponse.status}`;\n throw new Error(\n `Cloudflare account token verify failed for account ${accountId}: ${detail}`\n );\n }\n return accountId;\n}\n\n// ---------------------------------------------------------------------------\n// Zone id resolution\n// ---------------------------------------------------------------------------\n\nconst ZONES_LIST_URL = 'https://api.cloudflare.com/client/v4/zones';\n\nexport interface ResolveZoneIdOptions {\n /** Cloudflare API token. Must be account- or zone-scoped.*/\n token: string;\n /**\n * Account id the zone must belong to. The resolver scopes the zones\n * list query to this account so a single multi-account token isn't\n * tripped up by zones belonging to a different account.\n */\n accountId: string;\n /**\n * Override the `fetch` implementation for the zones list call.\n * Defaults to the global `fetch`. Tests inject a mock here.\n */\n fetcher?: typeof fetch;\n}\n\ninterface ZonesListResponse {\n success?: boolean;\n result?: Array<{ id?: string; name?: string }>;\n errors?: Array<{ code?: number; message?: string }>;\n}\n\n/**\n * Resolve a Cloudflare zone id.\n *\n * Precedence:\n * 1. `CLOUDFLARE_ZONE_ID` env var.\n * 2. The single zone the token can see under `accountId`, via\n * `GET /zones?account.id=<accountId>&per_page=2`.\n *\n * Step 2 deliberately fetches at most two results: one is the happy path,\n * two (or more) means the token is ambiguous and we refuse to guess.\n * Multi-zone tokens must set `CLOUDFLARE_ZONE_ID` explicitly so the\n * caller's intent is unambiguous.\n */\nexport async function resolveZoneId(\n env: Record<string, unknown>,\n options: ResolveZoneIdOptions\n): Promise<string> {\n const envZone = getEnvString(env, 'CLOUDFLARE_ZONE_ID');\n if (envZone) return envZone;\n\n const fetcher = options.fetcher ?? fetch;\n const url = `${ZONES_LIST_URL}?account.id=${encodeURIComponent(options.accountId)}&per_page=2`;\n const response = await fetchWithTimeout(fetcher, url, {\n method: 'GET',\n headers: {\n authorization: `Bearer ${options.token}`,\n 'content-type': 'application/json'\n }\n });\n\n if (!response.ok) {\n throw new Error(\n `Cloudflare zones lookup failed with status ${response.status}. ` +\n 'Set CLOUDFLARE_ZONE_ID explicitly or grant the API token Zone:Read.'\n );\n }\n\n let body: ZonesListResponse;\n try {\n body = (await response.json()) as ZonesListResponse;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Cloudflare zones lookup returned malformed JSON: ${message}`\n );\n }\n\n const zones = body.result ?? [];\n if (zones.length === 0) {\n throw new Error(\n 'Cloudflare API token has access to no zones in account ' +\n `${options.accountId}. Set CLOUDFLARE_ZONE_ID explicitly or ` +\n 'grant the token Zone:Read on the intended zone.'\n );\n }\n if (zones.length > 1) {\n throw new Error(\n 'Cloudflare API token has access to multiple zones in account ' +\n `${options.accountId} (ambiguous). Set CLOUDFLARE_ZONE_ID ` +\n 'explicitly to disambiguate.'\n );\n }\n const zoneId = zones[0]?.id;\n if (!zoneId) {\n throw new Error(\n 'Cloudflare zones lookup returned a result without an id field.'\n );\n }\n return zoneId;\n}\n","import { getEnvString } from '@repo/shared';\nimport { resolveAccountId, resolveZoneId } from './credentials';\n\nexport interface NamedTunnelConfig {\n token: string;\n accountId: string;\n zoneId: string;\n}\n\nexport interface NamedTunnelConfigResolverOptions {\n getEnv: () => unknown;\n fetcher?: typeof fetch;\n}\n\nfunction asEnvRecord(env: unknown): Record<string, unknown> {\n return env && typeof env === 'object' ? (env as Record<string, unknown>) : {};\n}\n\nexport class NamedTunnelConfigResolver {\n readonly #getEnv: () => unknown;\n readonly #fetcher?: typeof fetch;\n #accountIdPromise: Promise<string> | null = null;\n #zoneIdPromise: Promise<string> | null = null;\n\n constructor(options: NamedTunnelConfigResolverOptions) {\n this.#getEnv = options.getEnv;\n this.#fetcher = options.fetcher;\n }\n\n async getConfig(): Promise<NamedTunnelConfig> {\n const env = this.#env();\n const token = getEnvString(env, 'CLOUDFLARE_API_TOKEN');\n if (!token) {\n throw new Error(\n 'Named tunnels require CLOUDFLARE_API_TOKEN. ' +\n 'Set it as a secret in your wrangler.jsonc.'\n );\n }\n\n const accountId = await this.#getAccountId();\n const zoneId = await this.#getZoneId(token, accountId);\n return { token, accountId, zoneId };\n }\n\n #env(): Record<string, unknown> {\n return asEnvRecord(this.#getEnv());\n }\n\n #getAccountId(): Promise<string> {\n if (!this.#accountIdPromise) {\n const pending = resolveAccountId(this.#env(), {\n overrideKey: 'CLOUDFLARE_TUNNEL_ACCOUNT_ID',\n fetcher: this.#fetcher\n });\n this.#accountIdPromise = pending;\n pending.catch(() => {\n if (this.#accountIdPromise === pending) {\n this.#accountIdPromise = null;\n }\n });\n }\n return this.#accountIdPromise;\n }\n\n #getZoneId(token: string, accountId: string): Promise<string> {\n if (!this.#zoneIdPromise) {\n const pending = resolveZoneId(this.#env(), {\n token,\n accountId,\n fetcher: this.#fetcher\n });\n this.#zoneIdPromise = pending;\n pending.catch(() => {\n if (this.#zoneIdPromise === pending) {\n this.#zoneIdPromise = null;\n }\n });\n }\n return this.#zoneIdPromise;\n }\n}\n","/**\n * Cloudflare API client for named-tunnel orchestration.\n *\n * Design notes:\n *\n * - The Cloudflare API envelope is `{ success, result, errors }`. We\n * unwrap `result` on success and surface a thrown `Error` with the\n * API error code/message on failure. Transport-level errors\n * propagate unchanged.\n * - Delete endpoints are idempotent from the caller's perspective:\n * a 404 (already gone) resolves successfully so destroy() can run\n * without special-casing.\n * - `upsertCNAME` is the most subtle wrapper: it lists existing\n * records, reuses a matching one, and refuses to mutate a record\n * whose content differs from what we want. This is the fence that\n * stops two sandboxes from racing on the same hostname.\n */\n\nconst API_BASE = 'https://api.cloudflare.com/client/v4';\n\n/** Cloudflare's standard envelope around every response. */\ninterface CloudflareResponse<T> {\n success: boolean;\n result?: T;\n errors?: Array<{ code?: number; message?: string }>;\n}\n\ntype Fetcher = typeof fetch;\n\ninterface BaseArgs {\n token: string;\n fetcher?: Fetcher;\n}\n\n/**\n * Tag attached to every tunnel resource the SDK creates. Survives\n * round-tripping through the Cloudflare API so `findTunnelByName` can\n * reconcile orphaned resources from a previous failed attempt.\n */\nexport interface TunnelMetadata {\n sandboxId: string;\n createdBy: 'sandbox-sdk';\n name: string;\n port: number;\n}\n\ninterface RequestOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE';\n body?: unknown;\n /** Treat these HTTP statuses as success and skip envelope parsing. */\n acceptStatuses?: number[];\n /**\n * Per-request timeout in milliseconds. Defaults to `DEFAULT_TIMEOUT_MS`.\n * Without a timeout a hung Cloudflare call wedges the per-port lock in\n * `rpc-target.ts` indefinitely, which then blocks every subsequent\n * `get(port)` / `destroy(port)` on that port. The shared\n * `#zoneNamePromise` makes the impact span every port for named\n * tunnels.\n */\n timeoutMs?: number;\n}\n\n/**\n * Default request timeout. Cloudflare API P99 latency is well under\n * this; values much smaller risk false positives on cold control-plane\n * paths (e.g. first `cfd_tunnel` POST in a new account).\n */\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\n/**\n * Internal request helper. Centralises auth header, JSON encoding,\n * timeout enforcement, and envelope unwrapping so each wrapper above\n * stays declarative.\n */\nasync function cfRequest<T>(\n url: string,\n token: string,\n fetcher: Fetcher,\n options: RequestOptions = {}\n): Promise<T | undefined> {\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const init: RequestInit = {\n method: options.method ?? 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json'\n },\n signal: AbortSignal.timeout(timeoutMs)\n };\n if (options.body !== undefined) {\n init.body = JSON.stringify(options.body);\n }\n\n let response: Response;\n try {\n response = await fetcher(url, init);\n } catch (err) {\n // `AbortSignal.timeout` rejects with a DOMException whose name is\n // 'TimeoutError'. Surface it as a clearly-labelled error so callers\n // can distinguish a transport hang from a Cloudflare-side failure;\n // a SandboxSecurityError-shaped class would be better but we keep\n // the error shape consistent with the rest of this module.\n if (err instanceof Error && err.name === 'TimeoutError') {\n throw new Error(\n `Cloudflare API request to ${url} timed out after ${timeoutMs}ms`\n );\n }\n throw err;\n }\n if (options.acceptStatuses?.includes(response.status)) {\n return undefined;\n }\n\n let envelope: CloudflareResponse<T>;\n try {\n envelope = (await response.json()) as CloudflareResponse<T>;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Cloudflare API returned non-JSON response (status ${response.status}): ${message}`\n );\n }\n\n if (!response.ok || envelope.success === false) {\n const errs = envelope.errors ?? [];\n const summary = errs.length\n ? errs\n .map((e) => `${e.code ?? '???'}: ${e.message ?? 'unknown'}`)\n .join(', ')\n : `HTTP ${response.status}`;\n throw new Error(`Cloudflare API error: ${summary}`);\n }\n\n return envelope.result;\n}\n\n/**\n * Heuristic for the \"tags are an Enterprise-only feature\" error class.\n * Empirically grounded against a non-Enterprise account:\n *\n * - DNS create with `tags: [...]` on a non-Enterprise zone rejects with\n * Cloudflare error code 9300 and the message \"DNS record has N tags,\n * exceeding the quota of 0.\". The error string `cfRequest` constructs\n * embeds both the code and the message, so we match on either signal.\n * - Tunnel create with `tags: [...]` silently succeeds and drops the\n * field on the floor (no error to retry on). The fallback wrapper\n * therefore costs nothing on tunnel writes.\n *\n * Generic \"requires Enterprise\" phrasing is also matched as a forward-\n * compatibility hedge in case Cloudflare changes the response shape on\n * future endpoints.\n */\nfunction isEnterpriseOnlyTagError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const msg = error.message.toLowerCase();\n // Code 9300 is the observed DNS \"tags exceed quota\" code.\n if (msg.includes('9300') && msg.includes('tag')) return true;\n // Generic phrasings used by Cloudflare's documented Enterprise gates.\n if (!msg.includes('tag')) return false;\n return (\n msg.includes('quota') ||\n msg.includes('enterprise') ||\n msg.includes('not allowed') ||\n msg.includes('not entitled') ||\n msg.includes('not available') ||\n msg.includes('not supported')\n );\n}\n\n/**\n * Build the `tags` field attached to created Cloudflare resources. The\n * tag is `sandboxId:<id>`, the same key used in DNS comments / tunnel\n * metadata; together they let an operator find every resource a given\n * sandbox owns from the Cloudflare dashboard.\n *\n * Tags are an Enterprise-only feature. The wrapper `createWithTagFallback`\n * automatically retries the request without tags on the documented\n * \"requires Enterprise\" error so non-enterprise accounts succeed without\n * any configuration.\n */\nfunction buildSandboxTags(sandboxId: string | undefined): string[] | undefined {\n if (!sandboxId) return undefined;\n return [`sandboxId:${sandboxId}`];\n}\n\n/**\n * Wrap a tagged-create request with an automatic tag-strip retry. The\n * callback receives `tags`: pass it through to the request body as-is on\n * the first call (`undefined` on the retry). The retry only fires for\n * the Enterprise-only tag error class; any other failure surfaces\n * verbatim.\n */\nasync function createWithTagFallback<T>(\n sandboxId: string | undefined,\n send: (tags: string[] | undefined) => Promise<T>\n): Promise<T> {\n const tags = buildSandboxTags(sandboxId);\n if (!tags) return send(undefined);\n try {\n return await send(tags);\n } catch (err) {\n if (!isEnterpriseOnlyTagError(err)) throw err;\n return send(undefined);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Tunnels\n// ---------------------------------------------------------------------------\n\nexport interface CreateTunnelArgs extends BaseArgs {\n accountId: string;\n /**\n * The on-Cloudflare display name for the tunnel resource. Conventionally\n * `sandbox-<sandboxId>-<userName>` so it's stable per (sandbox, name).\n */\n tunnelName: string;\n metadata: TunnelMetadata;\n}\n\nexport interface CreatedTunnel {\n id: string;\n /** Opaque `--token` for `cloudflared tunnel run --token <T>`. */\n token: string;\n}\n\nexport async function createTunnel(\n args: CreateTunnelArgs\n): Promise<CreatedTunnel> {\n const fetcher = args.fetcher ?? fetch;\n // The `/cfd_tunnel` endpoint silently ignores unknown body fields,\n // including `tags`, so today this is effectively a no-op on the wire\n // — the tagging story for tunnels lives on the Resource Tagging API\n // (PUT /accounts/{id}/tags). We keep the inline field in case the\n // endpoint adopts it later; the wrapper handles the Enterprise-only\n // fallback for DNS (see upsertCNAME), where it does fire.\n const result = await createWithTagFallback(args.metadata.sandboxId, (tags) =>\n cfRequest<{ id: string; token: string }>(\n `${API_BASE}/accounts/${encodeURIComponent(args.accountId)}/cfd_tunnel`,\n args.token,\n fetcher,\n {\n method: 'POST',\n body: {\n name: args.tunnelName,\n // `cloudflare` lets cloudflared run with just --token, no local\n // config file. The alternative `local` requires a YAML config.\n config_src: 'cloudflare',\n metadata: args.metadata,\n ...(tags ? { tags } : {})\n }\n }\n )\n );\n if (!result) {\n throw new Error('Cloudflare tunnel create returned no result body');\n }\n return { id: result.id, token: result.token };\n}\n\nexport interface FindTunnelArgs extends BaseArgs {\n accountId: string;\n tunnelName: string;\n /**\n * When set, only return tunnels whose `metadata.sandboxId` equals this\n * value. Otherwise the function matches by name alone.\n *\n * Use this to defend against the case where two sandboxes happen to\n * mint the same tunnel name (the name conventionally encodes the\n * sandbox id, but the API does not enforce that): without the\n * metadata check, sandbox B's `findTunnelByName` would happily claim\n * sandbox A's tunnel and start managing it.\n */\n expectedSandboxId?: string;\n}\n\nexport interface ExistingTunnel {\n id: string;\n name: string;\n}\n\n/**\n * Look up an existing tunnel by exact name match. Filters out tunnels\n * marked `deleted_at != null` defensively in case the API ignores the\n * `is_deleted=false` query parameter.\n *\n * When `expectedSandboxId` is provided, also verify that the tunnel's\n * `metadata.sandboxId` tag matches — this is the authoritative \"this\n * resource was created by this sandbox\" check, and the tag is set by\n * `createTunnel`. Mismatches are treated as \"not found\" so the caller\n * falls through to creating a fresh tunnel.\n */\nexport async function findTunnelByName(\n args: FindTunnelArgs\n): Promise<ExistingTunnel | null> {\n const fetcher = args.fetcher ?? fetch;\n const url =\n `${API_BASE}/accounts/${encodeURIComponent(args.accountId)}/cfd_tunnel` +\n `?name=${encodeURIComponent(args.tunnelName)}&is_deleted=false`;\n const result = await cfRequest<\n Array<{\n id: string;\n name: string;\n deleted_at?: string | null;\n metadata?: unknown;\n }>\n >(url, args.token, fetcher);\n if (!result) return null;\n const live = result.find((t) => !t.deleted_at);\n if (!live) return null;\n if (args.expectedSandboxId !== undefined) {\n const meta = live.metadata as { sandboxId?: unknown } | undefined;\n if (meta?.sandboxId !== args.expectedSandboxId) return null;\n }\n return { id: live.id, name: live.name };\n}\n\nexport interface DeleteTunnelArgs extends BaseArgs {\n accountId: string;\n tunnelId: string;\n}\n\nexport async function deleteTunnel(args: DeleteTunnelArgs): Promise<void> {\n const fetcher = args.fetcher ?? fetch;\n await cfRequest<unknown>(\n `${API_BASE}/accounts/${encodeURIComponent(args.accountId)}/cfd_tunnel/${encodeURIComponent(args.tunnelId)}`,\n args.token,\n fetcher,\n {\n method: 'DELETE',\n // 404 is a successful (already-gone) outcome so destroy() can\n // chain other cleanup steps without surfacing benign races.\n acceptStatuses: [404]\n }\n );\n}\n\nexport interface GetTunnelTokenArgs extends BaseArgs {\n accountId: string;\n tunnelId: string;\n}\n\n/**\n * Fetch the opaque `--token` for an existing tunnel. Used on the retry\n * path: when `findTunnelByName` discovers a tunnel left behind from a\n * previous failed attempt, we need its token to run `cloudflared` again.\n *\n * The Cloudflare API returns the token as a bare quoted string in the\n * `result` envelope (e.g. `\"<base64-token>\"`).\n */\nexport async function getTunnelToken(\n args: GetTunnelTokenArgs\n): Promise<string> {\n const fetcher = args.fetcher ?? fetch;\n const result = await cfRequest<string>(\n `${API_BASE}/accounts/${encodeURIComponent(args.accountId)}/cfd_tunnel/${encodeURIComponent(args.tunnelId)}/token`,\n args.token,\n fetcher\n );\n if (typeof result !== 'string' || result.length === 0) {\n throw new Error(\n `Cloudflare did not return a token for tunnel ${args.tunnelId}`\n );\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Zones\n// ---------------------------------------------------------------------------\n\nexport interface GetZoneNameArgs extends BaseArgs {\n zoneId: string;\n}\n\nexport async function getZoneName(args: GetZoneNameArgs): Promise<string> {\n const fetcher = args.fetcher ?? fetch;\n const result = await cfRequest<{ id: string; name: string }>(\n `${API_BASE}/zones/${encodeURIComponent(args.zoneId)}`,\n args.token,\n fetcher\n );\n if (!result?.name) {\n throw new Error(`Cloudflare zone ${args.zoneId} did not return a name`);\n }\n return result.name;\n}\n\n// ---------------------------------------------------------------------------\n// DNS records\n// ---------------------------------------------------------------------------\n\nexport interface UpsertCNAMEArgs extends BaseArgs {\n zoneId: string;\n hostname: string;\n /** `<tunnel-id>.cfargotunnel.com`. */\n cnameTarget: string;\n /** `sandbox-<sandbox-id>` — used both for tagging and reuse matching. */\n comment: string;\n /**\n * Sandbox id used to build the `sandboxId:<id>` Cloudflare tag\n * attached to the created DNS record. Tags are an Enterprise-only\n * feature; the create call falls back to an untagged record on the\n * documented \"requires Enterprise\" error. Omit to skip tagging.\n */\n sandboxId?: string;\n}\n\nexport interface UpsertCNAMEResult {\n recordId: string;\n /** True when an existing matching record was reused; false when created. */\n reused: boolean;\n}\n\ninterface DNSRecordEntry {\n id: string;\n type: string;\n name: string;\n content: string;\n comment?: string | null;\n proxied?: boolean;\n}\n\nasync function listCNAMERecords(args: {\n token: string;\n zoneId: string;\n hostname: string;\n fetcher: Fetcher;\n}): Promise<DNSRecordEntry[]> {\n const listUrl =\n `${API_BASE}/zones/${encodeURIComponent(args.zoneId)}/dns_records` +\n `?type=CNAME&name=${encodeURIComponent(args.hostname)}`;\n return (\n (await cfRequest<DNSRecordEntry[]>(listUrl, args.token, args.fetcher)) ?? []\n );\n}\n\nexport interface FindCNAMEArgs extends BaseArgs {\n zoneId: string;\n hostname: string;\n cnameTarget: string;\n}\n\nexport interface FoundCNAMEResult {\n recordId: string;\n}\n\nexport async function findCNAME(\n args: FindCNAMEArgs\n): Promise<FoundCNAMEResult | null> {\n const fetcher = args.fetcher ?? fetch;\n const records = await listCNAMERecords({\n token: args.token,\n zoneId: args.zoneId,\n hostname: args.hostname,\n fetcher\n });\n const existing = records.find(\n (r) =>\n r.type === 'CNAME' &&\n r.name === args.hostname &&\n r.content === args.cnameTarget\n );\n return existing ? { recordId: existing.id } : null;\n}\n\nexport async function upsertCNAME(\n args: UpsertCNAMEArgs\n): Promise<UpsertCNAMEResult> {\n const fetcher = args.fetcher ?? fetch;\n const records = await listCNAMERecords({\n token: args.token,\n zoneId: args.zoneId,\n hostname: args.hostname,\n fetcher\n });\n\n const existing = records.find(\n (r) => r.type === 'CNAME' && r.name === args.hostname\n );\n if (existing) {\n // The CNAME content `<tunnel-id>.cfargotunnel.com` is the\n // authoritative \"ours\" check: only the holder of the tunnel id\n // could have asked Cloudflare to mint that target. Comment is\n // free text that operators commonly edit through the dashboard,\n // so we deliberately do not key reuse on it.\n if (existing.content === args.cnameTarget) {\n return { recordId: existing.id, reused: true };\n }\n throw new Error(\n `DNS record for ${args.hostname} already exists with different content ` +\n `(owned by you, not us): existing content=\"${existing.content}\", ` +\n `existing comment=\"${existing.comment ?? ''}\". Delete the record ` +\n 'manually to allow the sandbox to manage it.'\n );\n }\n\n const createResult = await createWithTagFallback(args.sandboxId, (tags) =>\n cfRequest<{ id: string }>(\n `${API_BASE}/zones/${encodeURIComponent(args.zoneId)}/dns_records`,\n args.token,\n fetcher,\n {\n method: 'POST',\n body: {\n type: 'CNAME',\n name: args.hostname,\n content: args.cnameTarget,\n proxied: true,\n comment: args.comment,\n ...(tags ? { tags } : {})\n }\n }\n )\n );\n if (!createResult) {\n throw new Error('Cloudflare DNS create returned no result body');\n }\n return { recordId: createResult.id, reused: false };\n}\n\nexport interface DeleteDNSRecordArgs extends BaseArgs {\n zoneId: string;\n recordId: string;\n}\n\nexport async function deleteDNSRecord(\n args: DeleteDNSRecordArgs\n): Promise<void> {\n const fetcher = args.fetcher ?? fetch;\n await cfRequest<unknown>(\n `${API_BASE}/zones/${encodeURIComponent(args.zoneId)}/dns_records/${encodeURIComponent(args.recordId)}`,\n args.token,\n fetcher,\n {\n method: 'DELETE',\n acceptStatuses: [404]\n }\n );\n}\n","import type { NamedTunnelInfo, TunnelInfo, TunnelOptions } from '@repo/shared';\nimport type { RuntimeIncarnationID } from '../runtime';\nimport type { RuntimeIdentityID } from '../runtime/types';\nimport type { SandboxLifetimeID } from '../sandbox-lifetime';\n\n/** DO storage key for the `port → TunnelInfo` map. */\nexport const STORAGE_KEY = 'tunnels';\n\n/**\n * Sidecar storage key for per-port metadata the handler needs but the\n * public `TunnelInfo` shape does not carry.\n */\nexport const META_STORAGE_KEY = 'tunnels:meta';\n\nexport const CLEANUP_STORAGE_KEY = 'tunnels:cleanup';\n\nexport type TunnelMap = Record<string, TunnelInfo>;\n\nexport interface TunnelMetaEntry {\n /** Stable hash of the `options` object the tunnel was created with. */\n optionsHash: string;\n /** Cloudflare DNS record id for named tunnels; absent for quick. */\n dnsRecordId?: string;\n /** Runtime identity that owns the current cloudflared process. */\n runtimeIdentityID?: RuntimeIdentityID;\n /** Exact runtime incarnation that owns the current cloudflared process. */\n runtimeIncarnationID?: RuntimeIncarnationID;\n /** Sandbox lifetime that owns this tunnel record. */\n sandboxLifetimeID?: SandboxLifetimeID;\n /** Runtime-local cloudflared run that owns current process callbacks. */\n tunnelRunId?: string;\n /** Cloudflare tunnel id for hidden named records that can respawn or clean up. */\n tunnelId?: string;\n /** User-provided named-tunnel label for hidden respawn/cleanup records. */\n name?: string;\n /** Public named-tunnel hostname for hidden respawn/cleanup records. */\n hostname?: string;\n /**\n * Resolved `(accountId, zoneId)` the named tunnel was provisioned\n * against. Compared on cache hit to detect env-var changes that\n * would otherwise silently serve a stale URL.\n */\n accountId?: string;\n zoneId?: string;\n /**\n * Set for hidden named tunnels whose Cloudflare resources outlived\n * the runtime-local cloudflared process.\n */\n needsRespawn?: boolean;\n}\n\nexport type TunnelMetaMap = Record<string, TunnelMetaEntry>;\n\nexport type TunnelCleanupPhase = 'planned' | 'tunnel_ready' | 'claimed';\n\nexport interface TunnelCleanupEntry {\n tunnelId?: string;\n port: number;\n name: string;\n hostname: string;\n tunnelName?: string;\n sandboxId?: string;\n dnsRecordId?: string;\n accountId?: string;\n zoneId?: string;\n phase: TunnelCleanupPhase;\n updatedAt: string;\n}\n\nexport type TunnelCleanupMap = Record<string, TunnelCleanupEntry>;\n\n/**\n * Subset of `DurableObjectTransaction` (and `DurableObjectStorage`) used\n * inside a transaction closure — no nested `transaction()`.\n */\nexport interface TunnelsStorageTxn {\n get<T>(key: string): Promise<T | undefined>;\n put<T>(key: string, value: T): Promise<void>;\n delete(key: string): Promise<boolean>;\n}\n\n/**\n * Subset of `DurableObjectStorage` the handler uses.\n *\n * `transaction` gives optimistic concurrency for read-modify-write paths.\n */\nexport interface TunnelsStorage extends TunnelsStorageTxn {\n transaction<T>(closure: (txn: TunnelsStorageTxn) => Promise<T>): Promise<T>;\n}\n\nexport async function readMap(storage: TunnelsStorageTxn): Promise<TunnelMap> {\n return (await storage.get<TunnelMap>(STORAGE_KEY)) ?? {};\n}\n\nexport async function readMetaMap(\n storage: TunnelsStorageTxn\n): Promise<TunnelMetaMap> {\n return (await storage.get<TunnelMetaMap>(META_STORAGE_KEY)) ?? {};\n}\n\nexport async function readCleanupMap(\n storage: TunnelsStorageTxn\n): Promise<TunnelCleanupMap> {\n return (await storage.get<TunnelCleanupMap>(CLEANUP_STORAGE_KEY)) ?? {};\n}\n\nexport interface TunnelPortState {\n info?: TunnelInfo;\n meta?: TunnelMetaEntry;\n cleanup?: TunnelCleanupEntry;\n}\n\nexport async function readPortState(\n storage: TunnelsStorageTxn,\n port: number\n): Promise<TunnelPortState> {\n const portKey = port.toString();\n const [map, meta, cleanup] = await Promise.all([\n readMap(storage),\n readMetaMap(storage),\n readCleanupMap(storage)\n ]);\n return {\n info: map[portKey],\n meta: meta[portKey],\n cleanup: cleanup[portKey]\n };\n}\n\nfunction setOptionalMapValue<T>(\n map: Record<string, T>,\n key: string,\n value: T | undefined\n): void {\n if (value === undefined) {\n delete map[key];\n return;\n }\n map[key] = value;\n}\n\nexport interface TunnelPortStatePatch {\n info?: TunnelInfo;\n meta?: TunnelMetaEntry;\n cleanup?: TunnelCleanupEntry;\n}\n\nexport async function updatePortState(\n storage: TunnelsStorage,\n port: number,\n updater: (\n state: Readonly<TunnelPortState>,\n txn: TunnelsStorageTxn\n ) =>\n | TunnelPortStatePatch\n | undefined\n | Promise<TunnelPortStatePatch | undefined>\n): Promise<void> {\n await storage.transaction(async (txn) => {\n const portKey = port.toString();\n const [map, meta, cleanup] = await Promise.all([\n readMap(txn),\n readMetaMap(txn),\n readCleanupMap(txn)\n ]);\n const state: TunnelPortState = {\n info: map[portKey],\n meta: meta[portKey],\n cleanup: cleanup[portKey]\n };\n const patch = await updater(state, txn);\n\n const writes: Array<Promise<void>> = [];\n if (patch && 'info' in patch && patch.info !== state.info) {\n setOptionalMapValue(map, portKey, patch.info);\n writes.push(txn.put(STORAGE_KEY, map));\n }\n if (patch && 'meta' in patch && patch.meta !== state.meta) {\n setOptionalMapValue(meta, portKey, patch.meta);\n writes.push(txn.put(META_STORAGE_KEY, meta));\n }\n if (patch && 'cleanup' in patch && patch.cleanup !== state.cleanup) {\n setOptionalMapValue(cleanup, portKey, patch.cleanup);\n writes.push(txn.put(CLEANUP_STORAGE_KEY, cleanup));\n }\n await Promise.all(writes);\n });\n}\n\n/**\n * Stable hash of `options`. Empty/undefined options collapse to the same\n * hash so `get(port)`, `get(port, {})`, and `get(port, { name: undefined })`\n * all hit the same cache entry. Named tunnels hash on `name` alone.\n */\nexport function computeOptionsHash(options?: TunnelOptions): string {\n if (!options || !options.name) return 'v1:quick';\n return `v1:named:${options.name}`;\n}\n\n/** Strip the optional `v1:` prefix so legacy hashes compare equal. */\nfunction normaliseHash(hash: string): string {\n return hash.startsWith('v1:') ? hash.slice(3) : hash;\n}\n\nexport function optionsHashesEqual(a: string, b: string): boolean {\n return normaliseHash(a) === normaliseHash(b);\n}\n\nexport function fallbackOptionsHash(info: TunnelInfo): string {\n return info.name ? computeOptionsHash({ name: info.name }) : 'v1:quick';\n}\n\nexport function effectiveOptionsHash(\n info: TunnelInfo,\n meta: TunnelMetaEntry | undefined\n): string {\n return meta?.optionsHash ?? fallbackOptionsHash(info);\n}\n\nexport function tunnelConfigChanged(\n meta: TunnelMetaEntry | undefined,\n config: { accountId: string; zoneId: string }\n): boolean {\n return (\n (meta?.accountId !== undefined && meta.accountId !== config.accountId) ||\n (meta?.zoneId !== undefined && meta.zoneId !== config.zoneId)\n );\n}\n\nexport function namedTunnelInfoFromMeta(\n port: number,\n meta: TunnelMetaEntry | undefined\n): NamedTunnelInfo | undefined {\n if (!meta?.needsRespawn || !meta.tunnelId || !meta.name || !meta.hostname) {\n return undefined;\n }\n return {\n id: meta.tunnelId,\n port,\n name: meta.name,\n hostname: meta.hostname,\n url: `https://${meta.hostname}`,\n createdAt: new Date().toISOString()\n };\n}\n\nexport function createNamedTunnelCleanupEntry(\n info: TunnelInfo,\n meta: TunnelMetaEntry | undefined\n): TunnelCleanupEntry | undefined {\n if (!info.name || !meta?.dnsRecordId) return undefined;\n return {\n tunnelId: info.id,\n port: info.port,\n name: info.name,\n hostname: info.hostname,\n dnsRecordId: meta.dnsRecordId,\n accountId: meta.accountId,\n zoneId: meta.zoneId,\n phase: 'claimed',\n updatedAt: new Date().toISOString()\n };\n}\n\nexport function createNamedTunnelResourceIntent(args: {\n port: number;\n name: string;\n hostname: string;\n tunnelName: string;\n sandboxId: string;\n accountId: string;\n zoneId: string;\n}): TunnelCleanupEntry {\n return {\n port: args.port,\n name: args.name,\n hostname: args.hostname,\n tunnelName: args.tunnelName,\n sandboxId: args.sandboxId,\n accountId: args.accountId,\n zoneId: args.zoneId,\n phase: 'planned',\n updatedAt: new Date().toISOString()\n };\n}\n\nexport function namedRespawnMeta(\n info: NamedTunnelInfo,\n existing?: TunnelMetaEntry\n): TunnelMetaEntry {\n return {\n optionsHash:\n existing?.optionsHash ?? computeOptionsHash({ name: info.name }),\n dnsRecordId: existing?.dnsRecordId,\n accountId: existing?.accountId,\n zoneId: existing?.zoneId,\n tunnelId: info.id,\n name: info.name,\n hostname: info.hostname,\n needsRespawn: true\n };\n}\n\nexport function markCleanupTunnelReady(\n entry: TunnelCleanupEntry,\n tunnelId: string\n): TunnelCleanupEntry {\n return {\n ...entry,\n tunnelId,\n phase: 'tunnel_ready',\n updatedAt: new Date().toISOString()\n };\n}\n\nexport function markCleanupDNSReady(\n entry: TunnelCleanupEntry,\n dnsRecordId: string\n): TunnelCleanupEntry {\n return {\n ...entry,\n dnsRecordId,\n phase: 'claimed',\n updatedAt: new Date().toISOString()\n };\n}\n","import type { Logger } from '@repo/shared';\nimport {\n deleteDNSRecord,\n deleteTunnel,\n findCNAME,\n findTunnelByName\n} from './cloudflare-api';\nimport {\n CLEANUP_STORAGE_KEY,\n readCleanupMap,\n type TunnelCleanupEntry,\n type TunnelsStorage\n} from './storage';\n\nexport interface NamedTunnelCleanupHost {\n logger: Logger;\n getNamedTunnelConfig?: () => Promise<{\n token: string;\n accountId: string;\n zoneId: string;\n }>;\n fetcher?: typeof fetch;\n}\n\nexport async function cleanupNamedTunnelResources(\n host: NamedTunnelCleanupHost,\n entry: TunnelCleanupEntry,\n options?: {\n logPrefix?: string;\n credentialsUnavailableMessage?: string;\n onResolved?: (entry: TunnelCleanupEntry) => Promise<void>;\n }\n): Promise<boolean> {\n const logPrefix = options?.logPrefix ?? 'tunnel.cleanup';\n if (!host.getNamedTunnelConfig) {\n host.logger.warn(\n options?.credentialsUnavailableMessage ??\n `${logPrefix}: credentials unavailable`,\n {\n port: entry.port,\n tunnelId: entry.tunnelId,\n dnsRecordId: entry.dnsRecordId,\n error: 'Named tunnel configuration resolver is unavailable'\n }\n );\n return false;\n }\n\n let config: { token: string; accountId: string; zoneId: string };\n try {\n config = await host.getNamedTunnelConfig();\n } catch (err) {\n host.logger.warn(\n options?.credentialsUnavailableMessage ??\n `${logPrefix}: credentials unavailable`,\n {\n port: entry.port,\n tunnelId: entry.tunnelId,\n dnsRecordId: entry.dnsRecordId,\n error: err instanceof Error ? err.message : String(err)\n }\n );\n return false;\n }\n\n const accountId = entry.accountId ?? config.accountId;\n const zoneId = entry.zoneId ?? config.zoneId;\n const fetcher = host.fetcher;\n let tunnelId = entry.tunnelId;\n if (!tunnelId && entry.tunnelName && entry.sandboxId) {\n const tunnel = await findTunnelByName({\n token: config.token,\n accountId,\n tunnelName: entry.tunnelName,\n expectedSandboxId: entry.sandboxId,\n fetcher\n });\n tunnelId = tunnel?.id;\n }\n\n let dnsRecordId = entry.dnsRecordId;\n if (!dnsRecordId && tunnelId) {\n const dnsRecord = await findCNAME({\n token: config.token,\n zoneId,\n hostname: entry.hostname,\n cnameTarget: `${tunnelId}.cfargotunnel.com`,\n fetcher\n });\n dnsRecordId = dnsRecord?.recordId;\n }\n\n if (tunnelId || dnsRecordId) {\n await options?.onResolved?.({\n ...entry,\n ...(tunnelId && { tunnelId }),\n ...(dnsRecordId && { dnsRecordId }),\n phase: dnsRecordId ? 'claimed' : 'tunnel_ready',\n updatedAt: new Date().toISOString()\n });\n }\n\n if (!tunnelId && !dnsRecordId) return true;\n\n let cleanupFailed = false;\n await Promise.allSettled([\n dnsRecordId\n ? deleteDNSRecord({\n token: config.token,\n zoneId,\n recordId: dnsRecordId,\n fetcher\n }).catch((err) => {\n cleanupFailed = true;\n host.logger.warn(`${logPrefix}: dns delete failed`, {\n port: entry.port,\n tunnelId,\n recordId: dnsRecordId,\n zoneId,\n error: err instanceof Error ? err.message : String(err)\n });\n })\n : Promise.resolve(),\n tunnelId\n ? deleteTunnel({\n token: config.token,\n accountId,\n tunnelId,\n fetcher\n }).catch((err) => {\n cleanupFailed = true;\n host.logger.warn(`${logPrefix}: tunnel delete failed`, {\n port: entry.port,\n tunnelId,\n accountId,\n error: err instanceof Error ? err.message : String(err)\n });\n })\n : Promise.resolve()\n ]);\n\n return !cleanupFailed;\n}\n\nexport async function resumeNamedTunnelCleanupEntry(\n host: NamedTunnelCleanupHost,\n storage: TunnelsStorage,\n portKey: string,\n entry: TunnelCleanupEntry,\n options?: {\n logPrefix?: string;\n credentialsUnavailableMessage?: string;\n onResolved?: (entry: TunnelCleanupEntry) => Promise<void>;\n }\n): Promise<boolean> {\n if (\n !(await cleanupNamedTunnelResources(host, entry, {\n ...options,\n onResolved: async (resolved) => {\n await storage.transaction(async (txn) => {\n const cleanup = await readCleanupMap(txn);\n cleanup[portKey] = resolved;\n await txn.put(CLEANUP_STORAGE_KEY, cleanup);\n });\n await options?.onResolved?.(resolved);\n }\n }))\n ) {\n return false;\n }\n\n await storage.transaction(async (txn) => {\n const cleanup = await readCleanupMap(txn);\n delete cleanup[portKey];\n await txn.put(CLEANUP_STORAGE_KEY, cleanup);\n });\n return true;\n}\n\nexport async function resumeNamedTunnelCleanupRecords(\n host: NamedTunnelCleanupHost,\n storage: TunnelsStorage\n): Promise<void> {\n const cleanup = await readCleanupMap(storage);\n for (const [portKey, entry] of Object.entries(cleanup)) {\n try {\n await resumeNamedTunnelCleanupEntry(host, storage, portKey, entry);\n } catch (err) {\n host.logger.warn('tunnels.resumeCleanup: cleanup record failed', {\n port: entry.port,\n tunnelId: entry.tunnelId,\n error: err instanceof Error ? err.message : String(err)\n });\n }\n }\n}\n","import { ErrorCode, OperationInterruptedError } from '../errors';\n\nexport function createTunnelInterruptedError(params: {\n reason: 'runtime_replaced' | 'sandbox_lifetime_changed';\n phase: string;\n admitted: true | 'unknown';\n retryable: boolean;\n message: string;\n}): OperationInterruptedError {\n return new OperationInterruptedError({\n message: params.message,\n code: ErrorCode.OPERATION_INTERRUPTED,\n httpStatus: 409,\n context: {\n reason: params.reason,\n operation: 'tunnel.get',\n phase: params.phase,\n admitted: params.admitted,\n retryable: params.retryable\n },\n timestamp: new Date().toISOString(),\n suggestion: 'Retry tunnels.get() with the same port and options.'\n });\n}\n","// Crockford base32 alphabet (lowercase), excluding i, l, o, u to avoid\n// visual ambiguity. 32 symbols means each character carries exactly 5 bits\n// of entropy drawn uniformly from crypto.getRandomValues, and the alphabet\n// is safe to use as a DNS label or URL path segment.\nconst ID_ALPHABET = '0123456789abcdefghjkmnpqrstvwxyz';\n\n/**\n * Generate a URL- and DNS-safe random id. The default 20 characters give\n * ~100 bits of entropy, which keeps collisions negligible at any realistic\n * volume without a uniqueness retry loop. 32 divides 256 evenly, so masking\n * the low 5 bits of each random byte stays uniform.\n */\nexport function randomId(size = 20): string {\n const bytes = new Uint8Array(size);\n crypto.getRandomValues(bytes);\n let id = '';\n for (let i = 0; i < size; i += 1) {\n id += ID_ALPHABET[bytes[i] & 31];\n }\n return id;\n}\n","import type {\n EnsureTunnelRunRequest,\n EnsureTunnelRunResult,\n NamedTunnelInfo,\n QuickTunnelInfo,\n SandboxTunnelsAPI\n} from '@repo/shared';\nimport {\n createTunnel,\n findTunnelByName,\n getTunnelToken,\n getZoneName,\n upsertCNAME\n} from './cloudflare-api';\nimport { randomId } from './random-id';\nimport {\n computeOptionsHash,\n createNamedTunnelResourceIntent,\n type TunnelCleanupEntry,\n type TunnelMetaEntry\n} from './storage';\n\nexport interface TunnelProvisionerHost {\n sandboxId?: string;\n getNamedTunnelConfig?: () => Promise<{\n token: string;\n accountId: string;\n zoneId: string;\n }>;\n fetcher?: typeof fetch;\n}\n\nexport interface PreparedNamedTunnel {\n tunnelId: string;\n tunnelToken: string;\n info: NamedTunnelInfo;\n meta: TunnelMetaEntry;\n}\n\nexport interface NamedTunnelPreparationHooks {\n onIntentReady?: (entry: TunnelCleanupEntry) => Promise<void>;\n onTunnelReady?: (tunnelId: string) => Promise<void>;\n onDNSReady?: (dnsRecordId: string) => Promise<void>;\n}\n\nfunction createQuickTunnelId(): string {\n return `quick-${randomId()}`;\n}\n\nexport class TunnelProvisioner {\n readonly #host: TunnelProvisionerHost;\n #zoneNamePromise: Promise<string> | null = null;\n\n constructor(host: TunnelProvisionerHost) {\n this.#host = host;\n }\n\n async provisionQuickTunnel(\n tunnels: SandboxTunnelsAPI,\n port: number,\n tunnelRunId: string,\n tunnelId = createQuickTunnelId()\n ): Promise<QuickTunnelInfo> {\n const result = await this.#ensureTunnelRun(tunnels, {\n mode: 'quick',\n tunnelId,\n runId: tunnelRunId,\n port\n });\n if (result.run.mode !== 'quick') {\n throw new Error('Container returned a non-quick tunnel run');\n }\n return {\n id: result.run.tunnelId,\n port: result.run.port,\n url: result.run.url,\n hostname: result.run.hostname,\n createdAt: result.run.startedAt\n };\n }\n\n async prepareNamedTunnel(\n port: number,\n name: string,\n hooks: NamedTunnelPreparationHooks = {}\n ): Promise<PreparedNamedTunnel> {\n if (!this.#host.sandboxId) {\n throw new Error(\n 'Named tunnels require host.sandboxId on the tunnel service.'\n );\n }\n if (!this.#host.getNamedTunnelConfig) {\n throw new Error(\n 'Named tunnels require host.getNamedTunnelConfig on the tunnel service.'\n );\n }\n\n const config = await this.#host.getNamedTunnelConfig();\n const zoneName = await this.#getZoneName({\n token: config.token,\n zoneId: config.zoneId\n });\n const hostname = `${name}.${zoneName}`;\n const sandboxId = this.#host.sandboxId;\n const tunnelName = `sandbox-${sandboxId}-${name}`;\n await hooks.onIntentReady?.(\n createNamedTunnelResourceIntent({\n port,\n name,\n hostname,\n tunnelName,\n sandboxId,\n accountId: config.accountId,\n zoneId: config.zoneId\n })\n );\n\n let tunnelId: string;\n let tunnelToken: string;\n const existingTunnel = await findTunnelByName({\n token: config.token,\n accountId: config.accountId,\n tunnelName,\n expectedSandboxId: sandboxId,\n fetcher: this.#host.fetcher\n });\n if (existingTunnel) {\n tunnelId = existingTunnel.id;\n tunnelToken = await getTunnelToken({\n token: config.token,\n accountId: config.accountId,\n tunnelId,\n fetcher: this.#host.fetcher\n });\n } else {\n const created = await createTunnel({\n token: config.token,\n accountId: config.accountId,\n tunnelName,\n metadata: {\n sandboxId,\n createdBy: 'sandbox-sdk',\n name,\n port\n },\n fetcher: this.#host.fetcher\n });\n tunnelId = created.id;\n tunnelToken = created.token;\n }\n await hooks.onTunnelReady?.(tunnelId);\n\n const dnsResult = await upsertCNAME({\n token: config.token,\n zoneId: config.zoneId,\n hostname,\n cnameTarget: `${tunnelId}.cfargotunnel.com`,\n comment: `sandbox-${sandboxId}`,\n sandboxId,\n fetcher: this.#host.fetcher\n });\n await hooks.onDNSReady?.(dnsResult.recordId);\n\n const info: NamedTunnelInfo = {\n id: tunnelId,\n port,\n name,\n hostname,\n url: `https://${hostname}`,\n createdAt: new Date().toISOString()\n };\n\n return {\n tunnelId,\n tunnelToken,\n info,\n meta: {\n optionsHash: computeOptionsHash({ name }),\n dnsRecordId: dnsResult.recordId,\n accountId: config.accountId,\n zoneId: config.zoneId,\n tunnelId,\n name,\n hostname\n }\n };\n }\n\n async startNamedTunnelRun(\n tunnels: SandboxTunnelsAPI,\n prepared: PreparedNamedTunnel,\n tunnelRunId: string\n ): Promise<void> {\n const result = await this.#ensureTunnelRun(tunnels, {\n mode: 'named',\n tunnelId: prepared.tunnelId,\n runId: tunnelRunId,\n port: prepared.info.port,\n cloudflaredToken: prepared.tunnelToken\n });\n if (result.run.mode !== 'named') {\n throw new Error('Container returned a non-named tunnel run');\n }\n }\n\n clearCachedZoneName(): void {\n this.#zoneNamePromise = null;\n }\n\n async #ensureTunnelRun(\n tunnels: SandboxTunnelsAPI,\n request: EnsureTunnelRunRequest\n ): Promise<EnsureTunnelRunResult> {\n return await tunnels.ensureTunnelRun(request);\n }\n\n async #getZoneName(config: {\n token: string;\n zoneId: string;\n }): Promise<string> {\n if (!this.#zoneNamePromise) {\n const pending = getZoneName({\n token: config.token,\n zoneId: config.zoneId,\n fetcher: this.#host.fetcher\n });\n this.#zoneNamePromise = pending;\n pending.catch(() => {\n if (this.#zoneNamePromise === pending) {\n this.#zoneNamePromise = null;\n }\n });\n }\n return this.#zoneNamePromise;\n }\n}\n","import {\n META_STORAGE_KEY,\n namedRespawnMeta,\n readMap,\n readMetaMap,\n STORAGE_KEY,\n type TunnelMap,\n type TunnelMetaEntry,\n type TunnelMetaMap,\n type TunnelsStorage\n} from './storage';\n\n/**\n * Reconcile storage with a fresh container.\n *\n * Called after a proven runtime replacement or stop. The old runtime's\n * `cloudflared` processes no longer back any stored tunnel record.\n *\n * Quick tunnels are dropped because the `*.trycloudflare.com` URL is bound\n * to the dead process. Named tunnels keep private metadata so a later\n * `get(port, { name })` can respawn cloudflared against the existing\n * Cloudflare tunnel and DNS record.\n */\nfunction isHiddenNamedRespawnMeta(\n entry: TunnelMetaEntry | undefined\n): entry is TunnelMetaEntry & {\n needsRespawn: true;\n tunnelId: string;\n name: string;\n hostname: string;\n} {\n return (\n entry?.needsRespawn === true &&\n typeof entry.tunnelId === 'string' &&\n typeof entry.name === 'string' &&\n typeof entry.hostname === 'string'\n );\n}\n\nexport async function pruneTunnelsForRestart(\n storage: TunnelsStorage\n): Promise<void> {\n await storage.transaction(async (txn) => {\n const map = await readMap(txn);\n const meta = await readMetaMap(txn);\n const nextMap: TunnelMap = {};\n const nextMeta: TunnelMetaMap = {};\n for (const [portKey, info] of Object.entries(map)) {\n // Discriminate by the public `name` field on `TunnelInfo`: named\n // tunnels carry the user-provided label, quick tunnels omit it.\n if (info.name) {\n nextMeta[portKey] = namedRespawnMeta(info, meta[portKey]);\n }\n }\n for (const [portKey, entry] of Object.entries(meta)) {\n if (nextMeta[portKey] || !isHiddenNamedRespawnMeta(entry)) continue;\n nextMeta[portKey] = entry;\n }\n await txn.put(STORAGE_KEY, nextMap);\n await txn.put(META_STORAGE_KEY, nextMeta);\n });\n}\n","/**\n * Tunnel domain service. Owns public tunnel operations, runtime restart\n * reconciliation, process-exit handling, and durable named-resource cleanup.\n */\n\nimport type {\n Logger,\n NamedTunnelInfo,\n QuickTunnelInfo,\n SandboxTunnelsAPI,\n TunnelInfo,\n TunnelOptions\n} from '@repo/shared';\nimport { logCanonicalEvent } from '@repo/shared';\nimport {\n OperationInterruptedError,\n RuntimeControlProtocolError\n} from '../errors';\nimport {\n RuntimeIdentity,\n type RuntimeLease,\n type RuntimeRecordStorage\n} from '../runtime';\nimport { RuntimeIdentityInactiveError } from '../runtime/types';\nimport type {\n CurrentSandboxLifetime,\n SandboxLifetime\n} from '../sandbox-lifetime';\nimport { SandboxLifetimeChangedError } from '../sandbox-lifetime';\nimport {\n SandboxSecurityError,\n validatePort,\n validateTunnelName\n} from '../security';\nimport {\n cleanupNamedTunnelResources,\n resumeNamedTunnelCleanupEntry,\n resumeNamedTunnelCleanupRecords\n} from './cleanup';\nimport { createTunnelInterruptedError } from './lifecycle';\nimport { TunnelProvisioner } from './provisioner';\nimport { randomId } from './random-id';\nimport { pruneTunnelsForRestart } from './restart';\nimport type {\n TunnelCleanupEntry,\n TunnelMetaEntry,\n TunnelsStorage\n} from './storage';\nimport {\n CLEANUP_STORAGE_KEY,\n computeOptionsHash,\n createNamedTunnelCleanupEntry,\n effectiveOptionsHash,\n META_STORAGE_KEY,\n markCleanupDNSReady,\n markCleanupTunnelReady,\n namedRespawnMeta,\n namedTunnelInfoFromMeta,\n optionsHashesEqual,\n readMap,\n readMetaMap,\n readPortState,\n STORAGE_KEY,\n tunnelConfigChanged,\n updatePortState\n} from './storage';\n\nexport type { TunnelsStorage, TunnelsStorageTxn } from './storage';\n\n/** Subset of the Sandbox DO the service reads from. */\nexport type TunnelProvisionLease = Pick<RuntimeLease, 'runtime' | 'retain'> & {\n tunnels: SandboxTunnelsAPI;\n};\n\nexport type TunnelExistingRuntimeCall = <T>(\n runtime: RuntimeIdentity,\n operation: string,\n call: (tunnels: SandboxTunnelsAPI) => Promise<T>\n) => Promise<T | null>;\n\nexport interface TunnelServiceHost {\n runProvision<T>(\n call: (lease: TunnelProvisionLease) => Promise<T>\n ): Promise<T>;\n runExisting: TunnelExistingRuntimeCall;\n getStoredRuntime(\n storage?: RuntimeRecordStorage\n ): Promise<RuntimeIdentity | null>;\n storage: TunnelsStorage;\n logger: Logger;\n /**\n * Sandbox identifier used for tagging Cloudflare resources\n * (`metadata.sandboxId` on tunnels, `comment: 'sandbox-<id>'` on DNS).\n * Required only when callers exercise `get(port, { name })`; quick\n * tunnels do not touch the Cloudflare API.\n */\n sandboxId?: string;\n /**\n * Lazy provider of the three credentials needed for named-tunnel\n * provisioning. The service calls it when validating named cache\n * entries and when preparing named tunnel resources.\n *\n * Throws (via the underlying resolver) when any required value is\n * missing or unresolvable. The service surfaces that error verbatim.\n */\n getNamedTunnelConfig?: () => Promise<{\n token: string;\n accountId: string;\n zoneId: string;\n }>;\n /**\n * Override the global `fetch` used for Cloudflare API calls. Defaults\n * to the global `fetch`. Tests inject a mock here.\n */\n fetcher?: typeof fetch;\n /** Sandbox lifetime fence for operations that must not cross destroy(). */\n currentLifetime?: Pick<\n CurrentSandboxLifetime,\n 'getOrCreate' | 'assertCurrent'\n >;\n}\n\nexport interface TunnelsHandler {\n get(port: number, options?: TunnelOptions): Promise<TunnelInfo>;\n list(): Promise<TunnelInfo[]>;\n destroy(portOrInfo: number | TunnelInfo): Promise<void>;\n}\n\n/**\n * Container-driven exit hook. Invoked by `SandboxControlCallbackImpl`\n * when the container reports that a `cloudflared` process has\n * exited. NOT part of the public `TunnelsHandler` interface —\n * exposed only through the factory's return shape so the public\n * `sandbox.tunnels` API stays narrow.\n */\nexport type TunnelExitHandler = (\n id: string,\n port: number,\n exitCode: number | null,\n tunnelRunId: string,\n runtime: RuntimeIdentity,\n isSessionCurrent: () => boolean\n) => Promise<void>;\n\nexport interface TunnelsHandle {\n tunnels: TunnelsHandler;\n handleTunnelExit: TunnelExitHandler;\n /**\n * Tear down every tunnel currently stored. Called by the Sandbox DO's\n * `destroy()` so the Cloudflare-side resources don't outlive the\n * sandbox that provisioned them.\n *\n * Best-effort: a failure on one port is logged but doesn't abort the\n * rest. NOT part of the public `TunnelsHandler` surface — users don't\n * call this; they call `destroy(port)` for an individual tunnel.\n */\n destroyAll: () => Promise<void>;\n /** Stop stored container-side tunnel runs without mutating durable state. */\n destroyAllRuntimeRuns: () => Promise<void>;\n /** Resume retained Cloudflare-side cleanup records. Internal lifecycle hook. */\n resumeCleanup: () => Promise<void>;\n /** Reconcile durable tunnel state after the container runtime stops. */\n onRuntimeStop: () => Promise<void>;\n /** Clear public tunnel state after sandbox destroy processing completes. */\n clearDurableStateAfterDestroy: () => Promise<void>;\n}\n\n/** Per-port serializer shared by public operations and exit hooks. */\ntype WithPortLock = <T>(port: number, fn: () => Promise<T>) => Promise<T>;\n\ntype TunnelGetCacheState = 'hit' | 'miss';\n\ninterface TunnelGetResult {\n info: TunnelInfo;\n cacheState: TunnelGetCacheState;\n}\n\nfunction validateTunnelPort(port: number): void {\n if (!validatePort(port)) {\n throw new SandboxSecurityError(\n `Invalid port number: ${port}. Must be 1024-65535, excluding reserved ports.`\n );\n }\n}\n\nfunction tunnelMetaOwnsRuntime(\n meta: TunnelMetaEntry | undefined,\n runtime: RuntimeIdentity | null\n): boolean {\n return Boolean(\n runtime &&\n meta?.runtimeIdentityID === runtime.id &&\n meta.runtimeIncarnationID === runtime.runtimeIncarnationID\n );\n}\n\nfunction tunnelOwningRuntime(\n meta: TunnelMetaEntry | undefined\n): RuntimeIdentity | null {\n if (!meta?.runtimeIdentityID || !meta.runtimeIncarnationID) return null;\n return new RuntimeIdentity({\n id: meta.runtimeIdentityID,\n runtimeIncarnationID: meta.runtimeIncarnationID\n });\n}\n\n/**\n * Match a structured SandboxError code anywhere on the error — translated\n * SandboxErrors expose the code both as a top-level `code` field and on\n * the nested `errorResponse.code`. Used for SDK-recognized container\n * errors that have operation-specific recovery behavior.\n *\n * Structured matching keeps quoted code tokens in human-readable\n * messages from being treated as machine-readable error codes.\n */\nfunction hasErrorCode(error: unknown, code: string): boolean {\n if (!error || typeof error !== 'object') return false;\n const e = error as {\n code?: unknown;\n errorResponse?: { code?: unknown };\n };\n if (e.code === code) return true;\n if (e.errorResponse?.code === code) return true;\n return false;\n}\n\nfunction isTunnelNotFoundError(error: unknown): boolean {\n return hasErrorCode(error, 'TUNNEL_NOT_FOUND');\n}\n\nfunction isRuntimeActivationMismatch(error: unknown): boolean {\n return (\n error instanceof RuntimeControlProtocolError &&\n error.context.reason === 'activation-mismatch'\n );\n}\n\ninterface TunnelGetRecoveryState {\n quickRun?: {\n tunnelId: string;\n runId: string;\n };\n}\n\nfunction createTunnelRunId(): string {\n return `run-${randomId()}`;\n}\n\n/** Tunnel domain façade for public operations and lifecycle hooks. */\nexport class TunnelService implements TunnelsHandler {\n readonly #host: TunnelServiceHost;\n readonly #portLocks = new Map<number, Promise<unknown>>();\n readonly #provisioner: TunnelProvisioner;\n\n constructor(host: TunnelServiceHost) {\n this.#host = host;\n this.#provisioner = new TunnelProvisioner(host);\n }\n\n #withPortLock: WithPortLock = <T>(\n port: number,\n fn: () => Promise<T>\n ): Promise<T> => {\n const previous = this.#portLocks.get(port) ?? Promise.resolve();\n const next = previous.then(fn, fn);\n this.#portLocks.set(\n port,\n next.catch(() => undefined)\n );\n return next;\n };\n\n async get(port: number, options?: TunnelOptions): Promise<TunnelInfo> {\n const startTime = Date.now();\n let outcome: 'success' | 'error' = 'error';\n let cacheState: TunnelGetCacheState = 'miss';\n let caughtError: Error | undefined;\n try {\n validateTunnelPort(port);\n if (options?.name !== undefined) validateTunnelName(options.name);\n const requestedHash = computeOptionsHash(options);\n\n const recovery: TunnelGetRecoveryState = {};\n const result = await this.#withPortLock(port, () =>\n this.#getLocked(port, options, requestedHash, recovery)\n );\n cacheState = result.cacheState;\n outcome = 'success';\n return result.info;\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n if (caughtError instanceof OperationInterruptedError) {\n // Ensure retryable is false since we do not recover\n caughtError.context.retryable = false;\n }\n throw caughtError;\n } finally {\n logCanonicalEvent(this.#host.logger, {\n event: 'tunnel.get',\n outcome,\n port,\n cacheState,\n durationMs: Date.now() - startTime,\n error: caughtError\n });\n }\n }\n\n async #getLocked(\n port: number,\n options: TunnelOptions | undefined,\n requestedHash: string,\n recovery: TunnelGetRecoveryState\n ): Promise<TunnelGetResult> {\n const state = await readPortState(this.#host.storage, port);\n const existing = state.info;\n const metaEntry = state.meta;\n\n if (existing) {\n this.#assertSameOptions(\n port,\n effectiveOptionsHash(existing, metaEntry),\n requestedHash\n );\n\n if (metaEntry?.needsRespawn && existing.name) {\n return {\n info: await this.#provisionNamedTunnel(port, existing.name),\n cacheState: 'miss'\n };\n }\n\n if (existing.name && this.#host.getNamedTunnelConfig) {\n const currentConfig = await this.#host.getNamedTunnelConfig();\n if (tunnelConfigChanged(metaEntry, currentConfig)) {\n this.#provisioner.clearCachedZoneName();\n return {\n info: await this.#provisionNamedTunnel(port, existing.name),\n cacheState: 'miss'\n };\n }\n }\n\n const currentRuntime = await this.#host.getStoredRuntime();\n if (\n !tunnelMetaOwnsRuntime(metaEntry, currentRuntime) ||\n !(await this.#validateCachedRuntime(currentRuntime))\n ) {\n return {\n info: existing.name\n ? await this.#provisionNamedTunnel(port, existing.name)\n : await this.#provisionQuickTunnel(port, recovery),\n cacheState: 'miss'\n };\n }\n\n return { info: existing, cacheState: 'hit' };\n }\n\n if (metaEntry?.needsRespawn) {\n this.#assertSameOptions(port, metaEntry.optionsHash, requestedHash);\n }\n\n return {\n info: options?.name\n ? await this.#provisionNamedTunnel(port, options.name)\n : await this.#provisionQuickTunnel(port, recovery),\n cacheState: 'miss'\n };\n }\n\n async #validateCachedRuntime(\n runtime: RuntimeIdentity | null\n ): Promise<boolean> {\n if (!runtime) return false;\n try {\n return (\n (await this.#host.runExisting(\n runtime,\n 'tunnel.lookup',\n async () => true\n )) === true\n );\n } catch (error) {\n if (\n error instanceof OperationInterruptedError ||\n (error instanceof RuntimeControlProtocolError &&\n error.context.reason === 'activation-mismatch')\n ) {\n return false;\n }\n throw error;\n }\n }\n\n #assertSameOptions(\n port: number,\n existingHash: string,\n requestedHash: string\n ): void {\n if (optionsHashesEqual(existingHash, requestedHash)) return;\n throw new Error(\n `Tunnel on port ${port} was created with different options. ` +\n `Call destroy(${port}) before changing tunnel options.`\n );\n }\n\n async #resumePortCleanup(port: number): Promise<void> {\n const entry = (await readPortState(this.#host.storage, port)).cleanup;\n if (!entry) return;\n\n if (\n !(await resumeNamedTunnelCleanupEntry(\n this.#host,\n this.#host.storage,\n port.toString(),\n entry\n ))\n ) {\n throw new Error(\n `Pending named tunnel cleanup for port ${port} could not complete.`\n );\n }\n }\n\n async #updatePortCleanup(\n port: number,\n runtime: RuntimeIdentity,\n next: (entry: TunnelCleanupEntry | undefined) => TunnelCleanupEntry,\n isInterrupted: () => boolean = () => false\n ): Promise<void> {\n await updatePortState(this.#host.storage, port, async (state, txn) => {\n await this.#assertStoredRuntime(txn, runtime, isInterrupted);\n return { cleanup: next(state.cleanup) };\n });\n }\n\n #requireCleanupEntry(\n port: number,\n entry: TunnelCleanupEntry | undefined\n ): TunnelCleanupEntry {\n if (entry) return entry;\n throw new Error(`Missing named tunnel cleanup intent for port ${port}`);\n }\n\n /** Provision a fresh quick tunnel and persist it. Caller holds the per-port lock. */\n async #provisionQuickTunnel(\n port: number,\n recovery: TunnelGetRecoveryState\n ): Promise<QuickTunnelInfo> {\n const lifetime = await this.#host.currentLifetime?.getOrCreate();\n recovery.quickRun ??= {\n tunnelId: `quick-${randomId()}`,\n runId: createTunnelRunId()\n };\n const { tunnelId, runId } = recovery.quickRun;\n\n return await this.#host.runProvision(async (lease) => {\n let interrupted = false;\n let committedInfo: TunnelInfo | undefined;\n const hold = lease.retain(() => {\n interrupted = true;\n });\n const isInterrupted = () => interrupted;\n try {\n await this.#assertStoredRuntime(\n undefined,\n lease.runtime,\n isInterrupted\n );\n await this.#assertSandboxLifetime(lifetime, 'starting');\n const spawned = await this.#provisioner.provisionQuickTunnel(\n lease.tunnels,\n port,\n runId,\n tunnelId\n );\n await this.#assertSandboxLifetime(lifetime, 'process_ready');\n await updatePortState(this.#host.storage, port, async (_state, txn) => {\n await this.#assertStoredRuntime(txn, lease.runtime, isInterrupted);\n return {\n info: spawned,\n meta: {\n optionsHash: 'v1:quick',\n runtimeIdentityID: lease.runtime.id,\n runtimeIncarnationID: lease.runtime.runtimeIncarnationID,\n ...(lifetime && { sandboxLifetimeID: lifetime.id }),\n tunnelRunId: runId\n }\n };\n });\n committedInfo = spawned;\n await this.#assertStoredRuntime(\n undefined,\n lease.runtime,\n isInterrupted\n );\n await this.#assertSandboxLifetime(lifetime, 'committing');\n return spawned;\n } catch (error) {\n if (committedInfo) {\n await this.#invalidateCommittedPortIfUnchanged(\n port,\n committedInfo,\n lease.runtime,\n runId\n );\n }\n throw error;\n } finally {\n hold.release();\n }\n });\n }\n\n async #provisionNamedTunnel(\n port: number,\n name: string\n ): Promise<NamedTunnelInfo> {\n const lifetime = await this.#host.currentLifetime?.getOrCreate();\n\n return await this.#host.runProvision(async (lease) => {\n let interrupted = false;\n let committedInfo: TunnelInfo | undefined;\n let committedRunID: string | undefined;\n const hold = lease.retain(() => {\n interrupted = true;\n });\n const isInterrupted = () => interrupted;\n try {\n await this.#assertStoredRuntime(\n undefined,\n lease.runtime,\n isInterrupted\n );\n await this.#assertSandboxLifetime(lifetime, 'starting');\n await this.#resumePortCleanup(port);\n await this.#assertStoredRuntime(\n undefined,\n lease.runtime,\n isInterrupted\n );\n\n const prepared = await this.#provisioner.prepareNamedTunnel(\n port,\n name,\n {\n onIntentReady: (entry) =>\n this.#updatePortCleanup(\n port,\n lease.runtime,\n () => entry,\n isInterrupted\n ),\n onTunnelReady: (tunnelID) =>\n this.#updatePortCleanup(\n port,\n lease.runtime,\n (entry) =>\n markCleanupTunnelReady(\n this.#requireCleanupEntry(port, entry),\n tunnelID\n ),\n isInterrupted\n ),\n onDNSReady: (dnsRecordID) =>\n this.#updatePortCleanup(\n port,\n lease.runtime,\n (entry) =>\n markCleanupDNSReady(\n this.#requireCleanupEntry(port, entry),\n dnsRecordID\n ),\n isInterrupted\n )\n }\n );\n const cleanupEntry = createNamedTunnelCleanupEntry(\n prepared.info,\n prepared.meta\n );\n if (cleanupEntry) {\n await this.#updatePortCleanup(\n port,\n lease.runtime,\n () => cleanupEntry,\n isInterrupted\n );\n }\n await this.#assertSandboxLifetime(lifetime, 'cloudflare_ready');\n\n const tunnelRunID = createTunnelRunId();\n await this.#provisioner.startNamedTunnelRun(\n lease.tunnels,\n prepared,\n tunnelRunID\n );\n await this.#assertSandboxLifetime(lifetime, 'process_ready');\n\n await updatePortState(this.#host.storage, port, async (_state, txn) => {\n await this.#assertStoredRuntime(txn, lease.runtime, isInterrupted);\n return {\n info: prepared.info,\n meta: {\n ...prepared.meta,\n runtimeIdentityID: lease.runtime.id,\n runtimeIncarnationID: lease.runtime.runtimeIncarnationID,\n ...(lifetime && { sandboxLifetimeID: lifetime.id }),\n tunnelRunId: tunnelRunID\n },\n cleanup: undefined\n };\n });\n committedInfo = prepared.info;\n committedRunID = tunnelRunID;\n await this.#assertStoredRuntime(\n undefined,\n lease.runtime,\n isInterrupted\n );\n await this.#assertSandboxLifetime(lifetime, 'committing');\n return prepared.info;\n } catch (error) {\n if (committedInfo && committedRunID) {\n await this.#invalidateCommittedPortIfUnchanged(\n port,\n committedInfo,\n lease.runtime,\n committedRunID\n );\n }\n throw error;\n } finally {\n hold.release();\n }\n });\n }\n\n async #invalidateCommittedPortIfUnchanged(\n port: number,\n expectedInfo: TunnelInfo,\n runtime: RuntimeIdentity,\n runID: string\n ): Promise<void> {\n await updatePortState(this.#host.storage, port, (state) => {\n if (\n state.info?.id !== expectedInfo.id ||\n state.meta?.runtimeIdentityID !== runtime.id ||\n state.meta.runtimeIncarnationID !== runtime.runtimeIncarnationID ||\n state.meta.tunnelRunId !== runID\n ) {\n return undefined;\n }\n if (state.info.name) {\n return {\n info: undefined,\n meta: namedRespawnMeta(state.info, state.meta)\n };\n }\n return { info: undefined, meta: undefined };\n });\n }\n\n async #assertStoredRuntime(\n storage: Parameters<TunnelServiceHost['getStoredRuntime']>[0],\n expected: RuntimeIdentity,\n isInterrupted: () => boolean = () => false\n ): Promise<void> {\n if (isInterrupted()) throw new RuntimeIdentityInactiveError();\n const current = await this.#host.getStoredRuntime(storage);\n if (\n isInterrupted() ||\n !current ||\n current.id !== expected.id ||\n current.runtimeIncarnationID !== expected.runtimeIncarnationID\n ) {\n throw new RuntimeIdentityInactiveError();\n }\n }\n\n async #assertSandboxLifetime(\n lifetime: SandboxLifetime | undefined,\n phase: string\n ): Promise<void> {\n if (!lifetime || !this.#host.currentLifetime) return;\n try {\n await this.#host.currentLifetime.assertCurrent(lifetime);\n } catch (error) {\n if (error instanceof SandboxLifetimeChangedError) {\n throw createTunnelInterruptedError({\n reason: 'sandbox_lifetime_changed',\n phase,\n admitted: true,\n retryable: false,\n message:\n 'Tunnel operation was interrupted by a sandbox lifetime change'\n });\n }\n throw error;\n }\n }\n\n async destroy(portOrInfo: number | TunnelInfo): Promise<void> {\n const port = typeof portOrInfo === 'number' ? portOrInfo : portOrInfo.port;\n const startTime = Date.now();\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n let tunnelId: string | undefined;\n try {\n await this.#withPortLock(port, async () => {\n const portKey = port.toString();\n const current = await readPortState(this.#host.storage, port);\n const existing =\n current.info ?? namedTunnelInfoFromMeta(port, current.meta);\n if (!existing) {\n if (current.cleanup) {\n await resumeNamedTunnelCleanupEntry(\n this.#host,\n this.#host.storage,\n portKey,\n current.cleanup,\n {\n logPrefix: 'tunnel.destroy',\n credentialsUnavailableMessage:\n 'tunnel.destroy: skipping CF cleanup, credentials unavailable'\n }\n );\n }\n return;\n }\n tunnelId = existing.id;\n const cleanupEntry = createNamedTunnelCleanupEntry(\n existing,\n current.meta\n );\n\n await updatePortState(this.#host.storage, port, () => ({\n info: undefined,\n meta: undefined,\n ...(cleanupEntry && { cleanup: cleanupEntry })\n }));\n\n try {\n const owningRuntime = tunnelOwningRuntime(current.meta);\n if (owningRuntime && current.meta?.tunnelRunId) {\n const tunnelRunId = current.meta.tunnelRunId;\n await this.#host.runExisting(\n owningRuntime,\n 'tunnel.destroy',\n (tunnels) =>\n tunnels.stopTunnelRun({\n tunnelId: existing.id,\n runId: tunnelRunId\n })\n );\n }\n } catch (error) {\n if (\n isTunnelNotFoundError(error) ||\n error instanceof OperationInterruptedError ||\n isRuntimeActivationMismatch(error)\n ) {\n // The owning runtime is already absent — continue durable cleanup.\n } else if (current.meta?.dnsRecordId) {\n this.#host.logger.warn(\n 'tunnel.destroy: container tunnel cleanup failed',\n {\n port,\n tunnelId,\n error: error instanceof Error ? error.message : String(error)\n }\n );\n } else {\n throw error;\n }\n }\n\n if (!current.meta?.dnsRecordId || !existing.name || !cleanupEntry) {\n return;\n }\n const cleaned = await cleanupNamedTunnelResources(\n this.#host,\n cleanupEntry,\n {\n logPrefix: 'tunnel.destroy',\n credentialsUnavailableMessage:\n 'tunnel.destroy: skipping CF cleanup, credentials unavailable'\n }\n );\n if (!cleaned) return;\n\n await updatePortState(this.#host.storage, port, () => ({\n cleanup: undefined\n }));\n });\n outcome = 'success';\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.#host.logger, {\n event: 'tunnel.destroy',\n outcome,\n port,\n tunnelId,\n durationMs: Date.now() - startTime,\n error: caughtError\n });\n }\n }\n\n async list(): Promise<TunnelInfo[]> {\n const { map, meta, runtime } = await this.#host.storage.transaction(\n async (txn) => {\n const [map, meta, runtime] = await Promise.all([\n readMap(txn),\n readMetaMap(txn),\n this.#host.getStoredRuntime(txn)\n ]);\n return { map, meta, runtime };\n }\n );\n return Object.entries(map)\n .filter(\n ([port]) =>\n !meta[port]?.needsRespawn &&\n tunnelMetaOwnsRuntime(meta[port], runtime)\n )\n .map(([, info]) => info);\n }\n\n async onTunnelExit(\n id: string,\n port: number,\n exitCode: number | null,\n tunnelRunId: string,\n runtime: RuntimeIdentity,\n isSessionCurrent: () => boolean\n ): Promise<void> {\n const startTime = Date.now();\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n try {\n await this.#withPortLock(port, async () => {\n await updatePortState(this.#host.storage, port, async (state, txn) => {\n const existing = state.info;\n const meta = state.meta;\n const activeRuntime = await this.#host.getStoredRuntime(txn);\n if (!isSessionCurrent() || existing?.id !== id) return undefined;\n if (\n !activeRuntime ||\n activeRuntime.id !== runtime.id ||\n activeRuntime.runtimeIncarnationID !==\n runtime.runtimeIncarnationID ||\n meta?.runtimeIdentityID !== runtime.id ||\n meta.runtimeIncarnationID !== runtime.runtimeIncarnationID ||\n meta.tunnelRunId !== tunnelRunId\n ) {\n return undefined;\n }\n\n return {\n info: undefined,\n meta: existing.name ? namedRespawnMeta(existing, meta) : undefined\n };\n });\n });\n outcome = 'success';\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.#host.logger, {\n event: 'tunnel.exit',\n outcome,\n port,\n tunnelId: id,\n exitCode: exitCode ?? undefined,\n durationMs: Date.now() - startTime,\n error: caughtError\n });\n }\n }\n\n async resumeCleanup(): Promise<void> {\n await resumeNamedTunnelCleanupRecords(this.#host, this.#host.storage);\n }\n\n async destroyAll(): Promise<void> {\n const ports = await this.destroyAllPorts();\n\n for (const port of ports) {\n try {\n await this.destroy(port);\n } catch (err) {\n this.#host.logger.warn('tunnels.destroyAll: destroy(port) failed', {\n port,\n error: err instanceof Error ? err.message : String(err)\n });\n }\n }\n\n await this.resumeCleanup();\n }\n\n async destroyAllRuntimeRuns(): Promise<void> {\n const map = await readMap(this.#host.storage);\n const meta = await readMetaMap(this.#host.storage);\n const ports = await this.destroyAllPorts();\n for (const port of ports) {\n const info = map[String(port)];\n const runId = meta[String(port)]?.tunnelRunId;\n if (!info || !runId) continue;\n const owningRuntime = tunnelOwningRuntime(meta[String(port)]);\n if (!owningRuntime) continue;\n try {\n await this.#host.runExisting(\n owningRuntime,\n 'tunnel.destroy',\n (tunnels) => tunnels.stopTunnelRun({ tunnelId: info.id, runId })\n );\n } catch (error) {\n if (isTunnelNotFoundError(error)) continue;\n this.#host.logger.warn('tunnels.destroyAllRuntimeRuns: stop failed', {\n port,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n }\n }\n\n private async destroyAllPorts(): Promise<Set<number>> {\n const map = await readMap(this.#host.storage);\n const meta = await readMetaMap(this.#host.storage);\n const ports = new Set(Object.keys(map).map((p) => Number(p)));\n for (const [portKey, entry] of Object.entries(meta)) {\n const port = Number(portKey);\n if (Number.isFinite(port) && namedTunnelInfoFromMeta(port, entry)) {\n ports.add(port);\n }\n }\n return ports;\n }\n\n async onRuntimeStop(): Promise<void> {\n await pruneTunnelsForRestart(this.#host.storage);\n }\n\n async clearDurableStateAfterDestroy(): Promise<void> {\n await Promise.all([\n this.#host.storage.delete(STORAGE_KEY),\n this.#host.storage.delete(META_STORAGE_KEY),\n this.#host.storage.delete(CLEANUP_STORAGE_KEY)\n ]);\n }\n}\n","import { RpcTarget } from 'cloudflare:workers';\nimport type { TunnelInfo, TunnelOptions } from '@repo/shared';\nimport {\n type TunnelExitHandler,\n TunnelService,\n type TunnelServiceHost,\n type TunnelsHandle,\n type TunnelsHandler\n} from './tunnel-service';\n\nexport { pruneTunnelsForRestart } from './restart';\nexport type { TunnelsStorage, TunnelsStorageTxn } from './storage';\nexport type {\n TunnelExitHandler,\n TunnelServiceHost,\n TunnelsHandle,\n TunnelsHandler\n} from './tunnel-service';\n\n/**\n * Workers RPC adapter for the public `sandbox.tunnels` namespace.\n * The service owns tunnel lifecycle and resource invariants; this class\n * only provides a `RpcTarget` shape for Workers RPC compatibility.\n */\nclass TunnelsRpcTarget extends RpcTarget implements TunnelsHandler {\n constructor(private readonly service: TunnelService) {\n super();\n }\n\n get(port: number, options?: TunnelOptions): Promise<TunnelInfo> {\n return this.service.get(port, options);\n }\n\n list(): Promise<TunnelInfo[]> {\n return this.service.list();\n }\n\n destroy(portOrInfo: number | TunnelInfo): Promise<void> {\n return this.service.destroy(portOrInfo);\n }\n}\n\nexport function createTunnelsHandle(host: TunnelServiceHost): TunnelsHandle {\n const service = new TunnelService(host);\n const tunnels = new TunnelsRpcTarget(service);\n\n return {\n tunnels,\n handleTunnelExit: (\n id,\n port,\n exitCode,\n tunnelRunId,\n runtime,\n isSessionCurrent\n ) =>\n service.onTunnelExit(\n id,\n port,\n exitCode,\n tunnelRunId,\n runtime,\n isSessionCurrent\n ),\n destroyAll: () => service.destroyAll(),\n destroyAllRuntimeRuns: () => service.destroyAllRuntimeRuns(),\n resumeCleanup: () => service.resumeCleanup(),\n onRuntimeStop: () => service.onRuntimeStop(),\n clearDurableStateAfterDestroy: () => service.clearDurableStateAfterDestroy()\n };\n}\n","/**\n * `SandboxControlCallbackImpl` — capnweb `RpcTarget` the DO exposes as\n * `localMain` on the container-control session. The container reaches\n * it via `session.getRemoteMain<SandboxControlCallback>()` to push\n * tunnel-run events back to the DO.\n */\n\nimport type {\n Logger,\n SandboxControlCallback,\n TunnelRunExitEvent\n} from '@repo/shared';\nimport { RpcTarget } from 'capnweb';\nimport type { RuntimeIdentity } from '../runtime';\nimport type { TunnelExitHandler } from './rpc-target';\n\nexport class SandboxControlCallbackImpl\n extends RpcTarget\n implements SandboxControlCallback\n{\n constructor(\n /**\n * Accessor (not a direct reference) so eviction can swap the handler\n * while keeping the capnweb session bound. Returns `null` if the\n * handler has not been constructed yet; callback methods no-op in\n * that window.\n */\n private readonly getHandler: () => TunnelExitHandler | null,\n private readonly logger: Logger,\n private readonly expectedRuntime?: RuntimeIdentity,\n private readonly getCurrentRuntime?: () =>\n | RuntimeIdentity\n | null\n | Promise<RuntimeIdentity | null>,\n private readonly isSessionCurrent?: () => boolean\n ) {\n super();\n }\n\n bindRuntime(\n runtime: RuntimeIdentity,\n isSessionCurrent: () => boolean\n ): SandboxControlCallbackImpl {\n return new SandboxControlCallbackImpl(\n this.getHandler,\n this.logger,\n runtime,\n this.getCurrentRuntime,\n isSessionCurrent\n );\n }\n\n async onTunnelRunExit(event: TunnelRunExitEvent): Promise<void> {\n const currentRuntime = this.getCurrentRuntime\n ? await this.getCurrentRuntime()\n : null;\n if (\n this.isSessionCurrent?.() !== true ||\n !this.expectedRuntime ||\n !currentRuntime ||\n currentRuntime.id !== this.expectedRuntime.id ||\n currentRuntime.runtimeIncarnationID !==\n this.expectedRuntime.runtimeIncarnationID\n ) {\n this.logger.debug('onTunnelRunExit: stale runtime callback ignored', {\n tunnelId: event.tunnelId,\n runId: event.runId,\n mode: event.mode,\n port: event.port\n });\n return;\n }\n const handler = this.getHandler();\n if (!handler) {\n this.logger.debug('onTunnelRunExit: no handler bound; ignoring', {\n tunnelId: event.tunnelId,\n runId: event.runId,\n mode: event.mode,\n port: event.port,\n exitCode: event.exitCode\n });\n return;\n }\n await handler(\n event.tunnelId,\n event.port,\n event.exitCode,\n event.runId,\n this.expectedRuntime,\n this.isSessionCurrent\n );\n }\n}\n","import type { ContainerStartConfigOptions } from '@cloudflare/containers';\nimport { Container, getContainer, switchPort } from '@cloudflare/containers';\nimport type {\n BackupOptions,\n CheckChangesOptions,\n CheckChangesResult,\n CreateTerminalOptions,\n DirectoryBackup,\n ExecOptions,\n FileEncoding,\n ISandbox,\n ListFilesOptions,\n MountBucketOptions,\n PortWatchEvent,\n ProcessStatus,\n ReadFileResult,\n ReadFileStreamResult,\n RestoreBackupResult,\n SandboxCommand,\n SandboxOptions,\n SandboxTerminalsAPI,\n SandboxTunnelsAPI,\n TerminalOutputEvent,\n TerminalOutputSubscriptionAPI,\n TerminalSnapshot,\n WaitForPortOptions,\n WatchOptions\n} from '@repo/shared';\nimport {\n createLogger,\n getEnvString,\n logCanonicalEvent,\n partitionEnvVars,\n TraceContext\n} from '@repo/shared';\nimport {\n getHttpStatus,\n getSuggestion,\n type OperationInterruptedContext\n} from '@repo/shared/errors';\nimport {\n type BackupRestoreTestFault,\n BackupService\n} from './backup/backup-service';\nimport type { ContainerControlClient } from './container-control';\nimport type { ErrorResponse } from './errors';\nimport {\n ContainerUnavailableError,\n ErrorCode,\n OperationInterruptedError,\n ProcessExitedBeforeReadyError,\n ProcessNotFoundError,\n ProcessReadyTimeoutError,\n SandboxError,\n StaleProcessHandleError,\n StaleTerminalHandleError\n} from './errors';\nimport type {\n ExtensionRuntimeCall,\n ExtensionRuntimeControl,\n HTTPAuthInterceptorParams as GitAuthInterceptorParams\n} from './extensions';\nimport { SandboxExtension, sandboxRuntimeCall } from './extensions';\nimport { collectFile, streamFile } from './file-stream';\nimport { isPlatformTransientError } from './platform-errors';\nimport { isPreviewProxyRequest } from './preview/protocol';\nimport {\n type PreviewForwardingContainer,\n PreviewService\n} from './preview/service';\nimport { createSandboxProcess } from './processes';\nimport {\n type ProcessCapabilityControl,\n ProcessCapabilityTarget\n} from './processes/process-capability';\nimport {\n openRemoteSubscription,\n PullSubscriptionTarget\n} from './processes/remote-subscription';\nimport type {\n ProcessPullSubscriptionRPC,\n ProcessRPCDescriptor\n} from './processes/rpc-types';\nimport { terminalHandleFromRPCDescriptor } from './pty';\nimport type { TerminalRPCDescriptor } from './pty/rpc-types';\nimport {\n type TerminalCapabilityControl,\n TerminalCapabilityTarget\n} from './pty/terminal-capability';\nimport { ResourceActivityGate } from './resource-activity-gate';\nimport {\n RUNTIME_ABSENT,\n RuntimeBootstrapProbe,\n type RuntimeIdentity,\n type RuntimeLease,\n RuntimeOperationRunner,\n RuntimeSessionManager,\n SandboxRuntimeLifecycle\n} from './runtime';\nimport { waitForRuntimePort } from './runtime/port-readiness';\nimport { CurrentSandboxLifetime } from './sandbox-lifetime';\nimport {\n SandboxSecurityError,\n sanitizeSandboxId,\n validatePort\n} from './security';\nimport { parseSSEStream } from './sse-parser';\nimport {\n BucketMountService,\n ContainerProxy,\n configureGitAuthInterceptor,\n type EgressContainerState,\n type MountOutboundHost\n} from './storage-mount';\nimport { NamedTunnelConfigResolver } from './tunnels/named-tunnel-config';\nimport {\n createTunnelsHandle,\n pruneTunnelsForRestart,\n type TunnelExitHandler,\n type TunnelsHandle,\n type TunnelsHandler\n} from './tunnels/rpc-target';\nimport { SandboxControlCallbackImpl } from './tunnels/sandbox-control-callback';\n\nexport { ContainerProxy };\n\nconst DESTROY_RUNTIME_CLEANUP_TIMEOUT_MS = 5_000;\n\nfunction validateExecArgv(command: SandboxCommand): SandboxCommand {\n if (!Array.isArray(command)) {\n throw invalidCommand('exec() requires argv as an array of strings.');\n }\n if (command.length === 0) {\n throw invalidCommand('exec() requires a non-empty argv command.');\n }\n const [executable] = command;\n if (typeof executable !== 'string' || executable.length === 0) {\n throw invalidCommand('exec() requires a non-empty executable.');\n }\n for (const [index, value] of command.entries()) {\n if (typeof value !== 'string') {\n throw invalidCommand(`exec() argv[${index}] must be a string.`);\n }\n }\n return command;\n}\n\nfunction invalidCommand(\n message: string\n): SandboxError<Record<string, unknown>> {\n return new SandboxError({\n code: ErrorCode.INVALID_COMMAND,\n message,\n context: {},\n httpStatus: getHttpStatus(ErrorCode.INVALID_COMMAND),\n suggestion: getSuggestion(ErrorCode.INVALID_COMMAND, {}),\n timestamp: new Date().toISOString()\n });\n}\n\nfunction toContainerHTTPRequest(request: Request): Request {\n const url = new URL(request.url);\n if (url.protocol === 'https:') url.protocol = 'http:';\n const headers = new Headers(request.headers);\n headers.delete('cf-container-target-port');\n headers.delete('x-sandbox-port-route-token');\n return new Request(new Request(url, request), { headers });\n}\n\nfunction retainedStream<T>(\n stream: ReadableStream<T>,\n lease: RuntimeLease,\n operation = 'stream.read'\n): {\n stream: ReadableStream<T>;\n cancel(reason?: unknown): void;\n release(): void;\n} {\n const reader = stream.getReader();\n let released = false;\n let interruptedError: Error | undefined;\n let hold: ReturnType<RuntimeLease['retain']> | undefined;\n let interrupt!: (error: Error) => void;\n const interrupted = new Promise<never>((_, reject) => {\n interrupt = reject;\n });\n interrupted.catch(() => undefined);\n const release = () => {\n if (released) return;\n released = true;\n hold?.release();\n };\n const cancelSource = (reason?: unknown) => {\n if (released) return;\n release();\n void reader.cancel(reason).catch(() => undefined);\n };\n hold = lease.retain(() => {\n if (released) return;\n interruptedError = runtimeInterrupted(operation);\n interrupt(interruptedError);\n cancelSource(interruptedError);\n });\n return {\n stream: new ReadableStream<T>({\n async pull(streamController) {\n try {\n const result = await Promise.race([reader.read(), interrupted]);\n if (interruptedError) throw interruptedError;\n if (result.done) {\n release();\n streamController.close();\n return;\n }\n streamController.enqueue(result.value);\n } catch (error) {\n release();\n streamController.error(error);\n }\n },\n cancel(reason) {\n cancelSource(reason);\n }\n }),\n cancel: cancelSource,\n release\n };\n}\n\nfunction retainStream<T>(\n stream: ReadableStream<T>,\n lease: RuntimeLease\n): ReadableStream<T> {\n return retainedStream(stream, lease).stream;\n}\n\nfunction extensionRuntimeControl(\n control: ContainerControlClient\n): ExtensionRuntimeControl {\n return {\n files: control.files,\n ports: control.ports,\n backup: control.backup,\n watch: control.watch,\n tunnels: control.tunnels,\n terminals: control.terminals,\n extensions: control.extensions,\n utils: control.utils\n };\n}\n\nfunction runtimeInterrupted(operation: string): OperationInterruptedError {\n const context: OperationInterruptedContext = {\n reason: 'runtime_replaced',\n operation,\n admitted: true,\n retryable: false\n };\n return new OperationInterruptedError({\n code: ErrorCode.OPERATION_INTERRUPTED,\n message: `Sandbox operation ${operation} was interrupted because the runtime changed`,\n context,\n httpStatus: 409,\n timestamp: new Date().toISOString()\n });\n}\n\nfunction isRuntimeAbsent(value: unknown): value is typeof RUNTIME_ABSENT {\n return value === RUNTIME_ABSENT;\n}\n\nfunction staleTerminal(\n terminalId: string,\n operation: string\n): StaleTerminalHandleError {\n return new StaleTerminalHandleError({\n code: ErrorCode.STALE_TERMINAL_HANDLE,\n message: 'Terminal handle refers to a previous runtime incarnation',\n context: { terminalId, operation },\n httpStatus: 409,\n timestamp: new Date().toISOString()\n });\n}\n\ntype InterruptibleTerminalSubscription = TerminalOutputSubscriptionAPI & {\n interrupt(): void;\n};\n\nfunction retainedTerminalSubscription(\n terminalId: string,\n operation: string,\n subscription: TerminalOutputSubscriptionAPI,\n releaseConnection: () => void\n): InterruptibleTerminalSubscription {\n let released = false;\n let reader: ReadableStreamDefaultReader<TerminalOutputEvent> | undefined;\n let controller:\n | ReadableStreamDefaultController<TerminalOutputEvent>\n | undefined;\n const stale = () => staleTerminal(terminalId, operation);\n const release = () => {\n if (released) return;\n released = true;\n releaseConnection();\n if (reader) {\n try {\n void reader.cancel().catch(() => undefined);\n } catch {}\n } else {\n try {\n void subscription.cancel().catch(() => undefined);\n } catch {}\n try {\n subscription[Symbol.dispose]();\n } catch {}\n }\n };\n const failStale = () => {\n const error = stale();\n controller?.error(error);\n release();\n };\n return {\n async stream() {\n const source = await openRemoteSubscription<TerminalOutputEvent>(\n Promise.resolve(subscription),\n { operation, protocol: 'stream' }\n );\n return new ReadableStream<TerminalOutputEvent>({\n start(streamController) {\n controller = streamController;\n reader = source.getReader();\n },\n async pull(streamController) {\n if (released) {\n streamController.error(stale());\n return;\n }\n try {\n const result = await reader!.read();\n if (released) {\n streamController.error(stale());\n return;\n }\n if (result.done) {\n release();\n streamController.close();\n return;\n }\n streamController.enqueue(result.value);\n if (result.value.type === 'terminal') {\n release();\n streamController.close();\n }\n } catch (error) {\n release();\n streamController.error(error);\n }\n },\n cancel() {\n release();\n }\n });\n },\n async cancel() {\n release();\n },\n [Symbol.dispose]() {\n release();\n },\n interrupt() {\n failStale();\n }\n };\n}\n\nfunction retainedResponse(\n response: Response,\n lease: RuntimeLease,\n operation: string\n): Response {\n if (!response.body) return response;\n return new Response(retainedStream(response.body, lease, operation).stream, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers\n });\n}\n\nfunction retainWebSocketResponse(\n response: Response,\n releaseConnection: () => void\n): { interrupt(): void; webSocketFound: boolean } {\n const webSocket = (\n response as {\n webSocket?: EventTarget & {\n close?: (code?: number, reason?: string) => void;\n };\n }\n ).webSocket;\n if (!webSocket) {\n releaseConnection();\n return { interrupt: () => {}, webSocketFound: false };\n }\n const release = once(releaseConnection);\n const closeInterrupted = once(() => {\n try {\n webSocket.close?.(1012, 'Runtime replaced');\n } catch {\n // The runtime may reject close() after handing the socket to its peer.\n } finally {\n release();\n }\n });\n webSocket.addEventListener('close', release, { once: true });\n webSocket.addEventListener('error', release, { once: true });\n return { interrupt: closeInterrupted, webSocketFound: true };\n}\n\nfunction once<T extends (...args: never[]) => void>(fn: T): T {\n let called = false;\n return ((...args: Parameters<T>) => {\n if (called) return;\n called = true;\n fn(...(args as never[]));\n }) as T;\n}\n\nfunction processCapabilityControl(\n lease: RuntimeLease\n): ProcessCapabilityControl {\n const client = lease.control;\n const processes = client.processes;\n return {\n retainRuntimeHold: () => lease.retain().release,\n getProcess: (id) => processes.get(id),\n openLogs: (id, options) => processes.openLogs(id, options),\n openPortWatch: (port, options) => client.ports.openWatch(port, options),\n kill: (id, signal) => processes.kill(id, signal)\n };\n}\n\ntype TerminalHandleStub = Omit<SandboxTerminalsAPI, 'output'> & {\n output(\n id: string,\n options?: Parameters<SandboxTerminalsAPI['output']>[1]\n ): Promise<ProcessPullSubscriptionRPC<TerminalOutputEvent>>;\n fetch(request: Request): Promise<Response>;\n};\n\nfunction localizeTerminalHandle(\n descriptor: TerminalRPCDescriptor,\n sandboxStub: {\n fetch(request: Request): Promise<Response>;\n }\n): ReturnType<typeof terminalHandleFromRPCDescriptor> {\n return terminalHandleFromRPCDescriptor(descriptor, async (request) => {\n const token = await descriptor.capability.authorizeConnection();\n const headers = new Headers(request.headers);\n headers.set('x-sandbox-port-route-token', token);\n return await sandboxStub.fetch(new Request(request, { headers }));\n });\n}\n\ntype SandboxConfiguration = {\n sandboxName?: {\n name: string;\n normalizeId?: boolean;\n };\n sleepAfter?: string | number;\n keepAlive?: boolean;\n containerTimeouts?: NonNullable<SandboxOptions['containerTimeouts']>;\n};\n\ntype CachedSandboxConfiguration = {\n sandboxName?: string;\n normalizeId?: boolean;\n sleepAfter?: string | number;\n keepAlive?: boolean;\n containerTimeouts?: NonNullable<SandboxOptions['containerTimeouts']>;\n};\n\ntype PreviewForwardingContainerState = DurableObjectState<{}> & {\n container?: PreviewForwardingContainer;\n};\n\ntype ConfigurableSandboxStub = {\n configure?: (configuration: SandboxConfiguration) => Promise<void>;\n setSandboxName?: (name: string, normalizeId?: boolean) => Promise<void>;\n setSleepAfter?: (sleepAfter: string | number) => Promise<void>;\n setKeepAlive?: (keepAlive: boolean) => Promise<void>;\n setContainerTimeouts?: (\n timeouts: NonNullable<SandboxOptions['containerTimeouts']>\n ) => Promise<void>;\n};\n\ntype SandboxProxyStub = ConfigurableSandboxStub & {\n fetch: (request: Request) => Promise<Response>;\n containerFetch: (\n requestOrUrl: Request | string | URL,\n portOrInit?: number | RequestInit,\n portParam?: number\n ) => Promise<Response>;\n callExtension: (\n extensionName: string,\n method: string,\n args: unknown[]\n ) => Promise<unknown>;\n createTerminal: (\n options: CreateTerminalOptions\n ) => Promise<TerminalRPCDescriptor>;\n getTerminal: (id: string) => Promise<TerminalRPCDescriptor | null>;\n listTerminals: () => Promise<TerminalRPCDescriptor[]>;\n wsConnect: (request: Request, port: number) => Promise<Response>;\n authorizePortRequest: (port: number, path: string) => Promise<string>;\n exec: (\n command: SandboxCommand,\n options?: ExecOptions\n ) => Promise<ProcessRPCDescriptor>;\n getProcess: (id: string) => Promise<ProcessRPCDescriptor | null>;\n listProcesses: () => Promise<ProcessStatus[]>;\n};\n\ntype InternalSandboxRPCMethod = 'authorizePortRequest';\n\nexport type SandboxClient<T> = Omit<\n T,\n keyof ISandbox | InternalSandboxRPCMethod\n> &\n ISandbox;\n\nconst sandboxConfigurationCache = new WeakMap<\n object,\n Map<string, CachedSandboxConfiguration>\n>();\nconst sandboxConfigurationPending = new WeakMap<\n object,\n Map<string, Promise<void>>\n>();\n\nconst BACKUP_DEFAULT_TTL_SECONDS = 259200;\nconst BACKUP_MAX_NAME_LENGTH = 256;\nconst BACKUP_CONTAINER_DIR = '/var/backups';\nconst BACKUP_STORAGE_PREFIX = 'backups';\nconst BACKUP_ARCHIVE_OBJECT_NAME = 'data.sqsh';\nconst BACKUP_METADATA_OBJECT_NAME = 'meta.json';\nconst BACKUP_DEFAULT_COMPRESSION = 'lz4';\nconst BACKUP_DEFAULT_COMPRESS_THREADS = 8;\nconst BACKUP_MULTIPART_MIN_SIZE = 10 * 1024 * 1024;\nconst BACKUP_MULTIPART_TARGET_PARTS = 16;\nconst BACKUP_MULTIPART_MIN_PART_SIZE = 5 * 1024 * 1024;\nconst BACKUP_MULTIPART_MAX_PARTS = 64;\nconst BACKUP_DOWNLOAD_PARALLEL_PARTS = 8;\nconst BACKUP_DOWNLOAD_PARALLEL_MIN_SIZE = 10 * 1024 * 1024;\nconst BACKUP_DOWNLOAD_MAX_PARTS = 64;\n\ntype CancellationOptions = {\n abort?: AbortSignal;\n instanceGetTimeoutMS?: number;\n portReadyTimeoutMS?: number;\n waitInterval?: number;\n};\n\ntype StartAndWaitForPortsOptions = {\n startOptions?: ContainerStartConfigOptions;\n ports?: number | number[];\n cancellationOptions?: CancellationOptions;\n};\n\nfunction normalizeStartAndWaitForPortsOptions(\n portsOrArgs?: number | number[] | StartAndWaitForPortsOptions,\n cancellationOptions?: CancellationOptions,\n startOptions?: ContainerStartConfigOptions\n): StartAndWaitForPortsOptions {\n if (typeof portsOrArgs === 'number' || Array.isArray(portsOrArgs)) {\n return { ports: portsOrArgs, cancellationOptions, startOptions };\n }\n return {\n ...(portsOrArgs ?? {}),\n cancellationOptions:\n portsOrArgs?.cancellationOptions ?? cancellationOptions,\n startOptions: portsOrArgs?.startOptions ?? startOptions\n };\n}\n\nfunction normalizePorts(ports: number | number[] | undefined): number[] {\n if (ports === undefined) return [];\n return Array.isArray(ports) ? ports : [ports];\n}\n\nfunction getNamespaceConfigurationCache(\n namespace: object\n): Map<string, CachedSandboxConfiguration> {\n const existing = sandboxConfigurationCache.get(namespace);\n if (existing) {\n return existing;\n }\n\n const created = new Map<string, CachedSandboxConfiguration>();\n sandboxConfigurationCache.set(namespace, created);\n return created;\n}\n\nfunction getNamespacePendingConfiguration(\n namespace: object\n): Map<string, Promise<void>> {\n const existing = sandboxConfigurationPending.get(namespace);\n if (existing) return existing;\n\n const created = new Map<string, Promise<void>>();\n sandboxConfigurationPending.set(namespace, created);\n return created;\n}\n\nfunction sameContainerTimeouts(\n left?: NonNullable<SandboxOptions['containerTimeouts']>,\n right?: NonNullable<SandboxOptions['containerTimeouts']>\n): boolean {\n return (\n left?.instanceGetTimeoutMS === right?.instanceGetTimeoutMS &&\n left?.portReadyTimeoutMS === right?.portReadyTimeoutMS &&\n left?.waitIntervalMS === right?.waitIntervalMS\n );\n}\n\nfunction buildSandboxConfiguration(\n effectiveId: string,\n options: SandboxOptions | undefined,\n cached: CachedSandboxConfiguration | undefined\n): SandboxConfiguration {\n const configuration: SandboxConfiguration = {};\n\n if (\n cached?.sandboxName !== effectiveId ||\n cached.normalizeId !== options?.normalizeId\n ) {\n configuration.sandboxName = {\n name: effectiveId,\n normalizeId: options?.normalizeId\n };\n }\n\n if (\n options?.sleepAfter !== undefined &&\n cached?.sleepAfter !== options.sleepAfter\n ) {\n configuration.sleepAfter = options.sleepAfter;\n }\n\n if (\n options?.keepAlive !== undefined &&\n cached?.keepAlive !== options.keepAlive\n ) {\n configuration.keepAlive = options.keepAlive;\n }\n\n if (\n options?.containerTimeouts &&\n !sameContainerTimeouts(cached?.containerTimeouts, options.containerTimeouts)\n ) {\n configuration.containerTimeouts = options.containerTimeouts;\n }\n\n return configuration;\n}\n\nfunction hasSandboxConfiguration(configuration: SandboxConfiguration): boolean {\n return (\n configuration.sandboxName !== undefined ||\n configuration.sleepAfter !== undefined ||\n configuration.keepAlive !== undefined ||\n configuration.containerTimeouts !== undefined\n );\n}\n\nfunction mergeSandboxConfiguration(\n cached: CachedSandboxConfiguration | undefined,\n configuration: SandboxConfiguration\n): CachedSandboxConfiguration {\n return {\n ...cached,\n ...(configuration.sandboxName && {\n sandboxName: configuration.sandboxName.name,\n normalizeId: configuration.sandboxName.normalizeId\n }),\n ...(configuration.sleepAfter !== undefined && {\n sleepAfter: configuration.sleepAfter\n }),\n ...(configuration.keepAlive !== undefined && {\n keepAlive: configuration.keepAlive\n }),\n ...(configuration.containerTimeouts !== undefined && {\n containerTimeouts: configuration.containerTimeouts\n })\n };\n}\n\nfunction applySandboxConfiguration(\n stub: ConfigurableSandboxStub,\n configuration: SandboxConfiguration\n): Promise<void> {\n if (stub.configure) {\n return stub.configure(configuration);\n }\n\n const operations: Promise<void>[] = [];\n\n if (configuration.sandboxName) {\n operations.push(\n stub.setSandboxName?.(\n configuration.sandboxName.name,\n configuration.sandboxName.normalizeId\n ) ?? Promise.resolve()\n );\n }\n\n if (configuration.sleepAfter !== undefined) {\n operations.push(\n stub.setSleepAfter?.(configuration.sleepAfter) ?? Promise.resolve()\n );\n }\n\n if (configuration.keepAlive !== undefined) {\n operations.push(\n stub.setKeepAlive?.(configuration.keepAlive) ?? Promise.resolve()\n );\n }\n\n if (configuration.containerTimeouts !== undefined) {\n operations.push(\n stub.setContainerTimeouts?.(configuration.containerTimeouts) ??\n Promise.resolve()\n );\n }\n\n return Promise.all(operations).then(() => undefined);\n}\n\nfunction createPlatformInterruptedError(\n error: unknown,\n operation: string\n): OperationInterruptedError | null {\n if (!isPlatformTransientError(error)) return null;\n\n const context: OperationInterruptedContext = {\n reason: 'runtime_replaced',\n operation,\n phase: 'durable_object_call',\n admitted: 'unknown',\n retryable: false\n };\n\n return new OperationInterruptedError({\n code: ErrorCode.OPERATION_INTERRUPTED,\n message: `Sandbox operation ${operation} was interrupted while the platform was updating the sandbox runtime`,\n context,\n httpStatus: getHttpStatus(ErrorCode.OPERATION_INTERRUPTED),\n suggestion: getSuggestion(\n ErrorCode.OPERATION_INTERRUPTED,\n context as unknown as Record<string, unknown>\n ),\n timestamp: new Date().toISOString()\n });\n}\n\nfunction translatePlatformInterruption(\n error: unknown,\n operation: string\n): never {\n throw createPlatformInterruptedError(error, operation) ?? error;\n}\n\nfunction withSandboxOperationContext<TArgs extends unknown[], TResult>(\n operation: string,\n fn: (...args: TArgs) => TResult\n): (...args: TArgs) => TResult {\n return (...args: TArgs): TResult => {\n try {\n const result = fn(...args);\n if (\n result != null &&\n typeof (result as { then?: unknown }).then === 'function'\n ) {\n const caught = (result as unknown as Promise<unknown>).catch(\n (error: unknown) => translatePlatformInterruption(error, operation)\n );\n return caught as TResult;\n }\n return result;\n } catch (error) {\n translatePlatformInterruption(error, operation);\n }\n };\n}\n\nexport function getSandbox<T extends Sandbox<any>>(\n ns: DurableObjectNamespace<T>,\n id: string,\n options?: SandboxOptions\n): SandboxClient<T> {\n const sanitizedId = sanitizeSandboxId(id);\n const effectiveId = options?.normalizeId\n ? sanitizedId.toLowerCase()\n : sanitizedId;\n\n const hasUppercase = /[A-Z]/.test(sanitizedId);\n if (!options?.normalizeId && hasUppercase) {\n const logger = createLogger({ component: 'sandbox-do' });\n logger.warn(\n `Sandbox ID \"${sanitizedId}\" contains uppercase letters, which causes issues with preview URLs (hostnames are case-insensitive). ` +\n `normalizeId will default to true in a future version to prevent this. ` +\n `Use lowercase IDs or pass { normalizeId: true } to prepare.`\n );\n }\n\n const stub = getContainer(\n ns as unknown as DurableObjectNamespace<Container<Cloudflare.Env>>,\n effectiveId\n ) as unknown as T & SandboxProxyStub;\n\n const namespaceCache = getNamespaceConfigurationCache(ns);\n const pendingConfigurations = getNamespacePendingConfiguration(ns);\n const applyRequestedConfiguration = async (): Promise<void> => {\n const cachedConfiguration = namespaceCache.get(effectiveId);\n const configuration = buildSandboxConfiguration(\n effectiveId,\n options,\n cachedConfiguration\n );\n if (!hasSandboxConfiguration(configuration)) return;\n\n await applySandboxConfiguration(stub, configuration);\n namespaceCache.set(\n effectiveId,\n mergeSandboxConfiguration(cachedConfiguration, configuration)\n );\n };\n const previousConfiguration = pendingConfigurations.get(effectiveId);\n const configurationReady = previousConfiguration\n ? previousConfiguration.then(applyRequestedConfiguration)\n : applyRequestedConfiguration();\n pendingConfigurations.set(effectiveId, configurationReady);\n void configurationReady\n .finally(() => {\n if (pendingConfigurations.get(effectiveId) === configurationReady) {\n pendingConfigurations.delete(effectiveId);\n }\n })\n .catch(() => undefined);\n\n const enhancedMethods = {\n authorizePortRequest: undefined,\n createPortRequestToken: undefined,\n fetch: (request: Request) => {\n const targetPort = request.headers.get('cf-container-target-port');\n return targetPort === null\n ? stub.fetch(request)\n : fetchAuthorizedPort(stub, request, Number(targetPort));\n },\n containerFetch: (...args: Parameters<SandboxProxyStub['containerFetch']>) =>\n stub.containerFetch(...args),\n exec: async (command: SandboxCommand, execOptions?: ExecOptions) =>\n createSandboxProcess(\n await stub.exec(command, sanitizeExecOptions(execOptions))\n ),\n getProcess: async (processId: string) => {\n const descriptor = await stub.getProcess(processId);\n return descriptor ? createSandboxProcess(descriptor) : null;\n },\n listProcesses: () => stub.listProcesses(),\n\n writeFile: (\n path: string,\n content: string | ReadableStream<Uint8Array>,\n fileOptions: { encoding?: string } = {}\n ) => stub.writeFile(path, content, fileOptions),\n readFile: (\n path: string,\n fileOptions:\n | { encoding: 'none' }\n | { encoding?: Exclude<FileEncoding, 'none'> } = {}\n ) => {\n if (fileOptions.encoding === 'none') {\n return stub.readFile(path, fileOptions);\n }\n return stub.readFile(path, fileOptions);\n },\n readFileStream: (path: string) => stub.readFileStream(path),\n mkdir: (path: string, mkdirOptions: { recursive?: boolean } = {}) =>\n stub.mkdir(path, mkdirOptions),\n deleteFile: (path: string) => stub.deleteFile(path),\n renameFile: (oldPath: string, newPath: string) =>\n stub.renameFile(oldPath, newPath),\n moveFile: (sourcePath: string, destinationPath: string) =>\n stub.moveFile(sourcePath, destinationPath),\n listFiles: (path: string, listOptions?: ListFilesOptions) =>\n stub.listFiles(path, listOptions),\n watch: (path: string, options: WatchOptions = {}) =>\n stub.watch(path, options),\n checkChanges: (path: string, options: CheckChangesOptions = {}) =>\n stub.checkChanges(path, options),\n createTerminal: async (options: CreateTerminalOptions) =>\n localizeTerminalHandle(await stub.createTerminal(options), stub),\n getTerminal: async (id: string) => {\n const terminal = await stub.getTerminal(id);\n return terminal ? localizeTerminalHandle(terminal, stub) : null;\n },\n listTerminals: async () =>\n (await stub.listTerminals()).map((terminal) =>\n localizeTerminalHandle(terminal, stub)\n ),\n wsConnect: connect(stub),\n tunnels: new Proxy({} as TunnelsHandler, {\n get: (_, method) => {\n if (typeof method !== 'string' || method === 'then') return undefined;\n return withSandboxOperationContext(\n `sandbox.tunnels.${method}`,\n async (...args: unknown[]) => {\n await configurationReady;\n return stub.callTunnels(method, args);\n }\n );\n }\n })\n };\n\n // Proxy intercepts enhanced methods, passes all others to stub directly.\n // We must access target[prop] directly (not via Reflect.get with receiver)\n // to preserve the RPC stub's internal Proxy handling. The final assertion is\n // isolated to this Workers RPC boundary where process wire descriptors are\n // replaced by caller-local ISandbox handles.\n return new Proxy(stub, {\n get(target, prop) {\n if (typeof prop === 'string' && prop in enhancedMethods) {\n const method = enhancedMethods[prop as keyof typeof enhancedMethods];\n if (typeof method === 'function') {\n return withSandboxOperationContext(\n `sandbox.${prop}`,\n async (...args: unknown[]) => {\n await configurationReady;\n return (method as (...args: unknown[]) => unknown)(...args);\n }\n );\n }\n return method;\n }\n if (typeof prop !== 'string' || prop === 'then') {\n // @ts-expect-error - RPC stub methods are Proxy-trapped, not visible to TypeScript\n return target[prop];\n }\n // @ts-expect-error - RPC stub methods are Proxy-trapped, not visible to TypeScript\n const value = target[prop];\n // Plain data properties (e.g. sleepAfter) pass through unchanged.\n if (value !== undefined && typeof value !== 'function') {\n return value;\n }\n // Methods and extension namespaces become a callable proxy: invoking it\n // forwards to the stub method (sandbox.method(...)), while a nested\n // access dispatches sandbox.<ext>.<method>(...) through callExtension.\n return new Proxy(\n async (...args: unknown[]) => {\n await configurationReady;\n // @ts-expect-error - RPC stub methods are Proxy-trapped, not visible to TypeScript\n return target[prop](...args);\n },\n {\n get: (_, method) => {\n if (typeof method !== 'string' || method === 'then') {\n return undefined;\n }\n return async (...args: unknown[]) => {\n await configurationReady;\n return stub.callExtension(prop, method, args);\n };\n }\n }\n );\n }\n }) as unknown as SandboxClient<T>;\n}\n\nfunction sanitizeExecOptions(options?: ExecOptions): ExecOptions | undefined {\n if (!options) return undefined;\n const sanitized: ExecOptions = {};\n if (options.cwd !== undefined) sanitized.cwd = options.cwd;\n if (options.env !== undefined) sanitized.env = options.env;\n if (options.timeout !== undefined) sanitized.timeout = options.timeout;\n return sanitized;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n Object.getPrototypeOf(value) === Object.prototype\n );\n}\n\nfunction mergeExecEnvironment(\n sandboxEnv: Record<string, string>,\n commandEnv: Record<string, string | null>\n): Record<string, string> {\n const merged = { ...sandboxEnv };\n for (const [name, value] of Object.entries(commandEnv)) {\n if (value === null) delete merged[name];\n else merged[name] = value;\n }\n return merged;\n}\n\nfunction getConcreteExtensionMethod(\n extension: SandboxExtension,\n method: string\n): ((...args: unknown[]) => unknown) | undefined {\n const ownValue = (extension as unknown as Record<string, unknown>)[method];\n if (Object.hasOwn(extension, method) && typeof ownValue === 'function') {\n return ownValue as (...args: unknown[]) => unknown;\n }\n\n let prototype = Object.getPrototypeOf(extension) as object | null;\n while (\n prototype &&\n prototype !== SandboxExtension.prototype &&\n prototype !== Object.prototype\n ) {\n const descriptor = Object.getOwnPropertyDescriptor(prototype, method);\n if (typeof descriptor?.value === 'function') {\n return descriptor.value as (...args: unknown[]) => unknown;\n }\n prototype = Object.getPrototypeOf(prototype) as object | null;\n }\n\n return undefined;\n}\n\nexport function connect(stub: {\n fetch: (request: Request) => Promise<Response>;\n authorizePortRequest: (port: number, path: string) => Promise<string>;\n}) {\n return async (request: Request, port: number) => {\n if (!validatePort(port)) {\n throw new SandboxSecurityError(\n `Invalid port number: ${port}. Must be 1024-65535, excluding 3000 (sandbox control plane).`\n );\n }\n return await fetchAuthorizedPort(stub, request, port);\n };\n}\n\nasync function fetchAuthorizedPort(\n stub: {\n fetch: (request: Request) => Promise<Response>;\n authorizePortRequest: (port: number, path: string) => Promise<string>;\n },\n request: Request,\n port: number\n): Promise<Response> {\n const token = await stub.authorizePortRequest(\n port,\n new URL(request.url).pathname\n );\n const switched = switchPort(request, port);\n const headers = new Headers(switched.headers);\n headers.set('x-sandbox-port-route-token', token);\n return await stub.fetch(new Request(switched, { headers }));\n}\n\nexport class Sandbox<Env = unknown> extends Container<Env> {\n defaultPort = 3000; // Default port for the container's Bun server\n sleepAfter: string | number = '10m'; // Sleep the sandbox if no requests are made in this timeframe\n\n private runtimeSessions: RuntimeSessionManager;\n private runtimeLifecycle: SandboxRuntimeLifecycle;\n private runtimeRunner: RuntimeOperationRunner;\n\n private sandboxName: string | null = null;\n // Tunnels subsystem handle. Lazily constructed on first access via the\n // `tunnels` getter or when sandbox lifecycle hooks need tunnel cleanup.\n // The public `tunnelsHandler` stays narrow while `tunnelServiceHandle`\n // carries internal runtime/destroy callbacks.\n private tunnelServiceHandle: TunnelsHandle | null = null;\n private tunnelsHandler: TunnelsHandler | null = null;\n private tunnelExitHandler: TunnelExitHandler | null = null;\n // capnweb localMain exposed to the container side of the RPC\n // session. Constructed once in the constructor (the lazy accessor\n // keeps it pointing at the current `tunnelExitHandler`), so the\n // container can call back into the DO without us re-binding the session.\n private readonly controlCallback: SandboxControlCallbackImpl;\n private normalizeId: boolean = false;\n envVars: Record<string, string> = {};\n private readonly portRequestTokens = new Map<\n string,\n {\n port: number;\n path: string;\n expiresAt: number;\n scope: 'port' | 'terminal';\n }\n >();\n private logger: ReturnType<typeof createLogger>;\n private keepAliveEnabled: boolean = false;\n private bucketMounts: BucketMountService;\n private currentLifetime: CurrentSandboxLifetime;\n private backupService: BackupService;\n private previewService: PreviewService;\n private resourceActivityGate: ResourceActivityGate;\n\n private r2AccessKeyId: string | null = null;\n private r2SecretAccessKey: string | null = null;\n private namedTunnelConfigResolver: NamedTunnelConfigResolver;\n\n /**\n * Default container startup timeouts (conservative for production)\n * Based on Cloudflare docs: \"Containers take several minutes to provision\"\n */\n private readonly DEFAULT_CONTAINER_TIMEOUTS = {\n // Time to get container instance and launch VM\n // @cloudflare/containers default: 8s (too short for cold starts)\n instanceGetTimeoutMS: 30_000, // 30 seconds\n\n // Time for application to start and ports to be ready\n // @cloudflare/containers default: 20s\n portReadyTimeoutMS: 90_000, // 90 seconds (allows for heavy containers)\n\n // Polling interval for checking container readiness\n waitIntervalMS: 300\n };\n\n /**\n * Active container timeout configuration\n * Can be set via options, env vars, or defaults\n */\n private containerTimeouts = { ...this.DEFAULT_CONTAINER_TIMEOUTS };\n\n /**\n * True once containerTimeouts has been written to storage at least once\n * (either via setContainerTimeouts or restored on cold start). Gates the\n * idempotency check in setContainerTimeouts so a first explicit call\n * persists even when the requested values already equal the in-memory\n * defaults, distinguishing \"user intent recorded\" from \"running on\n * env/SDK defaults\".\n */\n private hasStoredContainerTimeouts = false;\n\n /**\n * Dispatch method for tunnel operations.\n * Called by the client-side proxy created in getSandbox() to provide\n * the `sandbox.tunnels` API without relying on RPC pipelining through\n * property getters, which vite-plugin does not currently support. This\n * local method dispatch uses standard function application, so new\n * Workers RPC pipelining traits on `TunnelsRpcTarget` also need an\n * explicit path here before the proxy can expose them.\n */\n async callTunnels(method: string, args: unknown[]): Promise<unknown> {\n if (!['get', 'list', 'destroy'].includes(method)) {\n throw new Error(`Unknown tunnels method: ${method}`);\n }\n const client = this.tunnels;\n const fn = client[method as keyof typeof client];\n if (typeof fn !== 'function') {\n throw new Error(`sandbox.tunnels missing method: ${method}`);\n }\n return (fn as (...a: unknown[]) => unknown).apply(client, args);\n }\n\n async callExtension(\n extensionName: string,\n method: string,\n args: unknown[]\n ): Promise<unknown> {\n if (method === 'constructor' || method === 'then') {\n throw new Error(`Unknown extension method: ${extensionName}.${method}`);\n }\n const extension = (this as unknown as Record<string, unknown>)[\n extensionName\n ];\n if (!(extension instanceof SandboxExtension)) {\n throw new Error(`Unknown sandbox extension: ${extensionName}`);\n }\n const fn = getConcreteExtensionMethod(extension, method);\n if (!fn) {\n throw new Error(`Unknown extension method: ${extensionName}.${method}`);\n }\n return fn.apply(extension, args);\n }\n\n private renewActivityTimeoutIfAvailable(): void {\n const renewActivityTimeout = this.renewActivityTimeout;\n if (typeof renewActivityTimeout === 'function') {\n renewActivityTimeout.call(this);\n }\n }\n\n private getRuntimePortStub(port: number) {\n const stub = this.ctx.container?.getTcpPort?.(port);\n if (!stub) throw new Error('Container runtime port is not available');\n return stub;\n }\n\n constructor(ctx: DurableObjectState<{}>, env: Env) {\n super(ctx, env);\n\n const envObj = env as Record<string, unknown>;\n const sandboxEnvKeys = ['SANDBOX_LOG_LEVEL', 'SANDBOX_LOG_FORMAT'] as const;\n sandboxEnvKeys.forEach((key) => {\n if (envObj?.[key]) {\n this.envVars[key] = String(envObj[key]);\n }\n });\n\n // Initialize timeouts with env var fallbacks\n this.containerTimeouts = this.getDefaultTimeouts(envObj);\n\n this.logger = createLogger({\n component: 'sandbox-do',\n sandboxId: this.ctx.id.toString()\n });\n\n this.currentLifetime = new CurrentSandboxLifetime(this.ctx.storage);\n this.namedTunnelConfigResolver = new NamedTunnelConfigResolver({\n getEnv: () => this.env\n });\n this.backupService = new BackupService({\n ctx: this.ctx,\n getEnv: () => this.env,\n logger: this.logger,\n runBackupAttempt: (operation, call) =>\n this.runWakingComposite(operation, (lease) =>\n call({\n runtime: lease.runtime,\n control: lease.control,\n retain: lease.retain\n })\n ),\n runtimeReader: {\n get: () => this.runtimeLifecycle.get(),\n getStored: (storage) => this.runtimeLifecycle.getStored(storage),\n isActive: (runtime) => this.runtimeLifecycle.isActive(runtime),\n assertActive: (runtime) => this.runtimeLifecycle.assertActive(runtime)\n },\n currentLifetime: this.currentLifetime\n });\n\n this.r2AccessKeyId = getEnvString(envObj, 'R2_ACCESS_KEY_ID') ?? null;\n this.r2SecretAccessKey =\n getEnvString(envObj, 'R2_SECRET_ACCESS_KEY') ?? null;\n\n // Construct the control callback BEFORE the client — RPC clients\n // capture it as `localMain` on the capnweb session, and the\n // session is created eagerly in the connection's constructor.\n this.controlCallback = new SandboxControlCallbackImpl(\n () => this.tunnelExitHandler,\n this.logger,\n undefined,\n () => this.runtimeLifecycle.get()\n );\n this.resourceActivityGate = new ResourceActivityGate(\n () => this.renewActivityTimeoutIfAvailable(),\n () => this.performRuntimeStop(() => super.stop())\n );\n this.runtimeSessions = new RuntimeSessionManager({\n getTcpPort: (port) => this.getRuntimePortStub(port),\n logger: this.logger,\n callbackBinder: (runtime, isSessionCurrent) =>\n this.controlCallback.bindRuntime(runtime, isSessionCurrent)\n });\n this.runtimeLifecycle = new SandboxRuntimeLifecycle({\n storage: this.ctx.storage,\n isRuntimeRunning: () => this.ctx.container?.running === true,\n startControlPort: async (port, options) => {\n await super.startAndWaitForPorts({\n ports: [port],\n cancellationOptions: {\n instanceGetTimeoutMS: this.containerTimeouts.instanceGetTimeoutMS,\n portReadyTimeoutMS: this.containerTimeouts.portReadyTimeoutMS,\n waitInterval: this.containerTimeouts.waitIntervalMS,\n abort: options?.signal\n },\n startOptions: options?.startOptions\n });\n },\n waitForControlPort: async () => undefined,\n stopControlPort: async () => undefined,\n probe: new RuntimeBootstrapProbe({\n getTcpPort: (port) => this.getRuntimePortStub(port)\n }),\n sessions: this.runtimeSessions,\n observeVersionCompatibility: async (_client, _runtime) => undefined,\n reconcileReplacement: async () => {\n await pruneTunnelsForRestart(this.ctx.storage);\n }\n });\n this.runtimeRunner = new RuntimeOperationRunner({\n lifecycle: this.runtimeLifecycle,\n activityGate: this.resourceActivityGate\n });\n this.bucketMounts = new BucketMountService({\n getEnv: () => this.env,\n getEnvVars: () => this.envVars,\n runMountAttempt: (operation, call) =>\n this.runWakingComposite(operation, (lease) =>\n call({\n runtime: lease.runtime,\n control: lease.control,\n retain: lease.retain\n })\n ),\n runExistingMountAttempt: async (operation, call) => {\n const result = await this.runtimeRunner.runExisting(\n { kind: 'current' },\n operation,\n (lease) =>\n call({\n runtime: lease.runtime,\n control: lease.control,\n retain: lease.retain\n })\n );\n if (isRuntimeAbsent(result)) return { status: 'absent' };\n return { status: 'completed', value: result };\n },\n logger: this.logger,\n runtimeReader: {\n get: () => this.runtimeLifecycle.get(),\n getStored: (storage) => this.runtimeLifecycle.getStored(storage),\n isActive: (runtime) => this.runtimeLifecycle.isActive(runtime),\n assertActive: (runtime) => this.runtimeLifecycle.assertActive(runtime)\n },\n currentLifetime: this.currentLifetime,\n getR2AccessKeyID: () => this.r2AccessKeyId,\n getR2SecretAccessKey: () => this.r2SecretAccessKey,\n getOutboundHost: () => this.getMountOutboundHost()\n });\n this.previewService = new PreviewService({\n storage: this.ctx.storage,\n logger: this.logger,\n getStoredRuntime: (storage) => this.runtimeLifecycle.getStored(storage),\n assertRuntimeActive: (runtime) =>\n this.runtimeLifecycle.assertActive(runtime),\n getContainerState: () => this.getState(),\n getForwardingContainer: () => this.getPreviewForwardingContainer(),\n runWaking: (operation, call) =>\n this.runWakingComposite(operation, (lease) => call(lease)),\n runExisting: async (operation, call) => {\n const result = await this.runtimeRunner.runExisting(\n { kind: 'current' },\n operation,\n (lease) => call(lease)\n );\n if (isRuntimeAbsent(result)) return null;\n return result;\n },\n getSandboxName: () => this.sandboxName,\n getNormalizeID: () => this.normalizeId\n });\n\n this.ctx.blockConcurrencyWhile(async () => {\n this.sandboxName =\n (await this.ctx.storage.get<string>('sandboxName')) ?? null;\n this.normalizeId =\n (await this.ctx.storage.get<boolean>('normalizeId')) ?? false;\n this.keepAliveEnabled =\n (await this.ctx.storage.get<boolean>('keepAliveEnabled')) ?? false;\n\n // Load saved timeout configuration (highest priority)\n const storedTimeouts =\n await this.ctx.storage.get<\n NonNullable<SandboxOptions['containerTimeouts']>\n >('containerTimeouts');\n if (storedTimeouts) {\n this.containerTimeouts = {\n ...this.containerTimeouts,\n ...storedTimeouts\n };\n this.hasStoredContainerTimeouts = true;\n }\n\n // Restore sleep timeout if previously set via RPC\n const storedSleepAfter = await this.ctx.storage.get<string | number>(\n 'sleepAfter'\n );\n if (storedSleepAfter !== undefined) {\n this.sleepAfter = storedSleepAfter;\n this.renewActivityTimeout();\n }\n\n if (this.interceptHttps) {\n this.envVars = { ...this.envVars, SANDBOX_INTERCEPT_HTTPS: '1' };\n }\n });\n }\n\n // RPC method to set the sandbox name and normalizeId. Set-once — a\n // subsequent call is a no-op.\n //\n // sandboxName and normalizeId are one logical unit. Both storage.put\n // calls run without an intervening await, so they land in the same\n // in-memory write buffer and flush as a single atomic transaction on\n // SQLite-backed Durable Objects. In-memory state is updated only after\n // both writes commit.\n async setSandboxName(name: string, normalizeId?: boolean): Promise<void> {\n if (this.sandboxName !== null) return;\n const effectiveNormalizeId = normalizeId ?? false;\n\n await Promise.all([\n this.ctx.storage.put('sandboxName', name),\n this.ctx.storage.put('normalizeId', effectiveNormalizeId)\n ]);\n\n this.sandboxName = name;\n this.normalizeId = effectiveNormalizeId;\n }\n\n async configure(configuration: SandboxConfiguration): Promise<void> {\n if (configuration.sandboxName) {\n await this.setSandboxName(\n configuration.sandboxName.name,\n configuration.sandboxName.normalizeId\n );\n }\n\n if (configuration.sleepAfter !== undefined) {\n await this.setSleepAfter(configuration.sleepAfter);\n }\n\n if (configuration.keepAlive !== undefined) {\n await this.setKeepAlive(configuration.keepAlive);\n }\n\n if (configuration.containerTimeouts !== undefined) {\n await this.setContainerTimeouts(configuration.containerTimeouts);\n }\n }\n\n // RPC method to set the sleep timeout. Idempotent: re-applying the same\n // value returns early with no storage write and no timer reset. A real\n // change persists, then reschedules the activity timer against the new\n // window length.\n async setSleepAfter(sleepAfter: string | number): Promise<void> {\n if (this.sleepAfter === sleepAfter) return;\n await this.ctx.storage.put('sleepAfter', sleepAfter);\n this.sleepAfter = sleepAfter;\n this.renewActivityTimeout();\n }\n\n // RPC method to enable keepAlive mode. Idempotent: re-applying the same\n // value returns early. When disabling (true to false), the activity\n // timer is renewed so the inactivity window counts from now.\n async setKeepAlive(keepAlive: boolean): Promise<void> {\n if (this.keepAliveEnabled === keepAlive) return;\n await this.ctx.storage.put('keepAliveEnabled', keepAlive);\n this.keepAliveEnabled = keepAlive;\n\n if (!keepAlive) {\n this.renewActivityTimeout();\n }\n }\n\n async setEnvVars(envVars: Record<string, string | undefined>): Promise<void> {\n const { toSet, toUnset } = partitionEnvVars(envVars);\n\n for (const key of toUnset) {\n delete this.envVars[key];\n }\n this.envVars = { ...this.envVars, ...toSet };\n }\n\n async setContainerTimeouts(\n timeouts: NonNullable<SandboxOptions['containerTimeouts']>\n ): Promise<void> {\n const validated = { ...this.containerTimeouts };\n\n if (timeouts.instanceGetTimeoutMS !== undefined) {\n validated.instanceGetTimeoutMS = this.validateTimeout(\n timeouts.instanceGetTimeoutMS,\n 'instanceGetTimeoutMS',\n 5_000,\n 300_000\n );\n }\n\n if (timeouts.portReadyTimeoutMS !== undefined) {\n validated.portReadyTimeoutMS = this.validateTimeout(\n timeouts.portReadyTimeoutMS,\n 'portReadyTimeoutMS',\n 10_000,\n 600_000\n );\n }\n\n if (timeouts.waitIntervalMS !== undefined) {\n validated.waitIntervalMS = this.validateTimeout(\n timeouts.waitIntervalMS,\n 'waitIntervalMS',\n 100,\n 5_000\n );\n }\n\n if (\n this.hasStoredContainerTimeouts &&\n validated.instanceGetTimeoutMS ===\n this.containerTimeouts.instanceGetTimeoutMS &&\n validated.portReadyTimeoutMS ===\n this.containerTimeouts.portReadyTimeoutMS &&\n validated.waitIntervalMS === this.containerTimeouts.waitIntervalMS\n ) {\n return;\n }\n\n await this.ctx.storage.put('containerTimeouts', validated);\n this.containerTimeouts = validated;\n this.hasStoredContainerTimeouts = true;\n this.logger.debug('Container timeouts updated', this.containerTimeouts);\n }\n\n private validateTimeout(\n value: number,\n name: string,\n min: number,\n max: number\n ): number {\n if (\n typeof value !== 'number' ||\n Number.isNaN(value) ||\n !Number.isFinite(value)\n ) {\n throw new Error(`${name} must be a valid finite number, got ${value}`);\n }\n\n if (value < min || value > max) {\n throw new Error(\n `${name} must be between ${min}-${max}ms, got ${value}ms`\n );\n }\n\n return value;\n }\n\n private getDefaultTimeouts(\n env: Record<string, unknown>\n ): typeof this.DEFAULT_CONTAINER_TIMEOUTS {\n const parseAndValidate = (\n envVar: string | undefined,\n name: keyof typeof this.DEFAULT_CONTAINER_TIMEOUTS,\n min: number,\n max: number\n ): number => {\n const defaultValue = this.DEFAULT_CONTAINER_TIMEOUTS[name];\n\n if (envVar === undefined) {\n return defaultValue;\n }\n\n const parsed = parseInt(envVar, 10);\n\n if (Number.isNaN(parsed)) {\n this.logger.warn(\n `Invalid ${name}: \"${envVar}\" is not a number. Using default: ${defaultValue}ms`\n );\n return defaultValue;\n }\n\n if (parsed < min || parsed > max) {\n this.logger.warn(\n `Invalid ${name}: ${parsed}ms. Must be ${min}-${max}ms. Using default: ${defaultValue}ms`\n );\n return defaultValue;\n }\n\n return parsed;\n };\n\n return {\n instanceGetTimeoutMS: parseAndValidate(\n getEnvString(env, 'SANDBOX_INSTANCE_TIMEOUT_MS'),\n 'instanceGetTimeoutMS',\n 5_000,\n 300_000\n ),\n portReadyTimeoutMS: parseAndValidate(\n getEnvString(env, 'SANDBOX_PORT_TIMEOUT_MS'),\n 'portReadyTimeoutMS',\n 10_000,\n 600_000\n ),\n waitIntervalMS: parseAndValidate(\n getEnvString(env, 'SANDBOX_POLL_INTERVAL_MS'),\n 'waitIntervalMS',\n 100,\n 5_000\n )\n };\n }\n\n /**\n * Mount an S3-compatible bucket as a local directory.\n */\n async mountBucket(\n bucket: string,\n mountPath: string,\n options: MountBucketOptions\n ): Promise<void> {\n return this.bucketMounts.mountBucket(bucket, mountPath, options);\n }\n\n /**\n * Manually unmount a bucket filesystem.\n */\n async unmountBucket(mountPath: string): Promise<void> {\n return this.bucketMounts.unmountBucket(mountPath);\n }\n\n /**\n * In-flight `destroy()` promise. While set, concurrent callers coalesce\n * onto the same teardown instead of triggering a second one. Cleared when\n * the underlying work settles, so a later call that genuinely needs to\n * recreate a destroyed sandbox still runs.\n *\n * If the underlying teardown hangs (e.g. `super.destroy()` never resolves\n * because the Containers control plane is unresponsive), every coalesced\n * caller hangs on the same promise until the Durable Object is evicted.\n * This is deliberate: a second concurrent teardown would not make a stuck\n * control plane unstuck, and spawning one would defeat the point of\n * coalescing. Callers that need bounded waits must apply their own\n * timeout around `destroy()`.\n */\n private inflightDestroy: Promise<void> | null = null;\n\n /**\n * Cleanup and destroy the sandbox container.\n *\n * Concurrent calls coalesce: if a previous `destroy()` is still in flight,\n * subsequent calls await the same underlying work instead of starting a\n * second teardown. A canonical `sandbox.destroy.coalesced` event is logged\n * per coalesced call so repeated destroy traffic is observable.\n */\n override async destroy(): Promise<void> {\n if (this.inflightDestroy) {\n logCanonicalEvent(this.logger, {\n event: 'sandbox.destroy.coalesced',\n outcome: 'success',\n durationMs: 0\n });\n return this.inflightDestroy;\n }\n\n // Assigned synchronously so concurrent callers observe the promise\n // before any await point inside doDestroy().\n const work = this.resourceActivityGate.runDestroyTeardown(() =>\n this.doDestroy()\n );\n this.inflightDestroy = work;\n try {\n await work;\n } finally {\n // Clears only if the field still references this teardown.\n if (this.inflightDestroy === work) {\n this.inflightDestroy = null;\n }\n }\n }\n\n private async doDestroy(): Promise<void> {\n const startTime = Date.now();\n let mountsProcessed = 0;\n let mountFailures = 0;\n let outcome: 'success' | 'error' = 'error';\n let caughtError: Error | undefined;\n\n try {\n const cleanupRuntime =\n await this.runtimeLifecycle.invalidateAndObserveStoredActive();\n\n // Preview URL auth and activation are cleared before await-heavy\n // teardown work. Concurrent preview traffic should observe missing\n // auth or runtime state and fail from DO-owned state without reaching\n // the container.\n await this.currentLifetime.rotate();\n await this.previewService.clearPreviewState();\n\n ({ mountsProcessed, mountFailures } =\n await this.runBoundedDestroyRuntimeCleanup(cleanupRuntime));\n const durableTunnels = this.createDurableDestroyTunnelsHandle();\n await durableTunnels.destroyAll();\n await durableTunnels.clearDurableStateAfterDestroy();\n\n await super.destroy();\n outcome = 'success';\n } catch (error) {\n caughtError = error instanceof Error ? error : new Error(String(error));\n throw error;\n } finally {\n logCanonicalEvent(this.logger, {\n event: 'sandbox.destroy',\n outcome,\n durationMs: Date.now() - startTime,\n mountsProcessed,\n mountFailures,\n error: caughtError\n });\n }\n }\n\n private async performRuntimeStop(\n physicalStop: () => Promise<void>\n ): Promise<void> {\n await this.runtimeLifecycle.invalidate();\n await this.previewService.clearActivePreviewPorts();\n await physicalStop();\n }\n\n private async runBoundedDestroyRuntimeCleanup(\n cleanupRuntime: RuntimeIdentity | null\n ): Promise<{\n mountsProcessed: number;\n mountFailures: number;\n }> {\n if (!cleanupRuntime) {\n return await this.bucketMounts.cleanupForDestroyWithoutRuntime();\n }\n\n let timeoutID: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<'timeout'>((resolve) => {\n timeoutID = setTimeout(\n () => resolve('timeout'),\n DESTROY_RUNTIME_CLEANUP_TIMEOUT_MS\n );\n });\n const cleanup = this.cleanupRuntimeBeforeDestroy(cleanupRuntime);\n let result: 'timeout' | { mountsProcessed: number; mountFailures: number };\n try {\n result = await Promise.race([cleanup, timeout]);\n } catch (error) {\n this.logger.warn('Failed runtime cleanup before destroy()', {\n error: error instanceof Error ? error.message : String(error)\n });\n return await this.bucketMounts.cleanupForDestroyWithoutRuntime();\n } finally {\n if (timeoutID) clearTimeout(timeoutID);\n this.runtimeSessions.closeActive();\n }\n if (result === 'timeout') {\n this.logger.warn('Timed out during runtime cleanup before destroy()', {\n timeoutMs: DESTROY_RUNTIME_CLEANUP_TIMEOUT_MS\n });\n cleanup.catch((error) => {\n this.logger.warn('Late runtime cleanup failed after destroy timeout', {\n error: error instanceof Error ? error.message : String(error)\n });\n });\n return await this.bucketMounts.cleanupForDestroyWithoutRuntime();\n }\n return result;\n }\n\n private async cleanupRuntimeBeforeDestroy(\n cleanupRuntime: RuntimeIdentity\n ): Promise<{\n mountsProcessed: number;\n mountFailures: number;\n }> {\n const session = await this.runtimeSessions.acquireSession(cleanupRuntime);\n const hold = session.retain();\n const runControl = async <T>(\n _operation: string,\n call: (control: ContainerControlClient) => Promise<T>\n ) => {\n if (session.isInterrupted()) return session.interrupted;\n return Promise.race([call(session.client), session.interrupted]);\n };\n let mountsProcessed = 0;\n let mountFailures = 0;\n try {\n ({ mountsProcessed, mountFailures } =\n await this.bucketMounts.cleanupForDestroyUsing(runControl));\n if (mountFailures > 0) {\n this.logger.warn('Failed to clean up bucket mounts during destroy()', {\n mountFailures\n });\n }\n } catch (error) {\n this.logger.warn('Failed to clean up bucket mounts during destroy()', {\n error: error instanceof Error ? error.message : String(error)\n });\n }\n\n try {\n const teardownTunnels = this.createTeardownTunnelsHandle(\n runControl,\n cleanupRuntime\n );\n await teardownTunnels.destroyAllRuntimeRuns();\n } catch (error) {\n this.logger.warn('Failed to tear down tunnels during destroy()', {\n error: error instanceof Error ? error.message : String(error)\n });\n } finally {\n hold.release();\n }\n\n return { mountsProcessed, mountFailures };\n }\n\n private createTeardownTunnelsHandle(\n runControl: <T>(\n operation: string,\n call: (control: ContainerControlClient) => Promise<T>\n ) => Promise<T>,\n cleanupRuntime: RuntimeIdentity\n ): TunnelsHandle {\n return this.createDestroyTunnelsHandle(async (runtime, operation, call) => {\n if (\n runtime.id !== cleanupRuntime.id ||\n runtime.runtimeIncarnationID !== cleanupRuntime.runtimeIncarnationID\n ) {\n return null;\n }\n return await runControl(operation, (control) =>\n call(control.tunnels as SandboxTunnelsAPI)\n );\n });\n }\n\n private createDurableDestroyTunnelsHandle(): TunnelsHandle {\n return this.createDestroyTunnelsHandle(async () => null);\n }\n\n private createDestroyTunnelsHandle(\n runExisting: Parameters<typeof createTunnelsHandle>[0]['runExisting']\n ): TunnelsHandle {\n return createTunnelsHandle({\n runProvision: async () => {\n throw new Error('Tunnel provisioning is unavailable during teardown');\n },\n runExisting,\n getStoredRuntime: (storage) => this.runtimeLifecycle.getStored(storage),\n storage: this.ctx.storage,\n logger: this.logger,\n sandboxId: this.ctx.id.toString(),\n currentLifetime: this.currentLifetime,\n getNamedTunnelConfig: () => this.namedTunnelConfigResolver.getConfig()\n });\n }\n\n override async onStart() {\n const replacementStartTransitionCompleted =\n this.runtimeLifecycle.markRuntimeStarted();\n this.logger.debug('Sandbox started', {\n replacementStartTransitionCompleted\n });\n }\n\n override async start(\n options?: Parameters<Container<Env>['start']>[0]\n ): Promise<void> {\n await this.runtimeRunner.runWaking('runtime.start', async () => undefined, {\n startOptions: options as ContainerStartConfigOptions | undefined\n });\n }\n\n override startAndWaitForPorts(\n args: StartAndWaitForPortsOptions\n ): Promise<void>;\n override startAndWaitForPorts(\n portsOrArgs?: number | number[] | StartAndWaitForPortsOptions,\n cancellationOptions?: CancellationOptions,\n startOptions?: ContainerStartConfigOptions\n ): Promise<void>;\n override async startAndWaitForPorts(\n portsOrArgs?: number | number[] | StartAndWaitForPortsOptions,\n cancellationOptions?: CancellationOptions,\n startOptions?: ContainerStartConfigOptions\n ): Promise<void> {\n const normalizedOptions = normalizeStartAndWaitForPortsOptions(\n portsOrArgs,\n cancellationOptions,\n startOptions\n );\n const ports = normalizePorts(normalizedOptions.ports);\n await this.runtimeRunner.runWaking(\n 'runtime.port.ready',\n async (lease) => {\n await Promise.all(\n ports\n .filter((port) => port !== 3000)\n .map((port) =>\n waitForRuntimePort(lease, port, {\n timeout:\n normalizedOptions.cancellationOptions?.portReadyTimeoutMS,\n interval: normalizedOptions.cancellationOptions?.waitInterval,\n signal: normalizedOptions.cancellationOptions?.abort\n })\n )\n );\n },\n {\n signal: normalizedOptions.cancellationOptions?.abort,\n startOptions: normalizedOptions.startOptions\n }\n );\n }\n\n override async stop(\n signal?: Parameters<Container<Env>['stop']>[0]\n ): Promise<void> {\n await this.resourceActivityGate.runStopTeardown(() =>\n this.performRuntimeStop(() => super.stop(signal))\n );\n }\n\n override async onStop() {\n this.logger.debug('Sandbox stopped');\n\n const runtimeStopDisposition =\n await this.runtimeLifecycle.reconcileObservedStop();\n this.logger.debug('Sandbox runtime stop reconciled', {\n runtimeStopDisposition\n });\n await this.previewService.clearActivePreviewPorts();\n\n try {\n this.ensureTunnelsBuilt();\n await this.tunnelServiceHandle?.onRuntimeStop();\n } catch (error) {\n this.logger.error(\n 'Failed to reconcile tunnel storage after container stop',\n error instanceof Error ? error : new Error(String(error))\n );\n }\n\n await this.bucketMounts.cleanupForStop();\n\n // Port tokens are durable authorization and survive container restarts;\n // runtime-scoped preview activation is cleared separately above.\n }\n\n override onError(error: unknown) {\n this.logger.error(\n 'Sandbox error',\n error instanceof Error ? error : new Error(String(error))\n );\n }\n\n /**\n * Override Container.containerFetch to route direct forwarding through runtime admission.\n */\n override async containerFetch(\n requestOrUrl: Request | string | URL,\n portOrInit?: number | RequestInit,\n portParam?: number\n ): Promise<Response> {\n const { request, port } = this.parseContainerFetchArgs(\n requestOrUrl,\n portOrInit,\n portParam\n );\n const pathname = new URL(request.url).pathname;\n if (port === 3000 && pathname === '/rpc') {\n throw new SandboxSecurityError(\n 'Container RPC connection is not authorized'\n );\n }\n if (port === 3000 && pathname === '/ws/terminal') {\n throw new SandboxSecurityError('Terminal connection is not authorized');\n }\n let physicalForwardStarted = false;\n\n try {\n return await this.runtimeRunner.runWaking(\n 'container.fetch',\n async (lease) => {\n await waitForRuntimePort(lease, port, {\n timeout: this.containerTimeouts.portReadyTimeoutMS,\n interval: this.containerTimeouts.waitIntervalMS,\n signal: request.signal\n });\n try {\n await this.runtimeLifecycle.assertActive(lease.runtime);\n } catch {\n throw runtimeInterrupted('container.fetch');\n }\n physicalForwardStarted = true;\n return retainedResponse(\n await this.getRuntimePortStub(port).fetch(\n toContainerHTTPRequest(request)\n ),\n lease,\n 'container.fetch.body'\n );\n },\n { signal: request.signal }\n );\n } catch (error) {\n if (\n error instanceof OperationInterruptedError ||\n physicalForwardStarted\n ) {\n throw error;\n }\n if (request.signal.aborted) {\n throw request.signal.reason ?? error;\n }\n const state = await this.getState();\n const staleStateDetected =\n state.status === 'healthy' && this.ctx.container?.running === false;\n return this.containerStartupErrorResponse(error, staleStateDetected);\n }\n }\n\n private containerStartupErrorResponse(\n error: unknown,\n staleStateDetected: boolean\n ): Response {\n if (this.isNoInstanceError(error)) {\n const errorBody: ErrorResponse = {\n code: ErrorCode.CONTAINER_UNAVAILABLE,\n message:\n 'Container is currently provisioning. This can take several minutes on first deployment.',\n context: { reason: 'container_starting', retryable: true },\n httpStatus: 503,\n timestamp: new Date().toISOString(),\n suggestion:\n 'The container is still being provisioned. Retry the operation in a moment.'\n };\n return new Response(JSON.stringify(errorBody), {\n status: 503,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': '10'\n }\n });\n }\n\n if (this.isPermanentStartupError(error)) {\n this.logger.error(\n 'Permanent container startup error, returning 500',\n error instanceof Error ? error : new Error(String(error))\n );\n const errorBody: ErrorResponse = {\n code: ErrorCode.INTERNAL_ERROR,\n message:\n 'Container failed to start due to a permanent error. Check your container configuration.',\n context: {\n phase: 'startup',\n error: error instanceof Error ? error.message : String(error)\n },\n httpStatus: 500,\n timestamp: new Date().toISOString(),\n suggestion:\n 'This error will not resolve with retries. Check container logs, image name, and resource limits.'\n };\n return new Response(JSON.stringify(errorBody), {\n status: 500,\n headers: { 'Content-Type': 'application/json' }\n });\n }\n\n if (this.isTransientStartupError(error)) {\n if (staleStateDetected) {\n this.logger.warn('container.startup', {\n outcome: 'stale_state_abort',\n staleStateDetected: true,\n error: error instanceof Error ? error.message : String(error)\n });\n this.ctx.abort();\n } else {\n this.logger.debug('container.startup', {\n outcome: 'transient_error',\n staleStateDetected,\n error: error instanceof Error ? error.message : String(error)\n });\n }\n const errorBody: ErrorResponse = {\n code: ErrorCode.CONTAINER_UNAVAILABLE,\n message: 'Container is starting. Please retry in a moment.',\n context: { reason: 'container_starting', retryable: true },\n httpStatus: 503,\n timestamp: new Date().toISOString(),\n suggestion:\n 'The container is not ready yet. Retry the operation in a moment.'\n };\n return new Response(JSON.stringify(errorBody), {\n status: 503,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': '3'\n }\n });\n }\n\n this.logger.warn('container.startup', {\n outcome: 'unrecognized_error',\n staleStateDetected,\n error: error instanceof Error ? error.message : String(error)\n });\n const errorBody: ErrorResponse = {\n code: ErrorCode.CONTAINER_UNAVAILABLE,\n message: 'Container is starting. Please retry in a moment.',\n context: { reason: 'container_starting', retryable: true },\n httpStatus: 503,\n timestamp: new Date().toISOString(),\n suggestion:\n 'The container is not ready yet. Retry the operation in a moment.'\n };\n return new Response(JSON.stringify(errorBody), {\n status: 503,\n headers: {\n 'Content-Type': 'application/json',\n 'Retry-After': '5'\n }\n });\n }\n\n /**\n * Helper: Check if error is \"no container instance available\"\n * This indicates the container VM is still being provisioned.\n */\n private isNoInstanceError(error: unknown): boolean {\n return (\n error instanceof Error &&\n error.message.toLowerCase().includes('no container instance')\n );\n }\n\n /**\n * Helper: Check if error is a transient startup error that should trigger retry\n *\n * These errors occur during normal container startup and are recoverable:\n * - Port not yet mapped (container starting, app not listening yet)\n * - Connection refused (port mapped but app not ready)\n * - Timeouts during startup (recoverable with retry)\n * - Network transients (temporary connectivity issues)\n *\n * Errors NOT included (permanent failures):\n * - \"no such image\" - missing Docker image\n * - \"container already exists\" - name collision\n * - Configuration errors\n */\n private isTransientStartupError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n\n const msg = error.message.toLowerCase();\n\n // Transient errors from workerd container-client.c++ and @cloudflare/containers\n const transientPatterns = [\n // Port mapping race conditions (workerd DockerPort::connect)\n 'container port not found',\n 'connection refused: container port',\n\n // Application startup delays (@cloudflare/containers)\n 'the container is not listening',\n 'failed to verify port',\n 'container did not start',\n\n // Network transients (workerd)\n 'network connection lost',\n 'container suddenly disconnected',\n\n // Monitor race conditions (workerd)\n 'monitor failed to find container',\n\n // Container crashed during startup or from previous run (@cloudflare/containers)\n 'container exited with unexpected exit code',\n 'container exited before we could determine',\n\n // Timeouts (various layers)\n 'timed out',\n 'timeout',\n 'the operation was aborted'\n ];\n\n return transientPatterns.some((pattern) => msg.includes(pattern));\n }\n\n /**\n * Helper: Check if error is a permanent startup failure that will never recover\n *\n * These errors indicate resource exhaustion, misconfiguration, or missing images.\n * Retrying will never succeed, so the SDK should fail fast with HTTP 500.\n *\n * Error sources (traced from platform internals):\n * - Container runtime: OOM, PID limit\n * - Scheduling/provisioning: no matching app, no namespace configured\n * - workerd container-client.c++: no such image\n * - @cloudflare/containers: did not call start\n */\n private isPermanentStartupError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n\n const msg = error.message.toLowerCase();\n\n const permanentPatterns = [\n // Resource exhaustion (container runtime)\n 'ran out of memory',\n 'too many subprocesses',\n\n // Misconfiguration (scheduling/provisioning)\n 'no application that matches',\n 'no container application assigned',\n\n // Missing image (workerd container-client.c++)\n 'no such image',\n\n // User error (@cloudflare/containers)\n 'did not call start'\n ];\n\n return permanentPatterns.some((pattern) => msg.includes(pattern));\n }\n\n /**\n * Helper: Parse containerFetch arguments (supports multiple signatures)\n */\n private parseContainerFetchArgs(\n requestOrUrl: Request | string | URL,\n portOrInit?: number | RequestInit,\n portParam?: number\n ): { request: Request; port: number } {\n let request: Request;\n let port: number | undefined;\n\n if (requestOrUrl instanceof Request) {\n request = requestOrUrl;\n port = typeof portOrInit === 'number' ? portOrInit : undefined;\n } else {\n const url =\n typeof requestOrUrl === 'string'\n ? requestOrUrl\n : requestOrUrl.toString();\n const init = typeof portOrInit === 'number' ? {} : portOrInit || {};\n port =\n typeof portOrInit === 'number'\n ? portOrInit\n : typeof portParam === 'number'\n ? portParam\n : undefined;\n request = new Request(url, init);\n }\n\n port ??= this.defaultPort;\n\n if (port === undefined) {\n throw new Error('No port specified for container fetch');\n }\n\n return { request, port };\n }\n\n override async onActivityExpired(): Promise<void> {\n await this.resourceActivityGate.runExpiry(\n {\n availability: async () => {\n const state = await this.getState();\n if (state.status !== 'healthy') return 'absent';\n if (this.ctx.container?.running === false) return 'absent';\n if (this.ctx.container?.running !== true) return 'unknown';\n return 'available';\n },\n processesHasActive: async () => {\n const result = await this.runtimeRunner.probeExisting(\n { kind: 'current' },\n 'processes.hasActive',\n (lease) => lease.control.processes.hasActive()\n );\n return result === RUNTIME_ABSENT ? false : (result as boolean);\n },\n terminalsHasActive: async () => {\n const result = await this.runtimeRunner.probeExisting(\n { kind: 'current' },\n 'terminals.hasActive',\n (lease) => lease.control.terminals.hasActive()\n );\n return result === RUNTIME_ABSENT ? false : (result as boolean);\n }\n },\n this.keepAliveEnabled\n );\n }\n\n private getPreviewForwardingContainer(): PreviewForwardingContainerState['container'] {\n return (this.ctx as PreviewForwardingContainerState).container;\n }\n\n // Override fetch to route internal container requests to appropriate ports\n override async fetch(request: Request): Promise<Response> {\n // Extract or generate trace ID from request\n const traceId =\n TraceContext.fromHeaders(request.headers) || TraceContext.generate();\n\n // Create request-specific logger with trace ID\n const requestLogger = this.logger.child({ traceId, operation: 'fetch' });\n\n const url = new URL(request.url);\n\n if (isPreviewProxyRequest(request)) {\n return await this.previewService.proxyPreviewRequest(request);\n }\n\n // Capture and store the sandbox name from the header if present\n if (!this.sandboxName && request.headers.has('X-Sandbox-Name')) {\n const name = request.headers.get('X-Sandbox-Name')!;\n this.sandboxName = name;\n await this.ctx.storage.put('sandboxName', name);\n }\n\n // Detect WebSocket upgrade request (RFC 6455 compliant)\n const upgradeHeader = request.headers.get('Upgrade');\n const connectionHeader = request.headers.get('Connection');\n const isWebSocket =\n upgradeHeader?.toLowerCase() === 'websocket' &&\n connectionHeader?.toLowerCase().includes('upgrade');\n\n if (isWebSocket) {\n const port = this.determinePort(request);\n try {\n requestLogger.debug('WebSocket upgrade requested', {\n path: url.pathname,\n port\n });\n return await this.connectWebSocket(request, port);\n } catch (error) {\n requestLogger.error(\n 'WebSocket connection failed',\n error instanceof Error ? error : new Error(String(error)),\n { path: url.pathname }\n );\n throw error;\n }\n }\n\n // Non-WebSocket: Use existing port determination and HTTP routing logic\n const port = this.determinePort(request);\n\n // Route to the appropriate port\n return await this.containerFetch(request, port);\n }\n\n async wsConnect(request: Request, port: number): Promise<Response> {\n return await connect(this)(request, port);\n }\n\n async authorizePortRequest(port: number, path: string): Promise<string> {\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw new SandboxSecurityError(\n `Invalid port number: ${port}. Must be between 1 and 65535.`\n );\n }\n return this.#createPortRequestToken(port, path, 'port');\n }\n\n #createPortRequestToken(\n port: number,\n path: string,\n scope: 'port' | 'terminal'\n ): string {\n const now = Date.now();\n for (const [token, route] of this.portRequestTokens) {\n if (route.expiresAt <= now) this.portRequestTokens.delete(token);\n }\n const token = crypto.randomUUID();\n this.portRequestTokens.set(token, {\n port,\n path,\n expiresAt: now + 30_000,\n scope\n });\n return token;\n }\n\n private connectWebSocket(request: Request, port: number): Promise<Response> {\n return this.runtimeRunner.runWaking(\n 'container.websocket',\n async (lease) => {\n await waitForRuntimePort(lease, port, {\n timeout: this.containerTimeouts.portReadyTimeoutMS,\n interval: this.containerTimeouts.waitIntervalMS,\n signal: request.signal\n });\n try {\n await this.runtimeLifecycle.assertActive(lease.runtime);\n } catch {\n throw runtimeInterrupted('container.websocket');\n }\n let interrupted = false;\n let retained: ReturnType<typeof retainWebSocketResponse> | undefined;\n let releaseHold = () => {};\n const hold = lease.retain(() => {\n interrupted = true;\n retained?.interrupt();\n releaseHold();\n });\n releaseHold = hold.release;\n if (interrupted) {\n hold.release();\n throw runtimeInterrupted('container.websocket');\n }\n try {\n const response = await this.getRuntimePortStub(port).fetch(\n toContainerHTTPRequest(request)\n );\n retained = retainWebSocketResponse(response, hold.release);\n if (interrupted) {\n retained.interrupt();\n throw runtimeInterrupted('container.websocket');\n }\n return response;\n } catch (error) {\n hold.release();\n throw error;\n }\n },\n { signal: request.signal }\n );\n }\n\n async createTerminal(\n options: CreateTerminalOptions\n ): Promise<TerminalRPCDescriptor> {\n return this.runtimeRunner.runWaking('terminal.create', async (lease) =>\n this.terminalDescriptor(\n await lease.control.terminals.create(options),\n lease.runtime\n )\n );\n }\n\n async getTerminal(id: string): Promise<TerminalRPCDescriptor | null> {\n const result = await this.runtimeRunner.runExisting(\n { kind: 'current' },\n 'terminal.get',\n async (lease) => {\n const snapshot = await lease.control.terminals.get(id);\n return snapshot\n ? this.terminalDescriptor(snapshot, lease.runtime)\n : null;\n }\n );\n if (isRuntimeAbsent(result)) return null;\n return result;\n }\n\n async listTerminals(): Promise<TerminalRPCDescriptor[]> {\n const result = await this.runtimeRunner.runExisting(\n { kind: 'current' },\n 'terminal.list',\n async (lease) =>\n (await lease.control.terminals.list()).map((snapshot) =>\n this.terminalDescriptor(snapshot, lease.runtime)\n )\n );\n if (isRuntimeAbsent(result)) return [];\n return result;\n }\n\n private terminalDescriptor(\n snapshot: TerminalSnapshot,\n runtime: RuntimeIdentity\n ): TerminalRPCDescriptor {\n const stub = this.terminalStub(runtime);\n const control: TerminalCapabilityControl = {\n get: (id) => stub.get(id),\n openOutput: (id, options) => stub.output(id, options),\n write: (id, data) => stub.write(id, data),\n resize: (id, cols, rows) => stub.resize(id, cols, rows),\n interrupt: (id) => stub.interrupt(id),\n terminate: (id) => stub.terminate(id),\n authorizeConnection: async () =>\n this.#createPortRequestToken(3000, '/ws/terminal', 'terminal')\n };\n return {\n snapshot,\n runtimeIncarnationID: runtime.runtimeIncarnationID,\n capability: new TerminalCapabilityTarget(snapshot.id, control)\n };\n }\n\n private terminalStub(runtime: RuntimeIdentity): TerminalHandleStub {\n const runTerminal = async <T>(\n terminalId: string,\n operation: string,\n call: (lease: RuntimeLease) => Promise<T>\n ): Promise<T> => {\n let result: T | typeof RUNTIME_ABSENT;\n try {\n result = await this.runtimeRunner.runExisting<T>(\n { kind: 'runtime', runtime },\n operation,\n call\n );\n } catch (error) {\n if (error instanceof OperationInterruptedError) {\n throw staleTerminal(terminalId, operation);\n }\n throw error;\n }\n if (!isRuntimeAbsent(result)) return result;\n throw staleTerminal(terminalId, operation);\n };\n return {\n create: (options: CreateTerminalOptions) =>\n this.runtimeRunner.runWaking('terminal.create', (lease) =>\n lease.control.terminals.create(options)\n ),\n get: (id: string) =>\n runTerminal(id, 'terminal.getSnapshot', (lease) =>\n lease.control.terminals.get(id)\n ),\n list: async (): Promise<TerminalSnapshot[]> => {\n const result = await this.runtimeRunner.runExisting<TerminalSnapshot[]>(\n { kind: 'current' },\n 'terminal.list',\n (lease) => lease.control.terminals.list()\n );\n if (isRuntimeAbsent(result)) return [];\n return result;\n },\n output: (\n id: string,\n options?: Parameters<RuntimeLease['control']['terminals']['output']>[1]\n ) =>\n runTerminal(id, 'terminal.output', async (lease) => {\n let retained: InterruptibleTerminalSubscription | undefined;\n let releaseHold = () => {};\n const hold = lease.retain(() => {\n retained?.interrupt();\n releaseHold();\n });\n releaseHold = hold.release;\n try {\n const subscription = await lease.control.terminals.output(\n id,\n options\n );\n retained = retainedTerminalSubscription(\n id,\n 'terminal.output',\n subscription,\n hold.release\n );\n return new PullSubscriptionTarget(await retained.stream());\n } catch (error) {\n hold.release();\n throw error;\n }\n }),\n write: (id: string, data: Uint8Array) =>\n runTerminal(id, 'terminal.write', (lease) =>\n lease.control.terminals.write(id, data)\n ),\n resize: (id: string, cols: number, rows: number) =>\n runTerminal(id, 'terminal.resize', (lease) =>\n lease.control.terminals.resize(id, cols, rows)\n ),\n interrupt: (id: string) =>\n runTerminal(id, 'terminal.interrupt', (lease) =>\n lease.control.terminals.interrupt(id)\n ),\n terminate: (id: string) =>\n runTerminal(id, 'terminal.terminate', (lease) =>\n lease.control.terminals.terminate(id)\n ),\n hasActive: () =>\n this.runtimeRunner\n .probeExisting({ kind: 'current' }, 'terminals.hasActive', (lease) =>\n lease.control.terminals.hasActive()\n )\n .then((result) => (isRuntimeAbsent(result) ? false : result)),\n fetch: (request: Request) =>\n runTerminal('', 'terminal.connect', async (lease) => {\n let interrupted = false;\n let retained: ReturnType<typeof retainWebSocketResponse> | undefined;\n let releaseHold = () => {};\n const hold = lease.retain(() => {\n interrupted = true;\n retained?.interrupt();\n releaseHold();\n });\n releaseHold = hold.release;\n if (interrupted) {\n hold.release();\n throw staleTerminal('', 'terminal.connect');\n }\n try {\n const response = await this.getRuntimePortStub(3000).fetch(\n toContainerHTTPRequest(request)\n );\n retained = retainWebSocketResponse(response, hold.release);\n if (interrupted) {\n retained.interrupt();\n throw staleTerminal('', 'terminal.connect');\n }\n return response;\n } catch (error) {\n hold.release();\n throw error;\n }\n })\n };\n }\n\n private determinePort(request: Request): number {\n const targetPort = Number(request.headers.get('cf-container-target-port'));\n const routeToken = request.headers.get('x-sandbox-port-route-token');\n const pathname = new URL(request.url).pathname;\n const targetsControlPlane =\n !request.headers.has('cf-container-target-port') || targetPort === 3000;\n\n if (routeToken) {\n const route = this.portRequestTokens.get(routeToken);\n this.portRequestTokens.delete(routeToken);\n if (\n route &&\n route.expiresAt > Date.now() &&\n route.port === targetPort &&\n route.path === new URL(request.url).pathname\n ) {\n if (\n pathname === '/ws/terminal' &&\n targetsControlPlane &&\n route.scope !== 'terminal'\n ) {\n throw new SandboxSecurityError(\n 'Terminal connection is not authorized'\n );\n }\n if (pathname === '/rpc' && targetsControlPlane) {\n throw new SandboxSecurityError(\n 'Container RPC connection is not authorized'\n );\n }\n return targetPort;\n }\n }\n\n if (pathname === '/ws/terminal' && targetsControlPlane) {\n throw new SandboxSecurityError('Terminal connection is not authorized');\n }\n if (pathname === '/rpc' && targetsControlPlane) {\n throw new SandboxSecurityError(\n 'Container RPC connection is not authorized'\n );\n }\n\n if (request.headers.has('cf-container-target-port')) {\n if (\n !Number.isInteger(targetPort) ||\n targetPort < 1 ||\n targetPort > 65535\n ) {\n throw new SandboxSecurityError(\n `Invalid container target port: ${request.headers.get('cf-container-target-port')}`\n );\n }\n return targetPort;\n }\n\n // Direct fetches target the container server port.\n // SDK control operations use ContainerControlClient over /rpc instead.\n return 3000;\n }\n\n async exec(\n command: SandboxCommand,\n options: ExecOptions = {}\n ): Promise<ProcessRPCDescriptor> {\n const argv = validateExecArgv(command);\n const startTime = Date.now();\n const commandText = argv.join(' ');\n const canMergeEnvironment =\n options.env === undefined || isPlainObject(options.env);\n const launchOptions =\n canMergeEnvironment &&\n (Object.keys(this.envVars).length > 0 || options.env !== undefined)\n ? {\n ...options,\n env: mergeExecEnvironment(this.envVars, options.env ?? {})\n }\n : options;\n try {\n const descriptor = await this.runtimeRunner.runWaking(\n 'process.start',\n async (lease) => {\n const status = await lease.control.processes.start(\n argv,\n launchOptions\n );\n return this.processDescriptor(status, lease.runtime);\n }\n );\n\n logCanonicalEvent(this.logger, {\n event: 'sandbox.exec',\n outcome: 'success',\n command: commandText,\n processId: descriptor.id,\n pid: descriptor.pid,\n durationMs: Date.now() - startTime,\n origin: 'user'\n });\n return descriptor;\n } catch (error) {\n const execError =\n error instanceof Error ? error : new Error(String(error));\n logCanonicalEvent(this.logger, {\n event: 'sandbox.exec',\n outcome: 'error',\n command: commandText,\n durationMs: Date.now() - startTime,\n origin: 'user',\n error: execError,\n errorMessage: execError.message\n });\n throw error;\n }\n }\n\n private processDescriptor(\n status: ProcessStatus,\n runtime: RuntimeIdentity\n ): ProcessRPCDescriptor {\n const lifecycle = this.processCapabilityLifecycle(runtime, status);\n return {\n id: status.id,\n pid: status.pid,\n capability: new ProcessCapabilityTarget({\n id: status.id,\n pid: status.pid,\n runtime,\n lifecycle\n })\n };\n }\n\n private processCapabilityLifecycle(\n owningRuntime: RuntimeIdentity,\n process: Pick<ProcessStatus, 'id' | 'pid'>\n ) {\n const stale = (error: unknown) => {\n if (error instanceof OperationInterruptedError) {\n throw new StaleProcessHandleError({\n code: ErrorCode.STALE_PROCESS_HANDLE,\n message: 'Process handle refers to a previous runtime incarnation',\n context: {\n processId: process.id,\n pid: process.pid,\n operation: 'process.handle'\n },\n httpStatus: 409,\n timestamp: new Date().toISOString()\n });\n }\n throw error;\n };\n return {\n runRead: async <T>(\n _runtime: import('./processes/process-capability').ProcessCapabilityRuntime,\n operation: string,\n call: (control: ProcessCapabilityControl) => Promise<T>\n ) => {\n const result = await this.runtimeRunner\n .runExisting(\n { kind: 'runtime', runtime: owningRuntime },\n operation,\n (lease) => call(processCapabilityControl(lease))\n )\n .catch(stale);\n if (result === RUNTIME_ABSENT) stale(runtimeInterrupted(operation));\n return result as T;\n },\n runControl: async <T>(\n _runtime: import('./processes/process-capability').ProcessCapabilityRuntime,\n operation: string,\n call: (control: ProcessCapabilityControl) => Promise<T>\n ) => {\n const result = await this.runtimeRunner\n .runExisting(\n { kind: 'runtime', runtime: owningRuntime },\n operation,\n (lease) => call(processCapabilityControl(lease))\n )\n .catch(stale);\n if (result === RUNTIME_ABSENT) stale(runtimeInterrupted(operation));\n return result as T;\n }\n };\n }\n\n /** Internal bridge liveness read that neither starts nor renews a runtime. */\n async isRuntimeActive(): Promise<boolean> {\n return (await this.runtimeLifecycle.get()) !== null;\n }\n\n async getProcess(id: string): Promise<ProcessRPCDescriptor | null> {\n const result = await this.runtimeRunner.runExisting(\n { kind: 'current' },\n 'process.get',\n async (lease) => {\n const status = await lease.control.processes.get(id);\n return status ? this.processDescriptor(status, lease.runtime) : null;\n }\n );\n return result === RUNTIME_ABSENT\n ? null\n : (result as ProcessRPCDescriptor | null);\n }\n\n async listProcesses(): Promise<ProcessStatus[]> {\n const result = await this.runtimeRunner.runExisting(\n { kind: 'current' },\n 'process.list',\n (lease) => lease.control.processes.list()\n );\n return result === RUNTIME_ABSENT ? [] : (result as ProcessStatus[]);\n }\n\n async mkdir(path: string, options: { recursive?: boolean } = {}) {\n return this.runtimeRunner.runWaking('files.mkdir', (lease) =>\n lease.control.files.mkdir(path, { recursive: options.recursive })\n );\n }\n\n async writeFile(\n path: string,\n content: string | ReadableStream<Uint8Array>,\n options: { encoding?: string } = {}\n ) {\n return this.runtimeRunner.runWaking('files.write', async (lease) => {\n if (content instanceof ReadableStream) {\n const retained = retainedStream(content, lease);\n try {\n return await lease.control.files.writeFileStream(\n path,\n retained.stream\n );\n } finally {\n retained.cancel('writeFileStream completed');\n }\n }\n return lease.control.files.writeFile(path, content, {\n encoding: options.encoding\n });\n });\n }\n\n async deleteFile(path: string) {\n return this.runtimeRunner.runWaking('files.delete', (lease) =>\n lease.control.files.deleteFile(path)\n );\n }\n\n async renameFile(oldPath: string, newPath: string) {\n return this.runtimeRunner.runWaking('files.rename', (lease) =>\n lease.control.files.renameFile(oldPath, newPath)\n );\n }\n\n async moveFile(sourcePath: string, destinationPath: string) {\n return this.runtimeRunner.runWaking('files.move', (lease) =>\n lease.control.files.moveFile(sourcePath, destinationPath)\n );\n }\n\n /**\n * Read a file from the sandbox.\n *\n * @param encoding - How to encode the returned content:\n * - `undefined` (default): auto-detect from MIME type (text → UTF-8 string, binary → base64 string)\n * - `'utf-8'` / `'utf8'`: always return as UTF-8 string\n * - `'base64'`: always return as base64-encoded string\n * - `'none'`: return a result whose `content` is a raw binary `ReadableStream<Uint8Array>`\n * with no encoding overhead.\n */\n async readFile(\n path: string,\n options: { encoding: 'none' }\n ): Promise<ReadFileStreamResult>;\n async readFile(\n path: string,\n options?: { encoding?: Exclude<FileEncoding, 'none'> }\n ): Promise<ReadFileResult>;\n async readFile(\n path: string,\n options: { encoding?: FileEncoding } = {}\n ): Promise<ReadFileResult | ReadFileStreamResult> {\n return this.runtimeRunner.runWaking('files.read', async (lease) => {\n if (options.encoding === 'none') {\n const result = await lease.control.files.readFile(path, {\n encoding: 'none'\n });\n return {\n ...result,\n content: retainStream(result.content, lease)\n };\n }\n return lease.control.files.readFile(path, { encoding: options.encoding });\n });\n }\n\n /**\n * Stream a file from the sandbox using Server-Sent Events\n * Returns a ReadableStream that can be consumed with streamFile() or collectFile() utilities\n * @param path - Path to the file to stream\n */\n async createWorkspaceArchive(\n options: { root: string; excludes?: readonly string[] } = {\n root: '/workspace'\n }\n ): Promise<string> {\n const result = await this.runtimeRunner.runWaking(\n 'workspace.archive.create',\n (lease) =>\n lease.control.workspace.createArchive({\n root: options.root,\n excludes: options.excludes ?? []\n })\n );\n return result.archivePath;\n }\n\n async extractWorkspaceArchive(options: {\n root: string;\n archivePath: string;\n }): Promise<void> {\n await this.runtimeRunner.runWaking('workspace.archive.extract', (lease) =>\n lease.control.workspace.extractArchive(options)\n );\n }\n\n async cleanupWorkspaceArchive(archivePath: string): Promise<void> {\n await this.runtimeRunner.runWaking('workspace.archive.cleanup', (lease) =>\n lease.control.workspace.cleanupArchive(archivePath)\n );\n }\n\n async cleanupMountDirectory(mountPath: string): Promise<void> {\n await this.runtimeRunner.runWaking('mounts.cleanup', (lease) =>\n lease.control.mounts.removeMountDirectory({\n path: mountPath,\n onlyIfNotMountpoint: true\n })\n );\n }\n\n async readFileStream(path: string): Promise<ReadableStream<Uint8Array>> {\n return this.runtimeRunner.runWaking('files.stream.read', async (lease) =>\n retainStream(await lease.control.files.readFileStream(path), lease)\n );\n }\n\n async listFiles(path: string, options?: ListFilesOptions) {\n return this.runtimeRunner.runWaking('files.list', (lease) =>\n lease.control.files.listFiles(path, options)\n );\n }\n\n async exists(path: string) {\n return this.runtimeRunner.runWaking('files.exists', (lease) =>\n lease.control.files.exists(path)\n );\n }\n\n /**\n * Watch a directory for file system changes using native inotify.\n *\n * The returned promise resolves only after the watcher is established on the\n * filesystem, so callers can immediately perform actions that depend on the\n * watch being active. The returned stream contains the full event sequence\n * starting with the `watching` event.\n *\n * Consume the stream with `parseSSEStream<FileWatchSSEEvent>(stream)`.\n *\n * @param path - Path to watch (absolute or relative to /workspace)\n * @param options - Watch options\n */\n async watch(\n path: string,\n options: WatchOptions = {}\n ): Promise<ReadableStream<Uint8Array>> {\n return this.runtimeRunner.runWaking('watch.open', async (lease) =>\n retainStream(\n await openRemoteSubscription(\n lease.control.watch.watch({\n path,\n recursive: options.recursive,\n include: options.include,\n exclude: options.exclude\n }),\n { operation: 'open filesystem watch', protocol: 'stream' }\n ),\n lease\n )\n );\n }\n\n /**\n * Check whether a path changed while this caller was disconnected.\n *\n * Pass the `version` returned from a prior call in `options.since` to learn\n * whether the path is unchanged, changed, or needs a full resync because the\n * retained change state was reset.\n *\n * @param path - Path to check (absolute or relative to /workspace)\n * @param options - Change-check options\n */\n async checkChanges(\n path: string,\n options: CheckChangesOptions = {}\n ): Promise<CheckChangesResult> {\n return this.runtimeRunner.runWaking('watch.checkChanges', (lease) =>\n lease.control.watch.checkChanges({\n path,\n recursive: options.recursive,\n include: options.include,\n exclude: options.exclude,\n since: options.since\n })\n );\n }\n\n /**\n * Expose a port and get a preview URL for accessing services running in the sandbox\n *\n * Preview URL authorization survives transient container restarts, but\n * forwarding is active only for the runtime where `exposePort()` was last\n * called. Call `exposePort()` again after a restart to reactivate an\n * existing URL for the current runtime.\n *\n * @param port - Port number to expose (1024-65535)\n * @param options - Configuration options\n * @param options.hostname - Your Worker's domain name (required for preview URL construction)\n * @param options.name - Optional friendly name for the port\n * @param options.token - Optional custom token for the preview URL (1-16 characters: lowercase letters, numbers, underscores)\n * If not provided, a random 16-character token will be generated automatically\n * @returns Preview URL information including the full URL, port number, and optional name\n *\n * @example\n * // With auto-generated token\n * const { url } = await sandbox.exposePort(8080, { hostname: 'example.com' });\n * // url: https://8080-sandbox-id-abc123random4567.example.com\n *\n * @example\n * // With custom token for stable URLs across deployments\n * const { url } = await sandbox.exposePort(8080, {\n * hostname: 'example.com',\n * token: 'my_token_v1'\n * });\n * // url: https://8080-sandbox-id-my_token_v1.example.com\n */\n private async runWakingComposite<T>(\n operation: string,\n call: (lease: RuntimeLease) => Promise<T>\n ): Promise<T> {\n let callbackOutcome: Promise<T> | undefined;\n try {\n return await this.runtimeRunner.runWaking(operation, (lease) => {\n callbackOutcome = call(lease);\n return callbackOutcome;\n });\n } catch (error) {\n await callbackOutcome?.catch(() => undefined);\n throw error;\n }\n }\n\n [sandboxRuntimeCall] = (async (operation, call) =>\n await this.runWakingComposite(operation, (lease) =>\n call(extensionRuntimeControl(lease.control))\n )) as ExtensionRuntimeCall;\n\n async exposePort(\n port: number,\n options: { name?: string; hostname: string; token?: string }\n ) {\n return await this.previewService.exposePort(port, options);\n }\n\n /**\n * Revoke preview URL authorization and current-runtime activation for a port.\n *\n * Revocation is idempotent: calling this for a port with no preview state is\n * still successful. The operation clears Durable Object-owned preview state\n * only and does not contact, probe, wake, or clean up the container runtime.\n */\n async unexposePort(port: number): Promise<void> {\n return await this.previewService.unexposePort(port);\n }\n\n /**\n * Returns preview URLs that are currently forwardable in the active runtime.\n * Durable authorization without current-runtime activation is omitted.\n */\n async getExposedPorts(hostname: string) {\n return await this.previewService.getExposedPorts(hostname);\n }\n\n /**\n * Returns whether a port is currently preview-forwardable.\n * This checks Durable Object-owned auth and runtime activation without\n * contacting or waking the container.\n */\n async isPortExposed(port: number): Promise<boolean> {\n return await this.previewService.isPortExposed(port);\n }\n\n /**\n * Checks durable preview URL authorization for a port/token pair.\n *\n * This does not check whether the port is activated for the current runtime\n * and is not sufficient to decide whether preview traffic may forward.\n */\n async validatePortToken(port: number, token: string): Promise<boolean> {\n return await this.previewService.validatePortToken(port, token);\n }\n\n /**\n * Namespaced tunnel API. Quick tunnels are zero-config preview URLs\n * backed by Cloudflare's trycloudflare service. Named tunnels bind a\n * stable hostname under the configured Cloudflare zone.\n *\n * - `tunnels.get(port)` — idempotent. Returns the cached tunnel for\n * `port` if one exists in DO storage, otherwise spawns a fresh\n * cloudflared process and persists the record.\n * - `tunnels.list()` — returns tunnels currently usable through this\n * sandbox runtime.\n * - `tunnels.destroy(portOrInfo)` — tear down by port number or by\n * the record returned from `get()`.\n *\n * Container restarts drop quick-tunnel records because their\n * `*.trycloudflare.com` URLs are tied to the dead cloudflared process.\n * Named-tunnel records stay in storage and are marked for respawn so the\n * next `get(port, { name })` call reuses the Cloudflare tunnel and DNS\n * record while starting a fresh cloudflared process.\n */\n get tunnels(): TunnelsHandler {\n this.ensureTunnelsBuilt();\n // Non-null after ensureTunnelsBuilt(); cast for the type system.\n return this.tunnelsHandler as TunnelsHandler;\n }\n\n /**\n * Lazily construct the tunnel subsystem handle. Called from the\n * `tunnels` getter on first access and from sandbox lifecycle hooks\n * when tunnel reconciliation or cleanup is needed.\n */\n private ensureTunnelsBuilt(): void {\n if (this.tunnelsHandler) return;\n const built = createTunnelsHandle({\n runProvision: (call) =>\n this.runWakingComposite('tunnel.provision', (lease) =>\n call({\n runtime: lease.runtime,\n tunnels: lease.control.tunnels,\n retain: lease.retain\n })\n ),\n runExisting: async (runtime, operation, call) => {\n const result = await this.runtimeRunner.runExisting(\n { kind: 'runtime', runtime },\n operation,\n (lease) => call(lease.control.tunnels)\n );\n return isRuntimeAbsent(result) ? null : result;\n },\n getStoredRuntime: (storage) => this.runtimeLifecycle.getStored(storage),\n storage: this.ctx.storage,\n logger: this.logger,\n sandboxId: this.ctx.id.toString(),\n currentLifetime: this.currentLifetime,\n getNamedTunnelConfig: () => this.namedTunnelConfigResolver.getConfig()\n });\n this.tunnelServiceHandle = built;\n this.tunnelsHandler = built.tunnels;\n this.tunnelExitHandler = built.handleTunnelExit;\n }\n\n // ============================================================================\n // Backup methods — squashfs archive + R2 storage\n // ============================================================================\n\n /**\n * Create a backup of a directory and upload it to R2.\n *\n * The returned DirectoryBackup handle is serializable. Store it anywhere\n * (KV, D1, DO storage) and pass it to restoreBackup() later.\n *\n * Concurrent backup/restore calls on the same sandbox are serialized.\n */\n async createBackup(options: BackupOptions): Promise<DirectoryBackup> {\n return this.backupService.createBackup(options);\n }\n\n /**\n * Restore a backup from R2 into a directory.\n *\n * Production restores use a FUSE overlay mount. Local-bucket restores stream\n * the archive through the R2 binding and extract it with unsquashfs.\n *\n * Concurrent backup/restore calls on the same sandbox are serialized.\n */\n async restoreBackup(backup: DirectoryBackup): Promise<RestoreBackupResult> {\n return await this.backupService.restoreBackup(backup);\n }\n\n async __setBackupRestoreFaultForTesting(\n fault: BackupRestoreTestFault | null\n ): Promise<void> {\n await this.backupService.setRestoreFaultForTesting(fault);\n }\n\n async registerGitAuthInterceptor(\n params: GitAuthInterceptorParams\n ): Promise<void> {\n await configureGitAuthInterceptor(this.getMountOutboundHost(), params);\n }\n\n private getMountOutboundHost(): MountOutboundHost {\n return {\n ctx: this.ctx as EgressContainerState,\n constructorRef: this.constructor,\n enableInternet: this.enableInternet,\n logger: this.logger,\n setOutboundByHost: (host, method, params) =>\n this.setOutboundByHost<unknown>(host, method, params),\n removeOutboundByHost: (host) => this.removeOutboundByHost(host)\n };\n }\n}\n"],"mappings":"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"}
|