Overview & quick start
Install Agent Sandbox, prepare your environment, and launch your first isolated coding agent.
View source Markdown
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 usageQUICKSETUP.md: under-five-minute install and update flowdocs/COMMANDS.md: detailed command behavior and side effectsdocs/CONFIG.md: full config schema and semanticsdocs/ONEPASSWORD.md: CLI-only Secure Note environment-set workflowdocs/GLIMPSE.md: user-facing Glimpse/host UI setup and usagedocs/TROUBLESHOOTING.md: common problems and fixesdocs/ARCHITECTURE.md: internal architecture overviewCONTRIBUTING.md: contributor workflow and quality checklist
What this tool provides#
- Rootless containerized agent runs (Podman)
- Multi-agent support:
piclaudecodexgeminiopencodeshell(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
--lockdownruns 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
agsfrom source) - Podman (rootless recommended)
gitssh-keygenssh-addbashcurl(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
--browsermode) zenityorkdialog(for auth proxy allow/deny prompts; auto-denied if neither is available)
Tip: run
ags doctorafter setup to verify your environment.
Optional sandbox tool selection:
ags tools --packages config/tool-packages.example.jsonThis 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:
# build debug binary
cargo build -p ags
# build optimized release binary
cargo build -p ags --releaseRun without installing:
cargo run -p ags -- --agent piOptional self-link into ~/.local/bin/ags:
cargo run -p ags -- install --link-selfIf an existing file/symlink should be replaced:
cargo run -p ags -- install --link-self --forceYou can also use Make targets (see below).
First-time setup#
1) Install baseline assets and config layout#
cargo run -p ags -- installThis 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:
mkdir -p ~/.config/ags
cp config/config.example.toml ~/.config/ags/config.tomlThen replace placeholders with real values and paths.
Optional per-repo overrides can live at:
PROJECT_ROOT/.ags/config.tomlWhen 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#
cargo run -p ags -- setupsetup 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#
cargo run -p ags -- update-image
cargo run -p ags -- update-agentsAgent 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#
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:
make setup
make doctor
make update
make update-agents
make runAvailable targets:
make setupmake doctormake updatemake update-agentsmake runmake run-browsermake installmake install-selfmake uninstallmake aliases
Daily usage#
Run Pi agent:
ags --agent piRun inside a tmux session (opt-in):
ags --agent pi --tmuxRun with browser sidecar:
ags --agent pi --browserRun in hardened lockdown mode for untrusted repos:
ags --agent claude --lockdownLockdown 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:
ags --agent pi --yolo
ags --agent claude --yoloRun other agents:
ags --agent claude
ags --agent codex
ags --agent gemini
ags --agent opencode
ags --agent shellPass arguments through to the underlying agent CLI using --:
ags --agent pi -- --continue
ags --agent claude -- --model sonnet
ags --agent claude --defaults -- --model opusUse --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-kittyterminal support viakitty-terminfo- no plugin manager dependency
If you want the agent itself to start inside tmux, pass --tmux:
ags --agent pi --tmux
ags --agent shell --tmuxThis is intentionally not the default.
If tmux is reported as missing when using --tmux, rebuild the sandbox image first:
ags update-imageQuick check:
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:
ags --agent pi --config /path/to/config.tomlMCP 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:
[[mount]]
host = "$HOME/.mcp-auth"
container = "/home/dev/.mcp-auth"
mode = "rw"
kind = "dir"
create = trueTo skip the allow/deny dialog for known OAuth providers:
[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.internalAGS_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:
ags --agent shell -- -lc 'curl http://host.containers.internal:3000/health'Postgres example (psql is available in the sandbox image after ags update-image):
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:
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:
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.mdfor setup and troubleshootingdocs/CONFIG.mdfor the[host_ui]field reference
Commands reference#
Core commands#
ags setup— generate keys, ensure Pi assets in mounted host path, optional keyring secret setupags doctor— run environment + config health checksags 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-existingis set.--rebaserefreshes the Fedora base within its release and restarts the OS update layers. See docs/COMMANDS.mdags update-agents— install/update agent CLIs in persistent volumesags 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 maintenanceags node install <version>/ags node list— manage user Node versions through mise in the persistent AGS storeags tools --packages <catalog.json>— choose optional sandbox tools by profession and areaags install [--link-self] [--force] [--add-agent-mounts]— install assets/config layout, optional self-link, optional config mount block appendags uninstall— currently reserved/no-op cleanupags create-aliases— create managed wrappers and/or shell alias blocksags 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#
ags node install 22
ags node listNode 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#
ags create-aliases --mode wrappers|aliases|both --shell fish|zsh|bash --force- default mode:
wrappers - if
--shellomitted, shell is autodetected from$SHELL
Shell completions#
# 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.fishGlobal 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:
- user/global config (
~/.config/ags/config.toml, or--config <path>if provided) - 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 hostcommandargv arrays
- Map env var names to ordered source alternatives:
[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
- Enables browser sidecar integration used with
[clipboard]- Starts the socket-backed clipboard bridge (
off,read, orreadwrite) with host approval windows for reads by default
- Starts the socket-backed clipboard bridge (
[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
--lockdownto minimize host exposure for that run. - In lockdown, Bash command classification fails closed if
destructive_command_guard(dcg) is unavailable or errors; runags doctor/ags update-imageif Bash commands are unexpectedly blocked. - Treat
passthrough_envand 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-imageresolves 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 byags 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 toolsuse 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 = trueor--wayland-compositor-passthroughwhen 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 blocksgit worktree prunein sandbox sessions because not all host worktrees are necessarily visible from inside the container.
Project layout#
crates/ags/— Rust CLI implementationconfig/Containerfile— final assembly of the sandbox image from prebuilt componentsconfig/image/— component recipes (OS baseline and refresh, build foundation, Rust, pnpm, vendor tools, Glimpse) and the offline image verification scriptconfig/tmux.conf— minimal tmux defaults copied into the sandbox imageconfig/config.example.toml— full config templateagent/extensions/guard.ts— runtime guard extension mounted for Piagent/settings.example.json— example Pi settings templateagent/auth-proxy-shim— container-side$BROWSER,xdg-open, andsensible-browserreplacement for the auth proxyMakefile— convenience command wrappers
Troubleshooting#
- Run
ags doctorfirst. - 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].commandis valid - verify debug port is available
- ensure
- If auth proxy prompts are auto-denied: install
zenityorkdialog - If secrets are not found:
- verify env vars exist and are non-empty
- or verify
secret-toolentries match configured attributes
Contributing#
Contributions are welcome.
For the full contributor guide, see CONTRIBUTING.md.
Development setup#
# build
cargo build -p ags
# format
cargo fmt
# lint
cargo clippy -p ags -- -D warnings
# test
cargo test -p agsSuggested PR workflow#
- Create a focused branch.
- Make small, clear commits.
- Add/update tests for behavior changes.
- Run fmt/clippy/tests locally.
- Update docs (README/config example/help text) when behavior changes.
- 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.
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 runImprove this page where the source lives.