Gateway-Protokoll (WebSocket)

Das Gateway WS-Protokoll ist die zentrale Control Plane + Node-Transport für OpenClaw. Alle Clients (CLI, Web UI, macOS App, iOS/Android Nodes, Headless Nodes) verbinden sich per WebSocket und deklarieren ihre Role + Scope beim Handshake.

Transport

  • WebSocket, Text-Frames mit JSON-Payloads.
  • Der erste Frame muss ein connect-Request sein.

Handshake (connect)

Gateway an Client (Pre-Connect Challenge):

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

Client an 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 an Client:

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

Wenn ein Device Token ausgestellt wird, enthält hello-ok zusätzlich:

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

Node-Beispiel

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

  • Request: {type:"req", id, method, params}
  • Response: {type:"res", id, ok, payload|error}
  • Event: {type:"event", event, payload, seq?, stateVersion?}

Methoden mit Seiteneffekten erfordern Idempotency Keys (siehe Schema).

Roles + Scopes

Roles

  • operator = Control Plane Client (CLI/UI/Automation).
  • node = Capability Host (camera/screen/canvas/system.run).

Scopes (Operator)

Gängige Scopes:

  • operator.read
  • operator.write
  • operator.admin
  • operator.approvals
  • operator.pairing

Caps/Commands/Permissions (Node)

Nodes deklarieren ihre Capability Claims beim Connect:

  • caps: High-Level Capability-Kategorien.
  • commands: Command-Allowlist für Invoke.
  • permissions: Granulare Toggles (z.B. screen.record, camera.capture).

Das Gateway behandelt diese als Claims und erzwingt serverseitige Allowlists.

Presence

  • system-presence gibt Einträge zurück, die nach Device Identity geordnet sind.
  • Presence-Einträge enthalten deviceId, roles und scopes, sodass UIs eine einzelne Zeile pro Gerät anzeigen können, auch wenn es sich sowohl als Operator als auch als Node verbindet.

Node-Hilfsmethoden

  • Nodes können skills.bins aufrufen, um die aktuelle Liste der Skill-Executables für Auto-Allow-Checks abzurufen.

Exec Approvals

  • Wenn ein Exec-Request eine Genehmigung benötigt, broadcastet das Gateway exec.approval.requested.
  • Operator-Clients lösen dies durch Aufruf von exec.approval.resolve (erfordert operator.approvals Scope).

Versionierung

  • PROTOCOL_VERSION befindet sich in src/gateway/protocol/schema.ts.
  • Clients senden minProtocol + maxProtocol; der Server lehnt Mismatches ab.
  • Schemas + Models werden aus TypeBox-Definitionen generiert:
    • pnpm protocol:gen
    • pnpm protocol:gen:swift
    • pnpm protocol:check

Auth

  • Wenn OPENCLAW_GATEWAY_TOKEN (oder --token) gesetzt ist, muss connect.params.auth.token übereinstimmen, sonst wird der Socket geschlossen.
  • Nach dem Pairing stellt das Gateway ein Device Token aus, das auf die Verbindungs-Role
    • Scopes beschränkt ist. Es wird in hello-ok.auth.deviceToken zurückgegeben und sollte vom Client für zukünftige Connects gespeichert werden.
  • Device Tokens können über device.token.rotate und device.token.revoke rotiert/widerrufen werden (erfordert operator.pairing Scope).

Device Identity + Pairing

  • Nodes sollten eine stabile Device Identity (device.id) mitliefern, die von einem Keypair-Fingerprint abgeleitet ist.
  • Gateways stellen Tokens pro Device + Role aus.
  • Pairing-Genehmigungen sind für neue Device IDs erforderlich, es sei denn, lokale Auto-Approval ist aktiviert.
  • Lokale Verbindungen umfassen Loopback und die eigene Tailnet-Adresse des Gateway-Hosts (sodass Same-Host Tailnet-Binds weiterhin auto-approven können).
  • Alle WS-Clients müssen beim connect eine device Identity mitliefern (Operator + Node). Die Control UI kann sie nur weglassen, wenn gateway.controlUi.allowInsecureAuth aktiviert ist (oder gateway.controlUi.dangerouslyDisableDeviceAuth für Notfall-Zugriff).
  • Nicht-lokale Verbindungen müssen die vom Server bereitgestellte connect.challenge Nonce signieren.

TLS + Pinning

  • TLS wird für WS-Verbindungen unterstützt.
  • Clients können optional den Gateway-Cert-Fingerprint pinnen (siehe gateway.tls Config plus gateway.remote.tlsFingerprint oder CLI --tls-fingerprint).

Scope

Dieses Protokoll stellt die vollständige Gateway API bereit (Status, Channels, Models, Chat, Agent, Sessions, Nodes, Approvals, etc.). Die genaue Oberfläche wird durch die TypeBox-Schemas in src/gateway/protocol/schema.ts definiert.