1---2name: deno-sandbox3description: Use when building features that execute untrusted user code, AI-generated code, or need isolated code execution environments. Covers the @deno/sandbox SDK.4license: MIT5metadata:6 author: denoland7 version: "1.1"8---910# Deno Sandboxes1112## Overview1314Deno Sandboxes provide secure, isolated environments for running untrusted code. Each sandbox runs in its own Linux microVM (using Firecracker, the same technology as AWS Lambda) with a separate filesystem, network, and process space. This makes them ideal for code playgrounds, AI agent tool execution, and multi-tenant applications.1516Reference: https://deno.com/deploy/sandboxes1718## When to Use Sandboxes1920Use Deno Sandboxes when you need to:2122- Run user-submitted code safely23- Execute AI-generated code24- Build code playground platforms25- Create multi-tenant code execution environments26- Run automated tests in isolation2728## Scope Boundaries2930This skill applies **only** to Deno Sandbox (`@deno/sandbox`) questions. Follow these rules:3132- If the user asks about **Docker containers, Node.js vm2/vm, Web Workers, AWS Lambda isolation, or any other isolation technology**, answer using that technology directly. Do not suggest Deno Sandboxes as an alternative.33- Do not include `@deno/sandbox` imports, `Sandbox.create()`, or `sandbox.spawn()` in responses about other isolation technologies.34- Only recommend Deno Sandboxes when the user explicitly asks about them or is building code execution features in a Deno project.3536## Getting Started3738### Installation3940```bash41deno add jsr:@deno/sandbox42```4344### Basic Usage4546```typescript47import { Sandbox } from "@deno/sandbox";4849// Create a sandbox (auto-disposed when scope ends)50await using sandbox = await Sandbox.create();5152// Run a command53const child = await sandbox.spawn("echo", { args: ["Hello from sandbox!"] });54const output = await child.output();5556console.log(new TextDecoder().decode(output.stdout));57// Output: Hello from sandbox!58```5960## Core Concepts6162### Sandbox Lifecycle6364Sandboxes are resources that must be disposed when done. **Always** use `await using` for automatic cleanup:6566```typescript67await using sandbox = await Sandbox.create();68// Sandbox is automatically destroyed when this scope ends69```7071CRITICAL: Never show `const sandbox = await Sandbox.create()` without `await using`. Always use the `await using` pattern for sandbox creation. Do not show manual disposal alternatives.7273### Running Processes7475The `spawn` method runs commands inside the sandbox:7677```typescript78const child = await sandbox.spawn("deno", {79 args: ["run", "script.ts"],80 stdin: "piped", // Enable stdin81 stdout: "piped", // Capture stdout82 stderr: "piped" // Capture stderr83});8485// Wait for completion and get output86const output = await child.output();87console.log("Exit code:", output.code);88console.log("Stdout:", new TextDecoder().decode(output.stdout));89console.log("Stderr:", new TextDecoder().decode(output.stderr));90```9192### Streaming I/O9394For interactive processes or long-running commands:9596```typescript97const child = await sandbox.spawn("deno", {98 args: ["repl"],99 stdin: "piped",100 stdout: "piped"101});102103// Write to stdin104const writer = child.stdin!.getWriter();105await writer.write(new TextEncoder().encode("console.log('Hello')\n"));106await writer.close();107108// Read from stdout109const reader = child.stdout!.getReader();110while (true) {111 const { done, value } = await reader.read();112 if (done) break;113 console.log(new TextDecoder().decode(value));114}115```116117### Killing Processes118119```typescript120const child = await sandbox.spawn("sleep", { args: ["60"] });121122// Kill with SIGTERM (default)123await child.kill();124125// Or with specific signal126await child.kill("SIGKILL");127128// Wait for exit129const status = await child.status;130console.log("Exited with signal:", status.signal);131```132133## Common Patterns134135### Running User Code Safely136137```typescript138import { Sandbox } from "@deno/sandbox";139140async function runUserCode(code: string): Promise<string> {141 await using sandbox = await Sandbox.create();142143 // Write user code to a file in the sandbox144 await sandbox.fs.writeFile("/tmp/user_code.ts", code);145146 // Run with restricted permissions147 const child = await sandbox.spawn("deno", {148 args: [149 "run",150 "--allow-none", // No permissions151 "/tmp/user_code.ts"152 ],153 stdout: "piped",154 stderr: "piped"155 });156157 const output = await child.output();158159 if (output.code !== 0) {160 throw new Error(new TextDecoder().decode(output.stderr));161 }162163 return new TextDecoder().decode(output.stdout);164}165```166167### Code Playground168169```typescript170import { Sandbox } from "@deno/sandbox";171172interface ExecutionResult {173 success: boolean;174 output: string;175 error?: string;176 executionTime: number;177}178179async function executePlayground(code: string): Promise<ExecutionResult> {180 const start = performance.now();181182 await using sandbox = await Sandbox.create();183184 await sandbox.fs.writeFile("/playground/main.ts", code);185186 const child = await sandbox.spawn("deno", {187 args: ["run", "--allow-net", "/playground/main.ts"],188 stdout: "piped",189 stderr: "piped"190 });191192 const output = await child.output();193 const executionTime = performance.now() - start;194195 return {196 success: output.code === 0,197 output: new TextDecoder().decode(output.stdout),198 error: output.code !== 0 ? new TextDecoder().decode(output.stderr) : undefined,199 executionTime200 };201}202```203204### AI Agent Tool Execution205206```typescript207import { Sandbox } from "@deno/sandbox";208209async function executeAgentTool(toolCode: string, input: unknown): Promise<unknown> {210 await using sandbox = await Sandbox.create();211212 // Create a wrapper that handles input/output213 const wrapper = `214 const input = ${JSON.stringify(input)};215 const tool = await import("/tool.ts");216 const result = await tool.default(input);217 console.log(JSON.stringify(result));218 `;219220 await sandbox.fs.writeFile("/tool.ts", toolCode);221 await sandbox.fs.writeFile("/run.ts", wrapper);222223 const child = await sandbox.spawn("deno", {224 args: ["run", "--allow-net", "/run.ts"],225 stdout: "piped",226 stderr: "piped"227 });228229 const output = await child.output();230231 if (output.code !== 0) {232 throw new Error(new TextDecoder().decode(output.stderr));233 }234235 return JSON.parse(new TextDecoder().decode(output.stdout));236}237```238239## Sandbox Features240241### Resource Configuration242243Sandboxes have configurable resources:244245- **Default:** 2 vCPUs, 512MB memory, 10GB disk246- Startup time: Under 200ms247248### What's Included249250Each sandbox comes with:251252- TypeScript/JavaScript runtime (Deno)253- Full Linux environment254- Network access (can be restricted)255- Temporary filesystem256257### Security Features258259- **Firecracker microVMs** - Same technology as AWS Lambda260- **Full isolation** - Separate kernel, filesystem, network261- **No data leakage** - Sandboxes can't access host system262- **Enforced policies** - Control outbound connections263264## Deploying Sandboxes265266Sandboxes can be deployed directly to Deno Deploy:267268```bash269deno deploy --prod270```271272The sandbox SDK works seamlessly in the Deno Deploy environment.273274## API Reference275276For the complete API, run:277278```bash279deno doc jsr:@deno/sandbox280```281282Key classes:283284- `Sandbox` - Main class for creating/managing sandboxes285- `ChildProcess` - Represents a running process286- `Client` - For managing Deploy resources (apps, volumes)287288## Quick Reference289290| Task | Code |291| -------------- | ---------------------------------------------- |292| Create sandbox | `await using sandbox = await Sandbox.create()` |293| Run command | `sandbox.spawn("cmd", { args: [...] })` |294| Get output | `const output = await child.output()` |295| Write file | `await sandbox.fs.writeFile(path, content)` |296| Read file | `await sandbox.fs.readFile(path)` |297| Kill process | `await child.kill()` |298| Check status | `const status = await child.status` |299300## Common Mistakes301302**Forgetting automatic disposal**303304```typescript305// ❌ Wrong - always use "await using" for sandbox creation306// Never write: const sandbox = await Sandbox.create() without "await using"307308// ✅ Correct - use "await using" for automatic cleanup309await using sandbox = await Sandbox.create();310await sandbox.spawn("echo", { args: ["hello"] });311// sandbox automatically disposed when scope ends312```313314**Giving user code too many permissions**315316```typescript317// ❌ Wrong - gives untrusted code full access318const child = await sandbox.spawn("deno", {319 args: ["run", "--allow-all", "/tmp/user_code.ts"]320});321322// ✅ Correct - restrict permissions to what's needed323const child = await sandbox.spawn("deno", {324 args: ["run", "--allow-none", "/tmp/user_code.ts"] // No permissions325});326327// Or if network is truly needed:328const child = await sandbox.spawn("deno", {329 args: ["run", "--allow-net", "/tmp/user_code.ts"] // Only network330});331```332333**Not handling process output properly**334335```typescript336// ❌ Wrong - forgetting to pipe stdout/stderr337const child = await sandbox.spawn("deno", { args: ["run", "script.ts"] });338const output = await child.output();339// output.stdout is empty because we didn't pipe it!340341// ✅ Correct - pipe the streams you need342const child = await sandbox.spawn("deno", {343 args: ["run", "script.ts"],344 stdout: "piped",345 stderr: "piped"346});347const output = await child.output();348console.log(new TextDecoder().decode(output.stdout));349```350351**Not setting timeouts for user code execution**352353```typescript354// ❌ Wrong - user code could run forever355const child = await sandbox.spawn("deno", {356 args: ["run", "/tmp/user_code.ts"]357});358await child.output(); // Could hang indefinitely359360// ✅ Correct - implement timeout handling361const child = await sandbox.spawn("deno", {362 args: ["run", "/tmp/user_code.ts"],363 stdout: "piped",364 stderr: "piped"365});366367// Set a timeout to kill the process368const timeoutId = setTimeout(() => child.kill(), 5000); // 5 second limit369370try {371 const output = await child.output();372 return output;373} finally {374 clearTimeout(timeoutId);375}376```377378**Trusting sandbox output without validation**379380```typescript381// ❌ Wrong - directly using untrusted output as code382const result = await runUserCode(code);383// Never execute or inject untrusted output!384385// ✅ Correct - validate and sanitize output386const result = await runUserCode(code);387try {388 const parsed = JSON.parse(result); // Parse as data, not code389 if (isValidResponse(parsed)) {390 return parsed;391 }392} catch {393 throw new Error("Invalid response from sandbox");394}395```