automation/skills/openclaw-deploy/SKILL.md
Topic skill (no dedicated `charly openclaw` command — the surface is candy composition + box deployment). MUST be invoked before any work involving: OpenClaw gateway configuration, model auth, browser integration, channel setup, or any box composing `openclaw-*` candies (`openclaw`, `openclaw-full`, or a custom box composing `openclaw`/`openclaw-full` + `sway-desktop`).
npx skillsauth add overthinkos/overthink-plugins openclaw-deployInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
OpenClaw is an AI gateway that connects LLM agents to messaging channels (WhatsApp, Telegram, Discord, Slack, Signal, iMessage, IRC, Teams) and exposes them via a WebSocket API, CLI, and web Control UI. It runs as a Node.js process with an embedded agent runtime, model failover, browser automation, and multi-agent routing.
In OpenCharly, OpenClaw runs as a supervisord service inside containers. The openclaw candy provides the npm package; compose it with the sway-desktop metalayer (full Sway desktop + Chrome browser + VNC) for browser-based workflows like OAuth and web automation. For a desktop+browser box, compose your own from the candies (shown below); for non-browser channels use the headless openclaw / openclaw-full boxes.
The gateway listens on port 18789 (WebSocket + HTTP). All CLI commands (openclaw *) connect to the gateway WebSocket. The Control UI is served at the gateway root URL. Config is stored in ~/.openclaw/openclaw.json (JSON5 format).
| Action | Command | Description |
|--------|---------|-------------|
| Gateway health | openclaw health | Check gateway connectivity |
| Gateway status | openclaw status --all | Full status with channels |
| Model status | openclaw models status | Show model, auth, and usage |
| Browser status | openclaw browser status | Show browser connection state |
| Browser tabs | openclaw browser tabs | List open Chrome tabs |
| Open URL | openclaw browser open <url> | Open URL in Chrome |
| Page snapshot | openclaw browser snapshot | Accessible page structure (AI refs) |
| Screenshot | openclaw browser screenshot | Capture PNG to media dir |
| Config get/set | openclaw config get/set <key> <val> | Read/write config values |
| Doctor | openclaw doctor --fix | Health checks + auto-fixes |
| Security audit | openclaw security audit | Check security posture |
| Channel login | openclaw channels login --channel <ch> | Connect a messaging channel |
| Agent message | openclaw agent --agent main --message "..." | Send test prompt |
| Setup wizard | openclaw configure | Interactive config wizard |
| Logs | openclaw logs --follow | Tail gateway logs |
For container lifecycle, use charly commands -- see /charly-core:service.
The openclaw candy (candy/openclaw/) depends on nodejs and supervisord. It installs the openclaw npm package globally, exposes port 18789, declares a data volume at ~/.openclaw, and runs as a supervisord service:
[program:openclaw]
command=%(ENV_HOME)s/.npm-global/bin/openclaw gateway --port 18789
The gateway binds to loopback only (no --bind lan). External access is handled by port_relay (socat), which forwards from the container interface to loopback. This avoids CORS origin checks entirely — the gateway only ever sees loopback connections.
Compose your own desktop+browser box — e.g. an openclaw-desktop entry in charly.yml combining openclaw-full + sway-desktop on a Fedora base:
openclaw-desktop:
candy:
base: fedora
openclaw-desktop-candy:
candy:
- agent-forwarding
- openclaw-full
- sway-desktop
- dbus
- charly
openclaw-desktop-port:
port:
- "18789:18789"
- "5900:5900"
- "9222:9222"
- "9224:9224"
The resulting box exposes:
| Port | Service | Protocol | |------|---------|----------| | 18789 | Gateway WebSocket + Control UI | HTTP | | 5900 | VNC (wayvnc) | TCP | | 9222 | Chrome DevTools | HTTP |
Tunnel config exposes all ports via Tailscale (ports: all). Platform: linux/amd64 only.
charly alias add openclaw openclaw-desktop
# Now: openclaw --help (runs inside the container)
charly box build openclaw-desktop # Build image
charly config openclaw-desktop # Generate quadlet, daemon-reload
charly start openclaw-desktop # Start via systemd
charly stop openclaw-desktop # Stop
charly status openclaw-desktop # Check systemd status
charly logs openclaw-desktop -f # Follow container logs
The gateway binds to loopback only (no --bind lan). Only one config value is required before the gateway will start:
openclaw config set gateway.mode local
Without gateway.mode=local, the gateway refuses to start.
dangerouslyAllowHostHeaderOriginFallback is NOT needed because port_relay (socat) handles external access — the gateway only sees loopback connections, so no CORS origin checks are triggered.
For Chrome integration, also set:
openclaw config set browser.cdpUrl "http://127.0.0.1:9222"
After setting these, restart the gateway:
supervisorctl restart openclaw
| Mode | Description |
|------|-------------|
| none | No auth (loopback only) |
| token | Bearer token in WebSocket handshake |
| password | Password auth |
| trusted-proxy | Proxy handles auth (e.g., Tailscale identity headers) |
| Mode | Description |
|------|-------------|
| loopback | 127.0.0.1 only (default) |
| lan | All interfaces (0.0.0.0) -- requires auth |
| tailnet | Tailscale interface only |
openclaw health --json # Gateway health
openclaw doctor --fix # Diagnose and fix issues
openclaw status --all --deep # Full status with channels
# HTTP endpoints on gateway port:
# GET /healthz -- liveness
# GET /readyz -- readiness
// In ~/.openclaw/openclaw.json
agents: {
defaults: {
model: {
primary: "openai-codex/gpt-5.4",
fallbacks: ["anthropic/claude-sonnet-4-5"]
}
}
}
Critical: The openclaw models auth login TUI requires a real terminal to complete the post-callback token exchange. Do NOT pipe through tee or redirect stdout — it breaks the TUI event loop. Use charly tmux (see /charly-automation:tmux):
IMG=<image>
# 1. Start OAuth in a tmux session (real terminal)
charly tmux run $IMG -s oauth "openclaw models auth login --provider openai-codex --set-default"
# 2. Read the OAuth URL from tmux output
sleep 5
charly tmux capture $IMG -s oauth | grep -o 'https://auth.openai.com/[^ ]*'
# 3. Drive the browser via cdp:/vnc: plan steps (the cdp: verb is served
# out-of-process by candy/plugin-cdp): cdp: open the OAuth URL, then locate
# "Continue with Google" and "Continue" (consent) with cdp: coords on
# 'button._buttonStyleFix_wvuha_65' / 'button._primary_3rdp0_107' and deliver
# each click via the vnc: verb (chrome:// + anti-automation pages need the VNC
# pointer). Run the leg with: charly check live $IMG --filter cdp --filter vnc
# Full recipe: /charly-check:cdp.
# 4. Verify completion
sleep 10
charly tmux capture $IMG -s oauth
# Should show: "OpenAI OAuth complete", "Default model set to openai-codex/gpt-5.4"
Prerequisites: Chrome must have an active Google session. The "Continue with Google" button on OpenAI's auth page uses Chrome's Google cookies — sign Chrome into Google (with sync enabled) via the VNC desktop before starting the OAuth flow.
Callback architecture: The OAuth callback hits http://127.0.0.1:1455/auth/callback inside the container. Chrome and openclaw-models share the same network namespace — no port mapping needed for 1455. The BROWSER=browser-open env var (set by the chrome candy) auto-opens URLs via CDP, but may not trigger in all TTY contexts — author a cdp: open step (the cdp: verb, served out-of-process by candy/plugin-cdp) as a fallback.
Stale port 1455: If a previous attempt left port 1455 occupied: charly shell $IMG -c 'kill -9 $(ss -tlnp sport = :1455 | grep -oP "pid=\K\d+")'
Tokens persist in ~/.openclaw/agents/main/agent/auth-profiles.json in the data volume. Survive charly stop/charly start and image rebuilds. Only destroyed by charly remove --purge.
Model name: openai-codex/gpt-5.4. The --set-default flag sets it as the default model in openclaw.json.
openclaw models status
# Shows: provider, model, auth profiles, token expiry, usage quotas
Two-stage failure handling:
Backoff: 1min -> 5min -> 25min -> 1hr (capped). Auth profiles pin per session and reset on /new or compaction.
models: {
providers: {
"my-local": {
baseUrl: "http://localhost:4000/v1",
api: "openai-completions",
models: [{ id: "my-model", contextWindow: 128000, maxTokens: 32000 }]
}
}
}
In a composed openclaw desktop box (e.g. openclaw-desktop), Chrome already runs as a supervisord service on port 9222. OpenClaw must connect to it rather than launching a new instance:
openclaw config set browser.cdpUrl "http://127.0.0.1:9222"
supervisorctl restart openclaw
Do not use openclaw browser start in this box -- it attempts to launch a separate Chrome instance that fails without Wayland environment variables.
After configuration, verify:
openclaw browser status # Should show running: true, cdpPort: 9222
openclaw browser tabs # Lists open Chrome tabs
openclaw browser open https://example.com # Open URL in new tab
openclaw browser navigate https://other.com # Navigate current tab
openclaw browser snapshot # AI-accessible page structure
openclaw browser snapshot --format aria # Accessibility tree
openclaw browser screenshot # PNG to media dir
openclaw browser screenshot --full-page # Full page capture
openclaw browser click 12 # Click element by AI ref
openclaw browser type 23 "hello" --submit # Type + submit
openclaw browser press Enter # Key press
openclaw browser hover 44 # Hover element
openclaw browser fill --fields '[{"ref":"1","value":"Ada"}]'
openclaw browser wait --text "Done" # Wait for text
openclaw browser wait --url "**/dashboard" # Wait for URL pattern
openclaw browser evaluate --fn '(el) => el.textContent' --ref 7
openclaw browser console --level error # Console messages
openclaw browser cookies # Read cookies
openclaw browser pdf # Save page as PDF
Snapshots provide element references for interaction:
click 12, type 23 "text")--interactive): e-prefixed refs (click e12)--efficient): compact + interactive + depth reductionbrowser: {
profiles: {
openclaw: { cdpPort: 18800 }, // managed (default)
user: { driver: "existing-session", attachOnly: true }, // attach to running Chrome
remote: { cdpUrl: "http://10.0.0.42:9222" }, // remote CDP
cloud: { cdpUrl: "wss://connect.browserbase.com?apiKey=<KEY>" }
}
}
agents: {
defaults: {
workspace: "~/.openclaw/workspace",
maxConcurrent: 4,
compaction: { mode: "safeguard" },
subagents: { maxConcurrent: 8, maxChildrenPerAgent: 5, maxSpawnDepth: 1 },
tools: { profile: "coding" }, // coding|minimal|messaging|full
elevatedDefault: "off" // off|on|ask|full
}
}
Placed in the agent workspace, injected on first session turn:
AGENTS.md -- operating instructions and memorySOUL.md -- persona, boundaries, toneTOOLS.md -- user-maintained tool documentationIDENTITY.md -- agent name and characteristicsagents: {
list: [
{ id: "main", default: true, name: "Main", model: "openai-codex/gpt-5.4" },
{ id: "home", name: "Home Assistant", model: "anthropic/claude-sonnet-4-5" }
],
bindings: [
{ agentId: "home", match: { channel: "telegram", peer: { id: "123" } } }
]
}
WhatsApp, Telegram, Discord, Slack, Signal, Google Chat, Mattermost, iMessage, IRC, Microsoft Teams, BlueBubbles.
| Policy | Description |
|--------|-------------|
| pairing | Unknown senders get approval code (default) |
| allowlist | Only pre-approved senders |
| open | Accept all (requires allowFrom: ["*"]) |
| disabled | Ignore all DMs |
# Connect WhatsApp (QR code linking)
openclaw channels login --channel whatsapp
# Connect Telegram (requires bot token from BotFather)
openclaw config set channels.telegram.botToken "BOT_TOKEN"
openclaw channels login --channel telegram
# Connect Discord (requires bot token)
openclaw config set channels.discord.token "BOT_TOKEN"
# Check channel status
openclaw channels status --probe
| Path | Purpose |
|------|---------|
| ~/.openclaw/openclaw.json | Main config (JSON5) |
| ~/.openclaw/agents/<id>/agent/auth-profiles.json | Model auth tokens |
| ~/.openclaw/agents/<id>/sessions/ | Session data |
| ~/.openclaw/workspace/ | Agent workspace |
| ~/.openclaw/media/browser/ | Browser screenshots |
| ~/.openclaw/logs/ | Audit logs |
| /tmp/openclaw/openclaw-YYYY-MM-DD.log | Gateway runtime log |
Check supervisord: supervisorctl status openclaw. If cycling between STARTING and STOPPED:
# Run manually to see error output
supervisorctl stop openclaw
timeout 15 openclaw gateway --port 18789
Common fixes:
gateway.mode not set -> openclaw config set gateway.mode localallowedOrigins errors, verify the relay is running: supervisorctl status relay-18789If openclaw browser status shows running: false:
openclaw config set browser.cdpUrl "http://127.0.0.1:9222"
supervisorctl restart openclaw
Use --tty for the interactive CLI. The BROWSER=browser-open env var auto-opens OAuth URLs in Chrome. The callback URL (http://127.0.0.1:1455/auth/callback) is container-internal and needs no port mapping.
charly shell <image> --tty -c "openclaw models auth login --provider openai-codex --set-default"
For browser-assisted OAuth (Google sign-in), see /charly-check:cdp.
After the first container start, configure the gateway:
charly shell <image> -c "openclaw config set gateway.mode local"
charly shell <image> -c "openclaw config set browser.cdpUrl 'http://127.0.0.1:9222'"
charly shell <image> -c "supervisorctl restart openclaw"
/charly-check:cdp -- the cdp: check verb (Chrome DevTools Protocol automation, served out-of-process by candy/plugin-cdp)/charly-core:deploy -- Quadlet, tunnels, volume backing, VNC password/charly-core:service -- charly start/stop/enable/disable/status/logs/update/remove/charly-check:vnc -- VNC desktop automation and password management/charly-automation:alias -- Host command aliases (charly alias add openclaw)/charly-core:shell -- charly shell --tty for interactive container commandsMUST be invoked when the task involves OpenClaw gateway configuration, model auth, browser integration, channel setup, or openclaw boxes. Invoke this skill BEFORE reading source code or launching Explore agents.
Workflow position: Post-deployment. Configure the gateway after the container is running. See also /charly-openclaw:openclaw* (box variants).
tools
Use when authoring or modifying a charly PLUGIN — a candy with a `plugin:` block that contributes Providers (verbs/kinds/deploy-targets/steps/builders/commands), its own CUE schema, builtin (compiled-in) or external (out-of-tree git repo). Covers the unified Provider model, the per-plugin CUE-schema contract (single source → Go params for dev + schema-over-Describe RPC for runtime), the SDK, and the loader.
tools
The CUE data-validation / configuration CLI (cue), pinned to v0.16.1. Use when working with the cue candy, installing the cue binary into a box or onto a target:local dev host, or running the offline schema-vendoring pipeline that feeds charly's egress validation.
tools
CUE EGRESS validation — validating (and, where it adds value, generating) the config files charly WRITES to a system BEFORE the bytes hit disk. MUST be invoked before working on charly/egress.go, the vendored schemas under candy/plugin-egress/egress-schemas/vendor/, the ValidateEgress / registerVendoredEgressKind path, the offline `task cue:vendor` pipeline, or adding an egress schema for any written artifact (cloud-init, k8s manifests, traefik routes, runtime config, install ledger, systemd/quadlet units, ssh_config, libvirt XML).
tools
Kubernetes cluster-probe declarative check verb — the `kube:` check verb (nodes, pods, ingress, storage class, addon health, apply/delete, and arbitrary resource GETs) served out-of-process by the candy/plugin-kube plugin (vendored client-go; no external kubectl required).