OpenClawSkills
GitHub
Gateway / Operations β€’ TutorialHeader.readTime

Gateway Protocol

Gateway WebSocket protocol: handshake, frames, versioning

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.

Tutorial.step

Transport

- WebSocket, text frames with JSON payloads.

- First frame ''must'' be a ''connect'' request.

Tutorial.step

Handshake (connect)

Gateway β†’ Client (pre-connect challenge):

Json
{
  "type": "event",
  "event": "connect.challenge",
  "payload": { "nonce": "…", "ts": 1737264000000 }
}

Client β†’ Gateway:

Json
{
  "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:

Json
{
  "type": "res",
  "id": "…",
  "ok": true,
  "payload": { "type": "hello-ok", "protocol": 3, "policy": { "tickIntervalMs": 15000 } }
}

When a device token is issued, ''hello-ok'' also includes:

Json
{
  "auth": {
    "deviceToken": "…",
    "role": "operator",
    "scopes": ["operator.read", "operator.write"]
  }
}

#

Tutorial.step

Node example

Json
{
  "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": "…"
    }
  }
}
Tutorial.step

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).

Tutorial.step

Roles + scopes

#

Tutorial.step

Roles

- ''operator'' = control plane client (CLI/UI/automation).

- ''node'' = capability host (camera/screen/canvas/system.run).

#

Tutorial.step

Scopes (operator)

Common scopes:

- ''operator.read''

- ''operator.write''

- ''operator.admin''

- ''operator.approvals''

- ''operator.pairing''

#

Tutorial.step

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.

Tutorial.step

Presence

- ''system-presence'' returns entries keyed by device identity.

- Presence entries include ''deviceId'', ''roles'', and ''scopes'' so UIs can show a single row per device even when it connects as both ''operator'' and ''node''.

#

Tutorial.step

Node helper methods

- Nodes may call ''skills.bins'' to fetch the current list of skill executables for auto-allow checks.

Tutorial.step

Exec approvals

- When an exec request needs approval, the gateway broadcasts ''exec.approval.requested''.

- Operator clients resolve by calling ''exec.approval.resolve'' (requires ''operator.approvals'' scope).

Tutorial.step

Versioning

- ''PROTOCOL_VERSION'' lives in ''src/gateway/protocol/schema.ts''.

- Clients send ''minProtocol'' + ''maxProtocol''; the server rejects mismatches.

- Schemas + models are generated from TypeBox definitions:

- Schemas + models are generated from TypeBox definitions:

- ''pnpm protocol:gen''

- ''pnpm protocol:gen:swift''

Tutorial.step

Auth

- If ''OPENCLAW_GATEWAY_TOKEN'' (or ''--token'') is set, ''connect.params.auth.token'' must 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.deviceToken'' and should be persisted by the client for future connects.

- Device tokens can be rotated/revoked via ''device.token.rotate'' and ''device.token.revoke'' (requires ''operator.pairing'' scope).

Tutorial.step

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 ''device'' identity during ''connect'' (operator + node). Control UI can omit it ''only'' when ''gateway.controlUi.allowInsecureAuth'' is enabled (or ''gateway.controlUi.dangerouslyDisableDeviceAuth'' for break-glass use).

- Non-local connections must sign the server-provided ''connect.challenge'' nonce.

- Non-local connections must sign the server-provided ''connect.challenge'' nonce.

Tutorial.step

TLS + pinning

- TLS is supported for WS connections.

- Clients may optionally pin the gateway cert fingerprint (see ''gateway.tls'' config plus ''gateway.remote.tlsFingerprint'' or CLI ''--tls-fingerprint'').

Tutorial.step

Scope

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''.