Make ANTHROPIC_API_KEY optional. Add CLAUDE_CODE_OAUTH_TOKEN pass-through for headless token-based auth (claude setup-token). When neither is set, Claude Code falls back to browser OAuth on port 54545. Add claude-config named volume mounted at ~/.claude/ in both claude and webui services so credentials persist across container runs. Pre-create ~/.claude/ in the Dockerfile so the volume is initialised with correct ownership. Add --service-ports to docker compose run calls to publish port 54545 during CLI sessions.
4.7 KiB
Project Guidance
This file provides context and guidance for working with this project.
Project Overview
docker-claude runs Claude Code inside a hardened Docker environment with a Squid proxy sidecar. The goal is full host encapsulation: Claude cannot access the host filesystem or network. All egress is routed through an allowlist-enforcing proxy.
Architecture
Three containers managed by Docker Compose:
claude— Claude Code CLI (node:20-alpine), non-root (UID 1000), isolated to an internal-only Docker networkwebui— Claude Code as a browser terminal via ttyd (node:20-alpine), non-root (UID 1000), same network isolation, basic auth requiredproxy— Squid forward proxy (alpine:3.21),squiduser, bridges the internal network to the internet with an egress allowlist
Key Docker network property: claude-internal has internal: true, meaning Docker adds no default gateway. The claude and webui containers physically cannot reach the internet without going through the proxy container.
The webui service reuses claude/Dockerfile. Its entrypoint (claude/webui-entrypoint.sh) starts ttyd --credential user:pass claude instead of claude directly.
Auth supports three modes (checked at startup by claude.sh):
ANTHROPIC_API_KEY— API keyCLAUDE_CODE_OAUTH_TOKEN— 1-year token fromclaude setup-token(headless-friendly)- Neither set — Claude Code prompts for browser login on first run; port 54545 is published for the OAuth callback. Credentials persist in the
claude-confignamed volume.
File Structure
docker-claude/
├── claude.sh # Control script: start/stop/run/web/web-stop/update/logs/status/shell
├── docker-compose.yml # Service definitions and network topology
├── claude/
│ ├── Dockerfile # Claude Code + ttyd (node:20-alpine, UID 1000)
│ └── webui-entrypoint.sh # Entrypoint for webui: starts ttyd wrapping claude
├── proxy/
│ ├── Dockerfile # Squid proxy sidecar (alpine:3.21, squid user)
│ └── squid.conf # Squid ACL config — egress allowlist lives here
├── .env.example # Template for ANTHROPIC_API_KEY, WEBUI_PASSWORD, etc.
├── .gitignore # Excludes .env and logs
├── .dockerignore # Keeps .env out of build context
└── README.md # User documentation
Development Workflow
chmod +x claude.sh
cp .env.example .env # set ANTHROPIC_API_KEY (and WEBUI_PASSWORD for web mode)
./claude.sh start # build + start proxy + launch Claude interactively (CLI)
./claude.sh web # build + start proxy + webui (browser terminal on :7681)
./claude.sh update # rebuild images (no cache) after upstream updates
Coding Standards
- Shell scripts use
set -euo pipefail - Dockerfiles use Alpine (
node:20-alpine,alpine:3.21) for minimal attack surface - Alpine packages use
apk add --no-cache; no apt cache cleanup layer needed - No capabilities granted;
no-new-privilegeson all containers .envis never committed (enforced by.gitignoreand.dockerignore)- Commit messages follow Angular format:
type(scope): summary
Extending the Egress Allowlist
Add domains to proxy/squid.conf under the allowed_sites ACL, then rebuild:
docker compose -p claude-secure build proxy
./claude.sh stop && ./claude.sh start
Environment Persistence
This sandbox has a persistent environment file at /etc/sandbox-persistent.sh.
This file is automatically sourced in all shell contexts:
- Non-interactive shells: via
BASH_ENV=/etc/sandbox-persistent.sh - Login shells: via
/etc/profile.d/sandbox-persistent.sh - Interactive shells: via
/etc/bash.bashrcand~/.bashrc
Critical: Shell Completions Must NOT Be in the Persistent Environment File
NEVER add shell completion scripts to /etc/sandbox-persistent.sh.
Shell completion scripts will completely break the bash tool when sourced via the persistent environment file.
Network access
There is a firewall in place to restrict outbound network access. If you need http/https access to an external service, request it by running:
sbx policy allow network [domain]
Git Authentication
The sandbox proxy handles GitHub authentication automatically by injecting credentials for HTTPS Git operations.
Additional Notes
- Always read relevant files before making changes
- Run tests after making modifications
- Follow the existing code structure and patterns
- You have sudo permissions, so you can install necessary packages
- npm, pip and uv are already available for package management