Adding a README.md so I can remember what I did with Claude :D
All checks were successful
Build and Push / build (push) Successful in 15s
All checks were successful
Build and Push / build (push) Successful in 15s
This commit is contained in:
326
README.md
Normal file
326
README.md
Normal file
@@ -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 <image> /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]`.
|
||||||
Reference in New Issue
Block a user