Protocolo del Gateway (WebSocket)

El protocolo WS del Gateway es el único plano de control + transporte de nodos para OpenClaw. Todos los clientes (CLI, web UI, app de macOS, nodos iOS/Android, nodos headless) se conectan por WebSocket y declaran su rol + alcance en el momento del handshake.

Transporte

  • WebSocket, tramas de texto con payloads JSON.
  • La primera trama debe ser una solicitud connect.

Handshake (connect)

Gateway -> Cliente (desafío pre-conexión):

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

Cliente -> 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 -> Cliente:

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

Cuando se emite un token de dispositivo, hello-ok también incluye:

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

Ejemplo de nodo

{
  "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": "…"
    }
  }
}

Tramas

  • Solicitud: {type:"req", id, method, params}
  • Respuesta: {type:"res", id, ok, payload|error}
  • Evento: {type:"event", event, payload, seq?, stateVersion?}

Los métodos con efectos secundarios requieren claves de idempotencia (consulta el esquema).

Roles + alcances

Roles

  • operator = cliente del plano de control (CLI/UI/automatización).
  • node = host de capacidades (cámara/pantalla/canvas/system.run).

Alcances (operator)

Alcances comunes:

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

El alcance del método es solo la primera compuerta. Algunos comandos slash alcanzados a través de chat.send aplican verificaciones más estrictas a nivel de comando por encima. Por ejemplo, las escrituras persistentes /config set y /config unset requieren operator.admin.

Caps/commands/permissions (nodo)

Los nodos declaran claims de capacidad en el momento de la conexión:

  • caps: categorías de capacidades de alto nivel.
  • commands: lista de comandos permitidos para invocación.
  • permissions: toggles granulares (por ejemplo, screen.record, camera.capture).

El Gateway trata estos como claims y aplica listas de permitidos del lado del servidor.

Presencia

  • system-presence devuelve entradas indexadas por identidad de dispositivo.
  • Las entradas de presencia incluyen deviceId, roles y scopes para que las UIs puedan mostrar una sola fila por dispositivo incluso cuando se conecta como operator y node simultáneamente.

Métodos auxiliares de nodo

  • Los nodos pueden llamar a skills.bins para obtener la lista actual de ejecutables de habilidades para verificaciones de auto-permitido.

Métodos auxiliares de operador

  • Los operadores pueden llamar a tools.catalog (operator.read) para obtener el catálogo de herramientas en tiempo de ejecución de un agente. La respuesta incluye herramientas agrupadas y metadatos de procedencia:
    • source: core o plugin
    • pluginId: propietario del plugin cuando source="plugin"
    • optional: si una herramienta de plugin es opcional

Aprobaciones de ejecución

  • Cuando una solicitud de ejecución necesita aprobación, el gateway difunde exec.approval.requested.
  • Los clientes operadores resuelven llamando a exec.approval.resolve (requiere alcance operator.approvals).
  • Para host=node, exec.approval.request debe incluir systemRunPlan (argv/cwd/rawCommand/metadatos de sesión canónicos). Las solicitudes sin systemRunPlan se rechazan.

Versionado

  • PROTOCOL_VERSION reside en src/gateway/protocol/schema.ts.
  • Los clientes envían minProtocol + maxProtocol; el servidor rechaza las discrepancias.
  • Los esquemas + modelos se generan a partir de definiciones TypeBox:
    • pnpm protocol:gen
    • pnpm protocol:gen:swift
    • pnpm protocol:check

Autenticación

  • Si OPENCLAW_GATEWAY_TOKEN (o --token) está configurado, connect.params.auth.token debe coincidir o el socket se cierra.
  • Después del emparejamiento, el Gateway emite un token de dispositivo con alcance para el rol + alcances de la conexión. Se devuelve en hello-ok.auth.deviceToken y el cliente debe persistirlo para futuras conexiones.
  • Los tokens de dispositivo pueden rotarse/revocarse mediante device.token.rotate y device.token.revoke (requiere alcance operator.pairing).
  • Los fallos de autenticación incluyen error.details.code más sugerencias de recuperación:
    • error.details.canRetryWithDeviceToken (boolean)
    • error.details.recommendedNextStep (retry_with_device_token, update_auth_configuration, update_auth_credentials, wait_then_retry, review_auth_configuration)
  • Comportamiento del cliente para AUTH_TOKEN_MISMATCH:
    • Los clientes de confianza pueden intentar un reintento limitado con un token de dispositivo en caché.
    • Si ese reintento falla, los clientes deben detener los bucles de reconexión automática y mostrar orientación para acción del operador.

Identidad de dispositivo + emparejamiento

  • Los nodos deben incluir una identidad de dispositivo estable (device.id) derivada de una huella digital de par de claves.
  • Los Gateways emiten tokens por dispositivo + rol.
  • Las aprobaciones de emparejamiento son obligatorias para nuevos IDs de dispositivo a menos que la aprobación automática local esté habilitada.
  • Las conexiones locales incluyen loopback y la propia dirección tailnet del host del gateway (para que los enlaces tailnet del mismo host puedan auto-aprobar).
  • Todos los clientes WS deben incluir identidad device durante connect (operator + node). La UI de control puede omitirla solo en estos modos:
    • gateway.controlUi.allowInsecureAuth=true para compatibilidad HTTP insegura solo en localhost.
    • gateway.controlUi.dangerouslyDisableDeviceAuth=true (solución de emergencia, degradación severa de seguridad).
  • Todas las conexiones deben firmar el nonce connect.challenge proporcionado por el servidor.

Diagnósticos de migración de autenticación de dispositivo

Para clientes heredados que aún usan el comportamiento de firma pre-desafío, connect ahora devuelve códigos de detalle DEVICE_AUTH_* en error.details.code con un error.details.reason estable.

Fallos de migración comunes:

Mensajedetails.codedetails.reasonSignificado
device nonce requiredDEVICE_AUTH_NONCE_REQUIREDdevice-nonce-missingEl cliente omitió device.nonce (o envió vacío).
device nonce mismatchDEVICE_AUTH_NONCE_MISMATCHdevice-nonce-mismatchEl cliente firmó con un nonce obsoleto/incorrecto.
device signature invalidDEVICE_AUTH_SIGNATURE_INVALIDdevice-signatureEl payload de firma no coincide con el payload v2.
device signature expiredDEVICE_AUTH_SIGNATURE_EXPIREDdevice-signature-staleLa marca de tiempo firmada está fuera del margen permitido.
device identity mismatchDEVICE_AUTH_DEVICE_ID_MISMATCHdevice-id-mismatchdevice.id no coincide con la huella de clave pública.
device public key invalidDEVICE_AUTH_PUBLIC_KEY_INVALIDdevice-public-keyEl formato/canonización de la clave pública falló.

Objetivo de migración:

  • Siempre espera connect.challenge.
  • Firma el payload v2 que incluye el nonce del servidor.
  • Envía el mismo nonce en connect.params.device.nonce.
  • El payload de firma preferido es v3, que vincula platform y deviceFamily además de los campos device/client/role/scopes/token/nonce.
  • Las firmas heredadas v2 siguen aceptándose por compatibilidad, pero el pinning de metadatos de dispositivos emparejados sigue controlando la política de comandos en la reconexión.

TLS + pinning

  • TLS es soportado para conexiones WS.
  • Los clientes pueden opcionalmente anclar la huella del certificado del gateway (consulta la configuración gateway.tls más gateway.remote.tlsFingerprint o CLI --tls-fingerprint).

Alcance

Este protocolo expone la API completa del gateway (estado, canales, modelos, chat, agente, sesiones, nodos, aprobaciones, etc.). La superficie exacta está definida por los esquemas TypeBox en src/gateway/protocol/schema.ts.