网关控制平面:连接、鉴权、分发、广播
这一章关注“入口层”的确定性:请求怎么进来、怎么被允许、怎么被路由、怎么把结果与事件发回去。
适用范围
- 你要实现一个“控制面网关”(WS/HTTP)来承载 UI、CLI、渠道接入与运维接口
- 你要能解释为什么必须“先握手再业务”、为什么事件不能无差别广播
最小可验证闭环(先跑通再看细节)
- 启动 Gateway:
openclaw gateway- 打开 Control UI:
openclaw dashboard- 观察握手与事件流(
tick/presence/...):
执行链路(从“第一包”到“事件广播”)
- 握手:第一包必须是
connect(含 challenge/鉴权)。 - 鉴权:按
role + scopes决定能否调用某个 method。 - 分发:把
req(method, params)路由到 handler。 - 广播:把执行过程中的状态/输出作为
event推送给订阅者(并做事件级过滤与慢连接保护)。
具体协议形态与方法表见:
关键实现要点(按复刻顺序)
1) connect 必须是首包
- 未完成握手的连接,不应允许进入业务 method handler。
- 握手成功后再允许订阅事件、调用方法。
2) authorize 与 dispatch 解耦
建议把逻辑拆成两步:
authorize(method, role, scopes):决定“能不能调”dispatch(method, params):决定“交给谁处理”
这样能把权限策略与业务处理解耦,并让审计与排障更清晰。
3) handler 的优先级:插件/扩展点优先,再 core 兜底
当存在扩展点(例如插件注册的 gateway method)时,建议:
- 先尝试 extra/plugin handlers
- 再落到 core handlers
- 未命中返回明确的
unknown method(不要静默)
相关治理见:Hook 与插件治理。
4) 事件广播必须做 scope guard + 慢连接保护
- 不是所有事件都能对所有连接广播(尤其是审批、配对、敏感运维类事件)。
- 需要对慢消费者做保护:单连接写缓冲持续积压时,应降级/断开,避免拖垮整体。
最小帧形态(便于你在抓包/日志里对照)
请求:
{ "type": "req", "id": "1", "method": "chat.send", "params": { "text": "hi" } }响应(ACK,返回 runId):
{ "type": "res", "id": "1", "ok": true, "result": { "runId": "r_123" } }事件(流式输出/状态变更):
{ "type": "event", "event": "agent.delta", "payload": { "runId": "r_123", "delta": "..." } }你实现时最容易踩坑的点
- 没有把
connect作为首包约束:导致未认证请求也能进入业务 handler。 - 把广播做成“全量广播”:敏感事件(审批、配对等)必须按 scope 过滤。
- 缺少慢消费者保护:单个连接的写缓冲积压会拖垮整个网关进程。
排障入口(从症状快速定位)
- WS 连不上/握手失败:先读 Gateway 协议 的 connect/challenge,再看 健康检查。
unknown method:确认 method 名是否正确、插件是否启用(见 CLI:plugins)。- 能调用但收不到事件:确认是否已订阅、事件是否受 scope 保护(见 认证与权限)。
读源码入口(可选)
这些入口用于你在源码里快速定位控制平面骨架(建议用 rg 搜文件名/方法名):
src/gateway/server.impl.tssrc/gateway/server-runtime-state.tssrc/gateway/server-ws-runtime.tssrc/gateway/server/ws-connection/message-handler.tssrc/gateway/server-methods.tssrc/gateway/server-broadcast.ts