Gateway 控制平面
昨天看了启动链,知道 openclaw gateway 会拉起网关。今天就钻进这个网关:它是把渠道、大脑、原生设备粘起来的中枢。看它怎么起服务、怎么按 bind 控制暴露范围、怎么拉起渠道、以及它对外的 WebSocket JSON-RPC 接口。搞懂它,明天才能端到端追踪一条消息。
网关的职责
Day 01 说过 Gateway 是"控制平面"。具体它负责:
- 渠道生命周期:拉起/停止/监控每个渠道账号的连接(L04)。
- 路由:把入站消息路由到对的 agent/会话(Day 04)。
- 对外接口:WebSocket JSON-RPC,供 Web UI、原生 App(node/operator)、CLI 调用(L05)。
- 各种服务方法:聊天、cron、发现、TTS、审批等(L06)。
src/gateway/:server*(各服务模块)、call.ts(连接/绑定)、server-channels.ts(渠道编排)、server-methods/(RPC 方法)。它不生成回复、不懂平台协议——只协调。服务器组成
src/gateway/ 按功能拆成许多 server-*.ts:server-channels(渠道)、server-chat(聊天/agent 事件)、server-cron(定时)、server-discovery(设备发现)、server-http(HTTP + 钩子)、server-broadcast(广播)、server-browser、server-close…
/healthz(存活)、/readyz(就绪)由 HTTP server 提供。bind 绑定(安全关键)
// src/gateway/call.ts:152
const bindMode = config.gateway?.bind ?? "loopback"; // 默认只监听本机
// :186 安全检查:阻止所有明文 ws:// 连非回环地址(CWE-319, CVSS 9.8)
// "SECURITY ERROR: Gateway URL uses plaintext ws:// to a non-loopback address"
// 建议:keep gateway.bind=loopback + SSH 隧道,或用 wss/Tailscale
bind=loopback(默认):网关只监听 127.0.0.1,只有本机能连——最安全。bind=lan:监听局域网,手机等设备能连,但必须配 token(Day 20)。网关连着你所有聊天账号、能执行命令,暴露范围必须谨慎。代码里还硬性拦截"用明文 ws:// 连非本机地址"——因为那样密码/消息在网络上裸奔(CWE-319 是这个漏洞的官方编号,CVSS 9.8 是最高危级别)。非本机必须用加密 wss 或走 SSH 隧道/Tailscale。这是"默认最严、要放开得显式且安全"的又一体现。•
bind=loopback(默认)→ 只有这台笔记本自己能连;手机在同一 WiFi 也连不上(安全,但要用手机得配隧道)。•
bind=lan → 同一局域网的手机能连上用语音/相机,但必须配 token,否则同网段任何人都能操控你的助理。• 想从外网连又图省事写
ws://公网IP → 直接被 call.ts 拦下报 SECURITY ERROR,逼你改用 wss。拉起渠道
startChannelInternal(src/gateway/server-channels.ts:149)——网关拉起每个渠道账号(Day 14 详看过):
// 1. getChannelPlugin(channelId) 取插件,读 plugin.gateway.startAccount
// 2. plugin.config.listAccountIds(cfg) 枚举多账号
// 3. 检查 isEnabled / isConfigured
// 4. 建 AbortController,写 running:true
// 5. startAccount({ ..., abortSignal, setStatus, channelRuntime }) ← 连接、开始收消息
// 6. .finally 回写 running:false(配合外层重连自动重启)
abortSignal 统一管停、setStatus 收状态、崩了自动重启。这就是"控制平面管理渠道生命周期"的落地。(Day 13-14 已从渠道角度看过,这里从网关角度再看一遍。)WebSocket JSON-RPC
网关对外提供 WebSocket JSON-RPC 接口(Day 18 从 App 角度见过):帧 type = req/res/event,客户端有 Web UI、原生 App(node/operator 角色)、CLI。
👨🏫 老师:问题在于 HTTP 是"你问一句我答一句",网关没法主动找你。可新消息到了、答复逐字蹦出来、要你审批一条命令,这些都得网关主动实时推给你。
👶 小白:所以要一条"双向都能随时说话"的线?
👨🏫 老师:对,那就是 WebSocket——好比对讲机(双向常开)vs 写信(一来一回)。于是协议分 req/res(你问我答)和 event(我主动喊你)两类。
server-methods(RPC 方法)
网关暴露的具体方法在 src/gateway/server-methods/(如 tts.ts,Day 15 见过),还有 server-chat.ts(聊天/agent 事件)、server-cron.ts(定时任务)等。客户端通过 RPC 调用这些方法。
src/gateway/method-scopes.ts。角色与作用域
客户端连接时声明角色(Day 18):
- node:设备节点,暴露相机/定位等能力给助理。
- operator:操作者,用于控制/配置(Web UI、CLI)。
方法按 scope 门控(method-scopes.ts)——不同角色/token 能调的方法不同。
今日小结 + 动手
🧠 今天你应该能回答
- 网关的四大职责?它不做什么?
- bind=loopback vs lan 的区别?为什么拦截明文 ws:// 连非本机?
- startChannelInternal 怎么拉起渠道?
- 为什么用 WebSocket(req/res/event 三类帧)?
- node vs operator 角色?作用域门控什么?
✋ 动手
cd /Users/bitmart/work/codes/github/openclaw
ls src/gateway/ | grep '^server' | head -20
sed -n '150,200p' src/gateway/call.ts # bind + 安全检查
sed -n '149,170p' src/gateway/server-channels.ts
ls src/gateway/server-methods/ 2>/dev/null | head