Day 03 / 共 20 天 · 第 1 周 入门与全景

Gateway 控制平面

昨天看了启动链,知道 openclaw gateway 会拉起网关。今天就钻进这个网关:它是把渠道、大脑、原生设备粘起来的中枢。看它怎么起服务、怎么按 bind 控制暴露范围、怎么拉起渠道、以及它对外的 WebSocket JSON-RPC 接口。搞懂它,明天才能端到端追踪一条消息。

📍 你在整门课的位置(第 1 周 · 入门与全景)
Day1 全景 Day2 入口启动 Day3 Gateway Day4 消息旅程 Day5 配置· W2 大脑· W3 技能/渠道· W4 安全/部署
L01

网关的职责

🤔 痛点:20 多个聊天平台 + 大脑 + 手机,谁来管? Telegram 有连接要保活、Slack 掉线要重连、手机 App 要能远程看状态、大脑要知道"这条消息归哪个会话"……如果没有一个"总调度",这些东西就各自为政、互相打架。
💡 本质:Gateway = 公司前台/总机 它自己不干业务(不生成回复、不懂平台协议),只做"协调":管渠道连接的生死、把进来的消息领到对的会话、对外开一个统一接口让 App/CLI 调用。把"协调"抽成独立一层,渠道和大脑就能各自演化、互不干扰。

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 方法)。它不生成回复、不懂平台协议——只协调。
⚠️ 常见误解:小白常以为"Gateway 就是那个会聊天的 AI"。其实 Gateway 一个字的回复都不生成——那是大脑(Day 06-10)的事;它也不懂 Telegram/Slack 协议——那是渠道(Day 13-14)的事。Gateway 只做"协调",像门卫从不替你办业务。
L02

服务器组成

src/gateway/ 按功能拆成许多 server-*.tsserver-channels(渠道)、server-chat(聊天/agent 事件)、server-cron(定时)、server-discovery(设备发现)、server-http(HTTP + 钩子)、server-broadcast(广播)、server-browserserver-close

读法:一个大网关拆成多个职责单一的 server 模块,组合成完整服务。这种"按能力切文件"的组织方式贯穿整个项目(呼应 Day 19 的单文件 ≤500 行约束)。健康探针 /healthz(存活)、/readyz(就绪)由 HTTP server 提供。
🎛️ Gateway控制平面·只协调 🌐 渠道连接(收发) 🧠 Agent 大脑 📱 原生App(node) 💻 Web UI/CLI 虚线=WebSocket JSON-RPC(L5)
Gateway 居中协调:左边接渠道与大脑,右边通过 WebSocket 服务 App/UI/CLI。它自己不产内容。
L03

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 = "谁能连到我" bind=loopback(默认):网关只监听 127.0.0.1,只有本机能连——最安全。bind=lan:监听局域网,手机等设备能连,但必须配 token(Day 20)。网关连着你所有聊天账号、能执行命令,暴露范围必须谨慎。代码里还硬性拦截"用明文 ws:// 连非本机地址"——因为那样密码/消息在网络上裸奔(CWE-319 是这个漏洞的官方编号,CVSS 9.8 是最高危级别)。非本机必须用加密 wss 或走 SSH 隧道/Tailscale。这是"默认最严、要放开得显式且安全"的又一体现。
📝 举个例子:同一台机、两种 bind 的后果 你在家用笔记本跑网关:
bind=loopback(默认)→ 只有这台笔记本自己能连;手机在同一 WiFi 也连不上(安全,但要用手机得配隧道)。
bind=lan → 同一局域网的手机能连上用语音/相机,但必须配 token,否则同网段任何人都能操控你的助理。
• 想从外网连又图省事写 ws://公网IP → 直接被 call.ts 拦下报 SECURITY ERROR,逼你改用 wss。
L04

拉起渠道

startChannelInternalsrc/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 已从渠道角度看过,这里从网关角度再看一遍。)
L05

WebSocket JSON-RPC

网关对外提供 WebSocket JSON-RPC 接口(Day 18 从 App 角度见过):帧 type = req/res/event,客户端有 Web UI、原生 App(node/operator 角色)、CLI。

💬 小白 vs 老师 👶 小白:客户端要什么就发个 HTTP 请求问网关不就好了?
👨‍🏫 老师:问题在于 HTTP 是"你问一句我答一句",网关没法主动找你。可新消息到了、答复逐字蹦出来、要你审批一条命令,这些都得网关主动实时推给你。
👶 小白:所以要一条"双向都能随时说话"的线?
👨‍🏫 老师:对,那就是 WebSocket——好比对讲机(双向常开)vs 写信(一来一回)。于是协议分 req/res(你问我答)和 event(我主动喊你)两类。
为什么用 WebSocket 而不是普通 HTTP? 普通 HTTP 是"你问一句、我答一句",服务端没法主动找你。但网关需要主动推送——新消息来了、agent 有了新回复、要你审批一个命令,都得实时推给客户端。WebSocket 是双向长连接,服务端可随时推 event。所以协议设计成 req(客户端请求)/res(响应)/event(服务端主动推)三类帧。这也是为什么部署时不能让机器休眠(Day 20,长连接要保持)。
L06

server-methods(RPC 方法)

网关暴露的具体方法在 src/gateway/server-methods/(如 tts.ts,Day 15 见过),还有 server-chat.ts(聊天/agent 事件)、server-cron.ts(定时任务)等。客户端通过 RPC 调用这些方法。

读法:每个方法对应一个客户端能"请求网关做"的操作:发消息、订阅 agent 事件、管理 cron、触发 TTS、执行审批。方法受作用域(scope)门控(L07)。方法作用域定义在 src/gateway/method-scopes.ts
L07

角色与作用域

客户端连接时声明角色(Day 18):

  • node:设备节点,暴露相机/定位等能力给助理。
  • operator:操作者,用于控制/配置(Web UI、CLI)。

方法按 scope 门控(method-scopes.ts)——不同角色/token 能调的方法不同。

读法:角色 + 作用域 = 权限控制:一个只连来暴露相机的 node,不该能改配置或执行任意命令。结合 Day 17 的信任模型:通过网关认证的是可信操作者,但作用域仍限制"每个连接具体能做什么",做最小权限。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 网关的四大职责?它不做什么?
  • bind=loopback vs lan 的区别?为什么拦截明文 ws:// 连非本机?
  • startChannelInternal 怎么拉起渠道?
  • 为什么用 WebSocket(req/res/event 三类帧)?
  • node vs operator 角色?作用域门控什么?
🎵 记忆口诀(把网关四职责记牢) "管连接、做路由、开接口、控权限"——渠道生命周期、消息路由、WebSocket 对外接口、角色作用域门控。一句话:门卫的四件事——看门、指路、开窗口、查证件。

✋ 动手

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
明天预告 · Day 04一条消息的完整旅程——把前三天串起来,端到端追踪:渠道收到 → 归一化 → 路由 → Agent → 回复原路返回,每一站的代码位置。
← Day 02 入口 Day 04 · 一条消息的完整旅程 →