Gateway Runbook (Operations Guide)
Gateway service, lifecycle, and operations
Last updated: 2025-12-09
What is this
- 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)
openclaw gateway --port 18789 openclaw gateway --port 18789 --verbose openclaw gateway --force pnpm gateway:watch
- 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". - 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。 - Toolscall(HTTP):
/tools/invoke。 - 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:
ssh -N -L 18789:127.0.0.1:18789 user@host
- 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>)
Install metadata is embedded in the service config:
OPENCLAW_SERVICE_MARKER=openclawOPENCLAW_SERVICE_KIND=gatewayOPENCLAW_SERVICE_VERSION=<version>
Rescue-Bot Pattern: keep a second Gateway isolated with its own profile, state dir, workspace, and base port spacing. Full guide: Rescue-bot guide.
Dev profile (`--dev`)
Fast path: run a fully-isolated dev instance (config/state/workspace) without touching your primary setup.
openclaw --dev setup openclaw --dev gateway --allow-unconfigured openclaw --dev status openclaw --dev health
Defaults (can be overridden via env/flags/config):
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)--devwithsetup/onboardmakesagents.defaults.workspacedefault to~/.openclaw/workspace-dev.
Derived ports (rules of thumb):
- 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).
Checklist per instance:
- unique
gateway.port - unique
OPENCLAW_CONFIG_PATH - unique
OPENCLAW_STATE_DIR - unique
agents.defaults.workspace - separate WhatsApp numbers (if using WA)
Service install per profile:
openclaw --profile main gateway install openclaw --profile rescue gateway install
Example:
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json OPENCLAW_STATE_DIR=~/.openclaw-a openclaw gateway --port 19001 OPENCLAW_CONFIG_PATH=~/.openclaw/b.json OPENCLAW_STATE_DIR=~/.openclaw-b openclaw gateway --port 19002
Protocol (operator view)
Full docs: Gateway protocol and Bridge protocol (legacy).
- Mandatory first frame from client: <code>req {type:"req", id, method:"connect", params:{minProtocol,maxProtocol,client:{id,displayName?,version,platform,deviceFamily?,modelIdentifier?,mode,instanceId?}, caps, auth?, locale?, userAgent? } }'</code>.
- Gateway replies <code>res {type:"res", id, ok:true, payload:hello-ok }'</code> (or <code>ok:false</code> with an error, then closes).
- After handshake:
- Requests: <code>'{type:"req", id, method, params}'</code> → <code>'{type:"res", id, ok, payload|error}'</code>
- event:<code>'{type:"event", event, payload, seq?, stateVersion?}'</code>
- Structured presence entries: <code>'{host, ip, version, platform?, deviceFamily?, modelIdentifier?, mode, lastInputSeconds?, ts, reason?, tags?[], instanceId? }'</code> (for WS clients, <code>instanceId</code> comes from <code>connect.client.instanceId</code>).
- <code>agent</code> responses are two-stage: first <code>res</code> ack <code>'{runId,status:"accepted"}'</code>, then a final <code>res</code> <code>'{runId,status:"ok"|"error",summary}'</code> after the run finishes; streamed output arrives as <code>event:"agent"</code>.
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).
See also: Presence for how presence is produced/deduped and why a stable 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
- <code>hello-ok</code> includes a <code>snapshot</code> with <code>presence</code>, <code>health</code>, <code>stateVersion</code>, and <code>uptimeMs</code> plus <code>policy {maxPayload,maxBufferedBytes,tickIntervalMs}'</code> 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>'{ code, message, details?, retryable?, retryAfterMs? }'</code>.
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 - 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:
openclaw gateway status openclaw gateway install openclaw gateway stop openclaw gateway restart openclaw logs --follow
Notes:
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).- <code>gateway status</code> prints config path + probe target to avoid "localhost vs LAN bind" confusion and profile mismatches.
- <code>gateway status</code> includes 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). gateway installis a no-op when already installed; useopenclaw gateway install --forceto reinstall (profile/env/path changes).
Bundled mac app:
- 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:
[Unit] Description=OpenClaw Gateway (profile: <profile>, v<version>) After=network-online.target Wants=network-online.target [Service] ExecStart=/usr/local/bin/openclaw gateway --port 18789 Restart=always RestartSec=5 Environment=OPENCLAW_GATEWAY_TOKEN= WorkingDirectory=/home/youruser [Install] WantedBy=default.target
Enable lingering (required so the user service survives logout/idle):
sudo loginctl enable-linger youruser
Onboarding runs this on Linux/WSL2 (may prompt for sudo; writes /var/lib/systemd/linger). Then enable the service:
<strong>Alternative (system service)</strong> - for always-on or multi-user servers, you can install a systemd <strong>system</strong> unit instead of a user unit (no lingering needed).
systemctl --user enable --now openclaw-gateway[-<profile>].service
Alternative (system service): For always-on/multi-user servers, use systemd's system unit installation (no linger required).
Create /etc/systemd/system/openclaw-gateway[-<profile>].service (copy the unit above, change WantedBy=multi-user.target, set User= + WorkingDirectory=) and run:
sudo systemctl daemon-reload sudo systemctl enable --now openclaw-gateway[-<profile>].service
Windows(WSL2)
On Windows, use WSL2 and follow the Linux systemd section above.
Operational checks
- Liveness: open WS and expect
req:connect→payload.type="hello-ok"(with snapshot)res. - Readiness: expect
health→ok: trueandlinkChannelhas linked channels (if applicable). - Debug: subscribe to
tick/presence, checkstatuslink/auth age, and confirm presence gateway host / connected clients.
Safety guarantees
- Default assumes one Gateway per host. If running multiple profiles, isolate port/state and specify the correct instance.
- No direct fallback to Baileys connection. If the Gateway is down, sends fail immediately.
- Rejects unconnected first frames and invalid JSON, closing the socket.
- Graceful shutdown: sends
shutdownbefore close. Clients handle close + reconnect.
CLI helpers
openclaw gateway health|status— Get health/status via Gateway WS.openclaw message send --target <num> --message "hi" [--media ...]— Send via Gateway (WhatsApp is idempotent).openclaw agent --message "hi" --to <num>— Run an agent turn (waits for final by default).openclaw gateway call <method> --params {"k":"v"}— Raw method call for debugging.openclaw gateway stop|restart— Stop/restart the supervised Gateway service (launchd/systemd).- Gateway helpers assume
--urlis running; they do not auto-start.
Migration guidance
- Stop using the old
openclaw gatewayand old TCP control port. - Update clients to required connect and structured presence with its own WS protocol.