No description
  • Python 91.9%
  • Shell 8.1%
Find a file
2026-07-02 03:19:07 -04:00
src/agentbox feat: overlay mount mode 2026-07-02 03:19:07 -04:00
.gitignore feat: initial commit 2026-06-20 15:13:51 -04:00
.python-version feat: initial commit 2026-06-20 15:13:51 -04:00
prek.toml feat: initial commit 2026-06-20 15:13:51 -04:00
pyproject.toml feat: box snapshots 2026-06-26 03:06:26 -04:00
README.md feat: overlay mount mode 2026-07-02 03:19:07 -04:00
uv.lock feat: box snapshots 2026-06-26 03:06:26 -04:00

agentbox

Fair warning: this project is partially vibe-coded (i.e., there is AI-written code I have not fully reviewed -- especially the bash scripts). Use at your own risk.

The purpose of this project is to run "agentic" tools such as codex and claude-code in a sandboxed environment. A particular problem with these tools is that they default to too-permissive setups. Codex defaults to allow-read across your entire filesystem. Claude-code disables the sandbox by default, and instead uses (likely vibe-coded) static analysis of bash commands. Beyond the incorrect defaults these tools use, you have to trust that there are no bugs in their implementation.

Agentbox adds another layer of protection by using buildah working containers.

By default, agentbox mounts your current workspace read/write, with no other mounts. This blocks all reads and modification of your host system, but allows the agent to interact with the codebase. You can change how the workspace is mounted per run with --mode.

A non-goal of this project is blocking intentionally malicious actions, and I make no guarantees about the security of agentbox.

Installation and Basic Usage

uv tool install git+https://forgejo.circuitsacul.dev/circuitsacul/agentbox.git
# create a box
agentbox box new default
# enter the box (new session; mounts your _current_ folder read/write)
agentbox run default
# options go before the box name, e.g. mount the workspace read-only:
agentbox run --mode read default

Changes made inside a box are persistent, so you can install tools and setup configuration for them.

Workspace mount modes

agentbox run mounts your current directory into the box at /home/agent/session/workspace. The --mode (-m) flag selects how.

--mode workspace mount
write (default) current directory, read/write
read current directory, read-only (:ro)
overlay current directory via an overlay (:O); writable in the box, changes discarded on exit
none no project mount; a fresh, ephemeral scratch directory unique to each run
agentbox run --mode read default  # let the agent read the repo but not modify it
agentbox run --mode overlay default  # writable view of the repo; all changes vanish on exit
agentbox run --mode none default  # scratch session; the repo is not mounted at all

overlay uses buildah's overlay volume flag (:O): your current directory is the read-only lower layer, and all writes go to a temporary upper layer in container storage that buildah discards when the run exits -- workspace writes in the box never modify the host directory. Each run gets its own upper layer; nothing is shared between sessions. Avoid modifying the directory on the host while an overlay run is active (overlayfs behavior is undefined when the lower layer changes underneath it). Rootless overlay requires kernel overlayfs-in-userns support or fuse-overlayfs on the host; buildah errors out if neither is available.

none yields an empty workspace that is discarded when the run exits (never shared between sessions). Everything outside the workspace persists as normal, only the workspace is ephemeral. Scratch dirs live under $XDG_DATA_HOME/agentbox/scratch and are cleaned up on a best-effort basis.

Configuration

Agentbox reads config from $XDG_CONFIG_HOME/agentbox/config.json. If it does not exist, it will be seeded on first run from builtin defaults. Once it exists, it overrides the builtin config.

Built-in defaults

You can find the current built-in defaults in the source code at src/data/config.json. It just forwards some useful env vars and sets the HOME variable to /home/agent.

Schema

key type meaning
mounts list of mount objects extra bind mounts for agentbox run
forward_env list of names host env vars copied into the box if set
forward_env_prefixes list of prefixes forward every host var whose name starts with one of these
set_env map name→value env vars set in the box; overrides anything forwarded
flags.create list of tokens raw flags appended to buildah from (box creation)
flags.run list of tokens raw flags appended to buildah run

Mount object:

field default meaning
source host path; $VARS and ~ are expanded
dest (required) absolute container path; used literally (not expanded)
options "rw" volume options, e.g. "ro" (read-only)
if_missing "skip" when source is missing: skip (omit the mount), create (make a directory — fails if it already exists as a file), require (fail the run)

$VARS and ~ in source are expanded; an unknown $VAR is left verbatim. dest is used literally and must be an absolute container path.

Env handling

Agentbox applies forward_env_prefixes and forward_env first, then applies set_env (i.e., set_env can override forwarded env variabled.)

Raw flags

flags.create and flags.run are passed through buildah. Each item is a single token (they are quoted.) As such, either use the "--flag=value" format, or split into two tokens ("--flag", "value".)

flags.create only applies to new boxes; flags.run applies on every run. If you need to alter create-time flags for a box, see Snapshots.

SELinux

agentbox creates every box with SELinux confinement disabled (buildah from --security-opt label=disable), so the box process runs unconfined and can read the bind-mounted workspace without relabeling the host's files. You therefore never need the z/Z relabel option on a mount.

Snapshots

A snapshot is a buildah commit of a box's filesystem, stored as an image under the agentbox-snapshot repository.

command what it does
agentbox snapshot new <box> [--name NAME] commit <box> to a snapshot; default name is {utc-timestamp}-{box}-{short-image-id}
agentbox snapshot list [--box BOX] table of snapshots: source box, size, created, and dependent boxes; --box filters by source box
agentbox snapshot delete <name> delete a snapshot
agentbox snapshot prune [--box BOX] [--yes] delete every snapshot with no dependent boxes; --box to filter by source box
agentbox box new <name> --from <snapshot> create a box from a snapshot (with current flags.create)

Recipe: change a create flag on an existing box

flags.create only applies to a freshly created box. You can workaround this via snapshots:

  1. Add the flag to flags.create in $XDG_CONFIG_HOME/agentbox/config.json.
  2. agentbox snapshot new <current box>; this yields a snapshot name
  3. agentbox box new <new box> --from <snapshot>
  4. Confirm the new box works, then delete the old box: agentbox box delete <old box>.

Recipe: clipboard / image paste (Wayland)

Lets clipboard-image paste work inside the box by exposing the host Wayland socket. Add to your config at $XDG_CONFIG_HOME/agentbox/config.json:

{
  "mounts": [
+    {
+      "source": "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY",
+      "dest":   "/home/agent/.runtime/wayland-0",
+      "options": "rw"
+    }
  ],
  "set_env": {
+    "XDG_RUNTIME_DIR": "/home/agent/.runtime",
+    "WAYLAND_DISPLAY": "wayland-0"
  }
}

Then, once, inside the box: sudo dnf install -y wl-clipboard.

Recipe: per-box virtualenv (shadow .venv)

The workspace is bind-mounted into the box, so a host .venv (built for the host) would leak in and conflict. Keep a separate host .venv.agentbox and mount it over the workspace's .venv so the box gets its own. Add this to your config mounts:

{
  "source": "$PWD/.venv.agentbox",
  "dest":   "/home/agent/session/workspace/.venv",
  "options": "rw"
}

The default behavior for mounts is to skip if the source path does not exist. As such, you will need to manually create the .venv.agentbox folder the first time for it to work.