Zum Inhalt springen

OpenClaw Gateway-Protokoll: WebSocket-Verbindung aufbauen

Kennst du das Problem? Du arbeitest an einem verteilten System und jeder Client kocht sein eigenes Süppchen, wenn es um die Kommunikation geht. Das Gateway-Protokoll von OpenClaw löst dieses Chaos, indem es alles über einen zentralen Punkt steuert.

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 über WebSocket und geben beim Handshake ihre Role und ihren Scope an.

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

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 } }
}

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

{
"auth": {
"deviceToken": "…",
"role": "operator",
"scopes": ["operator.read", "operator.write"]
}
}
{
"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": "…"
}
}
}
  • Request: {type:"req", id, method, params}
  • Response: {type:"res", id, ok, payload|error}
  • Event: {type:"event", event, payload, seq?, stateVersion?}

Methoden mit Side-Effects benötigen Idempotency Keys (siehe Schema).

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

Häufige Scopes:

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

Der Method-Scope ist nur die erste Hürde. Einige Slash-Commands, die über chat.send erreicht werden, wenden zusätzlich strengere Prüfungen auf Command-Ebene an. Zum Beispiel erfordern persistente Schreibvorgänge via /config set und /config unset den Scope operator.admin.

Nodes deklarieren ihre Capability-Ansprüche beim Verbindungsaufbau:

  • caps: Übergreifende Capability-Kategorien.
  • commands: Allowlist für ausführbare Befehle.
  • permissions: Granulare Schalter (z. B. screen.record, camera.capture).

Das Gateway behandelt diese als Claims und setzt serverseitige Allowlists durch.

  • system-presence gibt Einträge zurück, die nach der Geräte-Identität (Device Identity) sortiert sind.
  • Presence-Einträge enthalten deviceId, roles und scopes. So können UIs eine einzelne Zeile pro Gerät anzeigen, selbst wenn es sich gleichzeitig als Operator und Node verbindet.
  • Nodes können skills.bins aufrufen, um die aktuelle Liste der Skill-Executables für automatische Allow-Checks abzurufen.
  • Operator können tools.catalog (operator.read) aufrufen, um den Runtime-Tool-Katalog für einen Agent abzurufen. Die Antwort enthält gruppierte Tools und Provenienz-Metadaten:
    • source: core oder plugin
    • pluginId: Plugin-Besitzer, wenn source="plugin"
    • optional: Gibt an, ob ein Plugin-Tool optional ist
  • Operator können tools.effective (operator.read) aufrufen, um das für eine Session aktuell gültige Tool-Inventar abzurufen.
    • sessionKey ist erforderlich.
    • Das Gateway leitet den vertrauenswürdigen Runtime-Kontext serverseitig aus der Session ab, anstatt vom Aufrufer bereitgestellte Auth- oder Delivery-Kontexte zu akzeptieren.
    • Die Antwort ist auf die Session beschränkt und spiegelt wider, was die aktive Konversation gerade nutzen kann (einschließlich Core-, Plugin-, Channel- sowie Session-Tools).
  • Wenn ein Exec-Request eine Freigabe benötigt, sendet das Gateway ein exec.approval.requested per Broadcast.
  • Operator-Clients lösen dies durch den Aufruf von exec.approval.resolve (erfordert operator.approvals Scope).
  • Für host=node muss exec.approval.request den systemRunPlan enthalten (kanonische Daten wie argv/cwd/rawCommand und Session-Metadaten). Anfragen ohne systemRunPlan werden abgelehnt.
  • agent-Requests können deliver=true enthalten, um eine ausgehende Zustellung anzufordern.
  • bestEffortDeliver=false erzwingt striktes Verhalten: Nicht auflösbare oder rein interne Delivery-Ziele geben INVALID_REQUEST zurück.
  • bestEffortDeliver=true erlaubt einen Fallback auf die reine Session-Ausführung, wenn keine externe Route aufgelöst werden kann (zum Beispiel bei internen Webchat-Sessions oder mehrdeutigen Multi-Channel-Konfigurationen).
  • Die PROTOCOL_VERSION ist in src/gateway/protocol/schema.ts definiert.
  • Clients senden minProtocol und maxProtocol; der Server lehnt Abweichungen ab.
  • Schemas und Modelle werden aus TypeBox-Definitionen generiert:
    • pnpm protocol:gen
    • pnpm protocol:gen:swift
    • pnpm protocol:check
    • Sowie die zugehörige Validierung.
  • 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 Rolle und die Scopes der Verbindung begrenzt ist. Es wird in hello-ok.auth.deviceToken zurückgegeben und sollte vom Client für zukünftige Verbindungen gespeichert werden.
  • Device-Token können über device.token.rotate und device.token.revoke rotiert oder widerrufen werden (erfordert operator.pairing Scope).
  • Auth-Fehler enthalten einen error.details.code sowie Hinweise zur Behebung:
    • 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-Versuch mit einem zwischengespeicherten Device-Token unternehmen.
    • Schlägt dieser Versuch fehl, sollten Clients automatische Reconnect-Schleifen stoppen und dem Operator Handlungsanweisungen anzeigen.
  • Nodes sollten eine stabile Geräte-Identität (device.id) übermitteln, die von einem Keypair-Fingerprint abgeleitet ist.
  • Gateways stellen Token pro Gerät und Rolle aus.
  • Pairing-Freigaben sind für neue Device-IDs erforderlich, außer die lokale Auto-Freigabe ist aktiviert.
  • Lokale Verbindungen umfassen Loopback-Adressen und die eigene Tailnet-Adresse des Gateway-Hosts (sodass Tailnet-Binds auf demselben Host weiterhin automatisch freigegeben werden können).
  • Alle WS-Clients müssen während des connect eine device-Identität angeben (Operator + Node). Die Control-UI kann dies nur in folgenden Modi weglassen:
    • gateway.controlUi.allowInsecureAuth=true für unsichere HTTP-Kompatibilität auf Localhost.
    • gateway.controlUi.dangerouslyDisableDeviceAuth=true (Notfall-Option, starke Sicherheitsminderung).
  • Alle Verbindungen müssen die vom Server bereitgestellte connect.challenge Nonce signieren.

Für ältere Clients, die noch das Verhalten vor der Challenge-Signierung nutzen, gibt connect jetzt DEVICE_AUTH_* Detail-Codes 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 weggelassen.
device nonce mismatchDEVICE_AUTH_NONCE_MISMATCHdevice-nonce-mismatchClient hat mit einer falschen Nonce signiert.
device signature invalidDEVICE_AUTH_SIGNATURE_INVALIDdevice-signatureSignatur-Payload passt nicht zum v2-Payload.
device signature expiredDEVICE_AUTH_SIGNATURE_EXPIREDdevice-signature-staleZeitstempel liegt außerhalb der Toleranz.
device identity mismatchDEVICE_AUTH_DEVICE_ID_MISMATCHdevice-id-mismatchdevice.id passt nicht zum Public-Key.
device public key invalidDEVICE_AUTH_PUBLIC_KEY_INVALIDdevice-public-keyFormat des Public-Key ist ungültig.

Migrations-Ziel:

  • Warte immer auf die connect.challenge.
  • Signiere den v2-Payload, der die Server-Nonce enthält.
  • Sende dieselbe Nonce in connect.params.device.nonce.
  • Der bevorzugte Signatur-Payload ist v3, der zusätzlich platform und deviceFamily an die Felder für Gerät, Client, Rolle, Scopes, Token und Nonce bindet.
  • Ältere v2-Signaturen werden aus Kompatibilitätsgründen weiterhin akzeptiert.
  • TLS wird für WS-Verbindungen unterstützt.
  • Clients können optional den Fingerprint des Gateway-Zertifikats pinnen (siehe gateway.tls Konfiguration sowie gateway.remote.tlsFingerprint oder CLI --tls-fingerprint).

Dieses Protokoll macht die gesamte Gateway-API verfügbar (Status, Channels, Models, Chat, Agent, Sessions, Nodes, Freigaben usw.). Der exakte Umfang ist durch die TypeBox-Schemas in src/gateway/protocol/schema.ts definiert.

AI Setup Assistant

OpenClaw

OpenClaw Expert

Noch festgefahren?

Wenn diese Seite nicht hilft, frage OpenClaw Expert nach Schritt-fuer-Schritt-Loesungen.