Gateway-Protokoll (WebSocket)

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

Transport

  • WebSocket, Text-Frames mit JSON-Payloads.
  • Der erste Frame muss eine connect-Anfrage 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?}

Seiteneffekt-behaftete Methoden erfordern Idempotenz-Keys (siehe Schema).

Rollen + Scopes

Rollen

  • operator = Control-Plane-Client (CLI/UI/Automatisierung).
  • node = Capability-Host (Kamera/Bildschirm/Canvas/system.run).

Scopes (Operator)

Gängige Scopes:

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

Der Methoden-Scope ist nur die erste Hürde. Einige Slash-Befehle, die über chat.send erreichbar sind, wenden darüber hinaus strengere Befehlsebene-Prüfungen an. Zum Beispiel erfordern persistente /config set- und /config unset-Schreibvorgänge operator.admin.

Caps/Commands/Permissions (Node)

Nodes deklarieren beim Connect ihre Capability-Ansprüche:

  • caps: übergeordnete Capability-Kategorien.
  • commands: Befehls-Allowlist für Aufrufe.
  • permissions: granulare Toggles (z. B. screen.record, camera.capture).

Das Gateway behandelt diese als Ansprüche und erzwingt serverseitige Allowlists.

Presence

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

Node-Hilfsmethoden

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

Operator-Hilfsmethoden

  • Operators können tools.catalog (operator.read) aufrufen, um den Runtime-Tool-Katalog für einen Agent abzurufen. Die Antwort enthält gruppierte Tools und Provenance-Metadaten:
    • source: core oder plugin
    • pluginId: Plugin-Besitzer wenn source="plugin"
    • optional: ob ein Plugin-Tool optional ist

Exec-Genehmigungen

  • Wenn eine Exec-Anfrage eine Genehmigung braucht, sendet das Gateway exec.approval.requested.
  • Operator-Clients lösen dies durch Aufruf von exec.approval.resolve (erfordert operator.approvals-Scope).
  • Für host=node muss exec.approval.request einen systemRunPlan enthalten (kanonisches argv/cwd/rawCommand/Session-Metadaten). Anfragen ohne systemRunPlan werden abgelehnt.

Versionierung

  • PROTOCOL_VERSION befindet sich in src/gateway/protocol/schema.ts.
  • Clients senden minProtocol + maxProtocol; der Server lehnt Inkompatibilitäten ab.
  • Schemas + Modelle 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 der Kopplung stellt das Gateway ein Device-Token aus, das auf die Verbindungsrolle
    • Scopes beschränkt ist. Es wird in hello-ok.auth.deviceToken zurückgegeben und sollte vom Client für zukünftige Verbindungen persistiert werden.
  • Device-Tokens können über device.token.rotate und device.token.revoke rotiert/widerrufen werden (erfordert operator.pairing-Scope).
  • Auth-Fehler enthalten error.details.code plus Recovery-Hinweise:
    • error.details.canRetryWithDeviceToken (Boolean)
    • error.details.recommendedNextStep (retry_with_device_token, update_auth_configuration, update_auth_credentials, wait_then_retry, review_auth_configuration)
  • Client-Verhalten bei AUTH_TOKEN_MISMATCH:
    • Vertrauenswürdige Clients können einen begrenzten Retry mit einem gecachten Per-Device-Token versuchen.
    • Wenn dieser Retry fehlschlägt, sollten Clients automatische Reconnect-Schleifen stoppen und Operator-Handlungshinweise anzeigen.

Device-Identität + Kopplung

  • Nodes sollten eine stabile Device-Identität (device.id) mitliefern, die aus einem Keypair-Fingerprint abgeleitet wird.
  • Gateways stellen Tokens pro Gerät + Rolle aus.
  • Kopplungsgenehmigungen sind für neue Device-IDs erforderlich, es sei denn, die lokale Auto-Genehmigung ist aktiviert.
  • Lokale Verbindungen umfassen Loopback und die eigene Tailnet-Adresse des Gateway-Hosts (damit Same-Host-Tailnet-Bindungen trotzdem auto-genehmigt werden können).
  • Alle WS-Clients müssen während connect eine device-Identität mitsenden (Operator + Node). Die Control-UI kann sie nur in diesen Modi weglassen:
    • gateway.controlUi.allowInsecureAuth=true für Localhost-only unsichere HTTP-Kompatibilität.
    • gateway.controlUi.dangerouslyDisableDeviceAuth=true (Break-Glass, schwerwiegende Sicherheitsherabstufung).
  • Alle Verbindungen müssen die vom Server bereitgestellte connect.challenge-Nonce signieren.

Diagnose zur Device-Auth-Migration

Für Legacy-Clients, die noch das Verhalten vor der Challenge-Signierung verwenden, gibt connect jetzt DEVICE_AUTH_*-Detailcodes unter error.details.code mit einem stabilen error.details.reason zurück.

Häufige Migrationsfehler:

Nachrichtdetails.codedetails.reasonBedeutung
device nonce requiredDEVICE_AUTH_NONCE_REQUIREDdevice-nonce-missingClient hat device.nonce ausgelassen (oder leer gesendet)
device nonce mismatchDEVICE_AUTH_NONCE_MISMATCHdevice-nonce-mismatchClient hat mit veralteter/falscher Nonce signiert
device signature invalidDEVICE_AUTH_SIGNATURE_INVALIDdevice-signatureSignatur-Payload stimmt nicht mit v2-Payload überein
device signature expiredDEVICE_AUTH_SIGNATURE_EXPIREDdevice-signature-staleSignierter Zeitstempel liegt außerhalb des erlaubten Skew
device identity mismatchDEVICE_AUTH_DEVICE_ID_MISMATCHdevice-id-mismatchdevice.id stimmt nicht mit dem Public-Key-Fingerprint überein
device public key invalidDEVICE_AUTH_PUBLIC_KEY_INVALIDdevice-public-keyPublic-Key-Format/Kanonisierung fehlgeschlagen

Migrationsziel:

  • Immer auf connect.challenge warten.
  • Den v2-Payload signieren, der die Server-Nonce enthält.
  • Dieselbe Nonce in connect.params.device.nonce senden.
  • Bevorzugter Signatur-Payload ist v3, der zusätzlich platform und deviceFamily neben device/client/role/scopes/token/nonce-Feldern bindet.
  • Legacy-v2-Signaturen werden weiterhin aus Kompatibilitätsgründen akzeptiert, aber Paired-Device-Metadaten-Pinning steuert weiterhin die Command-Policy bei Reconnects.

TLS + Pinning

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

Scope

Dieses Protokoll legt die vollständige Gateway-API offen (Status, Channels, Modelle, Chat, Agent, Sessions, Nodes, Genehmigungen usw.). Die genaue Oberfläche wird durch die TypeBox-Schemas in src/gateway/protocol/schema.ts definiert.