摘要 :Gateway(网关) 是 OpenClaw 的 常驻控制进程 。它在一个端口上同时处理多种协议:WebSocket 用于实时控制和远程调用(RPC),HTTP 提供 OpenAI 兼容的 API 接口,还有管理界面(UI)和画布(Canvas)功能。无论是即时通讯(IM)渠道、桌面客户端还是自动化脚本,都必须先连接到 Gateway,然后才能使用 Router(路由)、Brain(大脑)和 Hands(执行器)等功能。本文结合官方 Gateway Runbook(运行手册) 和 Network model(网络模型) 文档,讲解三个核心概念:单实例锁定 (通过独占端口实现)、默认身份验证 、健康检查与配置热重载 。同时,我们将基于源码(固定 commit)走读启动流程:从 openclaw gateway 命令行 到 runGatewayLoop(处理锁和信号) ,再到 startGatewayServer(组装运行时),为下一篇 Router 做好铺垫。
关键词 :OpenClaw;Gateway(网关);WebSocket;HTTP 多路复用;单实例;openclaw gateway;startGatewayServer;健康检查;源码走读
系列文章:
- OpenClaw 深度解析与源代码导读 · 第1篇:系列导读------术语、版本与读源码方法
- OpenClaw 深度解析与源代码导读 · 第2篇:Skills------能力扩展平面与源码中的「目录即技能」
源码版本说明 :本文引用路径基于 openclaw/openclaw 仓库;本地阅读使用的 commit 为 0dd4958bc8a78d26b3b526b1f2e63b15110c64a2 (2026-04-11)。GitHub 上可按该 SHA 查看对应版本的源码,避免 main 分支变动导致的差异。
1 OpenClaw 架构全景与 Gateway 的位置
在深入 Gateway 之前,先通过一张架构图理解 Gateway 在 OpenClaw 整体中的位置(延续第1篇的餐厅比喻):

1.1 各组件职责速查表
| 组件 | 餐厅比喻 | 技术职责 | 与 Gateway 的关系 |
|---|---|---|---|
| Gateway | 前台/入口 | 单进程常驻控制面;单端口多协议(WebSocket + HTTP);身份验证;健康检查 | 本文主角 ------ 所有流量的第一站 |
| Router | 订单分发员 | 决定消息流向:进 Brain?走快捷路径?直接回包? | Gateway 收到请求后交给 Router 决策 |
| Brain | 主厨/大脑 | LLM 推理、上下文管理、工具调用循环 | 经 Gateway → Router 后进入 |
| Hands | 端锅/执行 | Shell、文件操作、浏览器控制 | 被 Brain 调用,通过 Gateway 返回结果 |
| Skills | 预制酱料包 | SKILL.md 扩展能力,按需加载 | 经 Gateway 目录扫描后暴露给 Brain |
| Memory | 台账/便签墙 | 持久化存储、会话状态、长期记忆 | 各组件通过 Gateway 协调访问 |
| Channels | 外卖平台/电话线 | IM 渠道长连接管理(WhatsApp/Slack 等) | 挂载在 Gateway 上,Gateway 保持长连接 |
1.2 数据流向示例
场景:用户发送一条 WhatsApp 消息给 Agent

💡 理解要点 :Gateway 是 "一夫当关" 的入口,但它不处理业务逻辑 ------只负责 "接进来、送出去、保安全、管连接"。
2 为什么要单独写一篇 Gateway?
在第1篇里,我们把 Gateway 比作餐厅的 前台 :它不亲自炒菜(Brain 负责),也不端盘子(Hands 负责),但 所有顾客(渠道消息)、外卖订单(HTTP API)、排号屏幕(控制 UI)都必须先经过它 。如果跳过 Gateway 直接研究 auto-reply 或某个具体渠道插件,很容易混淆几个关键问题:谁负责维持长连接?谁在做身份验证?谁在检查服务是否存活?
本文解答三个核心问题:
- 产品定位 :Gateway 进程 负责什么、不负责什么 (与多 Agent、workspace 隔离的关系在第1篇已有,本文补充 网络边界和进程边界)。
- 运维操作 :如何 启动 / 检查健康状态 / 排查故障 ,以及 为什么默认开启身份验证、为什么独占端口。
- 源码结构 :从
openclaw gateway命令 到startGatewayServer,建立后续 Router 篇需要的 "入口函数地图"。
💡 理解要点 :本文刻意 不展开每一条 WebSocket RPC 协议细节 (那是协议层内容,见 D1 bridge-protocol);本文聚焦 进程模型 + 端口模型 + 启动调用链。
3 运行时模型:一个进程、一个端口、多种功能
官方 Runbook(D1:Gateway runbook)用几句话概括了运行时模型,核心要点如下:
- 一个常驻进程 负责消息路由、控制面和 渠道的长连接管理。
- 同一个端口 同时承载多种协议:
- WebSocket 用于实时控制和远程调用(RPC);
- HTTP 提供 OpenAI 兼容接口(如
/v1/models、/v1/chat/completions、/v1/responses、/v1/embeddings)、/tools/invoke等; - 管理 UI、Canvas 相关路由等。
- 默认绑定地址 :
loopback(回环地址,即 127.0.0.1);默认身份验证 :开启(通过共享密钥gateway.auth.token/password或环境变量OPENCLAW_GATEWAY_*);非回环地址场景可使用 trusted-proxy(可信代理) 等模式(见 D1 configuration、trusted-proxy-auth)。
🔍 实际例子 :默认地址是 ws://127.0.0.1:18789 (端口可修改)。本地命令行工具(CLI)、终端界面(TUI)、桌面客户端都通过 同一个 WebSocket 地址 进行远程调用;Open WebUI 这类客户端通常会先 GET /v1/models 获取模型列表,再 POST 聊天请求------这些请求都落在 同一个 Gateway 端口 上,由 HTTP 和 WebSocket 分别处理。
3.1 与网络模型文档的关系
docs/gateway/network-model.md 提示部分内容已并入站点 Network 总页,但核心结论值得深入理解:
为什么"建议每台主机运行一个 Gateway"?
核心原因是 WhatsApp Web 等渠道的技术限制 。这些 IM 渠道使用 QR 码扫码登录 机制,且一个手机号同一时间只能有一个活跃的 Web 会话。如果同一主机上运行多个 Gateway 进程,它们会互相竞争 WhatsApp Web 连接,导致:
- 频繁掉线:后启动的 Gateway 会踢掉先登录的会话
- 消息丢失:正在处理的对话可能中断
- 状态混乱:多个进程同时尝试维持心跳,触发平台的风控
其他渠道(Discord、Slack、Telegram 等 Bot Token 机制) 无此限制,但为统一管理,OpenClaw 仍建议 单主机单 Gateway 作为默认部署模式。
什么是"强隔离"?何时需要多 Gateway?
当以下场景出现时,需要在一台物理机上运行 多个完全独立的 Gateway 实例:
| 场景 | 隔离需求 | 实现方式 |
|---|---|---|
| 多用户/多租户 | 用户 A 和用户 B 的数据绝不能互通 | 不同的 OPENCLAW_HOME 目录 |
| 测试 vs 生产 | 测试环境的故障不能影响生产服务 | 不同的 profile (如 --profile test) |
| 多 WhatsApp 号 | 一个公司需要同时运营多个 WhatsApp 业务号 | 不同的 端口 + 不同的工作目录 |
| 不同网络环境 | 部分服务走 VPN,部分走公网 | 不同的绑定地址 + 路由规则 |
"多 profile、多端口、多 OPENCLAW_HOME" 详解
这三个是 正交维度,可以组合使用:
1. Profile(配置档案)
- 作用:隔离不同的配置集合(生产配置、测试配置、开发配置)
- 使用:
openclaw gateway --profile productionvs--profile staging - 存储位置:
~/.openclaw/profiles/<profile-name>/
2. 端口(Port)
- 作用:网络层隔离,避免端口冲突
- 使用:
openclaw gateway --port 18789(默认)vs--port 18790 - 注意:每个端口对应一个独立的 Gateway 单实例锁(§4)
3. OPENCLAW_HOME(主目录)
- 作用:彻底隔离所有数据:配置、会话存储、日志、技能目录
- 使用:
export OPENCLAW_HOME=/opt/company-a/openclaw - 效果:两个 Gateway 实例即使运行在同一用户下,也看不到彼此的数据
典型部署模式示例
模式一:单用户单机(默认)
bash
# 一个 Gateway,默认端口 18789
openclaw gateway
模式二:开发/测试分离
bash
# 终端 1:生产环境
echo "PROD"
OPENCLAW_HOME=/home/user/openclaw-prod openclaw gateway --port 18789 --profile default
# 终端 2:测试环境
echo "TEST"
OPENCLAW_HOME=/home/user/openclaw-test openclaw gateway --port 18790 --profile test
模式三:多租户隔离(伪代码)
bash
# 租户 A - 电商客服
sudo -u tenant-a OPENCLAW_HOME=/var/tenants/a openclaw gateway --port 18001
# 租户 B - 内部 IT 支持
sudo -u tenant-b OPENCLAW_HOME=/var/tenants/b openclaw gateway --port 18002
每个租户有:
- 独立的 Linux 用户(
tenant-avstenant-b) - 独立的
$OPENCLAW_HOME(/var/tenants/{a,b}) - 独立的端口(
18001vs18002) - 独立的 WhatsApp 登录会话
💡 理解要点 :"单主机单 Gateway" 是建议而非强制 。当业务需要隔离时,通过 profile × port × OPENCLAW_HOME 的组合,可以在同一台机器上构建出 逻辑上完全独立的多个 Gateway 实例 ,每个实例有自己的 进程边界、网络边界、存储边界。
详见 D1 Multiple gateways 完整文档。
4 单实例:不靠 PID 文件,靠"谁先占用端口谁运行"
D1 Gateway lock 文档解释得很清楚:
- 目的 :同一主机、同一 端口 上 只能运行一个 Gateway;第二个实例必须 立即失败 并给出清晰的错误提示。
- 机制 :启动时 立即 在 WebSocket 监听地址上建立 独占的 TCP 监听(exclusive TCP listen) ;如果端口已被占用(
EADDRINUSE错误),抛出GatewayLockError------不需要 额外的 lock 文件,进程崩溃或被强制终止(SIGKILL) 后,操作系统会自动回收端口。 - 运维 :如果端口被 非 OpenClaw 的程序占用,错误表现与"第二个 Gateway"相同;需要通过
openclaw gateway --port <port>更换端口,或释放占用该端口的程序。
这与"传统守护进程写 /var/run/foo.pid 文件"的方式不同:锁就是监听套接字(socket)本身 ,排查故障时直接用 netstat 或端口诊断工具即可。
4 生命周期(用户视角):启动、检查健康、看日志
Runbook 的 5-minute local startup 可直接作为本文的"验收清单":
- 启动 Gateway :
openclaw gateway --port 18789(或使用openclaw gateway --force强制释放占用端口后再启动)。 - 检查状态 :
openclaw gateway status/openclaw status------ 确认看到Runtime: running和RPC probe: ok等基础信息。 - 检查渠道 :
openclaw channels status --probe------ 当 Gateway 可访问 时,对各渠道进行 在线探测 ;如果 Gateway 不可达,命令行工具会 降级为仅显示配置摘要(Runbook 已说明这种分叉行为)。
更深入的健康检查见 D1 health.md:openclaw health、openclaw status --deep 等命令会 向运行中的 Gateway 请求快照或实时探测 ------注意 openclaw health 默认不直接连接各渠道的套接字 ,而是通过 WebSocket 询问 Gateway,由 Gateway 侧协调探测。
配置热重载 :Runbook 的 Note 说明:Gateway 监听 活动配置文件路径 (OPENCLAW_CONFIG_PATH 或 profile 默认路径),gateway.reload.mode 默认值为 hybrid ------首次成功加载后,进程持有 内存中的配置快照 ,成功重载时 原子化切换 快照。这样 Gateway 可以在 不完全重启 的情况下应用部分配置变更(具体支持哪些配置项以 D1 文档为准)。
5 源码走读:从命令行到 startGatewayServer
下面是一条 "可放入脑图"的主路径 ,供你在本地用 rg 或跳定义功能跟随走读。
5.1 命令行注册:gateway 子命令是什么?
src/cli/gateway-cli/register.ts 把子命令注册到 Commander 框架上,描述直接点题:WebSocket Gateway 的运行、巡检与发现功能:
ts
// src/cli/gateway-cli/register.ts(节选)
export function registerGatewayCli(program: Command) {
const gateway = addGatewayRunCommand(
program
.command("gateway")
.description("Run, inspect, and query the WebSocket Gateway")
// ... 帮助示例:gateway run / status / discover ...
💡 理解要点 :openclaw gateway 不是"另一个 REST 小工具",而是 同一套运行时 的 运维入口 ------与 openclaw status 等命令共享 "如何找到正在运行的那个进程" 这一语境。
5.2 运行循环:runGatewayLoop ------ 处理锁、信号、重启
src/cli/gateway-cli/run-loop.ts 在真正启动 startGatewayServer 之前调用 acquireGatewayLock ;持有锁期间创建 server,并处理 SIGINT/SIGTERM/SIGUSR1 等 优雅停机和重启 (包括 清空队列 、释放锁后再生成子进程 等细节,文件后半部分继续展开)。
ts
// src/cli/gateway-cli/run-loop.ts(节选)
export async function runGatewayLoop(params: {
start: (params?: { startupStartedAt?: number }) => Promise<Awaited<ReturnType<typeof startGatewayServer>>>;
runtime: RuntimeEnv;
lockPort?: number;
}) {
let lock = await acquireGatewayLock({ port: params.lockPort });
let server: Awaited<ReturnType<typeof startGatewayServer>> | null = null;
// ... 注册信号;关机时 releaseLock;重启路径可能生成新进程 ...
读这里的目的 :把 "GatewayLockError = 端口冲突" 与源码 入口 对齐------文档里的锁机制,对应代码里 acquireGatewayLock + start 失败路径,而不是散落在各渠道插件里。
5.3 服务器组装:startGatewayServer ------ 配置、插件、渠道、WebSocket
src/gateway/server.ts 只是 薄转发层(thin re-export) ;实现位于 server.impl.ts 。startGatewayServer(port, opts) 一进来就 把端口写入环境变量 OPENCLAW_GATEWAY_PORT,随后流水线大致如下:
- 加载配置快照
loadGatewayStartupConfigSnapshot→prepareGatewayStartupConfig(包含 身份验证引导(auth bootstrap) 、可能 生成并持久化 token); - 诊断心跳 、SIGUSR1 重启策略 、控制 UI 的 allowedOrigins 种子 等横切关注点;
prepareGatewayPluginBootstrap:整理 插件 / 渠道 相关注册表和 gatewayMethods 列表;resolveGatewayRuntimeConfig、startGatewayEarlyRuntime、attachGatewayWsHandlers(WebSocket 运行时)、createChannelManager、startManagedGatewayConfigReloader......直到 HTTP/WebSocket 监听建立。
节选(只看"头部在做什么"):
ts
// src/gateway/server.impl.ts --- startGatewayServer(节选)
export async function startGatewayServer(port = 18789, opts: GatewayServerOptions = {}): Promise<GatewayServer> {
const minimalTestGateway =
process.env.VITEST === "1" && process.env.OPENCLAW_TEST_MINIMAL_GATEWAY === "1";
process.env.OPENCLAW_GATEWAY_PORT = String(port);
const configSnapshot = await loadGatewayStartupConfigSnapshot({ minimalTestGateway, log });
const authBootstrap = await prepareGatewayStartupConfig({
configSnapshot,
authOverride: opts.auth,
tailscaleOverride: opts.tailscale,
activateRuntimeSecrets,
});
const cfgAtStart = authBootstrap.cfg;
// ... 诊断 / 重启策略 / 控制 UI 种子 ...
const pluginBootstrap = await prepareGatewayPluginBootstrap({
cfgAtStart,
startupRuntimeConfig: applyConfigOverrides(configSnapshot.config),
minimalTestGateway,
log,
});
// ... resolveGatewayRuntimeConfig → attachGatewayWsHandlers / channels / reload ...
🔍 实际例子 :如果启动日志出现 "生成了 runtime token 但未写入配置" 这类警告,对应 authBootstrap 分支里对 persistedGeneratedToken 的判断------这直接影响 "重启后 token 是否会变化" ,排查故障时应回到 D1 configuration + secrets 文档确定 持久化策略。
5.4 一张总图:把 §5.1~§5.3 串起来

6 与第2篇(Skills)的边界
Skills 子系统解决 "磁盘上的 SKILL.md 如何变成模型可见的 <available_skills>" (见第2篇);Gateway 解决 "谁长期在线、谁在哪个端口上同时接受 WebSocket/HTTP、谁去拉起渠道和健康探测" 。二者在运行时 交汇 (会话快照、配置重载、RPC),但 职责不混写 :读 Skills 不必深入 HTTP 路由;读 Gateway 也不必读完整个 SKILL.md 解析器。
7 Gateway 安全隐患与加固建议
Gateway 作为 所有流量的入口 和 常驻进程 ,其配置直接决定了系统的攻击面大小。以下是基于文档设置的关键安全风险及应对建议(更全面的安全分析将在 第14篇(安全与成本) 展开)。
7.1 网络暴露风险(与 bind 设置相关)
| Gateway 设置 | 安全风险 | 文档出处 |
|---|---|---|
默认 loopback(127.0.0.1) |
仅本机可访问,风险最低 | §2 |
改为 0.0.0.0 或公网 IP |
任何人可尝试连接,若身份验证配置不当则完全暴露 | §2 |
trusted-proxy 模式 |
依赖上游代理做鉴权,若代理配置错误(如未过滤伪造头),攻击者可绕过 | §2 |
实际风险场景 :如果将 Gateway 绑定到 0.0.0.0:18789 且使用弱 token 或关闭鉴权 ,攻击者可直接调用 /v1/chat/completions 消耗你的 API Key,或通过 WebSocket 接口操纵 Agent。
7.2 身份验证绕过(与 auth 设置相关)
文档提到的鉴权方式(§2):
gateway.auth.token/password(共享密钥)- 环境变量
OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORD trusted-proxy(可信代理,风险最高)
隐患 :若使用 password 且密码强度不足,或 token 泄露(如提交到 GitHub),攻击者可在任何能访问该端口的主机上伪装为合法客户端。
7.3 单实例锁的副作用(与端口占用相关)
文档§3说明:"锁就是监听套接字(socket)本身"。
安全隐患 :若 Gateway 崩溃后未正确释放端口(极少数内核异常),或恶意程序 故意占用 18789 端口并伪装成 Gateway,客户端可能连接到假 Gateway,导致中间人攻击(MITM)。
7.4 配置热重载的风险(与 reload.mode 相关)
§4提到 gateway.reload.mode 默认 hybrid ,支持不完全重启即可应用配置变更。
安全隐患 :如果攻击者通过某种方式(如配置文件注入、环境变量篡改)修改了 gateway.auth.token,Gateway 可能在不中断服务 的情况下应用新配置,导致合法用户被踢出 或非法用户获得访问权。
7.5 健康检查信息泄露(与 health 端点相关)
§4提到 openclaw health、openclaw status --deep 等命令会返回健康快照(health snapshot)。
隐患 :这些快照可能包含渠道连接状态、运行时配置片段、会话统计信息。如果 Gateway 的鉴权被绕过 ,攻击者可借此进行信息收集,为后续攻击做准备。
7.6 安全加固建议(基于文档设置)
| 层级 | 建议配置 | 对应文档 |
|---|---|---|
| 网络层 | 保持默认 loopback;如需远程访问,使用 Tailscale/SSH 隧道 而非直接暴露 |
§2, §2.1 |
| 认证层 | 使用强随机 token(gateway.auth.token),定期轮换;避免 password 模式 |
§2 |
| 进程层 | 使用 --force 重启确保旧进程完全终止;监控 GatewayLockError 日志 |
§3, §4 |
| 配置层 | 限制配置文件权限(~/.openclaw/ 目录 600);谨慎使用 hybrid reload |
§4 |
| 监控层 | 定期检查 openclaw gateway status 和日志,确认无异常连接 |
§4 |
💡 理解要点 :Gateway 的绑定地址(bind) 、身份验证模式(auth mode) 、端口占用策略 共同构成了 OpenClaw 的第一道防线。文档§2-§4 的所有设置项都应从安全视角重新审视------它们既是功能配置,也是安全策略。
8 D3 参考阅读与扩展学习
8.1 OpenClaw 官方视角博客
- OpenClaw Gateway Explained(dench.com) ------ 架构层面的 Gateway 解读,适合理解设计哲学。
- How to Set Up an OpenClaw AI Gateway in 2026(getclawhosting.com) ------ 运维部署向,含端口配置、systemd 服务设置等实操内容。注意核对其端口/路径描述是否与你本机
openclaw.json一致。
8.2 AI Gateway 安全通用参考(跨项目借鉴)
以下资源虽非 OpenClaw 专属,但讨论了 AI Gateway 共性的安全挑战:
- LLM API Gateway Security Patterns(Cloudflare Blog) ------ 讨论 API Key 轮换、速率限制、提示词过滤等通用模式,可借鉴到
gateway.auth配置思路。 - AI Infrastructure Security: Gateway & Proxy Patterns(MITRE/OWASP) ------ 模型供应链攻击、提示词注入防御,可与第14篇安全内容对照阅读。
- Tailscale + AI Tools 安全实践(Tailscale Blog) ------ 与本文§2.1提到的 Tailscale 替代直接暴露 思路一致,提供具体组网方案。
8.3 安全专项内容预告
更全面的安全分析将在 第14篇(安全与成本) 展开,包括:
- 多 Agent 攻击面与
OPENCLAW_HOME强隔离 - 沙箱逃逸风险与 Hands 权限边界
- 按 Agent 选择模型的成本与安全权衡
9 本篇小结与下一篇预告
- 小结 :Gateway = 单进程控制面 ;单端口多协议 ;独占 bind(绑定) = 单实例锁 ;身份验证默认开启 ;健康与渠道探测由 Gateway 侧协调 ;启动主链
registerGatewayCli→runGatewayLoop→startGatewayServer。安全方面需重点关注 bind 地址、auth 模式、token 生命周期。 - 下一篇(第4篇) :Router(路由器) ------入站消息进入 Gateway 之后,如何 分发到 Brain / 快捷路径 / 渠道回包(与官方 Data flow 第 2 步对齐)。
10 参考文献与链接
- OpenClaw 主仓库:https://github.com/openclaw/openclaw
- Architecture Overview(D2):http://clawdocs.org/architecture/overview
- Gateway(D2):http://clawdocs.org/architecture/gateway
- Gateway Runbook(D1):https://github.com/openclaw/openclaw/blob/0dd4958bc8a78d26b3b526b1f2e63b15110c64a2/docs/gateway/index.md
- Network model(D1):https://github.com/openclaw/openclaw/blob/0dd4958bc8a78d26b3b526b1f2e63b15110c64a2/docs/gateway/network-model.md
- Gateway lock(D1):https://github.com/openclaw/openclaw/blob/0dd4958bc8a78d26b3b526b1f2e63b15110c64a2/docs/gateway/gateway-lock.md
- Health checks(D1):https://github.com/openclaw/openclaw/blob/0dd4958bc8a78d26b3b526b1f2e63b15110c64a2/docs/gateway/health.md
- Gateway configuration(D1):https://github.com/openclaw/openclaw/blob/0dd4958bc8a78d26b3b526b1f2e63b15110c64a2/docs/gateway/configuration.md
- Bridge protocol(D1,WS/RPC):https://github.com/openclaw/openclaw/blob/0dd4958bc8a78d26b3b526b1f2e63b15110c64a2/docs/gateway/bridge-protocol.md