Skip to main content

网关协议(WebSocket)

网关 WS 协议是 OpenClaw 的唯一控制平面 + 节点传输方式。所有客户端(CLI、Web UI、macOS 应用、iOS/Android 节点、无头节点)均通过 WebSocket 连接,并在握手时声明它们的角色 + 权限范围

传输

  • WebSocket,文本帧,负载为 JSON。
  • 第一帧必须connect 请求。

握手(connect)

网关 → 客户端(连接前挑战):
{
  "type": "event",
  "event": "connect.challenge",
  "payload": { "nonce": "…", "ts": 1737264000000 }
}
客户端 → 网关:
{
  "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": "…"
    }
  }
}
网关 → 客户端:
{
  "type": "res",
  "id": "…",
  "ok": true,
  "payload": {
    "type": "hello-ok",
    "protocol": 3,
    "policy": { "tickIntervalMs": 15000 }
  }
}
当设备令牌被签发时,hello-ok 还会包含:
{
  "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": "…"
    }
  }
}

帧格式

  • 请求{type:"req", id, method, params}
  • 响应{type:"res", id, ok, payload|error}
  • 事件{type:"event", event, payload, seq?, stateVersion?}
有副作用的方法需要幂等键(详见模式)。

角色 + 权限范围

角色

  • operator = 控制平面客户端(CLI/UI/自动化)。
  • node = 功能主机(摄像头/屏幕/画布/系统运行)。

权限范围(operator)

常用权限范围:
  • operator.read
  • operator.write
  • operator.admin
  • operator.approvals
  • operator.pairing
方法权限范围只是第一道门槛。一些通过 chat.send 访问的斜杠命令,在命令级别上会施加更严格的检查。例如,持久化的 /config set/config unset 写操作需要 operator.admin 权限。

能力/命令/权限(node)

节点在连接时申明能力声明:
  • caps:高级能力类别。
  • commands:调用命令白名单。
  • permissions:具体开关(例如 screen.recordcamera.capture)。
网关将其视为声明,并在服务器端强制检查白名单。

在线状态

  • system-presence 返回以设备身份为键的条目。
  • 在线状态条目包含 deviceIdrolesscopes,UI 可以对同一设备连接的operatornode 显示为单行。

节点辅助方法

  • 节点可调用 skills.bins 以获取当前的技能可执行文件列表,用于自动允许检查。

操作员辅助方法

  • 操作员可调用 tools.catalog(需 operator.read)以获取代理的运行时工具目录。响应包含工具分组和出处元数据:
    • source: coreplugin
    • pluginId: 当 source="plugin" 时,指插件拥有者
    • optional: 插件工具是否为可选

执行审批

  • 当执行请求需要审批时,网关广播 exec.approval.requested
  • 操作客户端通过调用 exec.approval.resolve 来处理(需 operator.approvals 权限范围)。
  • 对于 host=nodeexec.approval.request 必须包含 systemRunPlan(规范的 argv/cwd/rawCommand/会话元数据)。缺少 systemRunPlan 的请求将被拒绝。

版本控制

  • PROTOCOL_VERSION 定义在 src/gateway/protocol/schema.ts
  • 客户端发送 minProtocol + maxProtocol;服务器拒绝不匹配的版本。
  • 通过 TypeBox 定义生成模式和模型:
    • pnpm protocol:gen
    • pnpm protocol:gen:swift
    • pnpm protocol:check

认证

  • 如果设置了 OPENCLAW_GATEWAY_TOKEN(或 --token),connect.params.auth.token 必须匹配,否则套接字连接将关闭。
  • 配对后,网关会签发一个针对连接角色和权限范围的设备令牌。该令牌会在 hello-ok.auth.deviceToken 中返回,客户端应持久保存以供未来连接使用。
  • 设备令牌可通过 device.token.rotatedevice.token.revoke 进行轮换和撤销(需 operator.pairing 权限范围)。

设备身份 + 配对

  • 节点应包含稳定的设备身份(device.id),该身份来源于密钥对指纹。
  • 网关针对设备 + 角色发放令牌。
  • 新设备 ID 需要配对审批,除非启用了本地自动审批。
  • 本地 连接包含回环地址和网关主机自有的 tailnet 地址(因此同机 tailnet 绑定仍能自动批准)。
  • 所有 WS 客户端在 connect 时必须包含 device 身份(操作员和节点均需)。控制 UI 仅在启用 gateway.controlUi.dangerouslyDisableDeviceAuth 破窗模式时可省略。
  • 所有连接必须使用服务器提供的 connect.challenge nonce 进行签名。

设备认证迁移诊断

对于仍采用预挑战签名行为的旧客户端,connect 现在会在 error.details.code 下返回 DEVICE_AUTH_* 详细代码,且有稳定的 error.details.reason 常见迁移失败:
消息details.codedetails.reason含义
device nonce requiredDEVICE_AUTH_NONCE_REQUIREDdevice-nonce-missing客户端省略了 device.nonce(或发送为空)。
device nonce mismatchDEVICE_AUTH_NONCE_MISMATCHdevice-nonce-mismatch客户端使用过期或错误的 nonce 签名。
device signature invalidDEVICE_AUTH_SIGNATURE_INVALIDdevice-signature签名负载不匹配 v2 负载。
device signature expiredDEVICE_AUTH_SIGNATURE_EXPIREDdevice-signature-stale签名时间戳超出允许的偏差范围。
device identity mismatchDEVICE_AUTH_DEVICE_ID_MISMATCHdevice-id-mismatchdevice.id 与公钥指纹不匹配。
device public key invalidDEVICE_AUTH_PUBLIC_KEY_INVALIDdevice-public-key公钥格式或规范化失败。
迁移目标:
  • 始终等待 connect.challenge
  • 签名包含服务器 nonce 的 v2 负载。
  • connect.params.device.nonce 中发送相同的 nonce。
  • 推荐签名负载是 v3,它除了设备/客户端/角色/权限/令牌/nonce 字段外,还绑定了 platformdeviceFamily
  • 为保持兼容性,旧的 v2 签名仍被接受,但配对设备元数据绑定仍控制重连时的命令策略。

TLS + 绑定

  • WS 连接支持 TLS。
  • 客户端可选择绑定网关证书指纹(见 gateway.tls 配置及 gateway.remote.tlsFingerprint 或 CLI --tls-fingerprint)。

范围

此协议暴露了完整的网关 API(状态、频道、模型、聊天、代理、会话、节点、审批等)。具体接口由 src/gateway/protocol/schema.ts 中的 TypeBox 模式定义。