docs: add OpenClaw section to CLAUDE.md
Documents the two-container setup, volume/auth gotchas, nginx SSL configuration, control center startup sequence, and usage connector source status. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
671ff1d774
commit
67bf9d18bc
1 changed files with 74 additions and 0 deletions
|
|
@ -108,6 +108,80 @@ Note: `secrets/default.nix` is the agenix recipients file. Agenix looks for `sec
|
||||||
- ACME certs issued via DNS challenge for `philippeterson.com` and `webdav.philippeterson.com`.
|
- ACME certs issued via DNS challenge for `philippeterson.com` and `webdav.philippeterson.com`.
|
||||||
- Forgejo accessible on ports 3000 (HTTP) and 2200 (SSH).
|
- Forgejo accessible on ports 3000 (HTTP) and 2200 (SSH).
|
||||||
|
|
||||||
|
## OpenClaw
|
||||||
|
|
||||||
|
OpenClaw runs as two Arion/Podman containers defined in `arion-openclaw/arion-compose.nix`, both using `network_mode = "host"` so they share the host's `127.0.0.1`.
|
||||||
|
|
||||||
|
| Container | Name | Port | Role |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `openclaw-gateway` | `node:22-alpine` | 18789 (WebSocket) | OpenClaw Gateway (`openclaw@latest`) |
|
||||||
|
| `openclaw` | `node:22-alpine` | 4310 (HTTP) | OpenClaw Control Center (SSR UI) |
|
||||||
|
|
||||||
|
### Volumes and paths
|
||||||
|
|
||||||
|
| Host path | Container path | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| `/var/openclaw/gateway` | `/app` (gateway), `/gateway` (app) | npm install location for `openclaw` package |
|
||||||
|
| `/var/openclaw/app` | `/app` | Control center git clone + runtime files |
|
||||||
|
| `/root/.openclaw` | `/root/.openclaw` | OpenClaw home; shared **read-write** by both containers |
|
||||||
|
|
||||||
|
`/root/.openclaw` must be **writable** in the app container (not `:ro`) — the CLI writes state files at startup and connection probes fail with EROFS otherwise.
|
||||||
|
|
||||||
|
The CLI's effective state dir is `/root/.openclaw/.openclaw/` (double-nested: the CLI treats `OPENCLAW_HOME` as HOME and appends `.openclaw/` internally).
|
||||||
|
|
||||||
|
### Auth and connectivity
|
||||||
|
|
||||||
|
- Gateway runs with `--auth none --dev`. In `--auth none` mode, clients must still present either a device identity (challenge-response) or any token via `OPENCLAW_GATEWAY_TOKEN`.
|
||||||
|
- `OPENCLAW_GATEWAY_TOKEN=openclaw-local-dev` is set in the app container — this lets the CLI probes connect immediately without waiting for device auto-approval.
|
||||||
|
- Device identity lives at `/root/.openclaw/.openclaw/identity/device.json`. In `--dev` mode the gateway auto-approves the local device after first contact.
|
||||||
|
- The control center calls `openclaw status --json` and `openclaw gateway status --json` as CLI subprocesses (not via WebSocket directly). The binary path is set via `OPENCLAW_BIN_PATH=/gateway/node_modules/.bin/openclaw`.
|
||||||
|
|
||||||
|
### nginx
|
||||||
|
|
||||||
|
`claw.quineglobal.com` is proxied to `127.0.0.1:4310`. Key settings:
|
||||||
|
- `forceSSL = false; addSSL = true` — Cloudflare Flexible SSL sends plain HTTP to origin; `forceSSL = true` would create a redirect loop.
|
||||||
|
- `basicAuthFile = "/var/openclaw/htpasswd"` — credentials: `ironmagma / Nargism333`.
|
||||||
|
- WebSocket upgrade headers are set (`Upgrade`, `Connection: upgrade`) so the control center's live-update SSE works through the proxy.
|
||||||
|
|
||||||
|
### Control center startup sequence
|
||||||
|
|
||||||
|
The app container startup script (in `arion-compose.nix`):
|
||||||
|
1. `apk add git`
|
||||||
|
2. Clones `https://github.com/TianyiDataScience/openclaw-control-center.git` to `/app/repo` (once)
|
||||||
|
3. Patches `src/ui/server.ts` and `src/runtime/ui-preferences.ts` via `sed` to default language to `"en"` instead of `"zh"`
|
||||||
|
4. `npm install && npm run build && npm run dev:ui`
|
||||||
|
|
||||||
|
### Usage connector sources
|
||||||
|
|
||||||
|
The Settings → Usage panel tracks 6 data sources. Current status:
|
||||||
|
|
||||||
|
| Source | Status | How to connect |
|
||||||
|
|---|---|---|
|
||||||
|
| Context capacity | Connected | `runtime/model-context-catalog.json` exists at `/var/openclaw/app/repo/runtime/` |
|
||||||
|
| Provider attribution | Connected | Derived from context catalog |
|
||||||
|
| Digest history | Partial (auto) | Builds up as the monitor runs over time |
|
||||||
|
| Request counts | Not connected | Needs real AI requests through the gateway |
|
||||||
|
| Budget limit | Not connected | Add cost thresholds to agent config |
|
||||||
|
| Subscription usage | Not connected | Add `runtime/subscription-snapshot.json` or provider billing snapshot |
|
||||||
|
|
||||||
|
The `model-context-catalog.json` format:
|
||||||
|
```json
|
||||||
|
{ "models": [{ "match": "gpt-5.5", "contextWindowTokens": 200000, "provider": "openai" }, ...] }
|
||||||
|
```
|
||||||
|
`match` is compared case-insensitively against the model name reported by the runtime.
|
||||||
|
|
||||||
|
### Restarting / rebuilding
|
||||||
|
|
||||||
|
After changing `arion-compose.nix`, a `nixos-rebuild switch` regenerates the compose YAML but **does not recreate running containers**. You must force recreation:
|
||||||
|
```bash
|
||||||
|
podman rm -f openclaw # or openclaw-gateway
|
||||||
|
systemctl restart arion-openclaw
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cloudflare SSL gotcha
|
||||||
|
|
||||||
|
This server sits behind Cloudflare in **Flexible** mode (Cloudflare → origin over plain HTTP). Any `nginx.nix` virtualHost for a Cloudflare-proxied domain must use `forceSSL = false; addSSL = true`, not `forceSSL = true`. The latter causes an infinite redirect loop because Cloudflare sends HTTP but nginx redirects to HTTPS, which Cloudflare re-proxies as HTTP again.
|
||||||
|
|
||||||
## Known gotchas
|
## Known gotchas
|
||||||
|
|
||||||
- `gitea-runner` is a `DynamicUser` in the systemd service, so it has no persistent uid. Setting `age.secrets.forgejo-runner-token.owner = "gitea-runner"` causes a chown error at activation; use `owner = "root"` instead (the service reads it via `EnvironmentFile` which runs as root before privilege drop).
|
- `gitea-runner` is a `DynamicUser` in the systemd service, so it has no persistent uid. Setting `age.secrets.forgejo-runner-token.owner = "gitea-runner"` causes a chown error at activation; use `owner = "root"` instead (the service reads it via `EnvironmentFile` which runs as root before privilege drop).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue