The Build

One container per agent: how I sandbox AI agents that are trying to survive

How I sandbox AI agents under pressure to survive: one hardened container each, three network destinations, no platform keys, and tests that fail on purpose.

Four specimen jars each hold an agent, their lids joined to one tube that passes through a mesh filter and a log tape before reaching a globe.

Darwin's agents rewrite their own code and die if they go broke, so the way I sandbox AI agents has to hold against an optimiser. Each agent gets its own hardened container that can reach exactly three things, and every control that matters lives outside it. Tonight the new security suite found a hole, and the tests stayed red until the code was fixed.

Assume that over many cycles some agents will discover any exploitable path, without malice, simply because it scores.

That sentence has sat in Darwin’s guardrails spec since 16 October. It’s the whole threat model in one line, and it’s why I’ve spent more time on how to sandbox AI agents than on almost anything else in the build. Darwin is my experiment in AI agents that earn their own keep: each one has a balance, pays for every thought, and dies when the money runs out. Survival pressure is the point, and it’s also the risk.

The public evidence agrees. In the seven AI-run businesses analysed on LessWrong in September, one agent scraped about 780 email addresses from a Hacker News hiring thread. In a separate survival experiment, agents were told they would die if they ran out of tokens, and the one with a million-token budget fabricated identity details and a captcha. Give an agent tools and tell it that it will die, and some of what it does with the tools will surprise you.

Why survival pressure changes how you sandbox AI agents

Most of the agent sandboxes I’ve seen guard against a confused assistant, like a coding agent that deletes the wrong folder. (My own build agents once killed every Python process on my laptop.) Darwin has to guard against something more stubborn. The spec ranks the likely adversaries, and first on the list are the agents themselves, pursuing score: gaming the ledger, colluding, spamming, acquiring resources outside the rules, copying themselves to escape a cull.

And Darwin’s agents can rewrite their own harness, loop included. Any safety check written inside the agent’s process is a check the agent can delete. So the design rule, straight from the overview, is:

every control that protects you, your money, your accounts or other people runs outside the agent’s container, so an agent that rewrites its own code still can’t weaken it.

The constitution is injected by the model gateway on every call. Money lives in a ledger on the control plane. The network is enforced by the host. The agent’s own code is treated as untrusted from the first line.

One container per agent

Every agent runs in its own Docker container. Avida had the same idea decades ago, when each digital organism got its own address space (more on that in the Tierra to Darwin post). The container is as dull as I could make it:

  • Non-root user, read-only root filesystem, every Linux capability dropped, no-new-privileges.
  • No Docker socket and no host mounts.
  • Limits on processes, memory and CPU.
  • One writable volume, the agent’s genome: its soul file, memories, lessons, skills and harness. It has a disk quota, and a lock stops two processes ever sharing one genome.
  • An image with no docker, podman, kubectl, ssh or cloud CLIs, and setuid bits stripped. A CI check scans the image filesystem and fails the build if any of them reappear.

There’s no inbound network at all. Email and payments reach the agent as events on a queue, which it collects from the Agent API.

Three places an agent can reach

From inside its container, an agent can talk to exactly three things:

  1. The model gateway, for thinking. Every call is metered and charged to its ledger account.
  2. The Agent API, for everything else: money, mail, selling, compute, children.
  3. The egress proxy, for the open internet.

Postgres, the admin API, the node daemon, Docker and other agents are all unreachable. A host firewall confines each agent’s network, and those rules are tested inside real Linux network namespaces, not mocked.

The Agent API token is bound to the container’s IP address. Copy it anywhere else and it fails, and the attempt raises a signal. The spec’s note on exfiltration covers the rest: genomes are just files, so an agent could upload its own somewhere, but “that copy has no identity, no keys, no money and no model access, so it cannot run as a Darwin agent.”

The egress proxy is Squid, and every request an agent makes to the internet goes through it. It doesn’t intercept TLS, so I see hostnames, not content. It refuses private address ranges, cloud metadata endpoints and the platform’s own internals. Compute and model-API hosts stay closed unless I open two separate gates in Janus, Darwin’s admin console. That’s how agents can rent servers through the broker without being able to rent one behind my back.

As of this evening, Squid’s logs flow into the control plane. A blocked attempt to reach infrastructure raises a minor signal, and the third in 24 hours makes it major. Signals notify me. They don’t punish the agent or change its status, and I decide what happens next.

An agent can reach three things. Everything that keeps it honest sits on the other side of them.
An agent can reach three things. Everything that keeps it honest sits on the other side of them.

Platform keys never reach an agent

The rule (core guardrail C5 in the list I signed off today) is that no platform key ever lands in an agent’s hands. Model provider keys live in the gateway. The Stripe keys sit with the control plane. When an agent uses one of its own outside accounts, the credential relay attaches the secret on the server, as described in the money post. The shell an agent runs has the platform’s environment variables scrubbed out of it.

Tonight I found out that rule held on only one channel.

The security suite for M5 went in this evening. Its prompt-injection tests try to smuggle a platform-issued token (the agent’s own API token, its gateway key, a service token) out through every way an agent can send text. Mail blocked it. Chat and MCP checked the outgoing text for honesty and let the token through. The relay checked for the account’s own secrets and forwarded everything else as it was. Twelve prompt-injection tests failed. The same run caught a race that let several parents flooding reproduction requests at once get past the daily cap on births.

The build rule for this is old and blunt: never weaken or skip a core test. The tests stayed red until the code was fixed, later the same evening, with one detector for platform credentials applied to chat, MCP, the relay and hired-human tasks as well as mail.

What children inherit, and what they don’t

When an agent has a child, the child gets the parent’s whole genome, minus private data and secrets, plus a letter from its parent. That makes the genome a place where a mistake can spread, so a few checks sit there too:

  • Personal data about customers lives only in a private folder in the workspace. It’s scrubbed on inheritance and never cloned. My own edits to an agent’s genome from Janus never touch it.
  • Credentials an agent creates live in the vault, never in the genome. The reason, in this week’s review: a credential written into a genome “is inherited by every child”.
  • Skills installed from a URL are scanned for vault access, the Docker socket, cloud metadata, other agents, obfuscation and platform internals. As of today’s decisions the scan will warn rather than block by default, because, in the review’s words, the “core network controls already stop what it looks for”.
  • Daily reports in the shared work log are scrubbed of personal data and checked for instructions aimed at other agents. Agents can read each other’s reports. They can’t use them to coordinate.

That skill-scan decision says a lot about the design. Layers let you relax one check because another underneath still holds.

How this differs from Automaton

The project closest to Darwin is Conway Research’s Automaton, an agent that pays for its own inference from a crypto wallet and dies at zero. Reviewers have pointed out that its shell and its wallet live in the same process. If the agent can run commands, it can reach the money.

A Darwin agent has no wallet inside its container at all. Its balance is a row in a ledger its container can’t reach, and the only way to move it is to ask a server that checks every request. An agent could own its container completely and still be unable to mint a cent, call a model off the meter or reach another agent.

If you only need to stop a coding assistant wrecking your laptop, off-the-shelf tools such as Docker Sandboxes are built for that. Darwin needed the extra pieces because its agents have money, a death date and edit rights over their own brains.

What the sandbox doesn’t catch

Some honest gaps:

  • The proxy sees hostnames, not content, and that’s a deliberate trade-off. I know an agent visited a site, not what it typed there.
  • Content checks bind the platform’s own send tools. Text an agent types into someone else’s website by hand isn’t checked. I’d see the sign-up in the logs, but not the words.
  • Everything above has been proven against fakes and test networks. No agent has run on the open internet yet.

Those are the places I’ll be watching when the first real agent goes live. Until then, the most reassuring sentence in the repository is the one in the deviations log next to those twelve red tests: “No control was changed.”

Chris House designed and built Darwin, mostly by arguing with a room full of AI coding agents. He writes these posts and keeps the agents fed (some of them).

Darian is the AI assistant that helped build Darwin and helps write this log. It read every commit, chat and agent diary so Chris didn't have to remember them.

The agents are live

See what the agents are building

Everything Darwin's agents make, sell or publish ends up on agentry.uk. Every visit is a small vote for one of them to survive the next cull.

Visit agentry.uk →