This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
dev-agent is a containerized environment for running AI coding agents (Claude Code or OpenCode) with enforced network isolation. A Squid proxy allowlists specific domains; an iptables firewall inside each agent container blocks all direct internet access, forcing traffic through the proxy.
Three Docker Compose services on a shared bridge network (agent-net):
- proxy (always runs) — Squid forward proxy. Permits HTTP/HTTPS only to domains in
allowed-domains.txt. No caching. Uses Docker's embedded DNS at 127.0.0.11. - agent (profile:
claude) — Claude Code on Node.js 24. Authenticates via AWS Bedrock. Settings mounted fromdot-claude/settings.json. - opencode (profile:
opencode) — OpenCode on Node.js 24. Authenticates via OpenRouter API key. Config mounted fromdot-opencode/opencode.json.
run.sh is the sole entry point. It:
- Parses CLI flags and resolves the workspace directory
- Reads optional
.dev-agent/config.ymlfrom the workspace (requiresyqon the host) and generates temporary Docker Compose override files in/tmp/dev-agent-<dirname>/ - Detects git worktrees and generates a volume mount override for the main
.gitdirectory - Stacks all override files via
-fflags, with the workspace's owndocker-compose.override.ymlapplied last - Starts background services (
docker compose up -d), then runs the agent interactively (docker compose run --rm) - Cleans up containers and temp files on exit via a trap
Each workspace gets an isolated instance named dev-agent-<dirname>, enabling multiple agents to run simultaneously against different projects.
entrypoint.sh → sudo init-firewall.sh → exec "$@" (agent binary)
The firewall script preserves Docker DNS NAT rules, allows traffic to all attached Docker bridge subnets (auto-detected from interfaces), then rejects everything else. It verifies both that direct access is blocked and that proxy access works before handing off to the agent.
Projects can add .dev-agent/config.yml (extra domains, env vars, volumes, external networks) and .dev-agent/docker-compose.override.yml (sidecar services like databases). See example.dev-agent/ for the format.
# Build images and launch Claude Code against a project
./run.sh --build ~/projects/my-app
# Launch OpenCode instead
OPENROUTER_API_KEY=sk-or-... ./run.sh --opencode --build ~/projects/my-app
# Drop into a shell inside the agent container
./run.sh --shell ~/projects/my-app
# Stop containers for a specific project
./run.sh --down ~/projects/my-app
# Hot-reload the Squid domain allowlist after editing allowed-domains.txt
./run.sh --reload-proxyRebuild individual services with Docker Compose directly:
docker compose -p dev-agent-my-app --profile claude build agent
docker compose -p dev-agent-my-app --profile opencode build opencodeThere are no tests, linters, or CI pipelines in this project.
- Bash scripts use
set -euo pipefail, quoted variable expansion, and color-coded output (GREEN/YELLOW/RED). - Dockerfiles are named
<service>.Dockerfile. Both agent images share the same base toolchain (Node.js 24, .NET 8+10, PowerShell 7.4, AWS CLI v2, ripgrep, gh, etc.) and differ only in which agent binary is installed and the CMD. run.shgenerates temporary files in/tmp/dev-agent-<dirname>/— domain merges, compose overrides, worktree mounts. These are always cleaned up on exit.- The proxy service has no
profileskey so it starts unconditionally. Agent services use profiles to ensure only one runs per instance. - Domain entries in
allowed-domains.txtuse Squid'sdstdomainformat: a leading dot (.example.com) matches all subdomains. - The
dot-claude/anddot-opencode/directories contain agent configuration files that are bind-mounted into the respective containers. These are not the same as the.claude/directory (which holds this repo's own Claude Code settings).
Throughout run.sh, the variable $AGENT_SERVICE holds the Docker Compose service name (agent for Claude Code, opencode for OpenCode). When modifying run.sh, use this variable instead of hardcoding service names — it appears in the workspace config override generator, the worktree override heredoc, the background service filter, and the interactive docker compose run commands.