Skip to content

Gateway 协议(WebSocket)

Gateway WS 协议是 OpenClaw 的唯一控制面 + 节点传输通道。所有客户端(CLI、Web UI、macOS 应用、iOS/Android 节点、无头节点)均通过 WebSocket 连接,并在握手时声明自己的角色(role)作用域(scope)

传输层

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

握手(connect)

Gateway → 客户端(预连接挑战):

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

客户端 → Gateway:

json
{
  "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 → 客户端:

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

颁发设备 token 时,hello-ok 还会包含:

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

节点连接示例

json
{
  "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?}

有副作用的方法需要携带幂等性 key(参见 schema)。

角色与作用域

角色

  • operator = 控制面客户端(CLI/UI/自动化)。
  • node = 能力宿主(摄像头/屏幕/画布/system.run)。

作用域(operator)

常用作用域:

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

方法作用域只是第一道门禁。通过 chat.send 触发的部分 slash 命令还会在此之上执行更严格的命令级别检查。例如,持久写入操作 /config set/config unset 需要 operator.admin 作用域。

能力/命令/权限(node)

节点在连接时声明能力:

  • caps:高层能力类别。
  • commands:调用允许的命令白名单。
  • permissions:细粒度开关(如 screen.recordcamera.capture)。

Gateway 将这些视为声明并在服务端执行白名单校验。

在线状态

  • system-presence 返回以设备身份为 key 的条目。
  • 在线条目包含 deviceIdrolesscopes,UI 可据此为同时以 operatornode 两种角色连接的设备显示单行记录。

节点辅助方法

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

Operator 辅助方法

  • Operator 可调用 tools.catalog(需 operator.read)获取 Agent 的运行时工具目录。响应包含分组工具及来源元数据:
    • sourcecoreplugin
    • pluginIdsource="plugin" 时的插件所有者
    • optional:插件工具是否为可选
  • Operator 可调用 tools.effective(需 operator.read)获取会话的运行时有效工具清单。
    • 必须提供 sessionKey
    • Gateway 从服务端的会话中推导受信运行时上下文,而不接受调用方提供的 auth 或投递上下文。
    • 响应以会话为范围,反映当前对话可使用的所有工具(包括 core、plugin 和 channel 工具)。

Exec 审批

  • 当 exec 请求需要审批时,Gateway 会广播 exec.approval.requested
  • Operator 客户端通过调用 exec.approval.resolve(需 operator.approvals 作用域)来解决审批。
  • 对于 host=node 的情况,exec.approval.request 必须包含 systemRunPlan(包含规范 argv/cwd/rawCommand/会话元数据)。缺少 systemRunPlan 的请求会被拒绝。

版本管理

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

认证

  • 若设置了 OPENCLAW_GATEWAY_TOKEN(或 --token),connect.params.auth.token 必须匹配,否则连接关闭。
  • 配对后,Gateway 颁发设备 token,其作用域绑定到连接角色和作用域。token 在 hello-ok.auth.deviceToken 中返回,客户端应持久化以供后续连接使用。
  • 设备 token 可通过 device.token.rotatedevice.token.revoke 轮换/吊销(需 operator.pairing 作用域)。
  • 认证失败时,error.details.code 包含错误码及恢复提示:
    • error.details.canRetryWithDeviceToken(boolean)
    • error.details.recommendedNextStepretry_with_device_tokenupdate_auth_configurationupdate_auth_credentialswait_then_retryreview_auth_configuration
  • 客户端处理 AUTH_TOKEN_MISMATCH 的行为:
    • 受信客户端可尝试使用缓存的设备 token 进行一次有限重试。
    • 若重试失败,客户端应停止自动重连循环,并提示 operator 采取行动。

设备身份与配对

  • 节点应携带从密钥对指纹派生的稳定设备身份(device.id)。
  • Gateway 按设备 + 角色颁发 token。
  • 新设备 ID 需要经过配对审批,除非本地自动审批已启用。
  • 本地连接包括 loopback 和 gateway 宿主机自身的 tailnet 地址(因此同主机的 tailnet 绑定仍可自动审批)。
  • 所有 WS 客户端在 connect 时必须包含 device 身份(operator 和 node 均如此)。控制 UI 仅在以下模式下可省略:
    • gateway.controlUi.allowInsecureAuth=true(仅限 localhost 的不安全 HTTP 兼容模式)。
    • gateway.controlUi.dangerouslyDisableDeviceAuth=true(紧急方案,严重安全降级)。
  • 所有连接必须对服务端提供的 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 签名载荷,它在 device/client/role/scopes/token/nonce 字段之外还绑定了 platformdeviceFamily
  • 向后兼容:v2 签名仍被接受,但配对后设备元数据固定仍会在重连时控制命令策略。

TLS 与证书固定

  • WS 连接支持 TLS。
  • 客户端可选择性地固定 gateway 证书指纹(参见 gateway.tls 配置,以及 gateway.remote.tlsFingerprint 或 CLI --tls-fingerprint)。

接口范围

本协议暴露了完整的 Gateway API(状态、频道、模型、对话、Agent、会话、节点、审批等)。确切接口范围由 src/gateway/protocol/schema.ts 中的 TypeBox schema 定义。