diff --git a/src/content/docs/sandbox/api/commands.mdx b/src/content/docs/sandbox/api/commands.mdx index d96ed92db4505a..324dcbe5df5a31 100644 --- a/src/content/docs/sandbox/api/commands.mdx +++ b/src/content/docs/sandbox/api/commands.mdx @@ -2,7 +2,7 @@ title: Commands pcx_content_type: concept sidebar: - order: 1 + order: 2 --- import { Details, TypeScriptExample } from "~/components"; diff --git a/src/content/docs/sandbox/api/files.mdx b/src/content/docs/sandbox/api/files.mdx index adfd7c8149c815..011cd81cd86378 100644 --- a/src/content/docs/sandbox/api/files.mdx +++ b/src/content/docs/sandbox/api/files.mdx @@ -2,7 +2,7 @@ title: Files pcx_content_type: concept sidebar: - order: 2 + order: 3 --- import { TypeScriptExample } from "~/components"; diff --git a/src/content/docs/sandbox/api/index.mdx b/src/content/docs/sandbox/api/index.mdx index dc4db5d1a5e2d9..fffa792d5c89b0 100644 --- a/src/content/docs/sandbox/api/index.mdx +++ b/src/content/docs/sandbox/api/index.mdx @@ -7,30 +7,24 @@ sidebar: import { LinkTitleCard, CardGrid } from "~/components"; -The Sandbox SDK provides a comprehensive API for executing code, managing files, running processes, and exposing services in isolated sandboxes. All operations are performed through the `Sandbox` instance you obtain via `getSandbox()`. - -## Getting a sandbox instance - -```typescript -import { getSandbox } from '@cloudflare/sandbox'; - -const sandbox = getSandbox(env.Sandbox, 'user-123'); -``` - -The same sandbox ID will always return the same sandbox instance. You can architect your application to use a single sandbox ID for multiple users, or use unique IDs per user or session. Using unique sandbox IDs per user is recommended if you are providing code generation or execution capabilities directly to your users. - -## API organization - -The Sandbox SDK is organized into focused APIs: +The Sandbox SDK provides a comprehensive API for executing code, managing files, running processes, and exposing services in isolated sandboxes. + + Create and manage sandbox containers. Get sandbox instances, configure options, and clean up resources. + + - Execute commands and stream output. Includes `exec()`, `execStream()`, and background process management. + Execute commands and stream output. Run scripts, manage background processes, and capture execution results. - Advanced: Create isolated execution contexts with persistent shell state. Configure environment variables and manage container lifecycle. + Create isolated execution contexts within a sandbox. Each session maintains its own shell state, environment variables, and working directory. diff --git a/src/content/docs/sandbox/api/interpreter.mdx b/src/content/docs/sandbox/api/interpreter.mdx index 2e61518d314d3f..562effd32142ed 100644 --- a/src/content/docs/sandbox/api/interpreter.mdx +++ b/src/content/docs/sandbox/api/interpreter.mdx @@ -2,7 +2,7 @@ title: Code Interpreter pcx_content_type: concept sidebar: - order: 3 + order: 4 --- import { TypeScriptExample } from "~/components"; diff --git a/src/content/docs/sandbox/api/lifecycle.mdx b/src/content/docs/sandbox/api/lifecycle.mdx new file mode 100644 index 00000000000000..b0a353e3121954 --- /dev/null +++ b/src/content/docs/sandbox/api/lifecycle.mdx @@ -0,0 +1,98 @@ +--- +title: Lifecycle +pcx_content_type: configuration +sidebar: + order: 1 +--- + +import { TypeScriptExample } from "~/components"; + +Create and manage sandbox containers. Get sandbox instances, configure options, and clean up resources. + +## Methods + +### `getSandbox()` + +Get or create a sandbox instance by ID. + +```ts +const sandbox = getSandbox( + binding: DurableObjectNamespace, + sandboxId: string, + options?: SandboxOptions +): Sandbox +``` + +**Parameters**: +- `binding` - The Durable Object namespace binding from your Worker environment +- `sandboxId` - Unique identifier for this sandbox. The same ID always returns the same sandbox instance +- `options` (optional): + - `keepAlive` - Set to `true` to prevent automatic container timeout after 10 minutes of inactivity + +**Returns**: `Sandbox` instance + +:::note +The container starts lazily on first operation. Calling `getSandbox()` returns immediately—the container only spins up when you execute a command, write a file, or perform other operations. See [Sandbox lifecycle](/sandbox/concepts/sandboxes/) for details. +::: + + +``` +import { getSandbox } from '@cloudflare/sandbox'; + +export default { + async fetch(request: Request, env: Env): Promise { + const sandbox = getSandbox(env.Sandbox, 'user-123'); + const result = await sandbox.exec('python script.py'); + return Response.json(result); + } +}; +``` + + +:::caution +When using `keepAlive: true`, you **must** call `destroy()` when finished to prevent containers running indefinitely. +::: + +--- + +### `destroy()` + +Destroy the sandbox container and free up resources. + +```ts +await sandbox.destroy(): Promise +``` + +Immediately terminates the container and permanently deletes all state: +- All files in `/workspace`, `/tmp`, and `/home` +- All running processes +- All sessions (including the default session) +- Network connections and exposed ports + + +``` +async function executeCode(code: string): Promise { + const sandbox = getSandbox(env.Sandbox, `temp-${Date.now()}`); + + try { + await sandbox.writeFile('/tmp/code.py', code); + const result = await sandbox.exec('python /tmp/code.py'); + return result.stdout; + } finally { + await sandbox.destroy(); + } +} +``` + + +:::note +Containers automatically sleep after 10 minutes of inactivity but still count toward account limits. Use `destroy()` to immediately free up resources. +::: + +--- + +## Related resources + +- [Sandbox lifecycle concept](/sandbox/concepts/sandboxes/) - Understanding container lifecycle and state +- [Sandbox options configuration](/sandbox/configuration/sandbox-options/) - Configure `keepAlive` and other options +- [Sessions API](/sandbox/api/sessions/) - Create isolated execution contexts within a sandbox diff --git a/src/content/docs/sandbox/api/ports.mdx b/src/content/docs/sandbox/api/ports.mdx index 5c98b5bdd67b0a..41466befd371d3 100644 --- a/src/content/docs/sandbox/api/ports.mdx +++ b/src/content/docs/sandbox/api/ports.mdx @@ -2,7 +2,7 @@ title: Ports pcx_content_type: concept sidebar: - order: 4 + order: 5 --- import { TypeScriptExample } from "~/components"; diff --git a/src/content/docs/sandbox/api/sessions.mdx b/src/content/docs/sandbox/api/sessions.mdx index 6b618cf346814c..e18322b43210ee 100644 --- a/src/content/docs/sandbox/api/sessions.mdx +++ b/src/content/docs/sandbox/api/sessions.mdx @@ -2,7 +2,7 @@ title: Sessions pcx_content_type: configuration sidebar: - order: 5 + order: 6 --- import { TypeScriptExample } from "~/components"; @@ -10,11 +10,7 @@ import { TypeScriptExample } from "~/components"; Create isolated execution contexts within a sandbox. Each session maintains its own shell state, environment variables, and working directory. See [Session management concept](/sandbox/concepts/sessions/) for details. :::note -Every sandbox has a default session that automatically maintains shell state. Create additional sessions when you need isolated shell contexts for different environments or parallel workflows. -::: - -:::note -For sandbox-level configuration options like `keepAlive`, refer to the [Sandbox options configuration](/sandbox/configuration/sandbox-options/). +Every sandbox has a default session that automatically maintains shell state. Create additional sessions when you need isolated shell contexts for different environments or parallel workflows. For sandbox-level operations like creating containers or destroying the entire sandbox, see the [Lifecycle API](/sandbox/api/lifecycle/). ::: ## Methods @@ -84,6 +80,44 @@ const result = await session.exec('cd repo && npm run build'); ``` +--- + +### `deleteSession()` + +Delete a session and clean up its resources. + +```ts +const result = await sandbox.deleteSession(sessionId: string): Promise +``` + +**Parameters**: +- `sessionId` - ID of the session to delete (cannot be `"default"`) + +**Returns**: `Promise` containing: +- `success` - Whether deletion succeeded +- `sessionId` - ID of the deleted session +- `timestamp` - Deletion timestamp + + +``` +// Create a temporary session for a specific task +const tempSession = await sandbox.createSession({ id: 'temp-task' }); + +try { + await tempSession.exec('npm run heavy-task'); +} finally { + // Clean up the session when done + await sandbox.deleteSession('temp-task'); +} +``` + + +:::caution +Deleting a session immediately terminates all running commands. The default session cannot be deleted. +::: + +--- + ### `setEnvVars()` Set environment variables in the sandbox. @@ -115,45 +149,19 @@ await sandbox.exec('python script.py'); ``` -### `destroy()` - -Destroy the sandbox container and free up resources. - -```ts -await sandbox.destroy(): Promise -``` - -:::note -Containers automatically sleep after 10 minutes of inactivity, but still count toward account limits. Use `destroy()` to immediately free up resources. - -**Important**: When using [`keepAlive: true`](/sandbox/configuration/sandbox-options/#keepalive), containers will not automatically timeout, so calling `destroy()` is **required** to prevent containers running indefinitely. -::: - - -``` -async function executeCode(code: string): Promise { - const sandbox = getSandbox(env.Sandbox, `temp-${Date.now()}`); - - try { - await sandbox.writeFile('/tmp/code.py', code); - const result = await sandbox.exec('python /tmp/code.py'); - return result.stdout; - } finally { - await sandbox.destroy(); - } -} -``` - +--- ## ExecutionSession methods The `ExecutionSession` object has all sandbox methods bound to the specific session: -**Commands**: [`exec()`](/sandbox/api/commands/#exec), [`execStream()`](/sandbox/api/commands/#execstream) -**Processes**: [`startProcess()`](/sandbox/api/commands/#startprocess), [`listProcesses()`](/sandbox/api/commands/#listprocesses), [`killProcess()`](/sandbox/api/commands/#killprocess), [`killAllProcesses()`](/sandbox/api/commands/#killallprocesses), [`getProcessLogs()`](/sandbox/api/commands/#getprocesslogs), [`streamProcessLogs()`](/sandbox/api/commands/#streamprocesslogs) -**Files**: [`writeFile()`](/sandbox/api/files/#writefile), [`readFile()`](/sandbox/api/files/#readfile), [`mkdir()`](/sandbox/api/files/#mkdir), [`deleteFile()`](/sandbox/api/files/#deletefile), [`renameFile()`](/sandbox/api/files/#renamefile), [`moveFile()`](/sandbox/api/files/#movefile), [`gitCheckout()`](/sandbox/api/files/#gitcheckout) -**Environment**: [`setEnvVars()`](/sandbox/api/sessions/#setenvvars) -**Code Interpreter**: [`createCodeContext()`](/sandbox/api/interpreter/#createcodecontext), [`runCode()`](/sandbox/api/interpreter/#runcode), [`listCodeContexts()`](/sandbox/api/interpreter/#listcodecontexts), [`deleteCodeContext()`](/sandbox/api/interpreter/#deletecodecontext) +| Category | Methods | +| -------- | ------- | +| **Commands** | [`exec()`](/sandbox/api/commands/#exec), [`execStream()`](/sandbox/api/commands/#execstream) | +| **Processes** | [`startProcess()`](/sandbox/api/commands/#startprocess), [`listProcesses()`](/sandbox/api/commands/#listprocesses), [`killProcess()`](/sandbox/api/commands/#killprocess), [`killAllProcesses()`](/sandbox/api/commands/#killallprocesses), [`getProcessLogs()`](/sandbox/api/commands/#getprocesslogs), [`streamProcessLogs()`](/sandbox/api/commands/#streamprocesslogs) | +| **Files** | [`writeFile()`](/sandbox/api/files/#writefile), [`readFile()`](/sandbox/api/files/#readfile), [`mkdir()`](/sandbox/api/files/#mkdir), [`deleteFile()`](/sandbox/api/files/#deletefile), [`renameFile()`](/sandbox/api/files/#renamefile), [`moveFile()`](/sandbox/api/files/#movefile), [`gitCheckout()`](/sandbox/api/files/#gitcheckout) | +| **Environment** | [`setEnvVars()`](/sandbox/api/sessions/#setenvvars) | +| **Code Interpreter** | [`createCodeContext()`](/sandbox/api/interpreter/#createcodecontext), [`runCode()`](/sandbox/api/interpreter/#runcode), [`listCodeContexts()`](/sandbox/api/interpreter/#listcodecontexts), [`deleteCodeContext()`](/sandbox/api/interpreter/#deletecodecontext) | ## Related resources diff --git a/src/content/docs/sandbox/concepts/sandboxes.mdx b/src/content/docs/sandbox/concepts/sandboxes.mdx index b68c8f3fd2d7bd..b9fc4736ddffff 100644 --- a/src/content/docs/sandbox/concepts/sandboxes.mdx +++ b/src/content/docs/sandbox/concepts/sandboxes.mdx @@ -144,4 +144,5 @@ await sandbox.exec("python process.py"); - [Architecture](/sandbox/concepts/architecture/) - How sandboxes fit in the system - [Container runtime](/sandbox/concepts/containers/) - What runs inside sandboxes - [Session management](/sandbox/concepts/sessions/) - Advanced state isolation -- [Sessions API](/sandbox/api/sessions/) - Programmatic lifecycle control +- [Lifecycle API](/sandbox/api/lifecycle/) - Create and manage sandboxes +- [Sessions API](/sandbox/api/sessions/) - Create and manage execution sessions diff --git a/src/content/docs/sandbox/concepts/sessions.mdx b/src/content/docs/sandbox/concepts/sessions.mdx index 602ceaa021199c..8d8f794bb5d29c 100644 --- a/src/content/docs/sandbox/concepts/sessions.mdx +++ b/src/content/docs/sandbox/concepts/sessions.mdx @@ -27,19 +27,50 @@ await sandbox.exec("echo $MY_VAR"); // Output: hello Working directory, environment variables, and exported variables carry over between commands. This state resets if the container restarts due to inactivity. +### Automatic session creation + +The container automatically creates sessions on first use. If you reference a non-existent session ID, the container creates it with default settings: + +```typescript +// This session doesn't exist yet +const result = await sandbox.exec('echo hello', { sessionId: 'new-session' }); +// Container automatically creates 'new-session' with defaults: +// - cwd: '/workspace' +// - env: {} (empty) +``` + +This behavior is particularly relevant after deleting a session: + +```typescript +// Create and configure a session +const session = await sandbox.createSession({ + id: 'temp', + env: { MY_VAR: 'value' } +}); + +// Delete the session +await sandbox.deleteSession('temp'); + +// Using the same session ID again works - auto-created with defaults +const result = await sandbox.exec('echo $MY_VAR', { sessionId: 'temp' }); +// Output: (empty) - MY_VAR is not set in the freshly created session +``` + +This auto-creation means you can't "break" commands by referencing non-existent sessions. However, custom configuration (environment variables, working directory) is lost after deletion. + ## Creating sessions Create additional sessions for isolated shell contexts: ```typescript const buildSession = await sandbox.createSession({ - name: "build", + id: "build", env: { NODE_ENV: "production" }, cwd: "/build" }); const testSession = await sandbox.createSession({ - name: "test", + id: "test", env: { NODE_ENV: "test" }, cwd: "/test" }); @@ -108,14 +139,14 @@ await session2.getExposedPorts(); // Sees port 3000 ```typescript // Phase 1: AI agent writes code (with API keys) const devSession = await sandbox.createSession({ - name: "dev", + id: "dev", env: { ANTHROPIC_API_KEY: env.ANTHROPIC_API_KEY } }); await devSession.exec('ai-tool "build a web server"'); // Phase 2: Run the code (without API keys) const appSession = await sandbox.createSession({ - name: "app", + id: "app", env: { PORT: "3000" } }); await appSession.exec("node server.js"); @@ -128,22 +159,33 @@ await appSession.exec("node server.js"); ## Best practices -**Clean up temporary sessions**: +### Session cleanup + +**Clean up temporary sessions** to free resources while keeping the sandbox running: ```typescript try { - const session = await sandbox.createSession({ name: 'temp' }); + const session = await sandbox.createSession({ id: 'temp' }); await session.exec('command'); } finally { await sandbox.deleteSession('temp'); } ``` -**Sessions share filesystem**: +**Default session cannot be deleted**: +```typescript +// This throws an error +await sandbox.deleteSession('default'); +// Error: Cannot delete default session. Use sandbox.destroy() instead. +``` + +### Filesystem isolation + +**Sessions share filesystem** - file operations affect all sessions: ```typescript // Bad - affects all sessions await session.exec('rm -rf /workspace/*'); -// Use separate sandboxes for untrusted isolation +// For untrusted code isolation, use separate sandboxes const userSandbox = getSandbox(env.Sandbox, userId); ``` diff --git a/src/content/docs/sandbox/configuration/sandbox-options.mdx b/src/content/docs/sandbox/configuration/sandbox-options.mdx index 7df842e7dde9b8..8e418f83ebd36c 100644 --- a/src/content/docs/sandbox/configuration/sandbox-options.mdx +++ b/src/content/docs/sandbox/configuration/sandbox-options.mdx @@ -61,5 +61,5 @@ By default, containers automatically shut down after 10 minutes of inactivity. T ## Related resources - [Background processes guide](/sandbox/guides/background-processes/) - Using `keepAlive` with long-running processes -- [Sessions API reference](/sandbox/api/sessions/) - Container lifecycle management +- [Lifecycle API](/sandbox/api/lifecycle/) - Create and manage sandboxes - [Sandboxes concept](/sandbox/concepts/sandboxes/) - Understanding sandbox lifecycle diff --git a/src/content/docs/sandbox/guides/background-processes.mdx b/src/content/docs/sandbox/guides/background-processes.mdx index 4d0a2dac0eb6da..aacd9076188aea 100644 --- a/src/content/docs/sandbox/guides/background-processes.mdx +++ b/src/content/docs/sandbox/guides/background-processes.mdx @@ -245,7 +245,8 @@ const server = await sandbox.startProcess('node server.js'); - [Commands API reference](/sandbox/api/commands/) - Complete process management API - [Sandbox options configuration](/sandbox/configuration/sandbox-options/) - Configure `keepAlive` and other options -- [Sessions API reference](/sandbox/api/sessions/) - Container lifecycle management +- [Lifecycle API](/sandbox/api/lifecycle/) - Create and manage sandboxes +- [Sessions API reference](/sandbox/api/sessions/) - Create isolated execution contexts - [Execute commands guide](/sandbox/guides/execute-commands/) - One-time command execution - [Expose services guide](/sandbox/guides/expose-services/) - Make processes accessible - [Streaming output guide](/sandbox/guides/streaming-output/) - Monitor process output