Gateway Protocol
Gateway protocol (WebSocket)
Section titled “Gateway protocol (WebSocket)”The Gateway WS protocol is the single control plane + node transport for OpenClaw. All clients (CLI, web UI, macOS app, iOS/Android nodes, headless nodes) connect over WebSocket and declare their role + scope at handshake time.
Transport
Section titled “Transport”- WebSocket, text frames with JSON payloads.
- First frame must be a
connectrequest.
Handshake (connect)
Section titled “Handshake (connect)”Gateway → Client (pre-connect challenge):
{ "type": "event", "event": "connect.challenge", "payload": { "nonce": "…", "ts": 1737264000000 }}Client → Gateway:
{ "type": "req", "id": "…", "method": "connect", "params": { "minProtocol": 3, "maxProtocol": 3, "client": { "id": "cli", "version": "1.2.3", "platform": "macos", "mode": "operator" }, "role": "operator", "scopes": ["operator.read", "operator.write"], "caps": [], "commands": [], "permissions": {}, "auth": { "token": "…" }, "locale": "en-US", "userAgent": "openclaw-cli/1.2.3", "device": { "id": "device_fingerprint", "publicKey": "…", "signature": "…", "signedAt": 1737264000000, "nonce": "…" } }}Gateway → Client:
{ "type": "res", "id": "…", "ok": true, "payload": { "type": "hello-ok", "protocol": 3, "policy": { "tickIntervalMs": 15000 } }}When a device token is issued, hello-ok also includes:
{ "auth": { "deviceToken": "…", "role": "operator", "scopes": ["operator.read", "operator.write"] }}Node example
Section titled “Node example”{ "type": "req", "id": "…", "method": "connect", "params": { "minProtocol": 3, "maxProtocol": 3, "client": { "id": "ios-node", "version": "1.2.3", "platform": "ios", "mode": "node" }, "role": "node", "scopes": [], "caps": ["camera", "canvas", "screen", "location", "voice"], "commands": ["camera.snap", "canvas.navigate", "screen.record", "location.get"], "permissions": { "camera.capture": true, "screen.record": false }, "auth": { "token": "…" }, "locale": "en-US", "userAgent": "openclaw-ios/1.2.3", "device": { "id": "device_fingerprint", "publicKey": "…", "signature": "…", "signedAt": 1737264000000, "nonce": "…" } }}Framing
Section titled “Framing”- Request:
{type:"req", id, method, params} - Response:
{type:"res", id, ok, payload|error} - Event:
{type:"event", event, payload, seq?, stateVersion?}
Side-effecting methods require idempotency keys (see schema).
Roles + scopes
Section titled “Roles + scopes”operator= control plane client (CLI/UI/automation).node= capability host (camera/screen/canvas/system.run).
Scopes (operator)
Section titled “Scopes (operator)”Common scopes:
operator.readoperator.writeoperator.adminoperator.approvalsoperator.pairing
Caps/commands/permissions (node)
Section titled “Caps/commands/permissions (node)”Nodes declare capability claims at connect time:
caps: high-level capability categories.commands: command allowlist for invoke.permissions: granular toggles (e.g.screen.record,camera.capture).
The Gateway treats these as claims and enforces server-side allowlists.
Presence
Section titled “Presence”system-presencereturns entries keyed by device identity.- Presence entries include
deviceId,roles, andscopesso UIs can show a single row per device even when it connects as both operator and node.
Node helper methods
Section titled “Node helper methods”- Nodes may call
skills.binsto fetch the current list of skill executables for auto-allow checks.
Exec approvals
Section titled “Exec approvals”- When an exec request needs approval, the gateway broadcasts
exec.approval.requested. - Operator clients resolve by calling
exec.approval.resolve(requiresoperator.approvalsscope).
Versioning
Section titled “Versioning”PROTOCOL_VERSIONlives insrc/gateway/protocol/schema.ts.- Clients send
minProtocol+maxProtocol; the server rejects mismatches. - Schemas + models are generated from TypeBox definitions:
pnpm protocol:genpnpm protocol:gen:swiftpnpm protocol:check
- If
OPENCLAW_GATEWAY_TOKEN(or--token) is set,connect.params.auth.tokenmust match or the socket is closed. - After pairing, the Gateway issues a device token scoped to the connection role + scopes. It is returned in
hello-ok.auth.deviceTokenand should be persisted by the client for future connects. - Device tokens can be rotated/revoked via
device.token.rotateanddevice.token.revoke(requiresoperator.pairingscope).
Device identity + pairing
Section titled “Device identity + pairing”- Nodes should include a stable device identity (
device.id) derived from a keypair fingerprint. - Gateways issue tokens per device + role.
- Pairing approvals are required for new device IDs unless local auto-approval is enabled.
- Local connects include loopback and the gateway host’s own tailnet address (so same‑host tailnet binds can still auto‑approve).
- All WS clients must include
deviceidentity duringconnect(operator + node). Control UI can omit it only whengateway.controlUi.allowInsecureAuthis enabled (orgateway.controlUi.dangerouslyDisableDeviceAuthfor break-glass use). - Non-local connections must sign the server-provided
connect.challengenonce.
TLS + pinning
Section titled “TLS + pinning”- TLS is supported for WS connections.
- Clients may optionally pin the gateway cert fingerprint (see
gateway.tlsconfig plusgateway.remote.tlsFingerprintor CLI--tls-fingerprint).
This protocol exposes the full gateway API (status, channels, models, chat, agent, sessions, nodes, approvals, etc.). The exact surface is defined by the TypeBox schemas in src/gateway/protocol/schema.ts.