- Shell 100%
0.2.1
agentbox doctor reported "could not interpret 'sbx policy check network'" on a
correctly locked-down host.
The exit status was not the problem; it was already tolerated. sbx explains a
denial like this:
Denied: agentbox-posture-probe.invalid:443
Reason: no matching allow rule (default deny)
The reason line contains the word "allow", so scanning the whole output for
verdict keywords matched both families and the ambiguity guard gave up. The
guard was right to refuse to guess; the input had been made ambiguous by
scanning text that was never meant to carry the verdict.
The verdict now comes from the "Allowed:" / "Denied:" prefix on its own line,
which the reason line cannot affect. Exit status 0 (allowed) and 1 (denied) are
documented as expected answers rather than failures, and any other status is
only reported when the output also carries no verdict. Output without a
recognised prefix is still reported as uninterpretable, with the raw text, and
never guessed.
The fake sbx now reproduces the real two-line answer and the real exit codes.
The previous single-word output could not have caught this, the same way the
two-column ls mock hid the status-column bug: a mock built from an assumption
only ever confirms the assumption.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||
|---|---|---|
| bin | ||
| config | ||
| docs | ||
| lib | ||
| scripts | ||
| template | ||
| tests | ||
| .gitignore | ||
| CHANGELOG.md | ||
| install.sh | ||
| LICENSE | ||
| README.md | ||
| VERSION | ||
Agentbox
Agentbox creates one isolated Docker Sandbox microVM per Git project and adds a small workflow around Claude Code and OpenAI Codex.
The goal is not to make prompt injection impossible. The goal is to keep a compromised coding agent away from the host, other projects, SSH keys and the real Git remote.
Security model
Host repository
├── origin → Forgejo/GitHub, controlled by the user
└── sandbox-<project>-agent → private clone inside the microVM
(created by sbx in clone mode)
Project microVM
├── working clone → agent can edit and commit
├── host remote → read-only host repository
├── no origin → removed on purpose
├── Claude Code
├── Codex CLI
└── network → default deny plus explicit allow rules
In clone mode, the host repository is mounted read-only at
/run/sandbox/source. The agent works in a separate clone whose only remote is
host, pointing at that read-only mount. Agentbox removes origin inside the
VM whatever it points at, so there is no configured path from the agent to the
real Git server.
Commits move in one direction at a time:
Host → VM git fetch host (inside the VM, or: agentbox sync)
VM → Host agentbox diff / merge (on the host, via sandbox-<name>)
Host → real upstream git push origin, by you, after review
Requirements
- Linux on x86_64
- CPU virtualization enabled
- readable and writable
/dev/kvm - Git
- Docker Sandboxes (
sbx) - a global Locked Down network policy
- for building the template:
curl,install,mktempon the host
This repository was tested on Omarchy/Arch Linux with:
sbx v0.37.1
Claude Code 2.1.220
Codex CLI 0.146.1
Docker Sandboxes is Early Access. Agentbox uses a large part of the sbx
command surface, so a different version may behave differently. agentbox doctor warns when the installed version is not the tested one.
Quick start
Install the CLI and build the clean agent template:
git clone <this-repository>
cd agentbox
./install.sh --build-template
Make sure ~/.local/bin is in PATH, then verify:
agentbox doctor
In an existing, clean Git repository:
cd ~/dev/projects/my-project
agentbox create
Agentbox stops before creation when it finds common secret filenames such as
.env, private keys or credentials.json. Review them rather than blindly
overriding the check.
Inside the microVM:
claude-agent
# or
codex-agent
On the first start, authenticate each agent inside the project VM. Never save a logged-in project sandbox as the shared template.
If a login fails with 403, the network allowlist is missing a host. See Troubleshooting.
Daily workflow
The agent edits and commits inside the microVM:
git add .
git commit -m "Implement feature"
Review on the host:
agentbox diff
Accept with a fast-forward merge:
agentbox merge
git push origin
Make new host commits visible inside the VM:
agentbox sync
Then inside the VM, on whatever branch you are on:
git merge --ff-only "host/$(git branch --show-current)"
main, master and project-specific branch names all work; nothing in
agentbox assumes a particular default branch.
Sandbox names
The sandbox name is derived from the project directory and stays readable:
~/dev/projects/my-project → my-project-agent
It never contains a hash, id or random suffix. That means two projects whose directories have the same name would map to the same sandbox. Agentbox records which project a sandbox was created for and refuses to act when the name belongs to a different project, instead of silently opening the wrong VM.
Give the second project its own readable name:
AGENTBOX_SANDBOX_NAME=my-other-project-agent agentbox create
Later commands find that sandbox again automatically, because the mapping is stored. Set the variable through direnv or a shell alias if you prefer to be explicit every time.
Commands
agentbox create [path] Create and open a sandbox
agentbox open [path] Open its default shell
agentbox shell [path] Open an interactive Bash shell
agentbox fetch [path] Fetch agent commits to the host
agentbox diff [path] [branch] Review commits and changes
agentbox merge [path] [branch] Fast-forward the host branch
agentbox sync [path] Fetch the read-only host remote in the VM
agentbox status [path] [--all] Show this project's sandbox and Git state
agentbox policy [path] Show effective network rules
agentbox stop [path] Stop without deleting state
agentbox remove [path] [--force] [--yes]
Delete the project VM
agentbox doctor Check prerequisites
agentbox version Print the agentbox version
Commands that need the VM to be up (fetch, diff, merge, sync) check
first and say so plainly when it is stopped:
There is no active Agentbox session. Start one with: agentbox open
agentbox remove fetches from the sandbox first and refuses to delete while
commits exist that the host does not have. Use --force to discard them
anyway, and --yes to skip the interactive confirmation in scripts. After the
checks pass it stops the sandbox the regular way before deleting it, and it
never passes sbx rm --force on your behalf: that flag would also override an
in-use sandbox, which is a different decision from discarding commits.
Inside the VM:
claude-agent Claude Code, started without a host API key
codex-agent Codex, started without a host API key
host-fetch Re-point and refresh the read-only host remote
host-fetch and agent-workspace-init are the same program. Running it makes
new host commits visible as host/<branch> and re-asserts the remote layout:
one remote named host, no origin.
Templates
Templates are addressed by the fully qualified name that sbx template ls
prints:
docker.io/library/jan-agent-shell:v1
A short name is normalised to that form, so AGENTBOX_TEMPLATE=jan-agent-shell:v1
and the full spelling mean the same template. A custom registry is kept as
written:
registry.example.com/team/agent-shell:v2
Detection compares the canonical names exactly. :v1 never matches :v10, and
agent-shell never matches other-agent-shell.
Configuration
~/.config/agentbox/config is created by the installer from
config/agentbox.conf.example.
The file is parsed, not sourced: only KEY=VALUE lines and comments are
accepted, only known keys are allowed, and a typo is an error rather than a
silently ignored line. Nothing in it is executed.
bin/agentbox and scripts/build-template.sh read the same file, so
AGENTBOX_TEMPLATE means the same template everywhere and the export filename
is derived from it (team-shell:v2 exports as team-shell-v2.tar).
Network
Project sandboxes get a narrow allowlist covering the AI providers only.
Package registries such as registry.npmjs.org, github.com and pypi.org
are not allowed by default, so npm install inside a fresh sandbox will
fail until you allow the host you need.
Find out what was blocked and allow exactly that, for one sandbox:
sbx policy log
sbx policy allow network --sandbox my-project-agent "registry.npmjs.org"
Every allowed host is also a possible exfiltration channel. That includes the AI provider domains: an agent that has been talked into it can encode data into a request to a domain you deliberately allowed. A narrow allowlist reduces the number of such channels; it does not remove them.
Template portability
scripts/build-template.sh creates:
~/.local/share/agentbox/exports/jan-agent-shell-v1.tar
Copy that file privately to another machine and import it with a full path:
sbx template load ~/.local/share/agentbox/exports/jan-agent-shell-v1.tar
The export is a full filesystem image built for x86_64. It is not portable to another architecture; build the template locally there instead.
The safer default for friends is to build the template locally from this repository. Do not commit template exports to Git. A saved template captures the complete filesystem, so a mistakenly logged-in builder could leak tokens. The builder is audited for credential files before it is saved, and the build fails if any are found.
The build is automated and repeatable, not fully reproducible: Codex is pinned exactly, but the base image tag and the Claude Code installer channel can change between runs. See Architecture.
Tests
./tests/run-tests.sh
The suite runs entirely offline against a fake sbx and throwaway Git
repositories in a temporary directory. It never touches a real sandbox,
template or your configuration.
Documentation
License
Agentbox itself is MIT licensed. Docker Sandboxes, Claude Code and Codex are separate products with their own licenses and terms.