agent/sandbox Documentation
Docs/Start here
Reference12 min readREADME.md

Overview & quick start

Install Agent Sandbox, prepare your environment, and launch your first isolated coding agent.

View source Markdown

agent logos in a sandbox

ags is a Rust CLI that launches AI coding agents inside a rootless Podman sandbox.

It is designed to keep your host clean while still giving agents controlled access to your repo, selected tools, and selected secrets.

Documentation map#

  • README.md (this file): quick start + daily usage
  • QUICKSETUP.md: under-five-minute install and update flow
  • docs/COMMANDS.md: detailed command behavior and side effects
  • docs/CONFIG.md: full config schema and semantics
  • docs/ONEPASSWORD.md: CLI-only Secure Note environment-set workflow
  • docs/GLIMPSE.md: user-facing Glimpse/host UI setup and usage
  • docs/TROUBLESHOOTING.md: common problems and fixes
  • docs/ARCHITECTURE.md: internal architecture overview
  • CONTRIBUTING.md: contributor workflow and quality checklist

What this tool provides#

  • Rootless containerized agent runs (Podman)
  • Multi-agent support:
    • pi
    • claude
    • codex
    • gemini
    • opencode
    • shell (interactive bash with agent environments mounted)
  • First-run setup for SSH auth + signing keys
  • Persistent per-agent host volumes by default (lockdown uses ephemeral staged homes)
  • Configurable mounts, tool binaries, and secret sources
  • Profession-guided TUI for choosing image tools and persistent agent CLIs
  • Optional hardened --lockdown runs for inspecting untrusted/foreign repos with reduced host exposure
  • Optional browser sidecar support for browser-enabled workflows
  • Narrow clipboard bridge for Pi image/text paste and copy flows without compositor passthrough; one paste waits for approval and completes once
  • Auth proxy for secure sandbox browser opens and OAuth loopback callbacks
  • Health checks via ags doctor
  • Convenience alias/wrapper generation via ags create-aliases

Requirements#

Required on host:

  • Rust toolchain (to build/run ags from source)
  • Podman (rootless recommended)
  • git
  • ssh-keygen
  • ssh-add
  • bash
  • curl (for mature GitHub Release resolution during image/agent updates)

Optional but useful:

  • make (for convenience targets)
  • secret-tool (GNOME keyring/libsecret integration)
  • Browser executable (for --browser mode)
  • zenity or kdialog (for auth proxy allow/deny prompts; auto-denied if neither is available)

Tip: run ags doctor after setup to verify your environment.

Optional sandbox tool selection:

bash
ags tools --packages config/tool-packages.example.json

This opens a profession-guided TUI with General, Software Development, and Operations and DevOps views. Tools are grouped by area, and shared tools keep one selection across every view. Press a for the separate Agent CLIs panel. Image tools are applied with ags update-image; agent CLIs live in persistent runtime volumes and are reconciled with ags update-agents.


Build and install#

From repository root:

bash
# build debug binary
cargo build -p ags

# build optimized release binary
cargo build -p ags --release

Run without installing:

bash
cargo run -p ags -- --agent pi

Optional self-link into ~/.local/bin/ags:

bash
cargo run -p ags -- install --link-self

If an existing file/symlink should be replaced:

bash
cargo run -p ags -- install --link-self --force

You can also use Make targets (see below).


First-time setup#

1) Install baseline assets and config layout#

bash
cargo run -p ags -- install

This writes:

  • ~/.config/ags/Containerfile
  • ~/.config/ags/tmux.conf
  • ~/.config/ags/uv.toml (copied into the image as /etc/uv/uv.toml)
  • ~/.config/ags/pi/extensions/guard.ts
  • ~/.config/ags/pi/settings.json (if missing)

2) Create and edit config#

Use config/config.example.toml as your template:

bash
mkdir -p ~/.config/ags
cp config/config.example.toml ~/.config/ags/config.toml

Then replace placeholders with real values and paths.

Optional per-repo overrides can live at:

text
PROJECT_ROOT/.ags/config.toml

When present, AGS loads your user/global config first, then applies the repo-local overlay. Repo-local scalar/table fields win, while repeatable sections like [[mount]] are appended so projects can add mounts without replacing your base config.

3) Run setup#

bash
cargo run -p ags -- setup

setup will:

  • Generate SSH keys if missing:
    • ~/.ssh/ags-agent-auth
    • ~/.ssh/ags-agent-signing
  • Print public keys so you can add them to GitHub
  • Ensure Pi guard/settings assets exist in the host path mounted to /home/dev/.pi
  • Optionally prompt to store configured secrets via secret-tool

4) Build/update sandbox image and agent installs#

bash
cargo run -p ags -- update-image
cargo run -p ags -- update-agents

Agent updates verify an isolated candidate and publish a generation only when runtime contents or identity change. No-op checks leave latest/previous unchanged; changed generations share identical immutable files. Running sandboxes keep their pinned runtime; new sandboxes use the update. update-agents reports generations referenced by running and stopped containers. Cleanup retains the latest, previous, and all in-use generations; see runtime generations and retention.

5) Verify#

bash
cargo run -p ags -- doctor
cargo run -p ags -- --agent shell -- -lc 'br --version && bv --version && dcg --version && tmux -V && test -f ~/.tmux.conf'

Quick start (Makefile)#

Equivalent convenience flow:

bash
make setup
make doctor
make update
make update-agents
make run

Available targets:

  • make setup
  • make doctor
  • make update
  • make update-agents
  • make run
  • make run-browser
  • make install
  • make install-self
  • make uninstall
  • make aliases

Daily usage#

Run Pi agent:

bash
ags --agent pi

Run inside a tmux session (opt-in):

bash
ags --agent pi --tmux

Run with browser sidecar:

bash
ags --agent pi --browser

Run in hardened lockdown mode for untrusted repos:

bash
ags --agent claude --lockdown

Lockdown keeps network access and workspace visibility, but disables secrets, SSH agent forwarding, generic config/tool mounts, host bridges/sidecars, and direct mounting of real agent home state. Instead AGS stages a sanitized ephemeral agent home/runtime for the selected agent and discards session/history artifacts when the run exits.

Disable AGS Pi/Claude guards for one run:

bash
ags --agent pi --yolo
ags --agent claude --yolo

Run other agents:

bash
ags --agent claude
ags --agent codex
ags --agent gemini
ags --agent opencode
ags --agent shell

Pass arguments through to the underlying agent CLI using --:

bash
ags --agent pi -- --continue
ags --agent claude -- --model sonnet
ags --agent claude --defaults -- --model opus

Use --defaults / -D to prepend AGS-managed passthrough defaults for the selected harness. Today that means Claude gets --strict-mcp-config --dangerously-skip-permissions, Gemini gets --yolo, and other agents currently add nothing.

tmux inside the sandbox#

The sandbox image includes tmux with a minimal version-controlled config for team-pane workflows. Defaults include:

  • default prefix remains Ctrl-b
  • mouse support enabled
  • pane/window creation starts in the current pane directory
  • xterm-kitty terminal support via kitty-terminfo
  • no plugin manager dependency

If you want the agent itself to start inside tmux, pass --tmux:

bash
ags --agent pi --tmux
ags --agent shell --tmux

This is intentionally not the default. If tmux is reported as missing when using --tmux, rebuild the sandbox image first:

bash
ags update-image

Quick check:

bash
ags --agent shell -- -lc 'tmux -V && test -f ~/.tmux.conf && tmux new-session -d -s smoke && tmux kill-session -t smoke'

Use a non-default config file:

bash
ags --agent pi --config /path/to/config.toml

MCP auth persistence#

MCP servers that use OAuth (e.g. Linear) store auth tokens in ~/.mcp-auth inside the container. Without a persistent mount, tokens are lost on every session restart. Add this to your config:

toml
[[mount]]
host = "$HOME/.mcp-auth"
container = "/home/dev/.mcp-auth"
mode = "rw"
kind = "dir"
create = true

To skip the allow/deny dialog for known OAuth providers:

toml
[auth_proxy]
auto_allow_domains = ["mcp.linear.app"]

Host service access from inside sandbox#

ags runs agent CLIs inside the container, so localhost refers to the container itself.

To connect to services running on your host machine, use host.containers.internal instead.

ags also exports runtime hints inside the container:

  • AGS_HOST_SERVICES_HOST=host.containers.internal
  • AGS_HOST_SERVICES_HINT (human-readable reminder)

For pi, claude, codex, and opencode, ags also injects a short startup hint into the agent prompt context.

Example:

bash
ags --agent shell -- -lc 'curl http://host.containers.internal:3000/health'

Postgres example (psql is available in the sandbox image after ags update-image):

bash
ags --agent shell -- -lc 'PGPASSWORD="${PGPASSWORD:-postgres}" psql -h "${AGS_HOST_SERVICES_HOST}" -p "${PGPORT:-5432}" -U "${PGUSER:-postgres}" "${PGDATABASE:-postgres}"'

Tip: add PGPASSWORD, PGUSER, PGDATABASE, and PGPORT to [sandbox].passthrough_env if you want host values to flow into the container automatically.

1Password Secure Note environment sets#

For a one-off database or service credential set, pass a host 1Password Secure Note explicitly:

console
ags --agent pi -1 'ExampleVault/readonly-database'

-1 is short for the repeatable --op-secret-set VAULT/ITEM flag. The item must have category SECURE_NOTE; every field with a present string value is injected using its custom-field label unchanged, so labels must already be safe environment-variable names. For example: PGHOST, PGPORT, PGUSER, PGPASSWORD, and PGDATABASE. Empty values are valid, and later fields/items override earlier labels.

This is CLI-only: there is no config.toml key, field mapping, preset, or automatic activation. Only 1Password SECURE_NOTE items are supported—not Login, Password, API Credential, or other item categories. AGS runs op on the host with normal interactive 1Password authentication; it is not mounted into the sandbox. The final agent process tree—including psql, MCP servers, subagents, tmux panes, and post-agent tmux shells—receives the variables. See docs/ONEPASSWORD.md for the complete workflow.

Values stay out of AGS argv, Podman configuration, regular files, and AGS's long-lived userspace memory. The host op process and the final container bootstrap necessarily see them briefly; the final environment remains inspectable by authorized same-user/root processes through /proc/<pid>/environ. Vault/item names are metadata and are visible in the host op command line. Use a dedicated least-privilege/readonly DB role and rotate or revoke it independently.

--lockdown cannot be combined with -1/--op-secret-set. This transport requires local Podman support for --preserve-fds; remote Podman is unsupported. To smoke-test a readonly item without printing a password or dumping the environment, check only required names and connectivity, for example:

console
ags --agent shell -1 'ExampleVault/readonly-database' -- \
  -lc 'test -n "${PGHOST-}" && test -n "${PGUSER-}" && psql -c "select 1" >/dev/null'

Host-owned Glimpse windows#

If you want sandboxed agent code to open Glimpse windows on your host desktop, enable [host_ui] in your config.

Start here:

  • docs/GLIMPSE.md for setup and troubleshooting
  • docs/CONFIG.md for the [host_ui] field reference

Commands reference#

Core commands#

  • ags setup — generate keys, ensure Pi assets in mounted host path, optional keyring secret setup
  • ags doctor — run environment + config health checks
  • ags update-image [--rebase] [--keep-existing] — check for and apply image updates incrementally (RPM updates, Rust stable, rustup, pnpm, and catalog-selected DNF and verified-download tools), reusing unchanged components, verifying the result offline before publishing it, and removing the previous image unless it is still referenced by a container or --keep-existing is set. --rebase refreshes the Fedora base within its release and restarts the OS update layers. See docs/COMMANDS.md
  • ags update-agents — install/update agent CLIs in persistent volumes
  • ags prune-workspace-caches [--dry-run] — safely collect orphaned checkout caches with seven-day CLI-default grace (one day in the systemd service) and bounded, low-priority deletion; see daily systemd maintenance
  • ags node install <version> / ags node list — manage user Node versions through mise in the persistent AGS store
  • ags tools --packages <catalog.json> — choose optional sandbox tools by profession and area
  • ags install [--link-self] [--force] [--add-agent-mounts] — install assets/config layout, optional self-link, optional config mount block append
  • ags uninstall — currently reserved/no-op cleanup
  • ags create-aliases — create managed wrappers and/or shell alias blocks
  • ags completions --shell <bash|zsh|fish> — print shell completion script

Workspace cache maintenance#

Workspace caches are not cleaned during agent launches or ags update-agents. Schedule ags prune-workspace-caches with the optional daily systemd user timer in config/systemd/; it runs around 03:17–03:32 local time, catches up missed runs, and preserves valid checkouts, container mounts, and pending launches. Installation and status/log commands are in the maintenance guide. No scheduler is installed by ags install.

The first run may report 26 orphaned; 0 removed; 0 pending; 0 filesystem deletions: that is normal while the observed-orphan grace period is starting (one day in the systemd service; seven days for the CLI without --grace-days). Large caches are deleted incrementally over subsequent scheduled runs. See cleanup troubleshooting.

node runtime management#

bash
ags node install 22
ags node list

Node versions are installed by mise in a throwaway Linux helper container. The store is persistent under ~/.cache/ags/mise by default and mounted read-only in ordinary runs, so an agent cannot install or alter managed versions. A nearest .nvmrc selects an installed version; it must contain exactly a numeric major, minor, or patch selector such as 22, 22.14, or 22.14.0 (an optional leading v is accepted). lts/*, aliases, shell content, project mise.toml, hooks, and environment directives are not evaluated. If the selected version is absent, AGS prints an ags node install <version> remedy and does not fall back; without .nvmrc, the image's fixed Node 24 baseline is used.

These are Linux-container runtimes, not host Node installations: install/list require local Podman able to run the configured Linux image, and the selected binaries are available only inside ordinary AGS runs. Rebuild an existing image with ags update-image before using this feature if it predates mise. --lockdown intentionally omits the managed store and wrappers to preserve its ephemeral agent-runtime isolation.

create-aliases options#

bash
ags create-aliases --mode wrappers|aliases|both --shell fish|zsh|bash --force
  • default mode: wrappers
  • if --shell omitted, shell is autodetected from $SHELL

Shell completions#

bash
# add extra same-path directory mounts for a single run
ags --agent claude -d ~/code -d ~/Downloads

# bash
ags completions --shell bash > ~/.local/share/bash-completion/completions/ags

# zsh
ags completions --shell zsh > ~/.zfunc/_ags

# fish
ags completions --shell fish > ~/.config/fish/completions/ags.fish

Global run flags#

  • --agent <pi|claude|codex|gemini|opencode|shell> (required for run mode)
  • --browser
  • --tmux
  • --stop-when-done (requires --tmux)
  • --psp
  • --psp-keep
  • --yolo
  • --root
  • --lockdown (harden the run by disabling host bridges, secrets, SSH agent, generic mounts/tools, direct agent-home mounts, host-loopback networking, and adding size-limited tmpfs mounts for that run)
  • --wayland-compositor-passthrough (mount the real host Wayland compositor socket; broad desktop access and separate from clipboard support)
  • --defaults / -D (prepend AGS-managed passthrough defaults for the selected harness)
  • --config <path>
  • --add-dir <path> / -d <path> (repeatable, run only; still allowed in lockdown)
  • --env <NAME=VALUE> (repeatable, run only; sets an explicit container environment variable, with later assignments winning; do not use for secrets because values appear in the host command line)

Configuration guide#

Default base config path:

  • ~/.config/ags/config.toml

Optional repo-local overlay path:

  • PROJECT_ROOT/.ags/config.toml

If the base config is missing, ags auto-creates a minimal default file on first run.

Precedence order:

  1. user/global config (~/.config/ags/config.toml, or --config <path> if provided)
  2. repo-local overlay (PROJECT_ROOT/.ags/config.toml) when running inside a git repo/worktree

Repo-local scalar/table fields override the base config. Repeatable sections ([[mount]], [[agent_mount]], [[tool]], [[secret]]) are additive. Host command secret sources are the exception: they are rejected in repo-local overlays and must be placed in trusted user/global config.

Use config/config.example.toml for full schema examples.

Important sections#

  • [sandbox]
    • Container image name and core paths
    • SSH key paths
    • bootstrap files
    • base env passthrough allowlist
    • optional DNF packages and the generated verified-tool download lock
  • [[agent_mount]]
    • Dedicated required mounts for agent home-state paths (explicit, no implicit agent mounts)
  • [[mount]]
    • Additional bind mounts from host to container
    • supports mode, kind, when, create, optional
  • [[tool]]
    • Tool binary mount
    • optional nested [[tool.directory]] mounts
    • optional nested [[tool.secret]] sources
  • [[secret]]
    • Map env var names to ordered source alternatives: from_env, secret_store, and trusted host command argv arrays
  • [auth_proxy]
    • auto_allow_domains: list of domains to skip the allow/deny prompt for (e.g. ["mcp.linear.app"])
  • [browser]
    • Enables browser sidecar integration used with --browser
  • [clipboard]
    • Starts the socket-backed clipboard bridge (off, read, or readwrite) with host approval windows for reads by default
  • [desktop_passthrough]
    • Explicit broad desktop passthrough controls such as raw Wayland compositor access
  • [update]
    • Controls Pi package spec and the minimum maturity age for pnpm and GitHub Release updates

Security notes#

  • Use least-privilege, short-lived tokens whenever possible.
  • Only mount what the agent needs.
  • Prefer read-only (ro) mounts unless write access is required.
  • For untrusted or foreign repos, prefer --lockdown to minimize host exposure for that run.
  • In lockdown, Bash command classification fails closed if destructive_command_guard (dcg) is unavailable or errors; run ags doctor/ags update-image if Bash commands are unexpectedly blocked.
  • Treat passthrough_env and configured secrets as sensitive data paths.
  • npm/pnpm lifecycle scripts are disabled in the sandbox (ignore-scripts=true). Normal pnpm operations use writable per-worktree store/cache mounts, separate from read-only agent generations and the updater-only download cache. ags update-image resolves the current stable pnpm release from the npm registry by default; [sandbox].pnpm_version = "12.9.1" pins an exact release. Both verify tarball integrity. For OpenCode, AGS resolves the catalog source saved by ags tools, verifies the architecture-specific GitHub Release archive, validates the staged binary version, and atomically activates it in a dedicated persistent volume outside pnpm.
  • Non-RPM tools selected through ags tools use pinned HTTPS artifacts for both supported architectures. AGS validates their catalog metadata and verifies SHA-256 before extracting only the declared executable.
  • Rotate/revoke credentials quickly if compromise is suspected.
  • The clipboard bridge is narrower than raw Wayland passthrough. Host clipboard reads require approval by default and can be allowed for [clipboard].approval_seconds; disabling approval restores session-wide read access.
  • Raw Wayland compositor passthrough is disabled by default; enable it only with [desktop_passthrough].wayland = true or --wayland-compositor-passthrough when you intentionally want sandbox GUI clients on the host desktop.
  • The auth proxy requires explicit user approval via the shared AGS desktop dialog before opening any URL requested by the sandbox agent. URLs are never opened silently.
  • OAuth loopback callbacks are relayed through the host proxy — the container never listens on host network ports directly.
  • This repo ships a project-local dcg policy (.dcg/packs/git-worktree-sandbox.yaml) that blocks git worktree prune in sandbox sessions because not all host worktrees are necessarily visible from inside the container.

Project layout#

  • crates/ags/ — Rust CLI implementation
  • config/Containerfile — final assembly of the sandbox image from prebuilt components
  • config/image/ — component recipes (OS baseline and refresh, build foundation, Rust, pnpm, vendor tools, Glimpse) and the offline image verification script
  • config/tmux.conf — minimal tmux defaults copied into the sandbox image
  • config/config.example.toml — full config template
  • agent/extensions/guard.ts — runtime guard extension mounted for Pi
  • agent/settings.example.json — example Pi settings template
  • agent/auth-proxy-shim — container-side $BROWSER, xdg-open, and sensible-browser replacement for the auth proxy
  • Makefile — convenience command wrappers

Troubleshooting#

  • Run ags doctor first.
  • If image is missing/stale: run ags update-image.
  • If agent CLIs are missing/stale: run ags update-agents.
  • If browser mode fails:
    • ensure [browser].enabled = true
    • verify [browser].command is valid
    • verify debug port is available
  • If auth proxy prompts are auto-denied: install zenity or kdialog
  • If secrets are not found:
    • verify env vars exist and are non-empty
    • or verify secret-tool entries match configured attributes

Contributing#

Contributions are welcome.

For the full contributor guide, see CONTRIBUTING.md.

Development setup#

bash
# build
cargo build -p ags

# format
cargo fmt

# lint
cargo clippy -p ags -- -D warnings

# test
cargo test -p ags

Suggested PR workflow#

  1. Create a focused branch.
  2. Make small, clear commits.
  3. Add/update tests for behavior changes.
  4. Run fmt/clippy/tests locally.
  5. Update docs (README/config example/help text) when behavior changes.
  6. Open PR with:
    • summary of user-visible changes
    • config/migration notes (if any)
    • test coverage notes

High-value contribution areas#

  • Better diagnostics in doctor
  • Config schema/documentation improvements
  • Additional agent/profile support
  • Better cross-platform behavior and install UX
  • Safer defaults and security hardening

Host startup preparation#

Optional cumulative [[prepare_hook]] declarations run host executables before container creation and contribute environment values, generated files, bind mounts and 1Password references. Approval grants host-user execution, tracking the entrypoint bytes and argv—not imported code/libraries or scripts passed as interpreter arguments. Deterministic merge, bounded parallel execution, protected targets and redacted discoverability commands are described in Startup hooks v1.

sh
ags hooks describe prepare
ags hooks schema prepare output
ags hooks validate response.json # inert validation
ags hooks test NAME --agent shell # executes trusted HOST CODE; not a dry run