services: # ─── Proxy sidecar ───────────────────────────────────────────────────────── # Bridges the isolated internal network to the internet. # Enforces an egress allowlist — see proxy/squid.conf. proxy: build: context: proxy dockerfile: Dockerfile networks: - claude-internal # reachable by claude and webui containers - proxy-external # has outbound internet access restart: unless-stopped security_opt: - no-new-privileges:true cap_drop: - ALL read_only: true tmpfs: - /tmp - /var/cache/squid - /var/log/squid # ─── Claude Code CLI container ───────────────────────────────────────────── # No direct internet access. All egress routes through the proxy sidecar. # Run via "docker compose run --rm --service-ports claude" (managed by claude.sh). claude: build: context: claude/ dockerfile: Dockerfile depends_on: proxy: condition: service_healthy networks: - claude-internal # only — no route to the internet environment: - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-} - HTTP_PROXY=http://proxy:3128 - HTTPS_PROXY=http://proxy:3128 - ALL_PROXY=http://proxy:3128 - NO_PROXY=localhost,127.0.0.1 # MCP server credentials — all optional; servers are skipped if unset - GITHUB_TOKEN=${GITHUB_TOKEN:-} - GITLAB_TOKEN=${GITLAB_TOKEN:-} - GITLAB_URL=${GITLAB_URL:-https://gitlab.com} - ATLASSIAN_SITE_NAME=${ATLASSIAN_SITE_NAME:-} - ATLASSIAN_USER_EMAIL=${ATLASSIAN_USER_EMAIL:-} - ATLASSIAN_API_TOKEN=${ATLASSIAN_API_TOKEN:-} ports: # OAuth callback — required for browser-based login (claude login) - "0.0.0.0:54545:54545" volumes: - claude-config:/home/node/.claude # Workspace is injected by claude.sh via --volume flag at run time. # Default: named Docker volume. Override: set WORKSPACE_DIR on the host. security_opt: - no-new-privileges:true cap_drop: - ALL stdin_open: true tty: true # ─── Claude Code web interface ───────────────────────────────────────────── # Serves Claude Code as a browser terminal via ttyd (port 7681). # Protected by HTTP basic auth — set WEBUI_USER / WEBUI_PASSWORD in .env. # Network isolation is identical to the CLI container. webui: build: context: claude/ dockerfile: Dockerfile entrypoint: ["/usr/local/bin/webui-entrypoint.sh"] depends_on: proxy: condition: service_healthy networks: - claude-internal # only — no route to the internet environment: - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-} - CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN:-} - HTTP_PROXY=http://proxy:3128 - HTTPS_PROXY=http://proxy:3128 - ALL_PROXY=http://proxy:3128 - NO_PROXY=localhost,127.0.0.1 # MCP server credentials — all optional; servers are skipped if unset - GITHUB_TOKEN=${GITHUB_TOKEN:-} - GITLAB_TOKEN=${GITLAB_TOKEN:-} - GITLAB_URL=${GITLAB_URL:-https://gitlab.com} - ATLASSIAN_SITE_NAME=${ATLASSIAN_SITE_NAME:-} - ATLASSIAN_USER_EMAIL=${ATLASSIAN_USER_EMAIL:-} - ATLASSIAN_API_TOKEN=${ATLASSIAN_API_TOKEN:-} - WEBUI_USER=${WEBUI_USER:-claude} - WEBUI_PASSWORD=${WEBUI_PASSWORD:-} - WEBUI_PORT=7681 ports: - "0.0.0.0:7681:7681" # OAuth callback — required for browser-based login (claude login) - "0.0.0.0:54545:54545" volumes: - claude-config:/home/node/.claude - claude-web-workspace:/workspace security_opt: - no-new-privileges:true cap_drop: - ALL stdin_open: true tty: true restart: unless-stopped networks: # Internal-only: Docker adds no default gateway → no direct internet route claude-internal: driver: bridge internal: true # External: standard bridge with internet access (proxy only) proxy-external: driver: bridge volumes: # Persists Claude Code auth credentials (~/.claude/) across container runs. # Shared between the CLI and web interface so login carries over. claude-config: # Persistent workspace for the web interface claude-web-workspace: