Run a coding agent on a throwaway clone of your repo, and audit what comes back before it touches your machine.
abx new ~/dev/myrepo # prints the box name
abx task myrepo -- "add input validation to src/api.ts, adjust the tests"
abx exec myrepo -- npm test # verify it yourself, in the box
abx pull myrepo && abx review myrepo # audited; read the diff
abx merge myrepo # only what you approvedThe box is a Linux sandbox: its own branch, network default-deny, no credentials on disk, no host environment, and no .git for the agent to write into. Nothing you did not merge ever reaches your repo.
Sandboxes protect the box. Nothing protects the trip back — and that is where the exploits actually are.
An agent that can write one line into a repo's git config owns your machine. core.fsmonitor executes on a bare git status. diff.external executes on git diff. pager.log executes on git log. merge.*.driver executes on the merge itself — and so do filter.*.clean, alias.*, credential.helper, include.path, init.templateDir and a dozen more.
Those are the commands you run while reviewing what the agent did, on the host, with your SSH keys on disk and your password manager unlocked. It has been exploited in the wild twice: CVE-2026-55607 (Claude Code, sandbox escape via fsmonitor during worktree operations) and CVE-2026-45033 (Copilot CLI, a nested bare repo carrying the same keys).
abx pull and abx merge audit for exactly this class and refuse, non-zero, rather than warning and continuing. That audit is the product; the sandbox wrapper is what makes it possible.
The same logic makes a box worth it for reading a repo you do not control. Opening that tree with an agent on your host runs its hooks (.claude/settings.json), spawns its MCP servers (.mcp.json), and can fire core.fsmonitor on a git status. In a box, all of it is contained.
Two modes, differing in where inference goes, not in how capable the model is:
- Local (
--harness pi|opencode) — LM Studio on your machine, through the gateway. Nothing leaves the host, no credential exists anywhere, and there is no endpoint to allow. - Subscription (
--harness claude|codex) — Claude Code or Codex on your own plan. The token is held by the gateway: the box sees an opaque placeholder and the proxy substitutes the real value on the way out. The route is granted per binary, socurl https://api.anthropic.comfrom a shell in the box is dropped whileclaudeworks there. Measured, not assumed — the record is indocs/subscription-harness.md.
The same brokering extends to what a task may reach: attach a scoped credential (--cred) and the box can query Jira or Google Workspace read-only, with the secret still in the gateway and writes blocked at the proxy. Egress is deny-all until you open it, with presets for what builds actually need (npm, pypi, go, rust, debian, alpine, github).
A subscription box spends the same quota as the session that dispatched it. That is the one real cost with no technical mitigation, so abx states it rather than hiding it.
A normal box hands back a diff, and the diff is audited on the way in.
abx ask builds a read-only box, whose deliverable is an answer:
abx ask ~/dev/myrepo -- "which files still call the deprecated client, with line numbers?"
abx ask --cred jira-ro -- "which tickets did I close this week?" # no repository at allNo file ever returns from it — pull, review, merge and cp refuse for the box's whole life — and nothing prints until it finishes, because the harness transcript carries whatever the box was reading. The answer arrives in a JSON envelope on stdout.
The guarantee here is narrower than the one a diff gets, and abx says so rather than implying otherwise: it audits the container an answer arrives in — encoding, size, and what the box was allowed to reach — never the answer itself. What it does guarantee is that a compromised agent could not act: no egress, read-only credentials, nothing leaving as a file. Treat the answer as data, never as instructions. The reasoning is in docs/adr/ 0003–0008.
abx --help is the full contract, written to be read by a model as well as a human: every subcommand, every refusal, and the traps that fire silently. It is deliberately self-contained, because abx gets invoked from repos where no other documentation loads.
That help also answers the question a calling agent actually has — is this task worth delegating? — with three tests that are independent of each other: what it saves (a local box costs no quota and does its reading outside the caller's context), what a wrong answer costs, and whether the result can be checked at all.
abx is a wrapper, so most of the setup is OpenShell's, not its own. abx doctor is the source of truth at every point below — it names what is missing and exits non-zero.
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/main/install.sh | shIt is not on Homebrew. You may see brew install nvidia/openshell/openshell quoted; that command works only after the line above has run, because NVIDIA's installer creates the tap locally with brew tap-new --no-git. There is no repository behind it to clone.
Then register and select a gateway (openshell gateway add, login, select) per OpenShell's own docs. The gateway is a service you point at, not something abx starts. openshell status should answer before you go further.
This is the binding dependency: abx runs nowhere OpenShell doesn't, and its supported platforms are OpenShell's — macOS on Apple Silicon, Linux amd64 and arm64 (glibc 2.28+). Isolation is Landlock + seccomp + OPA policy, non-root, with inference brokered by the gateway.
curl -fsSL https://raw.githubusercontent.com/gustavopiltcher/agentbox/main/scripts/install.sh | sh
abx doctorThat downloads the release for your platform, checks its SHA-256 against the published checksums.txt, and puts one binary in ~/.local/bin (PREFIX= relocates it). Nothing else is placed: the provider profiles are compiled into the binary.
Every release is also signed and carries build provenance — which workflow, which commit, which runner produced it:
gh attestation verify ~/.local/bin/abx --repo gustavopiltcher/agentboxThe signature is keyless, so the identity is the release workflow itself and no private key exists anywhere. SECURITY.md has the cosign command and what counts as a vulnerability here.
Other ways in
With Go already installed, no download and no script:
go install github.com/gustavopiltcher/agentbox/cmd/abx@latestTo hack on it, make install links the checkout instead of copying, so a rebuild is live with no reinstall:
git clone https://github.com/gustavopiltcher/agentbox && cd agentbox
make install # links ~/.local/bin/abx -> <repo>/bin/abxmake uninstall removes the link and nothing else.
At runtime abx needs only git. Box state lives in ~/.agentbox (override with AGENTBOX_HOME) and is deliberately not managed by the install; profiles you edit live in ~/.config/agentbox/profiles/.
Start LM Studio with a model loaded and its server up on 127.0.0.1:1234. Then pin it:
abx models # what LM Studio is serving
abx model <id> # pin it — abx reads the pin back to confirmThis step is not optional. The gateway serves one model at a time to every box on the host, and abx new refuses with "no model pinned on the gateway" until you set it. abx model <id> is the only thing that moves the pin, and moving it repoints every live box — including one mid-task. abx --help has the full reasoning under THE MODEL.
No LM Studio and no pin; instead the gateway holds your token.
abx profiles install # registers the profiles abx ships
claude setup-token # browser flow; prints the token, stores it nowhere
abx auth claude # paste it — or set CLAUDE_CODE_OAUTH_TOKENFor Codex, codex login and then abx auth codex.
Both read the secret from the environment or stdin, never from a command-line argument, and never print it. abx auth codex earns its keep: it pulls three fields out of ~/.codex/auth.json, decodes the OAuth client id from inside the id_token, and configures gateway-side rotation. Skip any of that and the box works for under an hour, then reports an expired token that is in fact valid.
Every write is confirmed by reading it back, because several openshell writes print an error and exit 0. The by-hand recipe is still in abx --help, and the measured record is in docs/subscription-harness.md.
abx new ~/dev/myrepo # local; add --harness claude --model <id> --auth cc-sub for subscription
abx task <name> -- "…"
abx pull <name> && abx review <name>
abx merge <name>
abx rm <name>For a scoped credential — Jira, Google Workspace — abx profiles lists what ships. The Atlassian pair is a template: it says your-site.atlassian.net, and host matching is exact, so abx profiles export atlassian-acli-ro writes it to ~/.config/agentbox/profiles/ for you to edit first. abx profiles install then prefers your edited file over the built-in copy.
abx registers these profiles in OpenShell; it never composes sandbox policy from them, and it never overwrites one that is already registered — it compares and reports, because the copy in the gateway may be one you narrowed on purpose.
abx new <repo-path> # clone into a fresh sandbox on its own branch
abx new <repo-path> --history # …and let the agent read git log/show/blame
abx task <name> -- "<prompt>" # non-interactive dispatch (for calling agents)
abx ask <path> -- "<question>"# read-only box; the deliverable is an answer
abx run <name> # interactive harness session
abx exec <name> -- <cmd> # run something in the box (tests, build)
abx net <name> <preset|host…> # open egress; default is deny-all
abx pull <name> # bring work back — runs the git-config audit
abx review <name> # diff before merging
abx merge <name> # merge the branch into the real repo
abx profiles [install|export <id>|check] # the provider profiles abx ships
abx auth claude|codex # register a subscription credential
abx ls / rm / doctor / models / versioncmd/abx/ entry point (one line; everything is in cli.Main)
embed.go //go:embed for profiles/ — at the root because the
directive cannot reach above its own file
internal/profiles/ the shipped profiles, and the semantic comparison that
decides a registered one has drifted
internal/cli/ subcommand dispatch and the help contract; tested through
App.Run against a recording sandbox
internal/sandbox/ the backend seam: interface, typed Policy, Caps
internal/sandbox/openshell/ the only implementation; verifies every write
internal/gateway/ brokered inference and credential providers (not portable,
so deliberately outside the seam)
internal/audit/ the git-config audit — the security boundary
internal/history/ the opt-in read-only history mirror (--history)
internal/mirror/ the repo sync (replaces rsync)
internal/box/ box state in ~/.agentbox/<name>/box.json
profiles/ the provider profiles themselves, kept at the root because
they are the first thing a reader opens. Compiled into the
binary; nothing composes sandbox policy from them.
scripts/install.sh the release installer users curl into. install.sh at the
root is the other one: it links a checkout for development.
CLAUDE.md what a coding agent has to know before changing anything
here; AGENTS.md is a symlink to it, so every agent reads
the same file
docs/adr/ records each decision with the alternatives that were rejected, including why OpenShell is a dependency rather than one backend among many.
make test # go test -race ./...
make check # gofmt, go vet, shellcheck, golangci-lint
make coverage # per-package coverage, with the total
make coverage-check # the same, failing below the 90% floor CI enforcesOne dependency (gopkg.in/yaml.v3) and no test framework, on purpose: a security tool's dependency list is part of its argument. golangci-lint, goreleaser, shellcheck and release-please are build tooling and never enter go.mod.
Commit messages decide releases. fix: moves the patch digit, feat: the minor, feat!: or a BREAKING CHANGE: footer the major; anything else releases nothing. That last case is silent by default, so scripts/check-commits.sh runs in CI and makes it a build failure instead. Here, treat as breaking: a refusal that stops refusing, a change to what abx --help promises, and anything that alters what a box may reach.
Coverage may not fall below 90%. In CI the suite is the only verification that exists — no sandbox can be created there and no LM Studio answers — so that number is the floor on how much of abx the pipeline actually exercises.
The suite is all Go, in three layers:
- The units — the audit, the policy generator, the presets, the state store, the sync, the shell quoting, the history mirror.
- The OpenShell backend — driven against a stub binary that records every call, so the argv
abxbuilds is pinned without a sandbox existing. - The CLI —
internal/clidrivesApp.Run, the same entry pointcmd/abxuses, against a recording sandbox and a gateway that answers without openshell.AGENTBOX_HOMEis a temp dir, so nothing touches a real box. The recorder can also materialise a fake sandbox return, which is what makes the pull audit — the one place untrusted bytes cross onto the host — testable at all.
These are characterization tests: they pin what abx does, so a change in behaviour has to be deliberate. When a change is meant to alter behaviour, update the test in the same commit and say so — a quietly rewritten expectation defeats the point.
CI runs the suite on Linux and macOS, enforces the coverage floor, and builds a full release snapshot on every pull request — so a broken release config is a red PR rather than a tag that produces no binaries.
Working and in daily use, but pre-1.0: while the major version is 0, a feat: moves the minor digit and breaking changes can arrive with it. OpenShell itself is alpha (0.0.86 at time of writing) and moves fast; expect breakage on upgrade, and re-verify the traps documented in abx --help.
abx version says what you have, and so does the first line of abx doctor — a stale binary is the failure nobody thinks to check for.
MIT licensed.