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. # Port 54545 must be reachable from your browser for the OAuth callback.
# Run: sbx ports <sandbox-name> --publish 54545:54545/tcp # 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) ─────────────────────────────────────────────── # ─── MCP servers (all optional) ───────────────────────────────────────────────
# GitHub — PAT with repo scope # GitHub — PAT with repo scope

View file

@ -8,16 +8,13 @@ This file provides context and guidance for working with this project.
## Architecture ## 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 - **`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 - **`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. 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`): Auth supports three modes (checked at startup by `claude.sh`):
- `ANTHROPIC_API_KEY` — API key - `ANTHROPIC_API_KEY` — API key
- `CLAUDE_CODE_OAUTH_TOKEN` — 1-year token from `claude setup-token` (headless-friendly) - `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/ 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 ├── docker-compose.yml # Service definitions and network topology
├── claude/ ├── claude/
│ ├── Dockerfile # Claude Code + ttyd (node:20-alpine, UID 1000) │ └── Dockerfile # Claude Code (node:20-alpine, UID 1000)
│ └── webui-entrypoint.sh # Entrypoint for webui: starts ttyd wrapping claude
├── proxy/ ├── proxy/
│ ├── Dockerfile # Squid proxy sidecar (alpine:3.21, squid user) │ ├── Dockerfile # Squid proxy sidecar (alpine:3.21, squid user)
│ └── squid.conf # Squid ACL config — egress allowlist lives here │ └── squid.conf # Squid ACL config — egress allowlist lives here
@ -45,9 +41,8 @@ docker-claude/
```bash ```bash
chmod +x claude.sh build.sh 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) 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 ./claude.sh update # pull latest images from registry
./build.sh # build images locally (development) ./build.sh # build images locally (development)
``` ```

View file

@ -37,6 +37,7 @@ load_env() {
fi fi
} }
# Wrapper so every docker compose call uses the right file and project name.
dc() { docker compose -f "$COMPOSE_FILE" -p "$PROJECT" "$@"; } dc() { docker compose -f "$COMPOSE_FILE" -p "$PROJECT" "$@"; }
# ─── Volume args ────────────────────────────────────────────────────────────── # ─── Volume args ──────────────────────────────────────────────────────────────
@ -121,32 +122,12 @@ cmd_shell() {
dc run --rm --service-ports --entrypoint /bin/bash "${VOLUME_ARGS[@]}" claude 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() { cmd_help() {
cat <<EOF cat <<EOF
Usage: $(basename "$0") [--kube] <command> [args] Usage: $(basename "$0") [--kube] <command> [args]
Commands: Commands:
start [args] Start proxy, launch Claude Code (CLI) 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 stop Stop and remove all containers
update Pull latest images from the registry update Pull latest images from the registry
logs [svc] Tail logs (default: proxy) logs [svc] Tail logs (default: proxy)
@ -161,13 +142,10 @@ 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') CLAUDE_CODE_OAUTH_TOKEN OAuth token auth (from 'claude setup-token')
IMAGE_TAG Image tag to use (default: latest) IMAGE_TAG Image tag to use (default: latest)
WEBUI_USER Web interface username (default: claude)
WEBUI_PASSWORD Required for web mode
Examples: Examples:
cd ~/myproject && ./claude.sh start cd ~/myproject && ./claude.sh start
cd ~/myproject && ./claude.sh --kube start cd ~/myproject && ./claude.sh --kube start
./claude.sh web
./claude.sh logs proxy ./claude.sh logs proxy
./claude.sh shell ./claude.sh shell
EOF EOF
@ -184,8 +162,6 @@ done
case "${1:-help}" in case "${1:-help}" in
start|run) shift; cmd_start "$@" ;; start|run) shift; cmd_start "$@" ;;
stop) cmd_stop ;; stop) cmd_stop ;;
web) cmd_web ;;
web-stop) cmd_web_stop ;;
update) cmd_update ;; update) cmd_update ;;
logs) shift; cmd_logs "${1:-}" ;; logs) shift; cmd_logs "${1:-}" ;;
status) cmd_status ;; status) cmd_status ;;

View file

@ -5,8 +5,7 @@ RUN apk add --no-cache \
git \ git \
curl \ curl \
ca-certificates \ ca-certificates \
bash \ bash
ttyd
# Install kubectl — architecture-aware, checksum-verified # Install kubectl — architecture-aware, checksum-verified
RUN KUBECTL_VERSION=$(curl -fsSL https://dl.k8s.io/release/stable.txt) \ 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 \ && rm /tmp/kubectl.sha256 \
&& chmod +x /usr/local/bin/kubectl && 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. # 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. # Restricts available models; cannot be bypassed via CLI flags or env vars.
COPY settings.json /etc/claude-code/managed-settings.json 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: proxy:
image: registry.zeidler.dev/docker-public/playground/docker-claude-proxy:${IMAGE_TAG:-latest} image: registry.zeidler.dev/docker-public/playground/docker-claude-proxy:${IMAGE_TAG:-latest}
networks: networks:
- claude-internal # reachable by claude and webui containers - claude-internal # reachable by claude container
- proxy-external # has outbound internet access - proxy-external # has outbound internet access
restart: unless-stopped restart: unless-stopped
security_opt: security_opt:
@ -55,50 +55,6 @@ services:
stdin_open: true stdin_open: true
tty: 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: networks:
# Internal-only: Docker adds no default gateway → no direct internet route # Internal-only: Docker adds no default gateway → no direct internet route
claude-internal: claude-internal:
@ -109,9 +65,3 @@ networks:
proxy-external: proxy-external:
driver: bridge 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: