把云上的 OpenClaw Control UI 接到我自己的浏览器上
一、Control UI 在我机器上的样子
我在一台 2C2G 的小云主机上跑 OpenClaw。它默认把 Control UI 起在 `127.0.0.1:18789`——绑定回环地址,不暴露到公网。我自己看的时候,习惯在终端敲一句:
```bashssh -L 18789:127.0.0.1:18789 mybox```
然后浏览器打开 `http://localhost:18789`,在网页聊天、修改配置等等。这个工作流在**我坐在自己电脑前**的时候没问题。
问题出在我离开这台电脑的时候。
– 出门只带了手机,地铁上想看一眼最近一小时机器人在干什么——`ssh -L` 在 iOS 上是真的难受,得装一个 SSH 客户端、把私钥导进去、记住端口转发的语法,还得切回 Safari 打开 `localhost`。每一步都不顺。
– 在公司用工作笔记本,我不太想把私人的 SSH key、Tailscale 客户端塞到一台公司设备上。家里那台机器和公司这台之间,本来就该有一条干净的边界。
– 如果换了台新笔记本,我又把”装 SSH 客户端 → 配 key → 写 `~/.ssh/config`”走了一遍,才能看一眼自己机器人的状态。
这个时候,我意识到:这件事不对。它们的共同点是:只有我一个合法访问者,但合法访问者会出现在很多设备上。OpenClaw 是个人助手,机器人是我自己的,要看的也只有我。可”我”这个身份,恰恰是工具最不擅长建模的——大多数方案要么把身份绑在一台设备上,要么干脆没有身份。
二、为什么 `127.0.0.1` 是个好默认
在抱怨”看不到”之前,先得替 OpenClaw 把这个默认讲清楚——它不是偷懒,它是有依据的。
OpenClaw 自带 Gateway Token机制:一个长字符串,调写操作的 API(创建请求、修改配置、触发执行)的时候放在 header 里校验。它决定了”谁能让机器人去做事”。
但 Gateway Token 只在写操作 API 上校验。Control UI 这一层的浏览动作不走那条认证路径——也就是说,如果你把 Control UI 绑到 `0.0.0.0`,任何看见你 IP 的人都能点开看里面的内容:你的网关运行版本、你的列表器人、你的通道在线状态。这些东西不算秘密,但也不是路人该看到的。
在这个事实下,OpenClaw 把 UI 绑回 `127.0.0.1` 是一个承认边界的设计:UI 这一层我没自带访问控制,所以我不给公网入口,至于谁要给公网入口、用什么方式给,部署者自己决定。
这是清醒的设计,不是不作为。但它把一个真问题原封不动地交给了部署者:我自己想从外面看,怎么办?
三、常见的几种”自己决定”,按代价分两类
我在做 Warded 之前,把这个问题的现成解法翻了一遍。它们大致能归成两类。
第一类:隧道方案。身份绑在设备上。
SSH 隧道、WireGuard、Tailscale、Cloudflare Tunnel + WARP 客户端,都属于这一类。思路是把”内网”延伸到我的每一台设备上:装一个客户端,认证一次,从此这台设备能直接访问 `127.0.0.1:18789`。
它们对工程师友好,但每多一台设备就要多配一次。手机要装 app,工作电脑要装客户端,临时借来的浏览器根本装不了。对一个**人在多个设备之间切换**的场景,这个成本是反复付的——而且付的不是一次性的钱,是每次”换设备”都得分一点心思。
第二类:反向代理 + 认证中间件。 身份不绑设备,但要自己拼。
Nginx + Basic Auth、OAuth2-proxy、Caddy + forward_auth、Cloudflare Access 自建版,都属于这一类。思路是把入口暴露在公网上,靠认证守门,任何浏览器打开都能登。
这是我最想要的形态——任何浏览器都能登。但代价是**没有自带的域名,没有现成的登录态**:
– 你得自己买一个域名,配 DNS,签证书。
– 你得调反代,写 location,处理 WebSocket。
– 你得选一个认证方案。Basic Auth 体验差,弹窗式输入框 + 密码再”私人”也只是一个字符串。OAuth2-proxy 体验好,但你得自己去 GitHub / Google 注册一个 OAuth 应用,写回调 URL,维护 cookie secret,再给 oauth2-proxy 加一个 systemd unit。
– 这一整套配下来,给一个只有我自己用的 Control UI 加登录这件事,大概要花掉一个周末。
我想要的是第二类的”任何浏览器都能登”,加上有人帮我把域名、证书、登录这套东西预先拼好——不必为了从手机上看一眼机器人,先去注册一个 OAuth 应用。
四、Warded 是什么
Warded 是一个 CLI + 一个平台的组合:
– CLI 在你的服务器上跑(同一台 OpenClaw 在的机器),起一个带 TLS 的入口,监听 `:443`,把请求转发到你指定的本地端口(OpenClaw Control UI 的默认 `127.0.0.1:18789`)。
– 平台 负责签发域名、做登录认证、维护”谁能进这道门”的访问名单。一个 ward = 一个域名 + 一个本地上游 = 一份订阅。
需要先讲清楚的产品边界:Warded 是为 OpenClaw 这一类个人自部署服务设计的,不是通用反向代理。它的形态、它的命令、它的契约,都是围绕”一个开发者把自己一台机器上的服务接到自己的浏览器上”这件事来的。如果你要做多人协作、做多上游聚合、做边缘计算,Warded 不是合适的工具。
登录方式:`warded.cn` 是微信扫码,`warded.me` 是 Google / GitHub OAuth。身份绑在”人”上——不是设备,也不是密码。这一点正好对上第三节末尾我想要的那种东西:任何浏览器都能登,因为身份在我这边,不在那台设备上。
还有一件事顺手提一下,下一篇会展开:Warded 默认假设你的上游是 daemon 形态——它自己起来、自己保活,Warded 只管认证和代理。OpenClaw Control UI 跟着 OpenClaw 主进程一起常驻,正好符合这个假设。这一篇里你不需要为这件事多写一个字段。
五、动手:把 Control UI 接出来
四步。命令都可以直接复制。
第一步,装 CLI,做一次预检。 预检会检查本地能不能监听 443、上游 `127.0.0.1:18789` 可不可达、域名能不能签发——它是后面 commit 之前的兜底。
```bashcurl -fsSL warded.cn/install.sh | shwarded doctor preflight```
OpenClaw Control UI 的默认地址 `127.0.0.1:18789` 正好是 Warded 的缺省 upstream,所以这里不需要 `–upstream` 参数。
第二步,提交 ward 配置。
```bashwarded new --commit```
`–commit` 会真的把预检再跑一遍,确认本地能监听 443、上游可达、域名能签发,然后才把这条 ward 提交到平台。**不通过就不会留下半生不熟的状态**——不会出现”装完了但是没起来”这种中间态。这一点和”装完 Nginx 再调参数”的体验有差别,差别就在于工具愿不愿意把”配置写错”在最早的时候暴露给你。
CLI 输出一个激活 URL。
第三步,浏览器打开这个 URL,扫码登录,确认绑定的域名。默认规格是 starter,平台自动分配一个子域名(形如 `a1b2c3d4.warded.cn`),有 72 小时试用期,足够你判断这条链路对你有没有用。如果你想自己挑子域名、要更长的试用,就在第二步加一个 `–spec pro`,pro 规格 14 天试用,子域名你自己定,也支持你自带域名。
第四步,回到服务器,起常驻进程。
```bashwarded serve```
把这个进程交给 systemd 或 tmux,让它常驻——它要一直在那里替你监听 443、向平台 heartbeat、按访问名单放行请求。
验证就一件事:在任意一台带浏览器的设备上打开你的域名。
– 在自己电脑上,浏览器打开 `a1b2c3d4.warded.cn`:先到 Warded 的登录页,由于上一步认领的时候已经登录过了,平台默认12小时登录有效期,直接就跳回Control UI。
– 在手机上,同一个域名,同一次扫码,同样到达 Control UI。
– 在公司笔记本上、在朋友家的浏览器里、在你刚买的新 iPad 上,都是同一条链路。没有客户端,没有 SSH key,没有 `~/.ssh/config`。
OpenClaw 自己的配置一行没动。Control UI 还绑在 `127.0.0.1:18789`。原来那个清醒的默认仍然成立——只是它前面多了一道带身份的门。
六、它不解决什么
Warded 不替代 OpenClaw 的 Gateway Token。 Warded 关心”谁能打开这个网页”,不关心”机器人对外的写操作 API 怎么验签”。这两层各管各的——Gateway Token 守住”让机器人去做事”的入口,Warded 守住”看机器人在做什么”的入口。把它们混在一起讲是危险的。
Warded 不是多人 RBAC。 现阶段一个 ward 的访问名单里只放你自己一个身份就够用;如果以后要给另一个人开权限、要做权限粒度划分,那是另一篇文章的事,这一篇不展开。
Warded 平台不可达时,已经在跑的 `warded serve` 仍然按本地缓存的状态运行;但 ward 状态过期之后会停止放行新请求。这一点是有意设计的——Warded 不是一个”平台一挂你就完蛋”的架构,但也不是一个完全脱离平台的本地工具,中间的边界写在契约里。
一个 ward 一个域名一个上游。 不要拿它当通用 IaP 用,不要拿它聚合多个后端,不要拿它做边缘路由。它就是为了”把一台机器上的一个服务接到一个只有经过授权才能访问的域名上”这件事造的。
七、回到开头
写到这里我意识到,这件事最让我满意的地方不是”加了认证”,也不是”省了一个周末”,而是这条链路里**OpenClaw 的好默认完整地保留下来了**。我没有为了让自己看得方便,去撤掉那个绑回 localhost 的决定;我只是在它前面接了一段我自己愿意承担的东西。
好的默认值是软件能给的礼物,但礼物之外的那一段路,得有工具替使用者走完。
—
下一篇预告 换上游:把 Hermes Dashboard 也接出来。它和 OpenClaw Control UI 的差别只有一个——**它不是 daemon**。同样的工具碰到这种上游,会发生一点不一样的事。
如果你想自己试一下:
```bashcurl -fsSL warded.cn/install.sh | sh```
文档:https://warded.cn/docs
夜雨聆风