AI Agent Sandbox: How POLYROB Contains Agent Code
An AI agent sandbox is an isolated environment where an agent runs the code it writes, so a bad command or an injected instruction cannot touch your machine, your files or your keys. POLYROB ships one: a hardened Docker container by default, plus four optional backends for remote or rootless sandboxes.
This page explains what a sandbox has to do, how POLYROB’s works flag by flag, which backend to pick, and what is still open. Every POLYROB claim comes from the public repository at release v1.7.8. The files are listed at the end.
What is an AI agent sandbox?
Northflank, in a guide checked on 2026-11-12, defines a code execution sandbox as an isolated environment where an agent can run generated code, install dependencies and manipulate files, and then return results. It adds that the sandbox supplies the boundary while the agent decides what to do (Northflank, best code execution sandbox for AI agents).
The reason to want one is simple. Code an agent writes is untrusted input. It may come from a model mistake, from a web page that told the model what to do, or from a task built to cause harm. POLYROB’s own security review treats it that way: the one rule is that agent code must never touch the trusted host process. For the attack side of this, see prompt injection examples, and for the wider picture read AI agent security.
How does the POLYROB sandbox contain code?
The default sandbox backend is docker. Each run_code call starts a container with a fixed set of flags, built in core/container_hardening.py. None of them is optional.
| Flag | What it stops |
|---|---|
--network none (default) | The code cannot reach the internet or your LAN, so it cannot send data out. |
--cap-drop ALL | Removes every Linux capability, which closes the usual privilege-escalation routes. |
--security-opt no-new-privileges | A setuid binary cannot grant more privilege than the process already has. |
--read-only plus --tmpfs /tmp | The root filesystem cannot be changed. The only scratch space is memory-backed and disappears with the container. |
--pids-limit 256 | Caps processes, which stops a fork bomb. |
--memory 1024m with --memory-swap set to the same value | Caps memory with no swap headroom. |
--cpus 1.0 | Caps CPU use. |
--user | Runs as a non-root user. When POLYROB itself runs as root, the container user is forced to 65534:65534. |
Three more rules matter as much as the flags:
- One writable host path. The container sees a single bind mount, the session workspace at
/workspace. It never sees the POLYROB install directory or your home directory. - No inherited environment. The container receives none of the host’s environment variables. Variables the caller passes in are also filtered, and any name containing
API_KEY,SECRET,TOKEN,PASSWORD,PRIVATE_KEY,MNEMONICor similar is dropped. - Hard limits on the run. Output is capped at 100,000 bytes and a run is killed after 30 seconds by default (
CODE_EXEC_MAX_OUTPUT_BYTES,CODE_EXEC_MAX_TIMEOUT_SEC).
The Docker socket is never mounted into the container and --privileged is never used. The repository’s security review says either would be an escape by design.
Is the sandbox on by default?
No, and that is deliberate. CODE_EXEC_ENABLED is off, so the code_execution tool does not exist for an agent until you turn it on. A delegated leaf sub-agent can never reach it, even when its parent has it. The related git write actions, such as git_push, wait for approval by default.
Can code in the sandbox ever reach the network?
Yes, if you allow it. CODE_EXEC_NETWORK takes four values:
noneis the default for a one-shot run.egressuses a Docker bridge network, so outbound traffic is open.proxyputs the persistent sandbox on an internal network behind one allowlist proxy. By default it allows package registries only (PyPI, npm, GitHub, crates, the Go proxy, RubyGems, Maven Central and Debian). You extend the list withCODE_EXEC_EGRESS_ALLOW.hostshares the host network and is an opt-in escape hatch.
What is the persistent dev sandbox?
Posture 0 is the default: confined, one-shot containers, no persistent shell. AGENT_COMPUTE_POSTURE=1 adds a persistent container per session, where the shell tool runs inside the container and pip installs persist. That is what an agent needs to build software.
The security review states the cost plainly. A persistent dev container defaults to bridge networking when CODE_EXEC_NETWORK is unset, because installs need a route out, and so it has open outbound internet. If you run posture 1, set CODE_EXEC_NETWORK=proxy to allowlist hosts, or none to close the network. Posture 3 runs the shell on the host for the owner’s own terminal turn and needs POLYROB_LOCAL on a single-tenant machine. Servers refuse it.
Which backend should you pick?
Set the backend with CODE_EXEC_BACKEND for run_code, and SHELL_BACKEND for the shell and process tools.
| Backend | Boundary | Use it when |
|---|---|---|
docker | Namespaces and cgroups on the host kernel, hardened as above | You run POLYROB on your own server or workstation. This is the one the sandbox guard is built around. |
modal | Modal Sandboxes (gVisor), one per session | You want the code off your machine. Install with pip install 'polyrob[modal]'. |
daytona | Daytona sandboxes, one per session | You want a remote sandbox that can stop and resume. DAYTONA_SANDBOX_PERSIST=true stops, not deletes, it at session end. |
vercel_sandbox | Vercel Sandbox, ephemeral | You accept a sandbox that always has network. Vercel provides no per-sandbox network deny. |
singularity | Apptainer or SingularityCE instance | You are on an HPC host or a single-user workstation. It runs as your user on the host kernel, a weaker boundary than Docker. |
ssh | None by default | The remote host is already a throwaway machine. |
local_subprocess | None | A single user on their own box. Nothing else. |
For the four packs, credentials authenticate the client in the agent process and never enter the sandbox. Files live in the remote sandbox, not in the local session workspace.
What does the sandbox guard refuse?
tools/code_exec/sandbox_guard.py is the rule that keeps a convenience backend from running on a server. A backend counts as a sandbox only if it reports capabilities["sandbox"] as true. On anything but a local, single-user run (POLYROB_LOCAL), the guard refuses to execute with a backend that does not.
That is why local_subprocess, the default value of CODE_EXEC_BACKEND, is refused on servers. The ssh backend reports itself as not a sandbox, because code on a remote host runs with the SSH user’s full privileges. CODE_EXEC_SSH_SANDBOXED=true flips that flag, but the documentation calls it an operator attestation rather than a technical guarantee. Set it only for a host that is actually disposable.
The guard also reacts to a wallet key. If an env file the agent’s user can read holds a wallet seed, model-written code (run_tests, run_code, and the host shell at posture 3) refuses to run on the host. The fix it names is to move the seed to the signer or run code in a sandbox backend such as docker. If you run a wallet, read the security model first.
What does the sandbox not protect against?
The repository lists its own gaps, and these are the ones that matter in practice:
- A container shares the host kernel. It is not hardware isolation. A kernel-level flaw could reach the host. For genuinely adversarial multi-tenant code, a hardware-isolated backend is the better answer, and the review says that work is scoped out of the Docker backend.
- Packs are tested against fakes. The documentation says the
modal,daytona,vercel_sandboxandsingularitypacks are tested against fakes only. - Approval is a checkpoint, not a sandbox. Once the owner approves a
shell_runcommand, it runs inside the session’s dev container, or on the host at posture 3. The command guard is pattern-based. It stops known destructive shapes, not a determined author of new ones. - Network policy is the only guard against exfiltration. If you open egress and the image has credentials, flags do not stop a
git pushto a hostile remote. - The image is your supply chain. The default image is
python:3.12-slim, pulled without a pinned digest. PointCODE_EXEC_DOCKER_IMAGEat an image you vetted. - Limits apply per container. The caps bound one run, not how many run at once.
How do you set it up?
Install POLYROB from the install page, then turn on code execution with the Docker backend and keep the network closed.
curl -fsSL https://polyrob.dev/install.sh | bash
export CODE_EXEC_ENABLED=true
export CODE_EXEC_BACKEND=docker
export CODE_EXEC_NETWORK=none
polyrob
To run the shell in a remote sandbox instead, install the pack extra and name the backend:
pip install 'polyrob[modal]'
export MODAL_TOKEN_ID=your-token-id MODAL_TOKEN_SECRET=your-token-secret
SHELL_BACKEND=modal polyrob
Run polyrob pack list to see which packs are installed. On a server, docker needs a reachable daemon: the guard returns one clear refusal, “do not retry”, when the Docker socket is missing, rather than letting the model loop on a permission error.
For the same setup in context, the self-hosted AI agent guide covers the whole install, and the autonomous AI agent guide covers what the agent does when it works toward goals on its own.
Frequently asked questions
What is an AI agent sandbox?
An isolated environment where an agent runs generated code, so that code cannot reach the host. In POLYROB that is a hardened Docker container or one of four pack backends.
Is Docker enough to sandbox an AI agent?
For one trusted operator, with the flags above and the network closed, it contains the common failures. It is a shared-kernel boundary, so it is not enough for hostile multi-tenant code. Use a hardware-isolated backend there.
Does POLYROB run agent code on my machine?
Not by default. Code execution is off until you enable it. On a server a sandbox backend is required. Only the owner’s own terminal turn at posture 3 can run a shell on the host.
Can the sandbox see my API keys?
No. The container gets none of the host environment, and secret-named variables are filtered even when passed on purpose. The one caveat is a key stored in a file the agent’s user can read, which the guard checks for.
Which is safest: Docker, Modal or Daytona?
The review ranks hardware-isolated backends above a container for adversarial code. The pack backends are tested against fakes only, so test your own workload before relying on one.
Sources
POLYROB facts: core/container_hardening.py, tools/code_exec/SANDBOX_SECURITY.md, tools/code_exec/sandbox_guard.py, docs/CONFIGURATION.md, docs/guide/security-model.md, packs/{modal,daytona,vercel_sandbox,singularity}/README.md, all at v1.7.8. Source code: github.com/theselfruleorg/polyrob.
POLYROB is an open-source AI agent you run and own. Install it or read the docs.