Gateway protocol(WebSocket)
Gateway WS 协议是 OpenClaw 的 单一控制面 + node 传输层。 所有客户端(CLI、web UI、macOS app、iOS/Android nodes、headless nodes)都通过 WebSocket 连接,并在握手阶段声明其 role + scope。
传输层
- WebSocket,文本帧,JSON payload。
- 第一帧 必须 是
connect请求。
握手(connect)
Gateway → Client(pre-connect challenge):
json
{
"type": "event",
"event": "connect.challenge",
"payload": { "nonce": "…", "ts": 1737264000000 }
}Client → 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 → Client:
json
{
"type": "res",
"id": "…",
"ok": true,
"payload": { "type": "hello-ok", "protocol": 3, "policy": { "tickIntervalMs": 15000 } }
}当签发了 device token 时,hello-ok 还会包含:
json
{
"auth": {
"deviceToken": "…",
"role": "operator",
"scopes": ["operator.read", "operator.write"]
}
}Node 示例
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": "…"
}
}
}帧格式(Framing)
- Request:
{type:"req", id, method, params} - Response:
{type:"res", id, ok, payload|error} - Event:
{type:"event", event, payload, seq?, stateVersion?}
有副作用的方法需要 幂等键(idempotency keys)(见 schema)。
Roles + scopes
Roles
operator= 控制面客户端(CLI/UI/automation)。node= 能力宿主(camera/screen/canvas/system.run)。
Scopes(operator)
常见 scopes:
operator.readoperator.writeoperator.adminoperator.approvalsoperator.pairing
Caps/commands/permissions(node)
Nodes 在 connect 时声明能力声明(capability claims):
caps:高层能力类别。commands:可被 invoke 的命令 allowlist。permissions:更细粒度的开关(例如screen.record、camera.capture)。
Gateway 将这些视为 claims,并在服务端强制 allowlists。
Presence
system-presence返回以 device identity 为 key 的条目。- Presence 条目包含
deviceId、roles、scopes,从而 UI 可以为每个设备显示一行, 即使它同时以 operator 与 node 角色连接。
Node helper 方法
- Nodes 可以调用
skills.bins来获取当前可执行的 skill 列表,用于 auto-allow 检查。
Exec approvals
- 当 exec 请求需要批准时,gateway 会广播
exec.approval.requested。 - operator 客户端通过调用
exec.approval.resolve进行处理(需要operator.approvalsscope)。
版本管理
PROTOCOL_VERSION位于src/gateway/protocol/schema.ts。- 客户端发送
minProtocol+maxProtocol;服务端会拒绝不匹配的版本。 - schema + models 由 TypeBox 定义生成:
pnpm protocol:genpnpm protocol:gen:swiftpnpm protocol:check
鉴权(Auth)
- 如果设置了
OPENCLAW_GATEWAY_TOKEN(或--token),connect.params.auth.token必须匹配,否则 socket 会被关闭。 - 配对后,Gateway 会签发一个按连接 role + scopes 作用域的 device token。 它会在
hello-ok.auth.deviceToken返回,并应由客户端持久化以便后续 connect。 - device token 可以通过
device.token.rotate与device.token.revoke轮换/撤销(需要operator.pairingscope)。
设备身份 + 配对
- Nodes 应包含一个稳定的设备身份(
device.id),通常由 keypair fingerprint 派生。 - Gateways 按 device + role 签发 tokens。
- 对新的 device IDs,除非启用 local auto-approval,否则需要配对审批。
- Local 连接包括 loopback 与 gateway host 自己的 tailnet 地址(因此同主机 tailnet bind 仍可 auto-approve)。
- 所有 WS 客户端在
connect时都必须包含device身份(operator + node)。 Control UI 只有在启用gateway.controlUi.allowInsecureAuth时才能省略(或使用gateway.controlUi.dangerouslyDisableDeviceAuth作为 break-glass 手段)。 - 非 local 连接必须对服务端提供的
connect.challengenonce 进行签名。
TLS + pinning
- WS 连接支持 TLS。
- 客户端可选择 pin gateway 证书 fingerprint(见
gateway.tls配置,以及gateway.remote.tlsFingerprint或 CLI--tls-fingerprint)。
Scope
该协议暴露 完整的 gateway API(status、channels、models、chat、agent、sessions、nodes、approvals 等)。 具体暴露面由 src/gateway/protocol/schema.ts 中的 TypeBox schemas 定义。