Runs AI coding agents inside isolated Docker containers. Agents get no root access, no Docker socket, and no ability to escalate privileges — safe to run on your host machine.
pi is an AI coding agent that runs in the terminal.
Also mounts ~/.pi/agent for persistent extensions and auth, ~/.agents/skills for skills, and forwards API keys and pi-related env vars from the host.
ln -sf "$(pwd)/pi/pi" "/usr/local/bin/pi"
pi --build# Run in the current directory (uses the base image, unrestricted network)
pi
# Run with a specific language runtime
pi --lang go
pi --lang php8.4
pi --lang php8.5
# Rebuild all images and run
pi --build
# Enable the proxy sidecar (restricted network access via allowlist)
pi --proxy
# Enable the proxy with a custom allowed-domains list
pi --proxy --allowed-domains /path/to/allowed_domains.txt
# Pass flags directly to the agent
pi -- --helpEach agent lives in its own directory with a Dockerfile and a bash wrapper script. The wrapper:
- Builds all image variants on first run, or when
--buildis passed - Starts a proxy sidecar container and an isolated internal Docker network
- Runs the selected agent variant with strict security settings (
--cap-drop=ALL,--no-new-privileges) - Bind-mounts the current working directory so the agent can read and edit your files
The Dockerfiles use a multi-stage build based on Microsoft's devcontainers/typescript-node image:
| Tag | Contents |
|---|---|
base |
Node.js + npm, agent, Git, curl, ca-certificates |
go |
base + Go 1.26 |
php8.4 |
base + PHP 8.4 |
php8.5 |
base + PHP 8.5 |
Containers run as your host UID (non-root), with all Linux capabilities dropped and no-new-privileges enforced. Agents cannot install system packages, access the Docker socket, or escape the container via privilege escalation.
Network access can optionally be restricted by a proxy sidecar (see Proxy below). When --proxy is passed, the agent container is placed on an isolated internal Docker network with no direct internet access — all outbound traffic is forced through the proxy. Without --proxy, the container has unrestricted internet access.
The proxy is a tinyproxy sidecar container that enforces an allowlist of permitted hostnames. It runs alongside the agent container and is the agent's only path to the internet.
- Isolated network — The wrapper creates a dedicated internal Docker network per run. The agent container joins only this network; it has no direct internet access.
- Proxy sidecar — A
pi-proxycontainer starts on both the internal network and the default bridge (so it can reach the internet). The agent'sHTTP_PROXY/HTTPS_PROXYenv vars are set to the proxy's address on the internal network. - Allowlist filtering — At startup,
entrypoint.shreads~/.agents/proxy_allowed_domains.txtand converts each entry into a tinyproxy regex filter rule:example.com→ exact match (^example\.com$)*.example.com→ domain + all subdomains (^(.*\.)?example\.com$)- Lines starting with
#and blank lines are ignored
- Default-deny — Any hostname not matched by the filter is blocked. Only connections to standard HTTPS/HTTP ports are permitted (tinyproxy
ConnectPort). - Teardown — When the agent exits, the wrapper stops the proxy container and removes the network.
The default allowlist is read from ~/.agents/proxy_allowed_domains.txt. Example:
# Allowed domains for the pi agent proxy.
# One entry per line. Wildcards supported: *.example.com
# Lines starting with # are ignored.
# GitHub
github.com
api.github.com
raw.githubusercontent.com
*.githubusercontent.com
*.github.com
# Atlassian
*.atlassian.com
*.atlassian.net
# Services running on host
host.docker.internal
To use a different file, pass --allowed-domains /path/to/file.
The proxy is opt-in — simply omit --proxy and the container runs with unrestricted internet access (the default).
pi # no proxy, unrestricted networkDocker Desktop must have Host Networking enabled so the OAuth callback from your browser reaches the container:
Docker Desktop → Settings → Resources → Network → Enable host networking
~/.config/acli is already mounted into the container, so credentials survive restarts.
Run login command inside the docker container:
acli jira auth login --webThe browser opens automatically. Complete the Atlassian OAuth flow — the callback redirects back to the browser and auth finishes without any manual steps.
Verify:
acli jira auth status✓ Authenticated
Site: your-org.atlassian.net
Email: your@email.com
Authentication Type: oauth