devbox #
A declaratively maintained development environment for coding agents, built with Docker: a paseo daemon plus a shared Chromium, running on the local OrbStack.
- The repo holds only declarations: the image, tool versions, agent config and compose file all live here. Changes take effect after
scripts/rebuild. - State lives outside the repo:
$DATA_DIR(default~/app/paseo) holds credentials, the browser profile and, underpaseo-home/workspace, the projects (/workspaceinside the container is a symlink to~/workspace). Rebuilds don't touch it. - Isolation: the container mounts only directories under
$DATA_DIR. It does not mount~/.ssh,~/.config/ghordocker.sock. Other services may run on the same Docker daemon, so never give the container Docker access.
Layout #
| Path | Purpose |
|---|---|
image/Dockerfile |
Based on the official paseo image (latest); installs the latest mise and apt packages |
image/mise.toml |
Tool list: agent-browser, gh, gogcli, pnpm, claude-code, codex, pi, grok, playwright-mcp, all at latest |
image/apt.txt |
Extra Debian packages |
config/AGENTS.md |
Instructions for agents, synced on start to the global instruction files of claude, codex and pi, and to ~/.grok/rules/devbox.md for grok |
config/mcp.json |
MCP servers, merged into mcpServers in ~/.claude/.claude.json and ~/.pi/agent/mcp.json |
config/codex.toml |
Managed block written into ~/.codex/config.toml |
config/grok.toml |
Overwrites ~/.grok/managed_config.toml, the layer below ~/.grok/config.toml; also turns off Grok's scan of ~/.claude |
config/gitconfig |
Included from ~/.gitconfig: git defaults for agents and the gh credential helper |
config/claude-settings.json |
Deep-merged into ~/.claude/settings.json; keys here override existing values |
image/rootfs/usr/local/bin/devbox-activate |
Runs on every container start and syncs config/ into home |
compose.yml |
Three services: tailscale (owns the network and is its own tailnet node), browser (Chromium) and paseo; the latter two share tailscale's network |
Config falls into two kinds:
- Files and blocks managed by
config/are overwritten on every start. Change them in the repo, not inside the container. - Everything else (login credentials, state agents write at runtime) is kept in
$DATA_DIR/paseo-home.
Daily use #
scripts/rebuild # build from the current tree, tag devbox:<commit>, then restart
scripts/rollback # list existing tags
scripts/rollback <tag> # switch back to an older image
scripts/shell # open a shell in the container as the paseo user
scripts/shell gh auth status
Upgrading: the paseo base image, mise and everything in image/mise.toml track latest, so every scripts/rebuild pulls the newest base image and reinstalls the newest versions. If an upgrade breaks something, use scripts/rollback to return to an older image.
Access #
| Service | Address |
|---|---|
| paseo daemon | 127.0.0.1:6767, or $TAILSCALE_IP:6767 over Tailscale; password is PASEO_PASSWORD. There is no bundled web UI; use the hosted app.paseo.sh, the desktop app or the mobile app |
| Dev servers | http://devbox:<port> (MagicDNS) or the container's own tailnet IP, from any device in the tailnet. Servers must listen on 0.0.0.0, not just localhost |
| Browser desktop | https://127.0.0.1:6901, self-signed certificate, username devbox, password is BROWSER_PASSWORD |
app.paseo.sh, the mobile app and the desktop app can all connect directly with the address and password above (from inside the tailnet). For devices outside Tailscale, run scripts/shell paseo daemon pair to enable the relay, then paste the pairing link.
For everyday chats that don't involve code, use the /workspace/chat project (a plain directory, not a git repo).
First-time setup #
cp .env.example .env, generate the two passwords and fill them in along withTAILSCALE_IPandTS_AUTHKEY(an auth key from the Tailscale admin console; the container joins the tailnet asdevbox, and the login persists in$DATA_DIR/tailscale, so the key can be removed from.envafterwards), runchmod 600 .env, then runscripts/rebuild.- Agent login (credentials are stored in
$DATA_DIR/paseo-home): runscripts/shell, thenclaude,codex,piandgrokin turn. - GitHub bot:
- Register a bot account and add it as a collaborator with Write access to the target repos.
- Fill in
DEVBOX_GIT_NAME/DEVBOX_GIT_EMAILin.env, then runscripts/rebuild. - Run
scripts/shell gh auth login(with a fine-grained PAT). The credential helper is already set up byconfig/gitconfig; there's no need to rungh auth setup-git.
- Google (gogcli):
- Fill in
GOG_KEYRING_PASSWORDin.env(generate like the others), then runscripts/rebuild. Tokens go to gog's encrypted file keyring under$DATA_DIR/paseo-home. - In a Google Cloud project, enable the APIs you need, set the OAuth consent screen to External and publish it (Testing apps get refresh tokens that expire after 7 days), then create a Desktop app OAuth client and download its JSON.
- Copy the JSON into the container's home, then run
scripts/shell gog auth credentials ~/client_secret.jsonandscripts/shell gog auth add you@gmail.com --services gmail,calendar,drive --gmail-scope readonly --extra-scopes https://www.googleapis.com/auth/gmail.modify --manual. Open the printed URL in any browser, grant access, and paste the final redirect URL back. Check withscripts/shell gog auth doctor --check. - Gmail is draft-only for agents. The OAuth grant is
gmail.modifywithout the settings scopes (no permanent delete, filters or forwarding), and/usr/local/lib/devbox/bin/gogshadows the real binary to block sending and trashing. Agents leave drafts; you send them from Gmail. The token itself can still send, so this guards against mistakes, not against an agent that bypassesgog.
- Fill in
- Browser: open the browser desktop and sign in to the sites you need by hand. Agents use the same browser session over CDP (
127.0.0.1:9222): theagent-browserCLI is pointed at it throughAGENT_BROWSER_CDP, and the playwright MCP is configured for claude, codex, pi and grok as a fallback.
Known limitations #
- There is no Docker in the container; agents can't run
docker. - The tailscale container needs
/dev/net/tunandNET_ADMIN; restarting it also restartsbrowserandpaseo's network, so runscripts/rebuild(ordocker compose up -d) rather than restarting it alone. - paseo's built-in browser tools need a connected browser host (the desktop app). This deployment doesn't enable it; browser automation goes through agent-browser and the playwright MCP against the shared Chromium.
License #
- Shell 67%
- Docker 33%