diff --git a/README.md b/README.md new file mode 100644 index 0000000..26e3ecb --- /dev/null +++ b/README.md @@ -0,0 +1,326 @@ +# 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]`.