npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zigotica/agent-sandbox

v2.0.0

Published

Run AI coding agents inside Docker containers for security isolation

Downloads

894

Readme

agent-sandbox

License: MIT Release Docker Base Dependabot

What / Why

Running AI agents directly on your host gives them full access to your filesystem — SSH keys, AWS credentials, other projects, system files, etc. agent-sandbox runs AI coding agents (like pi and opencode) inside Docker containers for security isolation, protecting your host machine from accidental or malicious actions by the agent.

This is not foolproof — no sandbox is — but it adds a meaningful layer of protection. A determined attacker with code execution could still exfiltrate data via the network or the mounted project directory. Think of it as seatbelts, not a vault.

How it works

┌──────────────────────────────────────────────────────────────────┐
│                         Host Machine                             │
│                                                                  │
│  $HOME/.pi/               $HOME/my-project/                      │
│  ├── agent/                ├── src/                              │
│  ├── auth.json             ├── tests/                            │
│  └── sessions/             └── ...                               │
│        │                         │                               │
│        │ (config mount, rw)      │ (project mount, rw)           │
│        ▼                         ▼                               │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │              Docker Container (ephemeral)                  │  │
│  │                                                            │  │
│  │  /agent-config/      ◄─── config (rw, persisted)           │  │
│  │  /agent-data/        ◄─── data (rw, persisted)             │  │
│  │  /my-project/        ◄─── project (rw, persisted)          │  │
│  │  /home/agentuser/   ◄─── HOME (ephemeral)                  │  │
│  │                                                            │  │
│  │  πi / opencode     ◄─── runs here                          │  │
│  │  API keys            ◄─── passed from host env             │  │
│  │                                                            │  │
│  └────────────────────────────────────────────────────────────┘  │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘
  • Project directory is mounted at its real path (preserves pi's per-project sessions)
  • Config directory ($HOME/.pi for pi, $HOME/.config/opencode for opencode) is mounted read-write so auth, sessions, and settings persist
  • Data directory ($HOME/.local/share/opencode for opencode) is mounted read-write so databases and session history persist
  • HOME is container-local — dotfiles, caches, and temp files never touch the host project
  • No git or SSH inside the container - by design, reduces attack surface
  • --privileged — required for pi's bwrap sandbox to work inside Docker (the container itself is still isolated; bwrap adds a second layer of filesystem sandboxing)
  • Container is removed on exit (--rm)
  • Runs as your UID/GID — files created in mounted directories are owned by you, not root

Mount points

| Host path | Container path | Mode | Purpose | | ----------------------------------------------------- | --------------------------------------------- | ---- | --------------------------------- | | harness config dir (e.g. $HOME/.pi) | /agent-config | rw | Config, credentials, settings | | harness data dir (e.g. $HOME/.local/share/opencode) | /agent-data | rw | Sessions, databases, runtime data | | Current working directory | Same real path (e.g. /Users/you/my-project) | rw | Project files |

All other runtime files (caches, temp files) go to container-local paths under /home/agentuser/ and are lost when the container exits.

Why two separate mounts?

Different agents store different types of data in different locations:

  • pi stores everything in $HOME/.pi — both mounts point to the same directory
  • opencode stores config in $HOME/.config/opencode and data in $HOME/.local/share/opencode — they must be separate so settings and sessions both persist

Requirements

  • Docker (Docker Desktop on macOS, Docker Engine on Linux)
  • Bash 4.0+
  • jq (for parsing config.json)
  • Internet connection (for pulling base images, API calls)

Installation

Homebrew

brew tap zigotica/tap
brew install agent-sandbox

Or in one command: brew install zigotica/tap/agent-sandbox (this ensures you get this tap's version even if agent-sandbox is ever added to Homebrew core).

npm

npm install -g @zigotica/agent-sandbox

Installs the agent-sandbox binary to your npm global bin directory.

GitHub Release

curl -fsSL https://raw.githubusercontent.com/zigotica/agent-sandbox/refs/heads/main/install.sh | bash

This downloads the latest release to $HOME/.agent-sandbox and offers to add it to your PATH.

To install a specific version:

AGENT_SANDBOX_VERSION=v1.0.0 curl -fsSL https://raw.githubusercontent.com/zigotica/agent-sandbox/refs/heads/main/install.sh | bash

Manual

git clone https://github.com/zigotica/agent-sandbox.git "$HOME/.agent-sandbox"

# Add to your shell profile ($HOME/.zshrc or $HOME/.bashrc):
echo 'export PATH="$HOME/.agent-sandbox/bin:$PATH"' >> "$HOME/.zshrc"
source "$HOME/.zshrc"

Quick Start

# One-time setup
agent-sandbox init

# Build the pi image
agent-sandbox pi build

# Run pi in a sandboxed container
cd "$HOME/my-project"
agent-sandbox pi

You might eventually forget to type agent-sandbox pi and just type pi. Set up aliases so the sandboxed version always takes priority:

# Add to $HOME/.zshrc or $HOME/.bashrc
alias pi='agent-sandbox pi'
alias opencode='agent-sandbox opencode'

# If you need the unsandboxed version temporarily, use:
\pi              # bypasses the alias
command pi       # also bypasses the alias

This way you always get the sandboxed version by default, and the raw command is still available with a backslash prefix if you need it.

Check everything is working

agent-sandbox doctor

Configuration

Breaking change: config/layout now lives under $HOME/.config/agent-sandbox/. If upgrading from older version, rerun agent-sandbox init to recreate harness files and config.

Config file: $HOME/.config/agent-sandbox/config.json (created by agent-sandbox init)

agent-sandbox init also copies built-in harness files into $HOME/.config/agent-sandbox/harnesses/<name>/. Custom harnesses registered with agent-sandbox register are scaffolded there too.

{
  "harnesses": {
    "opencode": {
      "name": "opencode",
      "harness_dir": "/Users/you/.config/agent-sandbox/harnesses/opencode",
      "config_dir": "/Users/you/.config/opencode",
      "data_dir": "/Users/you/.local/share/opencode",
      "image": "agent-sandbox:opencode",
      "run_command": "opencode",
      "env_vars": [],
      "version": ""
    },
    "pi": {
      "name": "pi",
      "harness_dir": "/Users/you/.config/agent-sandbox/harnesses/pi",
      "config_dir": "/Users/you/.pi",
      "data_dir": "/Users/you/.pi",
      "image": "agent-sandbox:pi",
      "run_command": "pi",
      "env_vars": ["ANTHROPIC_API_KEY"],
      "version": ""
    }
  }
}

Version pinning

By default, harnesses install the latest version of the agent on build. To pin a specific version, add a version field:

{
  "harnesses": {
    ...
    "pi": {
      ...
      "version": "0.66.1"
    }
  }
}

Remove the version field to go back to installing latest on next build. Note: agent-sandbox <harness> upgrade will warn you if a version is pinned and remind you to remove it from config.json.

Config resolution order (highest priority first)

  1. Env varAGENT_SANDBOX_CONFIG_DIR=/custom/path agent-sandbox pi
  2. config.jsonharnesses.<name>.config_dir / harnesses.<name>.data_dir
  3. Harness script defaultHARNESS_DEFAULT_CONFIG_DIR / HARNESS_DEFAULT_DATA_DIR

Commands

agent-sandbox init                 # Create config file with sensible defaults
agent-sandbox doctor               # Check setup for problems
agent-sandbox list                 # List all registered harnesses
agent-sandbox test pi              # Run security verification tests

agent-sandbox pi build             # Build (or rebuild) the pi Docker image
agent-sandbox pi                   # Run pi in a sandboxed container
agent-sandbox pi upgrade           # Rebuild image

agent-sandbox opencode build       # Build (or rebuild) the opencode Docker image
agent-sandbox opencode             # Run opencode in a sandboxed container
agent-sandbox opencode upgrade     # Rebuild image

# Register a custom harness
agent-sandbox register aider

# Unregister a custom harness
agent-sandbox unregister aider

Non-interactive usage

Pass a single prompt and exit:

agent-sandbox pi -p "summarize this repo"

Pipe input:

cat README.md | agent-sandbox pi -p "summarize this"

Built-in Harnesses

pi

| Setting | Value | | ---------- | ----------------------------------------------------- | | Config dir | $HOME/.pi | | Data dir | $HOME/.pi (same as config) | | Image | agent-sandbox:pi | | Dockerfile | $HOME/.config/agent-sandbox/harnesses/pi/Dockerfile |

Copied from repo on agent-sandbox init. Based on Chainguard Node.js. Includes: Node.js, curl, tmux, ripgrep, bubblewrap, socat, pi. No git or SSH - by design.

Installs the latest version by default. Pin a specific version in config.json if needed.

Environment variables passed through:

  • AI provider keys: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, AZURE_OPENAI_API_KEY, MISTRAL_API_KEY, GROQ_API_KEY, CEREBRAS_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY, AI_GATEWAY_API_KEY, ZAI_API_KEY, OPENCODE_API_KEY, KIMI_API_KEY, MINIMAX_API_KEY, MINIMAX_CN_API_KEY
  • Ollama: OLLAMA_API_BASE, OLLAMA_API_KEY
  • Pi config: PI_SKIP_VERSION_CHECK, PI_CACHE_RETENTION, PI_PACKAGE_DIR
  • Terminal: TERM, COLORTERM

opencode

| Setting | Value | | ---------- | ----------------------------------------------------------- | | Config dir | $HOME/.config/opencode | | Data dir | $HOME/.local/share/opencode | | Image | agent-sandbox:opencode | | Dockerfile | $HOME/.config/agent-sandbox/harnesses/opencode/Dockerfile |

Copied from repo on agent-sandbox init. Based on Chainguard Node.js. Includes: Node.js, curl, tmux, ripgrep, bubblewrap, socat, opencode-ai (via npm). No git or SSH - by design.

Installs the latest version by default. Pin a specific version in config.json if needed.

Environment variables passed through:

  • AI provider keys: ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, AZURE_OPENAI_API_KEY, MISTRAL_API_KEY, GROQ_API_KEY, CEREBRAS_API_KEY, XAI_API_KEY, OPENROUTER_API_KEY, AI_GATEWAY_API_KEY
  • Terminal: TERM, COLORTERM

Opencode's config (theme, models, tui.json) persists in $HOME/.config/opencode. Sessions, auth, and databases persist in $HOME/.local/share/opencode.

Adding a Custom Harness

  1. Install app on host first so you know correct config/data dirs and can populate initial config files. agent-sandbox register will later mount those host dirs into container.
  2. Scaffold harness:
agent-sandbox register myagent
  1. Answer prompts:
#    - config dir
#    - data dir (can be different, i.e. opencode uses ~/.local/share/opencode)
#    - install command
#    - run command
#    - image tag (optional, defaults to agent-sandbox:<name>)
  1. Build and run:
agent-sandbox myagent build
agent-sandbox myagent

agent-sandbox register scaffolds harness files under $HOME/.config/agent-sandbox/harnesses/myagent/, fills them with proper names/defaults, and stores absolute paths in config.json. If config/data dir uses ~, tool converts it to $HOME in generated harness files and to absolute path in config.json.

Each harness script should define:

  • HARNESS_NAME — display name (used in CLI commands like agent-sandbox cursor)
  • HARNESS_IMAGE — Docker image tag
  • HARNESS_DEFAULT_CONFIG_DIR — default config directory (use $HOME, not ~)
  • HARNESS_DEFAULT_DATA_DIR — default data directory
  • HARNESS_CONFIG_MOUNT_POINT — where config mounts in container (default: /agent-config)
  • HARNESS_DATA_MOUNT_POINT — where data mounts in container (default: /agent-data)
  • HARNESS_DOCKERFILE — path to Dockerfile (defaults to same folder as script)
  • HARNESS_ENV_VARS — array of environment variable names to pass through

Dockerfile install step is harness-specific; it may use npm, curl|bash, package manager, or binary download. Scaffold runs install with HOME=/home/agentuser, so user-level installers land in /home/agentuser/.local/bin and keep sibling files intact. Some installers expect node or npm in /usr/local/bin; scaffold symlinks them from /usr/bin when needed.

Version pinning works for custom harnesses too — add "version": "1.2.3" in config.json and Dockerfile receives it as --build-arg VERSION=1.2.3.

Security

Security Model

Running agent-sandbox pi instead of pi directly means:

| Attack vector | Raw pi | agent-sandbox pi | | -------------------------------------- | ------------------------------------- | ----------------------------------------- | | Accidental rm -rf / | 💀 Deletes your entire home directory | ✅ Cannot reach outside the project mount | | Agent reads $HOME/.ssh/id_rsa | 💀 Full access to SSH keys | ✅ Keys not mounted | | Agent reads $HOME/.aws/credentials | 💀 Full access to AWS creds | ✅ Not accessible | | Agent reads other projects | 💀 Can see all of $HOME/projects/ | ✅ Only sees the mounted project | | Malicious command targets system files | 💀 Can modify /etc, /var, etc. | ✅ Container filesystem is ephemeral | | Privilege escalation via sudo | 💀 May succeed on host | ✅ No sudo in container | | Docker socket takeover | 💀 N/A | ✅ Docker socket not mounted |

What the sandbox does not protect against:

  • Network exfiltration — the agent has full network access (needed for API calls)
  • Project data exfiltration — the agent can read/write everything in the mounted project
  • Config data exfiltration — the agent can read $HOME/.pi/agent/auth.json (needed for auth)

File ownership

The container runs as --user $(id -u):$(id -g) matching your host UID/GID. Files created in mounted directories (project, config, data) are owned by you. The container image creates /home/agentuser with chmod 1777 so any UID can write there. agent-sandbox never chowns host files.

Docker Flags

Every container run uses these flags:

--rm
--user $(id -u):$(id -g)
--privileged
--ipc=none

Why --privileged? Pi's internal sandbox (bwrap) requires the ability to create user namespaces and mount filesystems inside the container. Without --privileged, bwrap fails and pi cannot execute commands. The container is still isolated — no Docker socket is mounted, only the project and config directories are accessible, and the host filesystem is not reachable.

Security Verification

Run the built-in security test from any directory:

agent-sandbox test pi

This mounts the test script from the install directory into the container — no need to copy it into your project. The test verifies: host secrets, SSH keys, cloud credentials, Docker socket, privilege escalation, network exfiltration tools, git access, filesystem boundaries (sibling directories, sensitive dirs), write access outside mounts, mount points, process visibility, and environment variable leakage.

Development

$HOME/.agent-sandbox/
├── bin/
│   └── agent-sandbox               # Main CLI entry point
├── lib/
│   ├── common.sh                   # Shared functions and config resolution
│   └── tests/
│       ├── container-security.sh   # Security verification test suite
│       └── run-security-test.sh    # Test wrapper (echoes status, then runs suite)
├── harnesses/
│   ├── pi/
│   │   ├── pi.sh                   # Pi harness defaults
│   │   ├── Dockerfile              # Pi Docker image
│   │   └── entrypoint.sh           # Pi container entrypoint
│   ├── opencode/
│   │   ├── opencode.sh             # Opencode harness defaults
│   │   ├── Dockerfile              # Opencode Docker image
│   │   └── entrypoint.sh           # Opencode container entrypoint
│   └── template/
│       ├── template.sh             # Template for new harnesses
│       ├── Dockerfile.template     # Template Dockerfile
│       └── entrypoint.template.sh  # Template entrypoint
├── .github/
│   └── workflows/
│       └── release.yml             # CI: auto-publish GitHub Releases on tag push
├── install.sh                      # Install script (curl | bash)
├── DESIGN.md
├── LICENSE
└── README.md                       # This file

User-created files (not in the repo):

$HOME/.config/agent-sandbox/
├── config.json                     # User configuration (created by agent-sandbox init)
└── harnesses/
    ├── pi/
    │   ├── pi.sh
    │   ├── Dockerfile
    │   └── entrypoint.sh
    ├── opencode/
    │   ├── opencode.sh
    │   ├── Dockerfile
    │   └── entrypoint.sh
    └── <custom>/
        ├── <custom>.sh
        ├── Dockerfile
        └── entrypoint.sh

Troubleshooting

docker: command not found

Install Docker: https://docs.docker.com/get-docker/

jq: command not found

Install jq: https://jqlang.github.io/jq/

Config file not found

Run agent-sandbox init to create the config file.

Image not found

Images are auto-built on first run. You can also build manually:

agent-sandbox pi build
agent-sandbox opencode build

Pi can't run commands (bwrap errors)

Pi's sandbox (bwrap) requires --privileged to create user namespaces inside Docker. This is already configured by default. If you see bwrap errors, make sure you're not overriding the Docker flags.

Cleaning up bwrap artifacts

If bwrap creates files in your project directory (empty .bashrc, .env, .claude, etc.), remove them:

rm -rf .claude .env .bashrc .bash_profile .profile .zshrc .zprofile .gitconfig .gitmodules .mcp.json .ripgreprc .vscode .idea "*.key" "*.pem"

These are placeholder files created by the sandbox extension. Consider removing the sandbox extension since Docker already provides isolation:

rm -rf "$HOME/.pi/agent/extensions/sandbox"
rm -f "$HOME/.pi/agent/sandbox.json"

Inspiration

This project was inspired by three approaches to running AI coding agents safely:

Pi's official sandbox extension

Pi offers an optional sandbox extension that uses bwrap to isolate the agent's file access on the host. Users install it if they want sandboxing. When running pi directly on the host, this works well. However, when you try to run pi inside a Docker container with the sandbox extension active, bwrap creates placeholder dotfiles (.bashrc, .env, .claude, .gitconfig, etc.) in the project directory on the host mount — polluting your repo. It also only works for pi.

Daytona's opencode plugin

Daytona offers an opencode plugin that sandboxes each session in its own remote environment, synced to a local git branch. This means git is central to the isolation model — each session gets its own branch, and changes flow through git. That approach gives you version control as a safety net, but it also means git is available inside the sandbox, and the agent can push to remotes. If you don't want your agent touching git, this doesn't help.

pi-less-yolo

pi-less-yolo is a Docker-based sandbox for running pi without its built-in bwrap sandbox. It takes the approach of disabling bwrap entirely (--no-sandbox), relying solely on Docker for isolation. While simpler, this loses the second layer of sandboxing that bwrap provides. It also only supports pi.

agent-sandbox takes a different approach:

  • No project pollution — bwrap runs inside the container, so its placeholder files stay in the ephemeral container filesystem and never reach the host
  • No --no-sandbox — bwrap stays active, providing a second layer of isolation inside the container
  • No git or SSH — by design, the container has neither. If you want git, run it on the host
  • Works for any agent — the same Docker-based approach works for pi, opencode, and any agent you register

License

MIT