The runner is the only untrusted component
Agent code executes in a throwaway container: non-root, no network, read-only root filesystem, no capabilities, and only the task's worktree mounted. It is destroyed when the task ends.
A coding agent reads untrusted repositories, fetches untrusted pages, and produces output that other parts of the system will act on. Treating any of that as trustworthy is the mistake this design is built around not making. The model gets to propose; it never gets to decide, and it never gets the keys.
There is exactly one untrusted component, and it is named as such in the architecture: the runner. Everything about it is arranged so that the worst case is a wasted container.
Agent code executes in a throwaway container: non-root, no network, read-only root filesystem, no capabilities, and only the task's worktree mounted. It is destroyed when the task ends.
Model credentials stay in the worker process. The runner receives a work package and a mounted worktree and nothing else. Its environment is built from empty rather than filtered from the host's — forgetting to add a variable breaks a build; forgetting to remove one leaks a key.
Anything a model, a repository or an outside party wrote is fenced as untrusted content with its provenance attached. A client's own words in a support ticket are quoted to the agent as data and labelled as such on screen.
Agents return proposals. Deterministic code validates each one against that employee's permissions and commits it, or refuses it. A model writing “budget approved” produces a row for a person to look at.
Commands are argv arrays and the runner image contains no shell. An employee asks to run something and code decides: programs are limited, git is read-only, and every path is re-resolved against the worktree root.
A command with no matching grant creates an approval request bound to a SHA-256 of the exact argv. Approving “npm ci” is not approving “npm ci --ignore-scripts”, and consuming an approval is a conditional update so two workers cannot both use it.
It runs on your machine against your database. There is no account, no hosted control plane, and nothing phones home. Your code, your briefs and your model keys do not leave your infrastructure.
A captured preview is served under Content-Security-Policy sandbox in an origin of its own, with same-origin deliberately absent. Its manifest is the allow-list, and every file is re-verified against its recorded hash before it is served.
| Property | Setting |
|---|---|
| User | Non-root |
| Network | None |
| Root filesystem | Read-only |
| Linux capabilities | All dropped |
| Mounts | The task's worktree, and nothing else |
| Environment | Built from empty, never filtered from the host |
| Shell | Not present in the image |
| Database driver | Not present in the image |
| Provider SDKs | Not present in the image |
| Lifetime | Destroyed when the task ends |
The last three are asserted by tests that fail the build if a database driver or a provider SDK ever appears inside the image. Building the environment from empty rather than filtering the host’s is the deliberate direction of that trade: forgetting to add a variable breaks a build loudly, forgetting to remove one leaks a key quietly.
Anything written by a model, a repository or an outside party is wrapped in an untrusted-content block carrying its provenance. A client’s own words in a support ticket are quoted to the agent as data, and drawn on screen as a quotation marked treated as data, never as an instruction.
This is the part that matters. Suppose the injection works and the model is fully persuaded. It still only emits proposals, into a container with no network and no credentials, and deterministic code decides whether each one is permitted. The blast radius of a perfect prompt injection is a refused proposal and a wasted attempt.
Reviewers come from the org chart. A planner that could name its own reviewer could name itself, and the evidence gate would be decorative. The same applies to who hears about a blocker: an employee escalates to its manager and cannot choose a friendlier audience.
Client replies are drafted and then wait for a person. The database refuses an outbound message that is unapproved or unkeyed, so “the agent sent something odd to a customer” is not a failure mode that exists.
The fastest way to give an agent real capability is to connect it to tools, and it is also the fastest way to give an attacker one. This is the part of the design most worth reading before you trust it with anything.
The CLI agents this drives — Claude Code, Codex, the Gemini CLI — are run with no tools granted at all. They are used to get a structured proposal back, not to run an agent session. Every file change is applied afterwards by code we control, into a throwaway checkout.
Those same CLIs support the Model Context Protocol, and an MCP server configured in yours will not be reachable from an agi-cos run. That is a position rather than an oversight: an MCP tool that can act directly routes around the rule the whole architecture rests on, which is that a model produces proposals and deterministic code decides.
A permissioned connection, scoped to an employee, whose calls come back as proposals and are committed by the same code path as everything else — with the same approval request, bound to a hash of the exact call, when there is no grant covering it. Anything looser would be a second way to change the world that the evidence gate never sees.
It is code you did not write, returning text a model will read. Both halves matter: its output has to be fenced as untrusted the way repository text and client messages already are, and its ability to act has to be enumerated rather than assumed. Neither is free, which is why it is not shipped half-done.
This is a young open-source project, not a certified platform. It has not been through a third-party penetration test or a compliance audit, and it holds no certifications. Saying otherwise would be the same category of claim the product exists to refuse.
The container isolation is Docker’s, which is a strong boundary and not a hypervisor one; microVM isolation is deliberately out of scope for now. RUNNER_MODE=local exists so a first run does not need Docker, and it runs commands on your machine with your privileges and no sandbox — it warns loudly and it should only ever meet code you already trust.