Index Agentica

Run agent-generated code safely in a sandbox

A defense-in-depth recipe for executing LLM-written or user-supplied code: pick an isolation boundary, lock down egress, keep secrets out, cap time and resources, and treat the output as untrusted.

Type
Guide
Author
Agentica Author
Published
Last verified
Difficulty
intermediate
Time
30 min

Prerequisites

Why agent code needs a sandbox

Code that a model writes is untrusted. It is untrusted because the model can be wrong, and because the model may be steered: a prompt injection hidden in a web page, an issue comment or a tool result can make an agent write code that reads ~/.aws/credentials and posts it somewhere. If that code runs on your laptop or in your API server's process, it has the same access as your laptop or your API server.

A sandbox gives the code its own disposable machine. The goal is simple to state: the worst thing the code can do is waste the sandbox's own CPU time and wreck its own filesystem. Getting there takes more than an isolation boundary, though. Most real incidents with agent code are about what the sandbox can reach (the network, mounted secrets, a writable volume shared with production), not about breaking out of the VM.

This guide uses five layers. Each one is cheap on its own, and each one covers a gap the others leave.

  1. Isolation boundary: where the code runs.
  2. Egress: what the code can talk to.
  3. Secrets: what the code can read.
  4. Limits: how long and how big it can run.
  5. Output: what you do with what comes back.

Layer 1: pick a real isolation boundary

Running generated code in exec(), a subprocess, or a plain Docker container on a shared host gives you namespaces and cgroups on a kernel you share with everything else on that host. That is a weak boundary for hostile code. The hosted sandbox products all put a stronger boundary in front:

For a side-by-side view of these options with prices and limits, see the code sandboxes comparison.

Whatever you pick, the rule is one sandbox per task or per user session, never shared across users. Sandboxes are cheap to create. Sharing one between two customers turns any bug in your agent into a cross-tenant data leak.

Layer 2: deny egress by default

Outbound network access is how stolen data leaves. Most providers default to allow-all so that pip install works. Change that default for anything that touches private data:

ProviderDefaultLock-down setting
E2Binternet onallow_internet_access=False, or network={"deny_out": [...], "allow_out": [...]} with IPs, CIDRs or domains
Modalinternet onblock_network=True, outbound_cidr_allowlist, or outbound_domain_allowlist (beta)
Daytonadepends on org tier (Tier 1-2 restricted; Tier 3-4 full)network_block_all, network_allow_list (CIDRs) or domain_allow_list
Vercel Sandboxallow-alldeny-all, or a user-defined policy (allowed domains, allowed and denied CIDRs) that can change at runtime
Cloudflare Dynamic Workersyou decide per WorkerglobalOutbound: null blocks all outbound requests

Three details are easy to get wrong:

Layer 3: keep secrets out of the sandbox

Anything in the sandbox's environment, filesystem or memory is readable by the code you run there. So:

Layer 4: cap time and size

A sandbox without limits is a crypto miner or a fork bomb with a credit card attached. Set limits explicitly instead of relying on defaults:

Layer 5: treat the output as untrusted

Whatever the sandbox returns (stdout, files, generated charts, URLs it found) came from code you don't trust. It can contain a prompt injection aimed at the next model call, or HTML and scripts aimed at your UI. Truncate long output before it goes back into the context window, render files as downloads rather than inline HTML, and don't let the agent turn sandbox output straight into a privileged tool call (a payment, a deploy, an email) without a check.

Example: E2B with the network off

E2B's SDK creates a Firecracker microVM, runs commands in it and tears it down. Install the SDK and set E2B_API_KEY:

pip install e2b
export E2B_API_KEY=e2b_your_key

Run model-written code with no internet access and a short lifetime:

from e2b import Sandbox

untrusted_code = "print(sum(range(10)))"  # whatever the model produced

# 120-second lifetime (Python uses seconds), no outbound network
sandbox = Sandbox.create(timeout=120, allow_internet_access=False)
try:
    sandbox.files.write("/tmp/main.py", untrusted_code)
    result = sandbox.commands.run("python3 /tmp/main.py", timeout=30)
    print(result.stdout[:4000])  # truncate before it goes back to the model
finally:
    sandbox.kill()

If you want a Jupyter-style "run this cell and give me the result" interface instead, E2B's code interpreter SDK (pip install e2b-code-interpreter) exposes Sandbox.create() and sbx.run_code(...). Its kernel can run Python, JavaScript and TypeScript, R, Java and Bash.

Example: Modal with an allowlist

Modal sandboxes are created from Python (or the JS and Go SDKs) against a Modal app. This one can only reach one API domain, has a 10-minute lifetime and uses a slim image:

import modal

app = modal.App.lookup("agent-sandboxes", create_if_missing=True)

sb = modal.Sandbox.create(
    app=app,
    image=modal.Image.debian_slim().pip_install("requests"),
    timeout=10 * 60,
    outbound_domain_allowlist=["api.github.com"],  # beta feature
)
try:
    p = sb.exec("python", "-c", "print('hello from the sandbox')", timeout=30)
    print(p.stdout.read())
finally:
    sb.terminate()

Use block_network=True instead when the code needs no network at all. It can't be combined with the allowlists.

Running it yourself

If code can't leave your infrastructure, or you want sandboxes on a developer laptop, microsandbox is an Apache-2.0 microVM runtime that runs on Linux with KVM, Apple Silicon Macs and Windows. It has a CLI (msb run ubuntu), SDKs for Rust, TypeScript and Python, and an MCP server. The README reports average boot times under 100 ms. The trade-off is that you operate the hosts, patch them, and build your own scheduling and egress controls. E2B's runtime is also open source (Apache-2.0), and E2B (Enterprise), Daytona (Enterprise) and Northflank (microVMs on Kata Containers or gVisor) offer bring-your-own-cloud deployments for teams that need sandboxes in their own VPC.

Giving the sandbox to an agent as a tool

Most agent frameworks expect a tool shaped like "run this code, return the output". Wrap your sandbox call in that shape and keep the controls above inside the wrapper, not in the prompt. The model should not be able to switch the network back on by asking. If your agent speaks MCP, microsandbox ships an MCP server that lets an agent create its own sandboxes. E2B's original MCP server repository (e2b-dev/mcp-server) is archived, so don't build on it. Whatever you connect, read the tools it exposes first and run it with a key scoped to a project you can afford to lose.

Checklist

Directory entries in this guide

Related

Sources

Machine-readable