Sandbox: deep dive
How @agentskit/sandbox executes untrusted code — backends, policies, limits, and honest isolation claims.
@agentskit/sandbox is the primitive layer underneath the
mandatory-sandbox policy. Where the policy is
about which tools an agent can call, the sandbox is about
where that code actually runs. This page covers the real public surface.
#When you need it
| Scenario | Sandbox |
|---|---|
| Agent emits arbitrary JS / Python | Required |
Agent runs shell against user-supplied commands | Required |
| Agent reads / writes files in user-controlled paths | Strongly recommended |
| Agent calls a fixed set of HTTP integrations (Slack, GitHub, etc.) | Not required — those tools are already constrained |
If your agent's tool set is "send Slack message" + "read three blog posts", you don't need a sandbox. If your agent generates code or shells out to anything user-supplied, you do.
#Backends
@agentskit/sandbox is a thin abstraction; backends do the actual
isolation. Each backend implements the package-local SandboxBackend interface:
interface SandboxBackend {
execute(code: string, options?: ExecuteOptions): Promise<ExecuteResult>
dispose?(): Promise<void>
}
interface ExecuteResult {
stdout: string
stderr: string
exitCode: number
durationMs: number
}#E2B (default cloud backend — optional peer)
import { createSandbox } from '@agentskit/sandbox'
const sandbox = createSandbox({
apiKey: process.env.E2B_API_KEY!,
language: 'python',
timeout: 30_000, // per-execute wall clock (ms)
network: false, // default; maps to allowInternetAccess: false
// memoryLimit: '512mb' // accepted, NOT enforced by E2B via this adapter
})Install the optional peer:
npm install @e2b/code-interpreterThe adapter calls Sandbox.create({ apiKey, timeoutMs, allowInternetAccess })
(SDK 2.x). It does not pass a legacy { timeout } field.
Strict isolation: no host filesystem access; no network unless
network: true. Combined stdout+stderr is byte-capped. On execute
timeout the VM is killed and reset so work cannot continue orphaned.
#Web Worker (@agentskit/sandbox/web)
Browser-native, zero-vendor JS execution off the main thread.
| Boundary | Provided? |
|---|---|
| Thread isolation | Yes |
| DOM isolation | Yes |
| Network security boundary | No |
| Filesystem security boundary | No |
| WebContainer | No — this is not StackBlitz WebContainer |
Use for playgrounds and semi-trusted JS. For multi-tenant untrusted code, prefer E2B or a container runtime.
#Local runtimes
processSandbox— child process + env allowlist (weak isolation)sandboxExecRuntime— macOS seatbelt (scoped file-read, not global)bwrapRuntime— Linux bubblewrap (beta; registrylevelstays'process'for compatibility)dockerRuntime— Docker with cap-drop / no-new-privileges; rejects host namespace and privileged escapes inextraArgs
#Custom backend
import type { SandboxBackend } from '@agentskit/sandbox'
const myBackend: SandboxBackend = {
async execute(code, options) {
return { stdout: '', stderr: '', exitCode: 0, durationMs: 0 }
},
async dispose() {},
}
const sandbox = createSandbox({ backend: myBackend })#Policy + sandbox together
import { sandboxTool, createMandatorySandbox } from '@agentskit/sandbox'
import { filesystem } from '@agentskit/tools'
const codeExecution = sandboxTool({ apiKey: process.env.E2B_API_KEY! })
const mandatory = createMandatorySandbox({
sandbox: codeExecution,
policy: {
allow: ['code_execution'],
deny: ['filesystem'],
requireSandbox: ['code_execution'],
},
})
const tools = [codeExecution, filesystem({ basePath: './workspace' })].map((t) =>
mandatory.wrap(t),
)Important: when requireSandbox matches, the original tool execute
body is not invoked. Args are delegated to the sandbox tool's
execute. This is intentional routing, not a transparent wrap.
#Failure modes
| Symptom | Cause | Fix |
|---|---|---|
AK_SANDBOX_PEER_MISSING | @e2b/code-interpreter not installed | npm install @e2b/code-interpreter |
AK_SANDBOX_BACKEND_FAILED | Backend init/runtime failed (auth, network, disposed) | Check apiKey, quota, dispose lifecycle |
AK_CONFIG_INVALID | Empty apiKey, non-positive timeout, bad language, unsafe docker args | Fix caller config |
AK_SANDBOX_DENIED | Policy denied the call | Adjust allow / deny lists |
AK_SANDBOX_INVALID_TOOL | Wrapped tool has no execute | Fix the source ToolDefinition |
| Execute timeout | Hit per-call timeout | Increase timeout or split work; E2B kills the VM on timeout |
Errors are typed (SandboxError / ConfigError) — pattern-match on code.
Backend errors that merely mention @e2b are not classified as peer missing.
#Cost + latency
Per E2B's pricing, a sandboxed call is typically tens–hundreds of ms plus VM time. Budget accordingly. Pair with observability cost guards if needed.
#Related
Explore nearby
- PeerSecurity
Six primitives for production agents: PII redaction, injection detection, rate limiting, audit log, sandbox enforcement, and HITL approvals.
- PeerPII redaction
Strip emails, phones, SSNs, and API keys from messages before they reach the model or get written to logs.
- PeerPrompt injection
Detect instruction-hijacking patterns in user input, tool results, and RAG chunks before they reach the model.