原始内容
sandbox-agent
A Podman wrapper that runs agent CLIs (primarily codex, but also copilot, opencode, and pi) inside a container.
For codex, it always uses:
--search(web search tool availability)--config web_search=live|cached|disabled(defaults tocached, or uses host~/.codex/config.tomlif set)
Networking is enabled (full egress).
Quick start
1) Build an image
You need an image that includes codex, git, bash, and uv (plus a Python runtime; this image uses uv-managed Python by default, and exposes it as python3 in the container).
Use Containerfile:
podman build -t localhost/sandbox-agent:latest -f Containerfile .
Or use the Makefile (also installs the wrapper):
make install
If you're on a corporate network with an npm mirror, override the registry:
make install NPM_REGISTRY=https://your-registry.example.com/
If TLS is intercepted (transparent proxy / self-signed in chain), pass a corporate CA file:
make install EXTRA_CA_CERT_PATH=$HOME/.local/share/machine-setup/certs/wbg-corp-ca-bundle.pem
The build accepts a single PEM cert, a PEM bundle, or a single DER cert.
If EXTRA_CA_CERT_PATH is not set, the Makefile auto-detects WBG CA material
only on the WBG laptop hostname (PCACL-G7MKN94) by checking:
bin/wbg-ca-helper corp-bundle-path(preferred; generated local bundle), then~/.local/share/machine-setup/certs/wbg-corp-ca-bundle.pem, then~/wbg-cloud-root-ca.pemor../../wbg-cloud-root-ca.pem, then~/wbg_root_ca_g2.ceror../../wbg_root_ca_g2.ceras a legacy fallback.
These cert files are not stored in the repo; they are local machine files and should not be committed.
You can also override bundled tool versions:
make install CODEX_NPM_PKG=@openai/codex@latest OPENCODE_VERSION=latest \
MQ_VERSION=0.5.9 MQ_TARGET=x86_64-unknown-linux-gnu \
TYPST_VERSION=0.14.2 TYPST_TARGET=x86_64-unknown-linux-musl
mq is installed from its GitHub release assets and verified against the
upstream checksums.txt. If you override MQ_TARGET, make sure the selected
MQ_VERSION publishes a matching release artifact.
Override the default uv-managed Python version:
make install UV_DEFAULT_PYTHON=3.14
You can also override the bundled uv binary version (rarely needed):
make install UV_VERSION=0.9.21
If you want a smaller build (skip Playwright’s bundled browser download), set:
make install INSTALL_PLAYWRIGHT_BROWSERS=0
2) Install the wrapper
install -m 0755 ./sandbox-agent ~/.local/bin/sandbox-agent
install -m 0755 ./sandbox-agent-codex ~/.local/bin/sandbox-agent-codex
install -m 0755 ./sandbox-agent-copilot ~/.local/bin/sandbox-agent-copilot
install -m 0755 ./sandbox-agent-opencode ~/.local/bin/sandbox-agent-opencode
install -m 0755 ./sandbox-agent-pi ~/.local/bin/sandbox-agent-pi
3) (Optional) Configure mounts
Create ~/.config/sandbox-agent/config.sh:
CODEX_CONTAINER_SANDBOX_IMAGE="localhost/sandbox-agent:latest"
# Optional: force an OCI runtime (useful on some WSL/work setups).
# CODEX_CONTAINER_SANDBOX_PODMAN_RUNTIME="runc"
# Optional: override DNS servers used inside the container.
# Default behavior is to inherit host/Podman DNS.
# Enable override explicitly:
# CODEX_CONTAINER_SANDBOX_DISABLE_DNS_OVERRIDE=0
# Or override the server list (in order):
# CODEX_CONTAINER_SANDBOX_DNS_SERVERS=(45.90.28.212 45.90.30.212 1.1.1.1 8.8.8.8)
#
# Note: When DNS override is enabled, the wrapper bind-mounts a generated
# /etc/resolv.conf into the container to bypass Podman DNS forwarders (often a
# 169.254.x.x hop).
# Mount helper tools read-only (mapped under /home/codex/...)
CODEX_CONTAINER_SANDBOX_RO_MOUNTS=(
"$HOME/.local/bin"
"$HOME/bin"
)
# Persist caches if needed
CODEX_CONTAINER_SANDBOX_RW_MOUNTS=(
"$HOME/.cache/uv"
"$HOME/tmp"
)
# Optional: add host env vars to pass through into the container.
# Defaults already include common LLM providers + AWS Bedrock credentials.
# CODEX_CONTAINER_SANDBOX_ENV_PASSTHROUGH+=(MY_EXTRA_API_KEY)
#
# Optional: disable default passthrough list, then define an explicit allowlist.
# CODEX_CONTAINER_SANDBOX_DISABLE_DEFAULT_ENV_PASSTHROUGH=1
# CODEX_CONTAINER_SANDBOX_ENV_PASSTHROUGH=(
# GEMINI_API_KEY
# GOOGLE_API_KEY
# )
#
# Optional: override/disable host OpenCode agents mount.
# CODEX_CONTAINER_SANDBOX_OPENCODE_AGENTS_DIR="$HOME/.config/opencode/agents"
# CODEX_CONTAINER_SANDBOX_DISABLE_OPENCODE_AGENTS_MOUNT=1
Usage
Default: zsh shell in container
sandbox-agent
Codex (full network)
sandbox-agent codex exec "Summarize the repo"
OpenCode (full network)
sandbox-agent opencode agent list
Instruction-source dry run (recommended)
Prints effective global/project AGENTS sources and mount targets without launching the container:
sandbox-agent --dry-run-instructions codex exec "noop"
Project AGENTS modes for codex exec
For sandbox-agent codex exec, project AGENTS behavior is explicit and mode-driven:
suppress(default): do not mount projectAGENTS.mdrepo: mount repo-rootAGENTS.mdreplace: mount a custom project AGENTS fileconcat: composecustom + repo(custom first), then mount combined file
Examples:
# Use repo AGENTS.md
sandbox-agent --project-agents-mode repo codex exec "..."
# Replace project AGENTS.md with a custom file
sandbox-agent --project-agents-mode replace --project-agents-file ~/my-project-agents.md codex exec "..."
# Concatenate custom + repo AGENTS
sandbox-agent --project-agents-mode concat --project-agents-file ~/.codex/AGENTS.md codex exec "..."
You can also pin an explicit global AGENTS file for codex exec:
sandbox-agent --global-agents-file ~/machine-setup/tools/sandbox-agent/SANDBOXED-AGENT-AGENTS.md codex exec "..."
Single preset to combine sandbox global AGENTS + host ~/.codex/AGENTS.md (as project concat):
sandbox-agent --instruction-profile host-plus-sandbox codex exec "..."
Pi (pi-mono coding agent)
sandbox-agent pi
pi is available in the image, but sandbox-agent does not auto-install pi
plugins/packages. Install only what you need in your ~/.pi/ state.
Makefile helpers
From the repo root:
make image
make selftest
make selftest-instructions
make pii-scan
make validate-docs
Self-test (network + mount isolation)
Runs seven checks:
- Container has internet connectivity.
- Playwright + Chromium are usable.
- Codex + OpenCode CLIs are available in-container.
- Host files outside the workspace are not visible by default.
- An explicitly mounted host directory is readable and writable (RW mount).
- OpenCode agents mount wiring is visible in dry-run, mounted read-only when a host source exists, and
--rwoverlaps with~/.agentsare rejected. - Instruction-source flag matrix passes (
scripts/test_instruction_flags.sh).
./selftest.sh
Shell inside container
sandbox-agent --shell
Debug the podman command
CODEX_CONTAINER_SANDBOX_DEBUG=1 sandbox-agent codex exec "hello"
Hide the printed codex ... command
By default the wrapper prints the computed codex ... command (to stderr) before starting.
Disable with:
sandbox-agent --no-print-codex-cmd codex exec "hello"
Git commit identity
To avoid git commit failing with “Author identity unknown”, the wrapper will (by default)
copy user.name / user.email from the host git config into a persistent container-global
gitconfig under CODEX_HOME, and set GIT_CONFIG_GLOBAL accordingly.
Override (or set explicitly if host identity is not set):
CODEX_CONTAINER_SANDBOX_GIT_NAME="Your Name" \
CODEX_CONTAINER_SANDBOX_GIT_EMAIL="you@domain" \
sandbox-agent ...
Disable:
CODEX_CONTAINER_SANDBOX_DISABLE_GIT_IDENTITY_SYNC=1 sandbox-agent ...
Mount behavior
- If you run inside a git repo, the repo root is mounted read-write.
- The container working directory is set to your original
$PWDinside that mount. - For
codex exec, global AGENTS is mounted from canonicalSANDBOXED-AGENT-AGENTS.mdto container~/.codex/AGENTS.md(or from--global-agents-filewhen provided). - Extra mounts under
$HOMEare mapped to the same relative path under/home/codex. XDG_CACHE_HOMEis set to$CODEX_HOME/cacheso tools likeuvhave a writable cache by default.- If host OpenCode agents are discoverable, they are mounted read-only to
~/.config/opencode/agents. - Pi state in the container lives under
~/.pi/, backed by a wrapper-managed host directory:~/.local/state/sandbox-agent/pi(disable withCODEX_CONTAINER_SANDBOX_DISABLE_PI_MOUNT=1). - If host
~/.pi/agentexists, it is mounted read-only into the container at~/.pi-host/agentso you can reuse host extensions/prompts without allowing in-container mutation (disable withCODEX_CONTAINER_SANDBOX_DISABLE_PI_HOST_AGENT_MOUNT=1). - sandbox-agent does not seed pi harness plugins into
~/.pi/agent/settings.json. - Any mounts derived from
~/.agentsare read-only by design. - If
~/.agentsresolves into the mounted workspace tree, sandbox-agent adds a read-only submount at that workspace path so the repo bind mount cannot make it writable. - sandbox-agent refuses explicit
--rwmounts that overlap~/.agents.
Auth
Codex credentials live in CODEX_HOME (~/.local/state/sandbox-agent by default).
Login once inside the container:
sandbox-agent --shell
codex login
Reuse host auth.json (optional)
If you already have a working host login (for example ~/.codex/auth.json), you can mount it
into the container so codex doesn't prompt for login again:
- Auto-detects and mounts
~/.codex/auth.jsonif it exists. - Override the path with
CODEX_CONTAINER_SANDBOX_AUTH_FILE=/path/to/auth.json. - Disable mounting entirely with
CODEX_CONTAINER_SANDBOX_DISABLE_AUTH_MOUNT=1. - Control mount mode (default
ro) withCODEX_CONTAINER_SANDBOX_AUTH_MOUNT_MODE=ro|rw.
Reuse host skills and Pi customizations (optional)
To keep Codex skills consistent with your host setup, the wrapper can mount:
~/.agents/skills(preferred) or~/.codex/skills(fallback) ->$CODEX_HOME/skills(read-only)
Controls:
- Override paths:
CODEX_CONTAINER_SANDBOX_SKILLS_DIR=/path/to/skills
- Disable:
CODEX_CONTAINER_SANDBOX_DISABLE_SKILLS_MOUNT=1
Pi prompt templates ride through the existing read-only ~/.pi/agent mount. When setup.sh
wires ~/.pi/agent/prompts to .agents/generated/pi/prompts, the container sees the same
templates at ~/.pi-host/agent/prompts without any separate Codex prompt mount.
Reuse host OpenCode agents (optional)
To keep OpenCode role definitions consistent with your host setup, the wrapper can mount:
~/.config/opencode/agents(preferred)~/.agents/subagents/generated/opencode/agents(fallback)<workspace>/.agents/subagents/generated/opencode/agents(repo-local fallback)
All OpenCode agents mounts are read-only and target:
~/.config/opencode/agentsinside the container
Controls:
- Override path:
CODEX_CONTAINER_SANDBOX_OPENCODE_AGENTS_DIR=/path/to/agents
- Disable:
CODEX_CONTAINER_SANDBOX_DISABLE_OPENCODE_AGENTS_MOUNT=1
LLM env passthrough
The wrapper forwards a default allowlist of provider credential environment variables from host to container (only when each variable is present on host):
OPENAI_API_KEYANTHROPIC_API_KEYGEMINI_API_KEYGOOGLE_API_KEYGOOGLE_GENAI_API_KEYAZURE_OPENAI_API_KEYAZURE_OPENAI_ENDPOINTELEVENLABS_API_KEYMISTRAL_API_KEYCOHERE_API_KEYGROQ_API_KEYPERPLEXITY_API_KEYTOGETHER_API_KEYFIREWORKS_API_KEYOPENROUTER_API_KEYXAI_API_KEYDEEPSEEK_API_KEYCEREBRAS_API_KEYAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKENAWS_REGIONAWS_DEFAULT_REGIONAWS_PROFILE
Customization:
- Add extra vars in
~/.config/sandbox-agent/config.sh:CODEX_CONTAINER_SANDBOX_ENV_PASSTHROUGH+=(MY_EXTRA_API_KEY)
- Replace defaults with your own explicit list:
CODEX_CONTAINER_SANDBOX_DISABLE_DEFAULT_ENV_PASSTHROUGH=1CODEX_CONTAINER_SANDBOX_ENV_PASSTHROUGH=(VAR_A VAR_B)
Reuse host helper CLIs (read-pdf)
If you have read-pdf installed on the host at ~/.local/bin/read-pdf, the wrapper will
mount that executable (and its helper scripts like read_pdf_page_candidates.py) read-only
into the container (without mounting the entire ~/.local/bin directory) so it is available
on the container $PATH.
Disable with:
CODEX_CONTAINER_SANDBOX_DISABLE_LOCAL_BIN_MOUNT=1 sandbox-agent ...
Built-in tools (image is self-sufficient)
The image ships with a few common “skills dependencies” so you don’t need host mounts:
codexopencodecopilotpiimagemagick(convert,identify) forimage-croppoppler-utils(pdfinfo,pdftoppm) forread-pdf --as-imagesmarkitdownforread-webpage-content-as-markdownandread-pdf --as-text-fastpandocmqtypstchromium+playwright(JS/client-rendered pages)
Reuse host CLIs (optional; for extra tools/versions)
If you install CLIs on the host via:
uv tool install ...(often creates symlinks under~/.local/binpointing at~/.local/share/uv/tools/...)
the wrapper can mount the needed host directories read-only so those tools work inside the container.
Defaults (best-effort, only when detected):
- Mount
~/.local/share/uv/toolsread-only whenttokis detected as a uv tool install. - Also mount
~/.local/share/uv/pythonread-only (needed for uv tool shebang interpreters) when present. - Mount host
~/.opencode/bin/opencoderead-only when present (fallback path; in-image/usr/local/bin/opencoderemains preferred).
Disable:
CODEX_CONTAINER_SANDBOX_DISABLE_UV_TOOLS_MOUNT=1 sandbox-agent ...
CODEX_CONTAINER_SANDBOX_DISABLE_UV_PYTHON_MOUNT=1 sandbox-agent ...
Security note
This wrapper is about filesystem isolation (mount boundaries), not egress safety.
Because this runs full yolo with full networking, the agent can exfiltrate anything it can
read inside the container (including anything you mount, and CODEX_HOME/auth.json).