# 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, under `paseo-home/workspace`, the projects (`/workspace` inside 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/gh` or `docker.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 ```sh scripts/rebuild # build from the current tree, tag devbox:, then restart scripts/rollback # list existing tags scripts/rollback # 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:` (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 1. `cp .env.example .env`, generate the two passwords and fill them in along with `TAILSCALE_IP` and `TS_AUTHKEY` (an auth key from the Tailscale admin console; the container joins the tailnet as `devbox`, and the login persists in `$DATA_DIR/tailscale`, so the key can be removed from `.env` afterwards), run `chmod 600 .env`, then run `scripts/rebuild`. 2. Agent login (credentials are stored in `$DATA_DIR/paseo-home`): run `scripts/shell`, then `claude`, `codex`, `pi` and `grok` in turn. 3. 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_EMAIL` in `.env`, then run `scripts/rebuild`. - Run `scripts/shell gh auth login` (with a fine-grained PAT). The credential helper is already set up by `config/gitconfig`; there's no need to run `gh auth setup-git`. 4. Google (gogcli): - Fill in `GOG_KEYRING_PASSWORD` in `.env` (generate like the others), then run `scripts/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.json` and `scripts/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 with `scripts/shell gog auth doctor --check`. - Gmail is draft-only for agents. The OAuth grant is `gmail.modify` without the settings scopes (no permanent delete, filters or forwarding), and `/usr/local/lib/devbox/bin/gog` shadows 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 bypasses `gog`. 5. 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`): the `agent-browser` CLI is pointed at it through `AGENT_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/tun` and `NET_ADMIN`; restarting it also restarts `browser` and `paseo`'s network, so run `scripts/rebuild` (or `docker 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 [MIT](./LICENSE)