# OpenClaw (AI Assistant Gateway) Self-hosted AI assistant gateway running on the NUC via Docker Compose. Connects to messaging platforms (WhatsApp, Telegram, Discord, etc.) and routes messages through Claude. ## Access | Property | Value | |----------|-------| | **Control UI (HTTPS)** | `https://alezmad-nuc.tail58f5ad.ts.net:8443` | | **Gateway WS** | `ws://192.168.1.3:18789` | | **Gateway Token** | `3547c3f2b7b4a33eb077cf804bcca446057f81ba1578b2045dbb3aa4e04346ee` | | **Model** | `anthropic/claude-sonnet-4-5-20250929` | | **Repo on NUC** | `~/openclaw/` | | **Config** | `~/.openclaw/openclaw.json` | | **Version** | `2026.2.10` | **HTTPS Required:** The Control UI requires a secure context (HTTPS or localhost). Access via Tailscale Serve on port 8443. ## Tailscale Serve (HTTPS access) The gateway is exposed via Tailscale Serve (not Funnel - tailnet only, not public): ```bash # Start HTTPS proxy (requires sudo, must run from NUC terminal) ssh nuc sudo tailscale serve --bg --https=8443 http://localhost:18789 # Password: 7vXHpSTD ``` **The `--bg` flag makes it persistent.** Without it, Ctrl+C stops the proxy. ## Docker Compose Management ```bash # Start gateway ssh nuc "cd ~/openclaw && docker compose up -d openclaw-gateway" # Restart gateway ssh nuc "cd ~/openclaw && docker compose restart openclaw-gateway" # View logs ssh nuc "docker logs openclaw-openclaw-gateway-1 2>&1 | tail -30" # Run CLI commands (use docker exec for commands that need gateway connection) ssh nuc "docker exec openclaw-openclaw-gateway-1 node dist/index.js --token 3547c3f2b7b4a33eb077cf804bcca446057f81ba1578b2045dbb3aa4e04346ee --url ws://127.0.0.1:18789" # Run CLI commands that don't need gateway (use docker compose run) ssh nuc "cd ~/openclaw && script -qc 'docker compose run --rm openclaw-cli ' /dev/null" ``` ## Device Pairing When the Control UI shows "pairing required", approve the pending device: ```bash # List pending devices ssh nuc "docker exec openclaw-openclaw-gateway-1 node dist/index.js devices list --token 3547c3f2b7b4a33eb077cf804bcca446057f81ba1578b2045dbb3aa4e04346ee --url ws://127.0.0.1:18789" # Approve a device (use requestId from the list) ssh nuc "docker exec openclaw-openclaw-gateway-1 node dist/index.js devices approve --token 3547c3f2b7b4a33eb077cf804bcca446057f81ba1578b2045dbb3aa4e04346ee --url ws://127.0.0.1:18789" ``` **Dashboard URL with embedded token (auto-authenticates):** ``` https://alezmad-nuc.tail58f5ad.ts.net:8443/#token=3547c3f2b7b4a33eb077cf804bcca446057f81ba1578b2045dbb3aa4e04346ee ``` ## Channel Plugins Channels are plugins that must be enabled before use. 35 available, 4 loaded by default. ```bash # List all plugins ssh nuc "script -qc 'cd ~/openclaw && docker compose run --rm openclaw-cli plugins list' /dev/null" # Enable a channel plugin ssh nuc "script -qc 'cd ~/openclaw && docker compose run --rm openclaw-cli plugins enable ' /dev/null" # Then restart gateway ssh nuc "cd ~/openclaw && docker compose restart openclaw-gateway" ``` **Currently enabled channels:** WhatsApp (linked and active) **Available channel plugins:** | Plugin ID | Channel | |-----------|---------| | `whatsapp` | WhatsApp | | `telegram` | Telegram | | `discord` | Discord | | `slack` | Slack | | `signal` | Signal | | `matrix` | Matrix | | `msteams` | Microsoft Teams | | `googlechat` | Google Chat | | `imessage` | iMessage | | `irc` | IRC | ## Setting Up Channels That Require QR Codes (WhatsApp, etc.) **The CLI needs a TTY for QR display.** Cannot run directly via `ssh nuc "command"`. Use `script` to fake a TTY and capture output: ```bash # Step 1: Enable the plugin ssh nuc "script -qc 'cd ~/openclaw && docker compose run --rm openclaw-cli plugins enable whatsapp' /dev/null" # Step 2: Restart gateway ssh nuc "cd ~/openclaw && docker compose restart openclaw-gateway" # Step 3: Run login with TTY capture (captures QR to file) ssh nuc "script -q /tmp/openclaw-qr.txt -c 'cd ~/openclaw && docker compose run --rm openclaw-cli channels login'" # Step 4: If QR needs to be viewed remotely, copy and render as image scp nuc:/tmp/openclaw-qr.txt /tmp/ # Then use Python + Pillow to convert Unicode block chars to PNG ``` **QR to PNG conversion (run locally on Mac):** ```python from PIL import Image import re with open("/tmp/openclaw-qr.txt", "rb") as f: content = f.read().decode("utf-8", errors="replace") lines = content.split("\n") qr_lines = [] for l in lines: clean = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", l).replace(chr(0), "") if any(c in clean for c in ["\u2584", "\u2588", "\u2580"]): qr_lines.append(clean) scale = 10 width = max(len(l) for l in qr_lines) height = len(qr_lines) * 2 img = Image.new("RGB", (width * scale, height * scale), "white") for row_idx, line in enumerate(qr_lines): for col_idx, ch in enumerate(line): top_black = ch in ["\u2588", "\u2580"] bot_black = ch in ["\u2588", "\u2584"] for dy in range(scale): for dx in range(scale): if top_black: img.putpixel((col_idx*scale+dx, row_idx*2*scale+dy), (0,0,0)) if bot_black: img.putpixel((col_idx*scale+dx, (row_idx*2+1)*scale+dy), (0,0,0)) img.save("/tmp/qr.png") ``` ## Anthropic Authentication Uses a Claude Code OAuth token (valid 1 year). Set in both config and docker-compose env: **Config (`~/.openclaw/openclaw.json`):** ```json { "env": { "ANTHROPIC_API_KEY": "sk-ant-oat01-..." } } ``` **Docker env (`~/openclaw/.env`):** ``` ANTHROPIC_API_KEY=sk-ant-oat01-... ``` **To regenerate token (run on Mac where `claude` CLI is installed):** ```bash claude setup-token # Copy the output token and update both config + .env on NUC ``` ## Config Schema (current) ```json { "agents": { "defaults": { "model": { "primary": "anthropic/claude-sonnet-4-5-20250929" } } }, "gateway": { "port": 18789, "mode": "local", "bind": "lan", "auth": { "mode": "token" } } } ``` **Config gotchas:** - `agent.model` is **legacy** — use `agents.defaults.model.primary` instead - Run `docker exec openclaw-openclaw-gateway-1 node dist/index.js doctor --fix` to migrate legacy keys - The `gateway.pairing` key does NOT exist — device pairing is managed via the `devices` CLI, not config ## Troubleshooting 1. **"control ui requires HTTPS or localhost"**: Access via `https://alezmad-nuc.tail58f5ad.ts.net:8443` (Tailscale Serve), NOT `http://192.168.1.3:18789` 2. **"pairing required"**: Approve the device via `devices approve` command (see Device Pairing section above) 3. **"unauthorized: gateway token missing"**: Use the dashboard URL with `#token=...` hash to auto-authenticate 4. **CLI commands fail with "gateway closed"**: Use `docker exec` into the running gateway container instead of `docker compose run` (the CLI container can't reach the gateway on its internal Docker IP) 5. **Config "invalid" after edit**: Run `doctor --fix` inside the gateway container to clean up 6. **Channel "unsupported"**: Enable the plugin first with `plugins enable `, then restart gateway 7. **CLAUDE_AI_SESSION_KEY warnings**: Harmless — these are for Claude web session auth which isn't used when using API key