# OpenClaw + Claude Max API Proxy All-in-one Docker container that runs [OpenClaw](https://github.com/openclaw/openclaw) powered by your Claude Max subscription through the [wende/claude-max-api-proxy](https://github.com/wende/claude-max-api-proxy) fork. No separate API key costs. ## Architecture ``` Discord / Telegram / WhatsApp ↕ OpenClaw Gateway (:18789) ↓ claude-max-api-proxy (:3456) ↓ Claude Code CLI (OAuth) ↓ Anthropic API (via Max subscription) ``` ## Prerequisites - A server running Docker (tested on Debian Trixie with CasaOS) - A [Claude Max](https://claude.ai) subscription - A Discord bot token (or Telegram/WhatsApp — see OpenClaw docs) - Node.js 22+ is handled inside the container — no host dependency ## Container Image The image is built via Gitea Actions and pushed to the Gitea Container Registry: ``` gitea.pitthappens.dyndns.org/ragincajunbanjo/openclaw-proxy:latest ``` --- ## Deployment ### 1. Create data directories ```bash sudo mkdir -p /DATA/AppData/openclaw/{claude,config,workspace} sudo chown -R 1000:1000 /DATA/AppData/openclaw ``` ### 2. Log into the Gitea container registry ```bash docker login gitea.pitthappens.dyndns.org ``` Use your Gitea username and a personal access token with `package:read` scope. ### 3. Deploy the container Use Docker Compose or paste into CasaOS Custom Install: ```yaml services: openclaw: image: gitea.pitthappens.dyndns.org/ragincajunbanjo/openclaw-proxy:latest container_name: openclaw restart: unless-stopped ports: - "3456:3456" - "18789:18789" - "18790:18790" volumes: - /DATA/AppData/openclaw/claude:/home/node/.claude - /DATA/AppData/openclaw/config:/home/node/.openclaw - /DATA/AppData/openclaw/workspace:/home/node/.openclaw/workspace environment: - HOME=/home/node - TERM=xterm-256color - OPENCLAW_GATEWAY_BIND=lan - OPENCLAW_GATEWAY_TOKEN=your-secure-token-here - OPENAI_API_KEY=not-needed - OPENAI_BASE_URL=http://127.0.0.1:3456/v1 - TZ=America/Chicago healthcheck: test: ["CMD", "curl", "-sf", "http://127.0.0.1:3456/health"] interval: 30s timeout: 5s retries: 5 start_period: 30s init: true ``` Generate a secure gateway token with: ```bash openssl rand -hex 32 ``` ### 4. Authenticate Claude Code (one-time) The container will boot-loop until Claude is authenticated. Stop it first, then run auth in a one-off container: ```bash docker stop openclaw docker run -it --rm \ -v /DATA/AppData/openclaw/claude:/home/node/.claude \ gitea.pitthappens.dyndns.org/ragincajunbanjo/openclaw-proxy:latest \ /usr/local/bin/entrypoint.sh auth ``` This starts the Claude Code OAuth flow. You'll get a URL to open in your browser — sign in with your Claude Max account. Credentials are saved to the mounted volume and persist across container restarts. Start the container after auth completes: ```bash docker start openclaw ``` ### 5. Configure OpenClaw Edit the config file directly: ```bash cat > /DATA/AppData/openclaw/config/openclaw.json << 'EOF' { "env": { "OPENAI_API_KEY": "not-needed" }, "models": { "providers": { "claude-proxy": { "api": "openai-completions", "baseUrl": "http://127.0.0.1:3456/v1", "apiKey": "not-needed", "models": [ { "id": "claude-sonnet-4", "name": "Claude Sonnet 4" } ] } } }, "agents": { "defaults": { "workspace": "/home/node/.openclaw/workspace", "model": { "primary": "claude-proxy/claude-sonnet-4" } } }, "gateway": { "mode": "local", "bind": "lan" } } EOF ``` To add more models (e.g. Opus), add entries to the `models` array: ```json { "id": "claude-opus-4", "name": "Claude Opus 4" } ``` Then set the primary model to `claude-proxy/claude-opus-4`. Restart after editing config: ```bash docker restart openclaw ``` ### 6. Connect Discord ```bash docker exec -it openclaw bash -c \ "cd /opt/openclaw && node dist/index.js channels add --channel discord --token YOUR_DISCORD_BOT_TOKEN" ``` Then DM or mention the bot on Discord. It will respond with a pairing code. Approve it: ```bash docker exec openclaw bash -c \ "cd /opt/openclaw && node dist/index.js pairing approve discord YOUR_CODE" ``` Send another message — the bot should now respond. ### 7. Verify everything is working ```bash # Check logs docker logs -f openclaw # Test the proxy directly docker exec openclaw curl -s http://127.0.0.1:3456/health # Test a completion docker exec openclaw curl -s http://127.0.0.1:3456/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"claude-sonnet-4","messages":[{"role":"user","content":"Hello"}]}' # Check OpenClaw status docker exec openclaw bash -c "cd /opt/openclaw && node dist/index.js status" ``` --- ## Updating After a new image is built by CI: ```bash docker pull gitea.pitthappens.dyndns.org/ragincajunbanjo/openclaw-proxy:latest docker restart openclaw ``` Or from CasaOS, use the container's rebuild/update button. Your config, credentials, and workspace are on persistent volumes and survive updates. --- ## CI/CD Pipeline ### Gitea Actions The repo includes a workflow at `.gitea/workflows/build-deploy.yml` that builds the Docker image and pushes it to the Gitea Container Registry on every push to `main`. ### Required Gitea Setup 1. **Enable packages and actions** in Gitea's `app.ini`: ```ini [packages] ENABLED = true [actions] ENABLED = true DEFAULT_ACTIONS_URL = https://github.com ``` Restart Gitea after editing. 2. **Install and register an Act Runner** on the server: ```bash sudo wget -O /usr/local/bin/act_runner \ https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64 sudo chmod +x /usr/local/bin/act_runner sudo mkdir -p /opt/gitea-runner cd /opt/gitea-runner sudo act_runner register \ --instance https://gitea.pitthappens.dyndns.org \ --name local-runner \ --labels ubuntu-latest:host ``` Get the registration token from Gitea → Site Administration → Actions → Runners. 3. **Create a systemd service** for the runner: ```bash sudo tee /etc/systemd/system/gitea-runner.service << 'SVCEOF' [Unit] Description=Gitea Act Runner After=docker.service Requires=docker.service [Service] Type=simple User=root WorkingDirectory=/opt/gitea-runner ExecStart=/usr/local/bin/act_runner daemon Restart=always RestartSec=5 [Install] WantedBy=multi-user.target SVCEOF sudo systemctl daemon-reload sudo systemctl enable --now gitea-runner ``` 4. **Add repo secrets** (Repo → Settings → Actions → Secrets): - `REGISTRY_TOKEN` — Gitea personal access token with `package:write` scope - `DOCKERHUB_USERNAME` — Docker Hub username (avoids pull rate limits during builds) - `DOCKERHUB_TOKEN` — Docker Hub access token --- ## Persistent Volumes | Host Path | Container Path | Purpose | |---|---|---| | `/DATA/AppData/openclaw/claude` | `/home/node/.claude` | Claude Code OAuth credentials | | `/DATA/AppData/openclaw/config` | `/home/node/.openclaw` | OpenClaw settings and memory | | `/DATA/AppData/openclaw/workspace` | `/home/node/.openclaw/workspace` | Agent working directory | --- ## Troubleshooting | Problem | Solution | |---|---| | Container boot-loops with "No Claude credentials" | Run the one-off auth container (Step 4) | | "Unknown model" on startup | Ensure `models.providers` is defined in `openclaw.json` with the correct structure (Step 5) | | Proxy returns `[object Object]` | You may have the broken original npm package — rebuild with `--no-cache` | | OAuth token expired | Re-run the auth step: `docker run -it --rm -v /DATA/AppData/openclaw/claude:/home/node/.claude /usr/local/bin/entrypoint.sh auth` | | Discord bot doesn't respond | Check pairing: `docker exec openclaw bash -c "cd /opt/openclaw && node dist/index.js pairing list"` and approve any pending codes | | "Control UI build failed" in logs | Cosmetic only — the gateway works fine without it | | Docker Hub rate limit during CI build | Add `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` secrets to the repo | | OpenClaw requires Node >= 22 | Make sure the Dockerfile uses `node:22-bookworm-slim` as the base image | | `env: node: No such file or directory` | Background services (LaunchAgent/systemd) don't inherit shell PATH — use full paths to `node` | --- ## Important Notes - The `claude-max-api-proxy` is a **community tool**, not officially supported by Anthropic or OpenClaw. It wraps the Claude Code CLI to expose an OpenAI-compatible API using your Max subscription's OAuth credentials. - The proxy binds to `127.0.0.1:3456` inside the container. Port 3456 is exposed for debugging but the proxy is only used internally by OpenClaw. - This setup uses the [wende fork](https://github.com/wende/claude-max-api-proxy) which fixes a serialization bug in the original package that caused responses to return `[object Object]`.