16 KiB
summary, read_when
| summary | read_when | |
|---|---|---|
| Runbook for the Gateway service, lifecycle, and operations |
|
Gateway service runbook
Last updated: 2025-12-09
What 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:moltbot gateway. - Runs until stopped; exits non-zero on fatal errors so the supervisor restarts it.
How to run (local)
moltbot gateway --port 18789
# for full debug/trace logs in stdio:
moltbot gateway --port 18789 --verbose
# if the port is busy, terminate listeners then start:
moltbot gateway --force
# dev loop (auto-reload on TS changes):
pnpm gateway:watch
- Config hot reload watches
~/.clawdbot/moltbot.json(orCLAWDBOT_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/__moltbot__/canvas/from~/clawd/canvas. Disable withcanvasHost.enabled=falseorCLAWDBOT_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(orCLAWDBOT_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>CLAWDBOT_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:
com.clawdbot.<profile> - Linux:
moltbot-gateway-<profile>.service - Windows:
Moltbot Gateway (<profile>)
Install metadata is embedded in the service config:
CLAWDBOT_SERVICE_MARKER=moltbotCLAWDBOT_SERVICE_KIND=gatewayCLAWDBOT_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.
moltbot --dev setup
moltbot --dev gateway --allow-unconfigured
# then target the dev instance:
moltbot --dev status
moltbot --dev health
Defaults (can be overridden via env/flags/config):
CLAWDBOT_STATE_DIR=~/.clawdbot-devCLAWDBOT_CONFIG_PATH=~/.clawdbot-dev/moltbot.jsonCLAWDBOT_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~/clawd-devwhen you runsetup/onboardunder--dev.
Derived ports (rules of thumb):
- Base port =
gateway.port(orCLAWDBOT_GATEWAY_PORT/--port) - browser control service port = base + 2 (loopback only)
canvasHost.port = base + 4(orCLAWDBOT_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
CLAWDBOT_CONFIG_PATH - unique
CLAWDBOT_STATE_DIR - unique
agents.defaults.workspace - separate WhatsApp numbers (if using WA)
Service install per profile:
moltbot --profile main gateway install
moltbot --profile rescue gateway install
Example:
CLAWDBOT_CONFIG_PATH=~/.clawdbot/a.json CLAWDBOT_STATE_DIR=~/.clawdbot-a moltbot gateway --port 19001
CLAWDBOT_CONFIG_PATH=~/.clawdbot/b.json CLAWDBOT_STATE_DIR=~/.clawdbot-b moltbot gateway --port 19002
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 asmoltbot 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
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
moltbot - 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).
moltbot gateway installwrites~/Library/LaunchAgents/com.clawdbot.gateway.plist(orcom.clawdbot.<profile>.plist).moltbot 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:
moltbot gateway status
moltbot gateway install
moltbot gateway stop
moltbot gateway restart
moltbot 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).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 Moltbot 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:
moltbot gateway uninstall(current service) andmoltbot doctor(legacy migrations).
- Cleanup:
gateway installis a no-op when already installed; usemoltbot gateway install --forceto reinstall (profile/env/path changes).
Bundled mac app:
- Moltbot.app can bundle a Node-based gateway relay and install a per-user LaunchAgent labeled
com.clawdbot.gateway(orcom.clawdbot.<profile>). - To stop it cleanly, use
moltbot gateway stop(orlaunchctl bootout gui/$UID/com.clawdbot.gateway). - To restart, use
moltbot gateway restart(orlaunchctl kickstart -k gui/$UID/com.clawdbot.gateway).launchctlonly works if the LaunchAgent is installed; otherwise usemoltbot gateway installfirst.- Replace the label with
com.clawdbot.<profile>when running a named profile.
Supervision (systemd user unit)
Moltbot 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).
moltbot gateway install writes the user unit. moltbot doctor audits the
unit and can update it to match the current recommended defaults.
Create ~/.config/systemd/user/moltbot-gateway[-<profile>].service:
[Unit]
Description=Moltbot Gateway (profile: <profile>, v<version>)
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/moltbot gateway --port 18789
Restart=always
RestartSec=5
Environment=CLAWDBOT_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:
systemctl --user enable --now moltbot-gateway[-<profile>].service
Alternative (system service) - for always-on or multi-user servers, you can
install a systemd system unit instead of a user unit (no lingering needed).
Create /etc/systemd/system/moltbot-gateway[-<profile>].service (copy the unit above,
switch WantedBy=multi-user.target, set User= + WorkingDirectory=), then:
sudo systemctl daemon-reload
sudo systemctl enable --now moltbot-gateway[-<profile>].service
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
moltbot gateway health|status— request health/status over the Gateway WS.moltbot message send --target <num> --message "hi" [--media ...]— send via Gateway (idempotent for WhatsApp).moltbot agent --message "hi" --to <num>— run an agent turn (waits for final by default).moltbot gateway call <method> --params '{"k":"v"}'— raw method invoker for debugging.moltbot 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
moltbot gatewayand the legacy TCP control port. - Update clients to speak the WS protocol with mandatory connect and structured presence.