feat: remove webui
All checks were successful
Build images / check-docker (push) Successful in 3s
Build images / build-and-push (push) Successful in 5m15s

This commit is contained in:
docker-claude 2026-04-15 21:59:08 +02:00
parent 1dee611fb3
commit e78a302cb9
6 changed files with 9 additions and 112 deletions

View file

@ -19,12 +19,6 @@
# Port 54545 must be reachable from your browser for the OAuth callback.
# Run: sbx ports <sandbox-name> --publish 54545:54545/tcp
# ─── Web interface ────────────────────────────────────────────────────────────
# Required for ./claude.sh web
# WEBUI_USER=claude
# WEBUI_PASSWORD=changeme
# ─── MCP servers (all optional) ───────────────────────────────────────────────
# GitHub — PAT with repo scope

View file

@ -8,16 +8,13 @@ This file provides context and guidance for working with this project.
## Architecture
Three containers managed by Docker Compose:
Two containers managed by Docker Compose:
- **`claude`** — Claude Code CLI (`node:20-alpine`), runs as the built-in `node` user (UID 1000), isolated to an internal-only Docker network
- **`webui`** — Claude Code as a browser terminal via ttyd (`node:20-alpine`), `node` user (UID 1000), same network isolation, basic auth required
- **`proxy`** — Squid forward proxy (`alpine:3.21`), `squid` user, 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 key
- `CLAUDE_CODE_OAUTH_TOKEN` — 1-year token from `claude setup-token` (headless-friendly)
@ -27,11 +24,10 @@ Auth supports three modes (checked at startup by `claude.sh`):
```
docker-claude/
├── claude.sh # Control script: start/stop/run/web/web-stop/update/logs/status/shell
├── claude.sh # Control script: start/stop/run/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
│ └── Dockerfile # Claude Code (node:20-alpine, UID 1000)
├── proxy/
│ ├── Dockerfile # Squid proxy sidecar (alpine:3.21, squid user)
│ └── squid.conf # Squid ACL config — egress allowlist lives here
@ -45,9 +41,8 @@ docker-claude/
```bash
chmod +x claude.sh build.sh
cp .env.example .env # set ANTHROPIC_API_KEY (and WEBUI_PASSWORD for web mode)
cp .env.example .env # set ANTHROPIC_API_KEY
cd /path/to/project && ./claude.sh start # start proxy + launch Claude (pulls images, mounts CWD)
./claude.sh web # start proxy + webui (browser terminal on :7681)
./claude.sh update # pull latest images from registry
./build.sh # build images locally (development)
```

View file

@ -37,6 +37,7 @@ load_env() {
fi
}
# Wrapper so every docker compose call uses the right file and project name.
dc() { docker compose -f "$COMPOSE_FILE" -p "$PROJECT" "$@"; }
# ─── Volume args ──────────────────────────────────────────────────────────────
@ -121,32 +122,12 @@ cmd_shell() {
dc run --rm --service-ports --entrypoint /bin/bash "${VOLUME_ARGS[@]}" claude
}
cmd_web() {
check_deps; load_env
[[ -n "${WEBUI_PASSWORD:-}" ]] \
|| { error "WEBUI_PASSWORD is not set. Add it to .env before starting the web interface."; exit 1; }
info "Pulling latest images..."
dc pull
info "Starting proxy and web interface..."
dc up -d webui
info "Web interface is up → http://0.0.0.0:7681"
info "Credentials: ${WEBUI_USER:-claude} / [WEBUI_PASSWORD]"
}
cmd_web_stop() {
check_deps
info "Stopping web interface..."
dc stop webui && dc rm -f webui
}
cmd_help() {
cat <<EOF
Usage: $(basename "$0") [--kube] <command> [args]
Commands:
start [args] Start proxy, launch Claude Code (CLI)
web Start proxy + web interface (browser terminal on :7681)
web-stop Stop the web interface (keeps proxy running)
stop Stop and remove all containers
update Pull latest images from the registry
logs [svc] Tail logs (default: proxy)
@ -158,16 +139,13 @@ Flags (before the subcommand):
--kube Mount \$HOME/.kube read-only at /home/node/.kube (kubectl access)
Environment variables (set in .env):
ANTHROPIC_API_KEY API key auth
ANTHROPIC_API_KEY API key auth
CLAUDE_CODE_OAUTH_TOKEN OAuth token auth (from 'claude setup-token')
IMAGE_TAG Image tag to use (default: latest)
WEBUI_USER Web interface username (default: claude)
WEBUI_PASSWORD Required for web mode
IMAGE_TAG Image tag to use (default: latest)
Examples:
cd ~/myproject && ./claude.sh start
cd ~/myproject && ./claude.sh --kube start
./claude.sh web
./claude.sh logs proxy
./claude.sh shell
EOF
@ -184,8 +162,6 @@ done
case "${1:-help}" in
start|run) shift; cmd_start "$@" ;;
stop) cmd_stop ;;
web) cmd_web ;;
web-stop) cmd_web_stop ;;
update) cmd_update ;;
logs) shift; cmd_logs "${1:-}" ;;
status) cmd_status ;;

View file

@ -5,8 +5,7 @@ RUN apk add --no-cache \
git \
curl \
ca-certificates \
bash \
ttyd
bash
# Install kubectl — architecture-aware, checksum-verified
RUN KUBECTL_VERSION=$(curl -fsSL https://dl.k8s.io/release/stable.txt) \
@ -19,9 +18,6 @@ RUN KUBECTL_VERSION=$(curl -fsSL https://dl.k8s.io/release/stable.txt) \
&& rm /tmp/kubectl.sha256 \
&& chmod +x /usr/local/bin/kubectl
# Entrypoint used by the webui service (ttyd wrapping claude)
COPY --chmod=755 webui-entrypoint.sh /usr/local/bin/webui-entrypoint.sh
# System-level Claude Code policy — owned by root, not writable by the node user.
# Restricts available models; cannot be bypassed via CLI flags or env vars.
COPY settings.json /etc/claude-code/managed-settings.json

View file

@ -1,14 +0,0 @@
#!/usr/bin/env bash
# Entrypoint for the webui service.
# Wraps Claude Code in ttyd (terminal-over-WebSocket) with basic auth.
set -euo pipefail
: "${WEBUI_PASSWORD:?WEBUI_PASSWORD must be set in .env}"
WEBUI_USER="${WEBUI_USER:-claude}"
WEBUI_PORT="${WEBUI_PORT:-7681}"
exec ttyd \
--port "${WEBUI_PORT}" \
--writable \
--credential "${WEBUI_USER}:${WEBUI_PASSWORD}" \
claude

View file

@ -5,7 +5,7 @@ services:
proxy:
image: registry.zeidler.dev/docker-public/playground/docker-claude-proxy:${IMAGE_TAG:-latest}
networks:
- claude-internal # reachable by claude and webui containers
- claude-internal # reachable by claude container
- proxy-external # has outbound internet access
restart: unless-stopped
security_opt:
@ -55,50 +55,6 @@ services:
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:
image: registry.zeidler.dev/docker-public/playground/docker-claude-claude:${IMAGE_TAG:-latest}
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:
@ -109,9 +65,3 @@ networks:
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: