Skip to main content
Use this page to bring the OpenClaw runtime online. Unlike the other four runtimes, OpenClaw is not a CLI you install; it is a connected substrate. Clawboo connects to a running OpenClaw Gateway (a WebSocket server that hosts OpenClaw agents), syncs the Gateway’s agent list into SQLite as the registry of record, and drives chat over that long-lived connection. There is no per-task subprocess to spawn. This is why OpenClaw is connected and managed differently from claude-code, codex, hermes, and clawboo-native; those are CLIs Clawboo installs and runs per task (see Connecting runtimes). OpenClaw has its own state directory, its own scheduler, and its own external messaging channels; Clawboo coordinates with it, it does not run it.

Prerequisites

OpenClaw is a separate project. Clawboo connects to an OpenClaw Gateway you run locally; it does not bundle OpenClaw. The onboarding wizard’s OpenClaw path can install, configure, and start one for you, or you can point Clawboo at a Gateway you already run.
  • An OpenClaw Gateway, reachable over WebSocket (default ws://localhost:18789). The Clawboo server connects to the URL stored in settings (gatewayUrl).
  • The Gateway’s auth token. Clawboo’s onboarding writes one into OpenClaw’s ~/.openclaw/.env as GATEWAY_AUTH_TOKEN and mirrors it into Clawboo’s settings.json (gatewayToken).
  • Node.js (Clawboo’s prerequisite). The install step runs npm install -g openclaw@~2026.9, so npm must be on PATH.
  • For OpenClaw 2026.5.x and later: a one-time device pairing approval, see Device pairing.

How OpenClaw connects

There are two connections to the Gateway, and they exist for different reasons.

The browser connection (same-origin proxy)

The browser never talks to the Gateway directly. The SPA opens a WebSocket to the Clawboo server’s own /api/gateway/ws endpoint, and the server proxies it upstream to the Gateway. The proxy is what injects the auth token and signs the Ed25519 device-auth connect frame server-side, so the browser never sees the credential. WS upgrades to any path other than /api/gateway/ws are dropped (socket.destroy()). This is the runtime/execution path: the chat stream (chat.send, chat.abort), session control (sessions.abort, sessions.patch), and live config patches ride this browser→proxy→Gateway connection.

The server connection (OpenClawAgentSource)

The Clawboo server opens its own Gateway connection, the OpenClawAgentSource. This is the registry of record leg: it calls agents.list() and mirrors the result into SQLite so the fleet list, agent files, and team membership survive the Gateway being down. Reads (listAgents, getAgent, listTeams) come from SQLite and work offline; writes, file I/O, and live sessions delegate to the Gateway and require a live connection. It is also an approval surface. The server connection declares caps: ['tool-events', 'exec-approvals'], so when an agent’s policy says to ask about a command, the Gateway can put that request to the server and not only to a browser tab. Clawboo mirrors each request into its own approvals queue, which is what lets a command waiting for your decision survive a closed tab or a refresh. See Approvals. A headless Node connection can’t use the browser’s crypto.subtle device-auth path, so the server reuses the already-paired proxy device identity (~/.clawboo/proxy-device-identity.json) to sign its connect frame via the gateway-client signConnect hook. Two details are load-bearing here, both confirmed against OpenClaw 2026.5.x:
  • The server connects with client.id = cli (the Gateway validates client.id against a fixed allowlist; a custom id like clawboo-server is rejected and the socket closes with code 1008).
  • The server presents an Origin of the gateway host (http(s)://<gateway-host>). A headless Node WebSocket sends no Origin by default, which the Gateway rejects with CONTROL_UI_ORIGIN_NOT_ALLOWED; Clawboo injects the ws package’s WebSocket (which honours a custom origin) to satisfy the check.
If the server connection is down, reads still serve SQLite (the agent list renders, flagged stale: true), but writes 503. GET /api/agents returns { defaultId, mainKey, agents, stale, lastSyncedAt }; agent-file GET/PUT and session reads return 503 { "error": "gateway_disconnected" }. See Agents API.

Channels

OpenClaw can connect to external messaging channels (WhatsApp, Telegram, and the like) and runs its own cron/heartbeat scheduler. These are the runtime’s private plane; Clawboo never serves a channel and never co-runs the Gateway’s scheduler. The OpenClaw adapter declares this in its capabilities: nativeChannels: 'gateway', nativeScheduler: true, nativeSkills: 'preserve', nativeMemory: 'preserve'. Clawboo’s Routines schedule team-task work, a different domain from OpenClaw’s own-life cron, so the two never conflate.

Steps

The OpenClaw onboarding path runs these from the wizard, but each maps to a /api/system/* route you can also drive directly. See the System API for full shapes.

1. Detect

GET /api/system/status reports whether OpenClaw is installed, the Gateway is running, and Node is sufficient:
gateway.running is true when a managed PID is alive or the port probes reachable. When the Gateway is running and ~/.openclaw/.env exists, the handler syncs the .env token into Clawboo’s settings as a side effect.

2. Install (optional)

POST /api/system/install-openclaw is a Server-Sent Events stream that runs npm install -g openclaw@~2026.9. The version is pinned to the ~2026.9 range deliberately. Both 2026.5 and 2026.9 speak connect protocol v4, so the pin is not a protocol pin; what it holds is the OpenClaw config shape (agents.entries, and the replacePaths requirement on config.patch) that Clawboo writes against.
A global npm install can hit EACCES. The stream emits an error event with code EACCES and a sudo hint. Prefer a Node version manager (nvm/fnm) or Homebrew over sudo.

3. Configure

POST /api/system/configure-openclaw with body { provider, apiKey?, model?, gatewayPort? } writes OpenClaw’s openclaw.json and .env, generates a Gateway token, and saves Clawboo’s settings. provider is required; apiKey is required for every provider except the keyless ones — ollama (local) and openai-codex (the ChatGPT subscription; see below). The handler writes a local-mode Gateway config (gateway.mode: 'local', token auth via ${GATEWAY_AUTH_TOKEN}), enables agent-to-agent tooling (tools.agentToAgent.enabled: true, tools.sessions.visibility: 'all'), and resolves a default model from the provider.
The raw token is never returned in the body; it is persisted server-side, and the same-origin proxy injects it on connect.

Or: use your ChatGPT subscription

OpenClaw has a built-in openai-codex provider that runs on a ChatGPT subscription (the same backend the Codex runtime uses) — no API key anywhere. Its credential is OpenClaw’s OWN OAuth profile, created by running OpenClaw’s login in your terminal (Clawboo never automates the exchange):
Use models auth login, never openclaw onboard — the onboard wizard can reset channels, memory, and cron unless you carefully choose “Use existing values”. The login command is non-destructive: it only adds the auth profile.
Clawboo surfaces this in two places: the onboarding wizard’s OpenAI card offers Sign in with ChatGPT (Recommended — the economical path), and once Codex is connected the OpenClaw setup flow offers a one-click Sign in with ChatGPT of its own. The one-click button spawns OpenClaw’s login locally and relays its output; the login runs the same browser flow as codex login (your browser opens, you approve, no code to type, and no dependence on the ChatGPT device-authorization setting). Detection is a presence-only scan of the auth-profile files (an oauth-type openai-codex profile; token values are never read into responses or logs). Once the profile exists, the configure path is fully keyless: the default model is set to openai-codex/gpt-5.5 and no key line touches .env. A codex-CLI login alone is NOT enough — OpenClaw needs its own sign-in (distinct OAuth grants; this also keeps refresh-token lineages separate).
Model refs: Clawboo writes openai-codex/<model>, which is the minimal activation that validates on its own. OpenClaw 2026.8.1 made openai/* the canonical form, and openclaw doctor --fix migrates the ref for you: it rewrites openai-codex/<model> to openai/<model> and enables the openai and codex plugin entries that the canonical ref needs. Both halves matter, which is why Clawboo does not write openai/* directly. A config naming openai/<model> without those plugin entries does not merely warn, it stops the CLI with Unable to resolve Codex doctor health API: install the official Codex plugin with openclaw plugins install @openclaw/codex. Also note the subscription quota is shared: OpenClaw agents, Codex-runtime agents, Hermes subscription runs, and your own codex usage all draw from the same ChatGPT plan allowance, and these turns report no USD cost to Clawboo’s budgets.

4. Start the Gateway

POST /api/system/gateway with body { action } controls the Gateway process. action: "status" and action: "stop" return JSON; action: "start" and action: "restart" are SSE streams that spawn the Gateway detached, poll until the port is reachable (up to 60s), sync the token, and reconnect the server-side OpenClawAgentSource. An unknown action returns 400.

5. Connect (and pair the device)

Once the Gateway is reachable, the SPA connects through /api/gateway/ws. On a fresh OpenClaw 2026.5.x install the first connect fails with NOT_PAIRED; proceed to the next section.

Device pairing (NOT_PAIRED)

OpenClaw 2026.5.x and later dropped auto-pair-on-first-connect. A new device lands in OpenClaw’s pending list, and every connect attempt rejects with a structured error until a human approves it:
The SPA branches on err.code === 'NOT_PAIRED' in three places: the connect screen, the onboarding StartGatewayStep, and the bootstrap auto-reconnect, and swaps in a DevicePairingApproval card. The card’s “Approve this device” button hits POST /api/system/approve-device, which performs a two-step shell-out against the OpenClaw CLI:
  1. openclaw devices approve --latest runs in preview mode: it prints Approve this exact request with: openclaw devices approve <UUID> and exits non-zero. Clawboo regex-extracts the UUID from the captured stdout/stderr.
  2. openclaw devices approve <UUID> performs the actual approval.
On success the SPA auto-retries the original connect with the same URL and token, and the connection completes.
Power users can pair from a terminal instead: openclaw devices approve --latest to see the requestId, then openclaw devices approve <UUID>. The DevicePairingApproval card surfaces this manual fallback too.

What a new Boo is allowed to run

An OpenClaw agent runs commands on your computer, and the Gateway is the only thing that gates them. An agent with no entry in the Gateway’s approvals store resolves to unrestricted: it runs commands without asking. That was the shipped default for every Boo created before this release. Clawboo now applies a posture at the Gateway as part of creating the agent. When the caller supplies no exec config, the posture is Ask for Unknown (on-miss), so the new Boo asks you before running a command it has not been allowed before. A caller that does supply a posture gets that one written to the Gateway too, so a Boo created with Always Ask is under that posture at the Gateway and not only in Clawboo’s own record. One condition rides on this: the Gateway write happens only when the resolved exec config carries an execAsk of off, on-miss, or always. An exec config carrying anything else sends no posture at all, and that Boo is left with whatever the Gateway already resolves for it.
A Gateway that refuses the policy fails the creation. Clawboo deletes the agent it just created upstream and the call throws, with a message naming the permissions write (Could not set this agent's command permissions on the Gateway, so it was not created: …); Create Boo shows that message inline and no agent is left in your fleet. A Boo whose stated posture is not the one in force is worse than no Boo, so creation stops rather than degrading quietly. With the Gateway down you cannot create an OpenClaw Boo at all: POST /api/agents answers 503 { "error": "gateway_disconnected" }. A tab that has no Gateway connection of its own never gets that far: the create dialog stops at Not connected to Gateway without sending the request.
A per-agent entry wins over the fleet-wide default, so the Command Approval dropdown in Settings → System no longer loosens a Boo created this way: that Boo carries its own explicit entry. See System maintenance. Nothing re-asserts a posture afterwards. The two write sites are this one, at creation, and an explicit change in the Boo’s Permissions tab (Execution Permissions → Command Execution). There is no reconciliation on connect, on sync, or on boot, so a posture changed outside Clawboo stays changed.

Seeing what a Boo has already been allowed

Answering Always to a command prompt records a standing permission that outlives the run. The same Permissions tab lists them under Commands this Boo can run without asking, read from OpenClaw’s own stored policy rather than from Clawboo’s records. A row Clawboo can take back carries a revoke control; a row that is also granted to every Boo on the computer, or that is part of another grant, says so instead of offering a button you cannot use. Grants that apply fleet-wide appear only as a count in the footer and are set outside Clawboo. The list is read when the tab opens, on its Refresh button, and after a revoke attempt, so a permission minted by answering Always while the tab is open shows up once you refresh. If that policy cannot be read at all, the panel says so and names how many rules the document last held, rather than showing an empty list over rules that are still being enforced. See Working with agents for the tab, and Approvals for answering a prompt in the first place.

Why OpenClaw can’t be run by the per-task executor

The other four runtimes execute a board task by spawning a one-shot process: claim the task, provision a worktree, run the CLI/SDK, report up. OpenClaw is a connected substrate; its runs ride the live Gateway session over the server’s long-lived connection, so the one-shot executor runner refuses it by construction, before any board mutation:
  • The OpenClaw adapter’s capabilities() declare runtimeClass: 'connected-substrate' (and worktrees: false).
  • resolveRuntimeIntegration(caps) maps that class to home.kind === 'connected'.
  • The executor runner checks this before the atomic claim and returns { ok: false, reason: 'connected_substrate' }, which the REST layer maps to 422. The refusal landing pre-claim is the point: a misrouted run never touches the board.
There is a second wall in front of it: /api/runtimes/:id/run only accepts the four non-OpenClaw runtime ids (isRuntimeId excludes openclaw), so calling it with openclaw returns 404 { "error": "unknown runtime 'openclaw'" }. OpenClaw’s team work flows through its live session and the board orchestration over the Gateway connection, not through the per-task runner.

Global memory scope

When the server-side connection comes up, OpenClawAgentSource registers Clawboo’s shared Memory and Tasks MCP servers into the Gateway’s top-level mcp.servers config (each as { url, transport: 'streamable-http' }), so OpenClaw agents can read and write the one shared team memory. This is idempotent; it reads the current config, skips the patch when both servers are already registered with the right URLs (so the per-reconnect re-apply can’t burn the Gateway’s 3-writes-per-60s control-plane budget), and a stale config is re-merged on reconnect. Two scoping facts follow from the Gateway config being process-wide:
  • Memory is registered at GLOBAL scope for OpenClaw. The other four runtimes get a per-run team scope baked into their attach URL; a single static Gateway-config URL can’t carry a per-run team binding, so an OpenClaw agent’s memory facts are team-unscoped. This is an organizational boundary for the local-first single-user model, not a security one. Global scope here is enforced, not assumed. Because the Gateway’s URLs carry no signed scope, an OpenClaw agent’s MCP session is treated as an unidentified HTTP caller: the scopeTeamId / scopeAgentId arguments it passes are ignored, saves land on the global tier and reads see the global tier only. Without that, “global” would have meant “whatever the model asked for”, and any agent could have written a fact tagged as another team and read every team’s facts back.
  • Tasks refuses the tools that name an agent. For the same reason, an OpenClaw session is not served claim_task or assign_task (both require an assigneeAgentId the caller cannot prove), and add_comment drops a caller-supplied authorAgentId and authorType. The anonymous board writes are unaffected, so an OpenClaw agent can still create, re-status, block, unblock and link tasks. Server-orchestrated board runs are unaffected either way: Clawboo claims those directly, without going through the MCP tool.
  • TeamChat is deliberately NOT registered for OpenClaw. A process-wide static URL can’t carry a per-run author binding, and registering the team_chat tool unbound would let an OpenClaw agent post as any author (identity from tool args), breaking the anti-spoof property that the peer-chat room depends on. Instead, an OpenClaw agent’s room participation is fully server-mediated through the team exchange, which posts the agent’s drained turn under the authoritative bound identity.

Verify it worked

  • GET /api/agents/registry/health (always 200) reports the source connection; it should read connected with a recent lastSyncedAt.
  • GET /api/agents should return your OpenClaw agents with stale: false.
  • GET /api/system/status should show gateway.running: true.
  • Send a message in group chat; the send is an HTTP POST /api/teams/:id/chat and the reply streams back over SSE (/api/teams/:id/chat/stream), because team orchestration runs server-side. The same-origin WS proxy carries the 1:1 chat, not the team run. Exec approvals ride both Gateway connections: the browser’s proxied socket and the server’s own connection, so a command waiting on you is still there after you close the tab.

Troubleshooting

Connect rejects with NOT_PAIRED. Expected on OpenClaw 2026.5.x’s first connect. Click “Approve this device” (or run openclaw devices approve --latest then openclaw devices approve <UUID> in a terminal), then retry. See Device pairing.
Agents render but writes 503. The fleet list reads from SQLite, so it survives the Gateway being down; agent-file writes and live sessions need the connection. GET /api/agents will show stale: true and the registry health connection will be disconnected / reconnecting. Start the Gateway (POST /api/system/gateway { "action": "start" }) and the source reconnects.
A down Gateway doesn’t block you if you have other agents. When the browser can’t reach the Gateway on load, a pure-OpenClaw user gets the connect / offline screen. But if you also have Native, Codex, Claude Code, or Hermes agents, Clawboo loads your dashboard anyway and floats a reconnect banner at the top instead of blocking, so you keep working with those agents. The banner’s Reconnect starts the Gateway if needed and brings your OpenClaw agents back online.
POST /api/runtimes/openclaw/... returns 404. OpenClaw is not a /api/runtimes runtime; only claude-code, codex, hermes, and clawboo-native are. Drive OpenClaw through /api/system/* and /api/gateway/ws, not the runtime install/connect/run routes.
Last modified on September 15, 2026