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.
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.