Gateway service runbook
Last updated: 2025-12-09What it is
- The always-on process that owns the single Baileys/Telegram connection and the control/event plane.
-
Replaces the legacy
gatewaycommand. CLI entry point:openclaw gateway. - Runs until stopped; exits non-zero on fatal errors so the supervisor restarts it.
How to run (local)
-
Config hot reload watches
~/.openclaw/openclaw.json(orOPENCLAW_CONFIG_PATH).-
Default mode:
gateway.reload.mode="hybrid"(hot-apply safe changes, restart on critical). - Hot reload uses in-process restart via SIGUSR1 when needed.
- Disable with
gateway.reload.mode="off".
-
Default mode:
- Binds WebSocket control plane to
127.0.0.1:<port>(default 18789). -
The same port also serves HTTP (control UI, hooks, A2UI). Single-port multiplex.
-
OpenAI Chat Completions (HTTP):
/v1/chat/completions. -
OpenResponses (HTTP):
/v1/responses. -
Tools Invoke (HTTP):
/tools/invoke.
-
OpenAI Chat Completions (HTTP):
-
Starts a Canvas file server by default on
canvasHost.port(default18793), servinghttp://<gateway-host>:18793/__openclaw__/canvas/from~/.openclaw/workspace/canvas. Disable withcanvasHost.enabled=falseorOPENCLAW_SKIP_CANVAS_HOST=1. - Logs to stdout; use launchd/systemd to keep it alive and rotate logs.
-
Pass
--verboseto mirror debug logging (handshakes, req/res, events) from the log file into stdio when troubleshooting. -
--forceuseslsofto find listeners on the chosen port, sends SIGTERM, logs what it killed, then starts the gateway (fails fast iflsofis missing). -
If you run under a supervisor (launchd/systemd/mac app child-process mode), a stop/restart
typically sends SIGTERM; older builds may surface this as
pnpmELIFECYCLEexit code 143 (SIGTERM), which is a normal shutdown, not a crash. -
SIGUSR1 triggers an in-process restart when authorized (gateway tool/config
apply/update, or enable
commands.restartfor manual restarts). -
Gateway auth is required by default: set
gateway.auth.token(orOPENCLAW_GATEWAY_TOKEN) orgateway.auth.password. Clients must sendconnect.params.auth.token/passwordunless using Tailscale Serve identity. - The wizard now generates a token by default, even on loopback.
-
Port precedence:
--port>OPENCLAW_GATEWAY_PORT>gateway.port> default18789.
Remote access
-
Tailscale/VPN preferred; otherwise SSH tunnel:
- Clients then connect to
ws://127.0.0.1:18789through the tunnel. -
If a token is configured, clients must include it in
connect.params.auth.tokeneven over the tunnel.
Multiple gateways (same host)
Usually unnecessary: one Gateway can serve multiple messaging channels and agents. Use multiple Gateways only for redundancy or strict isolation (ex: rescue bot). Supported if you isolate state + config and use unique ports. Full guide: Multiple gateways. Service names are profile-aware:-
macOS:
bot.molt.<profile>(legacycom.openclaw.*may still exist) - Linux:
openclaw-gateway-<profile>.service - Windows:
OpenClaw Gateway (<profile>)
OPENCLAW_SERVICE_MARKER=openclawOPENCLAW_SERVICE_KIND=gatewayOPENCLAW_SERVICE_VERSION=<version>
Dev profile (--dev)
Fast path: run a fully-isolated dev instance (config/state/workspace) without
touching your
primary setup.
OPENCLAW_STATE_DIR=~/.openclaw-devOPENCLAW_CONFIG_PATH=~/.openclaw-dev/openclaw.jsonOPENCLAW_GATEWAY_PORT=19001(Gateway WS + HTTP)-
browser control service port =
19003(derived:gateway.port+2, loopback only) canvasHost.port=19005(derived:gateway.port+4)-
agents.defaults.workspacedefault becomes~/.openclaw/workspace-devwhen you runsetup/onboardunder--dev.
-
Base port =
gateway.port(orOPENCLAW_GATEWAY_PORT/--port) - browser control service port = base + 2 (loopback only)
-
canvasHost.port = base + 4(orOPENCLAW_CANVAS_HOST_PORT/ config override) -
Browser profile CDP ports auto-allocate from
browser.controlPort + 9 .. + 108(persisted per profile).
- unique
gateway.port - unique
OPENCLAW_CONFIG_PATH - unique
OPENCLAW_STATE_DIR - unique
agents.defaults.workspace - separate WhatsApp numbers (if using WA)
Protocol (operator view)
- Full docs: Gateway protocol and Bridge protocol (legacy).
-
Mandatory first frame from client:
req {type:"req", id, method:"connect", params:{minProtocol,maxProtocol,client:{id,displayName?,version,platform,deviceFamily?,modelIdentifier?,mode,instanceId?}, caps, auth?, locale?, userAgent? } }. -
Gateway replies
res {type:"res", id, ok:true, payload:hello-ok }(orok:falsewith an error, then closes). -
After handshake:
-
Requests:
{type:"req", id, method, params}→{type:"res", id, ok, payload|error} - Events:
{type:"event", event, payload, seq?, stateVersion?}
-
Requests:
-
Structured presence entries:
{host, ip, version, platform?, deviceFamily?, modelIdentifier?, mode, lastInputSeconds?, ts, reason?, tags?[], instanceId? }(for WS clients,instanceIdcomes fromconnect.client.instanceId). -
agentresponses are two-stage: firstresack{runId,status:"accepted"}, then a finalres{runId,status:"ok"|"error",summary}after the run finishes; streamed output arrives asevent:"agent".
Methods (initial set)
-
health— full health snapshot (same shape asopenclaw health --json). status— short summary.system-presence— current presence list.system-event— post a presence/system note (structured).send— send a message via the active channel(s).agent— run an agent turn (streams events back on same connection).-
node.list— list paired + currently-connected nodes (includescaps,deviceFamily,modelIdentifier,paired,connected, and advertisedcommands). -
node.describe— describe a node (capabilities + supportednode.invokecommands; works for paired nodes and for currently-connected unpaired nodes). -
node.invoke— invoke a command on a node (e.g.canvas.*,camera.*). -
node.pair.*— pairing lifecycle (request,list,approve,reject,verify).
client.instanceId matters.
Events
agent— streamed tool/output events from the agent run (seq-tagged).-
presence— presence updates (deltas with stateVersion) pushed to all connected clients. tick— periodic keepalive/no-op to confirm liveness.-
shutdown— Gateway is exiting; payload includesreasonand optionalrestartExpectedMs. Clients should reconnect.
WebChat integration
- WebChat is a native SwiftUI UI that talks directly to the Gateway WebSocket for history, sends, abort, and events.
-
Remote use goes through the same SSH/Tailscale tunnel; if a gateway token is configured, the
client includes it during
connect. -
macOS app connects via a single WS (shared connection); it hydrates presence from the initial
snapshot and listens for
presenceevents to update the UI.
Typing and validation
- Server validates every inbound frame with AJV against JSON Schema emitted from the protocol definitions.
- Clients (TS/Swift) consume generated types (TS directly; Swift via the repo’s generator).
-
Protocol definitions are the source of truth; regenerate schema/models with:
pnpm protocol:genpnpm protocol:gen:swift
Connection snapshot
-
hello-okincludes asnapshotwithpresence,health,stateVersion, anduptimeMspluspolicy {maxPayload,maxBufferedBytes,tickIntervalMs}so clients can render immediately without extra requests. -
health/system-presenceremain available for manual refresh, but are not required at connect time.
Error codes (res.error shape)
- Errors use
{ code, message, details?, retryable?, retryAfterMs? }. -
Standard codes:
NOT_LINKED— WhatsApp not authenticated.AGENT_TIMEOUT— agent did not respond within the configured deadline.INVALID_REQUEST— schema/param validation failed.UNAVAILABLE— Gateway is shutting down or a dependency is unavailable.
Keepalive behavior
-
tickevents (or WS ping/pong) are emitted periodically so clients know the Gateway is alive even when no traffic occurs. - Send/agent acknowledgements remain separate responses; do not overload ticks for sends.
Replay / gaps
-
Events are not replayed. Clients detect seq gaps and should refresh (
health+system-presence) before continuing. WebChat and macOS clients now auto-refresh on gap.
Supervision (macOS example)
-
Use launchd to keep the service alive:
- Program: path to
openclaw - Arguments:
gateway - KeepAlive: true
- StandardOut/Err: file paths or
syslog
- Program: path to
- On failure, launchd restarts; fatal misconfig should keep exiting so the operator notices.
-
LaunchAgents are per-user and require a logged-in session; for headless setups use a custom
LaunchDaemon (not shipped).
-
openclaw gateway installwrites~/Library/LaunchAgents/bot.molt.gateway.plist(orbot.molt.<profile>.plist; legacycom.openclaw.*is cleaned up). -
openclaw doctoraudits the LaunchAgent config and can update it to current defaults.
-
Gateway service management (CLI)
Use the Gateway CLI for install/start/stop/restart/status:-
gateway statusprobes the Gateway RPC by default using the service’s resolved port/config (override with--url). gateway status --deepadds system-level scans (LaunchDaemons/system units).-
gateway status --no-probeskips the RPC probe (useful when networking is down). gateway status --jsonis stable for scripts.-
gateway statusreports supervisor runtime (launchd/systemd running) separately from RPC reachability (WS connect + status RPC). -
gateway statusprints config path + probe target to avoid “localhost vs LAN bind” confusion and profile mismatches. -
gateway statusincludes the last gateway error line when the service looks running but the port is closed. -
logstails the Gateway file log via RPC (no manualtail/grepneeded). -
If other gateway-like services are detected, the CLI warns unless they are OpenClaw profile
services. We still recommend one gateway per machine for most setups; use
isolated profiles/ports for redundancy or a rescue bot. See
Multiple gateways.
-
Cleanup:
openclaw gateway uninstall(current service) andopenclaw doctor(legacy migrations).
-
Cleanup:
-
gateway installis a no-op when already installed; useopenclaw gateway install --forceto reinstall (profile/env/path changes).
-
OpenClaw.app can bundle a Node-based gateway relay and install a per-user LaunchAgent labeled
bot.molt.gateway(orbot.molt.<profile>; legacycom.openclaw.*labels still unload cleanly). -
To stop it cleanly, use
openclaw gateway stop(orlaunchctl bootout gui/$UID/bot.molt.gateway). -
To restart, use
openclaw gateway restart(orlaunchctl kickstart -k gui/$UID/bot.molt.gateway).-
launchctlonly works if the LaunchAgent is installed; otherwise useopenclaw gateway installfirst. -
Replace the label with
bot.molt.<profile>when running a named profile.
-
Supervision (systemd user unit)
OpenClaw installs a systemd user service by default on Linux/WSL2. We recommend user services for single-user machines (simpler env, per-user config). Use a system service for multi-user or always-on servers (no lingering required, shared supervision).openclaw gateway install writes the user unit.
openclaw doctor audits
the unit and can update it to match the current recommended defaults.
Create
~/.config/systemd/user/openclaw-gateway[-<profile>].service:
/var/lib/systemd/linger). Then enable the service:
/etc/systemd/system/openclaw-gateway[-<profile>].service (copy the unit
above, switch WantedBy=multi-user.target, set User= +
WorkingDirectory=), then:
Windows (WSL2)
Windows installs should use WSL2 and follow the Linux systemd section above.Operational checks
-
Liveness: open WS and send
req:connect→ expectreswithpayload.type="hello-ok"(with snapshot). -
Readiness: call
health→ expectok: trueand a linked channel inlinkChannel(when applicable). -
Debug: subscribe to
tickandpresenceevents; ensurestatusshows linked/auth age; presence entries show Gateway host and connected clients.
Safety guarantees
- Assume one Gateway per host by default; if you run multiple profiles, isolate ports/state and target the right instance.
- No fallback to direct Baileys connections; if the Gateway is down, sends fail fast.
- Non-connect first frames or malformed JSON are rejected and the socket is closed.
-
Graceful shutdown: emit
shutdownevent before closing; clients must handle close + reconnect.
CLI helpers
openclaw gateway health|status— request health/status over the Gateway WS.-
openclaw message send --target <num> --message "hi" [--media ...]— send via Gateway (idempotent for WhatsApp). -
openclaw agent --message "hi" --to <num>— run an agent turn (waits for final by default). -
openclaw gateway call <method> --params '{"k":"v"}'— raw method invoker for debugging. -
openclaw gateway stop|restart— stop/restart the supervised gateway service (launchd/systemd). -
Gateway helper subcommands assume a running gateway on
--url; they no longer auto-spawn one.
Migration guidance
- Retire uses of
openclaw gatewayand the legacy TCP control port. - Update clients to speak the WS protocol with mandatory connect and structured presence.