The container is the boundary, not the agent’s guardrails: two doors a laptop sandbox leaves open

Photo by Lan Gao on Unsplash

An autonomous agent team runs on the machine that holds your vaults, your source, and your secrets, so what its tools can reach is a boundary question. I wrapped the team’s file and shell tools in a container and treated the job as done. It was not. A container around the tools confines the tools; it does not close the two doors around them. One is the messaging gateway, which uploaded any host file whose path appeared in a reply or a kanban summary. The other is a shell that ignores the agent-side write limit and writes anywhere the mounts allow. Neither closes from inside the agent. Both close on the machine. This sits in Reliable agent systems.

TL;DR

The container is the first boundary, not the whole one. It confines the agent team’s file and shell tools to an explicit mount list, with no network. Two doors stay open around it: the gateway’s upload of any host file whose path appeared in a reply or a kanban summary, and a shell’s reach past the agent-side write limit. Strict mode with an allow-list closes the first; the mounts close the second. Neither fix lives inside the agent.

The boundary the container actually draws

Left alone, the team’s file tools and its shell ran directly on the machine that holds the vaults, so the exposure was concrete: the privileges that read one note could read anything else my user reached. The reflex is to wrap those tools in a container. That helps, and it closes less than it looks. The container sees only the mounts I give it and never the home directory, so the boundary I get is the boundary of the mount list I write. That constraint rules out the obvious answer, treating the box as the whole boundary. It closes the tools and leaves the two doors open.

The design decision, and what I did not take

The decision was to run the file toolset, the shell, and the code-execution tool inside a network-less Docker container hosted on Colima, a lightweight virtual machine. Everything that is not a file or a shell stays in the main process on the host, and the default profile keeps running locally. The confinement is a property of the mounts and the container, not of the agent’s behavior. The list the container reaches is explicit: each vault read-write, each source read-only, and a profile cache read-only. The one live alternative, the host’s default home mount, was rejected because it exposes the whole home directory. The same thesis one layer up, on the server’s own operating-system user, is The security layer is the OS boundary, not agent guardrails.

How the confinement works

Each profile carries a terminal block with a fixed working directory inside the container, the container running as root inside itself, the repository’s environment file masked, each volume at its own absolute path, and a container that does not persist between runs. The two profiles that were given a shell carry their write limit in the mounts: each vault is mounted read-only, and each write root is built read-write on top of it.

The per-profile write roots the confinement rests on are their own subject: Separating spec, build and QA in an agent team.

RiskThe layer that covers it
The agent reading the whole home directorythe explicit mount list; the home directory is never mounted
The agent reaching the networkthe container runs with no network
The agent reading the repository’s environment filethe file is masked inside the container
A shell writing outside its write rooteach vault is mounted read-only, and the write root is built read-write on top
State carrying over between runsthe container does not persist

What broke, and the guards that now hold

Only door one is recorded as a failure; door two is a structural gap. The gateway uploaded any host file whose path appeared in a reply or a kanban summary, so a host file left the machine; the guard is strict mode with an allow-list of the two vaults and recent-file trust off. A shell, by contrast, ignores the agent-side write limit and can write anywhere the mounts allow, so the mounts had to become the limit, and a shell had to be rationed to the two profiles whose measured file-search load was the highest.

The build carried its own friction. A container running as the host user could not read its own cache, and the fix was to run the container as root inside itself. Files still land owned by the host user, so paths resolve unchanged.

What the box does not do is check the agent’s output; that is a separate deterministic guard, Stop hallucinations with deterministic guards. How a chosen host-side automation behaves day to day, rather than what the container lets it reach, is a different discipline.

What it costs to run

No running-cost figure is recorded, so I will not put a number on it. The maintenance cost is real. The container host’s virtual machine must run whenever the gateway runs, because a card or a file read needs the container, and with it down the team’s work fails. The sandbox was applied step by step, and each step kept a pre-change copy of the profile’s config as its rollback, so the pre-sandbox state of every profile can be restored.

What this does not solve

Three boundaries, drawn plainly, because a security layer that is oversold is one that will disappoint.

  • The container confines the tools, not the gateway’s own upload, so a profile on a container backend without strict mode still exposes the host; the gateway fix is a separate layer with its own allow-list.
  • A shell sits outside the agent-side write limit by construction, so the mounts are the only limit it has, and a wrong mount list is the failure this design cannot catch.
  • The confinement is a property of the machine and its mounts, not of the agent, so it does not validate the agent’s output or separate its roles, and the human stays in the chain.

Where the model runs, as distinct from what the box lets it reach, is its own axis.

The pattern I would start from is the one above: run the agent’s file and shell tools in a container with a mount list you wrote, then close the gateway’s upload and the shell’s reach on the host, not inside the agent. The trade-offs I accepted to run this stack are on the about page. If this is the shape of your problem, let’s talk.