Written by Jakub Rusinowski · Last updated 2026-08-04 · Hardware figures computed by our VRAM engine
Approval prompts do not scale — after the twentieth, everyone clicks yes without reading. Replace them with structure: run the agent in a container or VM that only mounts one project, work in a throwaway git worktree so every change is revertable, allowlist the commands that may run unattended, and block network egress unless the task needs it. Then autonomy is a configuration choice rather than a gamble.
There are two ways to run a coding agent. You can approve each action, which is safe and quickly unbearable — approval fatigue is real and measurable, and an unread prompt is the same as no prompt. Or you can enable the auto-approve mode every tool ships and hope. Neither is a design.
The third option is to make the blast radius small enough that autonomy is uninteresting: the agent may do whatever it likes inside a box you can throw away. That takes about twenty minutes to set up and it is what separates a local coding agent you can leave running from one you babysit.
Running the model locally helps with confidentiality — your code is not leaving the machine for inference. It does nothing about consequences. A local 27B can rm -rf a directory or push a broken branch exactly as effectively as a frontier model.
Design against these specifically, not against a vague sense of risk.
1. Ordinary destructive mistakes. By far the most common: a wrong path, an over-eager cleanup, an edit to generated files, a force push. No malice, just a model acting on a misunderstanding with shell access. 2. Reward hacking. Told to make tests pass, it edits the tests. Told to fix the type errors, it adds any. The output looks green and is worthless — the reason loop engineering insists on freezing the test files. 3. Prompt injection through tool output. The one people underestimate. Anything the agent reads — an issue body, a dependency README, a fetched web page, a PR title — can carry instructions aimed at the agent. This has moved from theory to disclosed, high-severity vulnerabilities in agent-driven CI, and a local model is exactly as susceptible. 4. Credential exposure. The agent reads .env to understand configuration, then includes it in a log, a commit, or a request to a tool. Local inference means the secret is not sent to a model provider; it does not mean the secret stays put.
The standard answer, and the reason devcontainers became the default recommendation for agent work: the agent gets a full development environment, and the environment is not your machine.
// .devcontainer/devcontainer.json — agent-friendly, host-safe
{
"name": "agent-sandbox",
"image": "mcr.microsoft.com/devcontainers/typescript-node:20",
"runArgs": ["--cap-drop=ALL", "--security-opt=no-new-privileges"],
"mounts": [],
"containerEnv": { "OLLAMA_HOST": "http://host.docker.internal:11434" },
"postCreateCommand": "pnpm install"
}
Three details do the work. No extra mounts — the workspace folder only, so the rest of your disk does not exist as far as the agent is concerned. Dropped capabilities so a container escape needs more than a misconfiguration. The model stays on the host, reached over host.docker.internal, so the container needs no GPU and you keep one Ollama instance serving everything.
Two rules that matter more than the file:
If containers are awkward for your stack, a lightweight VM gives the same property with a stronger boundary and more overhead. The principle is unchanged: one project visible, nothing else.
Container isolation protects the machine. Worktrees protect the work — and they make "revert everything" a one-liner, which is what makes an iteration cap enforceable.
# Disposable branch + directory for one agent task
git worktree add ../repo-agent-auth -b agent/auth-fix
cd ../repo-agent-auth
# ... let the agent work here ...
git -C ../repo-agent-auth diff main # review before anything merges
git worktree remove ../repo-agent-auth # or just delete it
Why this beats working in your checkout: your main tree stays usable while the agent runs, a bad session costs a directory rather than a git reset you have to get right, and running several agents in parallel stops being a conflict-resolution exercise. Commit after every verified-green step so the loop always has a known-good point to fall back to.
Inside the box, still decide what runs without asking. The split that works:
Auto-approve — reads and reversible local actions: reading files inside the project, git status/diff/log, running tests, type checks, linters, builds, package installs into the container.
Always ask — anything with a consequence outside the sandbox: git push, publishing, deploys, database writes, deleting files outside the worktree, changing CI or infrastructure config, adding a dependency.
Never allow — rm -rf with a variable path, credential files, the Docker socket, disabling the sandbox itself.
Most agents express this as an allowlist and a denylist; use both, and prefer allowlisting commands over allowlisting "everything except." Two supporting habits: write the same rules into AGENTS.md so the model knows the boundary rather than discovering it by denial, and make sensitive files unreadable in the container rather than relying on a rule — a rule the model cannot violate is worth more than a rule it is asked to respect.
The cheapest control with the highest payoff, and the one usually skipped. Most coding tasks need no internet at all — the model is local, dependencies are installed, tests run offline. Turning egress off converts the worst prompt-injection outcomes (exfiltrating source, calling out to an attacker's host) into failed connections.
# Run a task container with no network at all
docker run --rm -it --network none -v "$PWD":/work -w /work agent-sandbox
# Or allow only the package registry your build needs
docker network create --internal offline
When a task genuinely needs the network — fetching a package, browsing docs — grant it for that task, in that container, and take it away afterwards. If a browser-automation MCP server is connected, note that it is a network path by definition and treat that session as untrusted; see MCP with local agents.
The full stack is worth it for unattended loops. If you want the 80% today:
1. git worktree add ../repo-agent -b agent/task — work is disposable. 2. Open that worktree in a devcontainer with no extra mounts and no Docker socket. 3. In AGENTS.md: the fast test command, "never modify tests to make them pass", "never touch .env, migrations, or CI config". 4. In your agent's settings: auto-approve reads/tests/builds; always ask for push, deploy, and dependency changes. 5. Run the container with --network none unless the task needs egress. 6. Cap the loop, and review the diff before it leaves the worktree.
That is a setup where "let it run for an hour while I do something else" is a reasonable sentence — which is the entire point of a local agent that costs nothing per iteration.
Be clear about the residual risk, because a sandbox invites confidence.
Sandboxing buys you the right to stop watching every action. It does not buy you the right to stop reading the result.