2315 字
12 分钟
OpenCode + OpenTunnel:让手机直接和家里电脑上的 AI 编程助手对话

我一直想在通勤路上给家里那台电脑上的 opencode 派活儿 —— 不是远程桌面那种”看着屏幕点点”,是真的把指令发过去,让它自己跑。

试过几条路:

方案结果
frp 自建服务器要有台有公网 IP 的 VPS,还得自己运维
Cloudflare Tunnel要绑自己的域名,配置一堆
Tailscale + 客户端 App得装 App,网页端不一定好用
SSH 端口转发 + 客户端手机上没法开 SSH 隧道
OpenTunnel一条命令,一个 https 网址,手机浏览器直接用

最后这个是 OpenCode 联合创始人 Dax Raad 做的开源项目:anomalyco/opentunnel。


一、它是什么:一个”盲”TLS 隧道#

一句话概括它的设计哲学:

服务端只读 TLS 握手的第一个包(ClientHello),拿到 SNI 就知道该转给谁,剩下的一概不看。

这是个很反常规的设计。传统内网穿透(ngrok、frp)的模型是:客户端和服务端之间建立一条隧道,所有流量都经过服务端。所以服务端能看到你的明文 —— 包括密码、cookie、API Key。

OpenTunnel 换了个思路:

┌─────────────────────────────┐
浏览器 ──HTTPS──▶ │ OpenTunnel Server (443) │
(以为在直连) │ 只读 ClientHello 里的 SNI │
└──────────────┬──────────────┘
│ 按 SNI 找到对应隧道
│ (剩余内容原样转发,加密状态不变)
▼
┌─────────────────────────────┐
│ 本地 opentunnel client │
│ ★ 持有证书私钥,在这里终结 TLS │
│ ★ 服务端从来看不到明文 │
└──────────────┬──────────────┘
│ 明文 HTTP
▼
┌─────────────────────────────┐
│ opencode server 127.0.0.1:4096 │
└─────────────────────────────┘

关键点:证书私钥在你自己电脑上,服务端从头到尾没有碰过。 它就像一个只认门牌号、然后原样把包裹送过去的快递员 —— 它知道包裹要送给谁,但包裹里是什么它拆不开。

它管这叫 blind TLS tunnel(盲 TLS 隧道)。


二、完整时序图#

NOTE

下图里的 ROUTE 是默认随机生成的 16 位路由名,ID 是服务端分配给本机的隧道子域名,两者拼起来就是一个完整地址。Mermaid 的标签里不能用尖括号,所以这里用大写占位。

阶段 A:建隧道(一次性)#

sequenceDiagram autonumber participant CLI as 你的终端 participant CLI2 as opentunnel client participant SRV as OpenTunnel Server
(Fly.io :443) participant ACME as ZeroSSL (ACME) participant CF as Cloudflare DNS CLI->>CLI2: opentunnel route add 4096 CLI2->>SRV: POST /api/routes (申请隧道) SRV-->>CLI2: 返回 id,例如 3f2a... Note over CLI2,SRV: 分配唯一子域名 ID.opentunnel.xyz CLI2->>ACME: 申请证书 (ACME DNS-01) ACME->>CF: 验证 _acme-challenge TXT 记录 CF-->>ACME: 验证通过 ACME-->>CLI2: 下发证书 + 私钥(只给客户端) Note over CLI2: ★ 私钥永不离开本机 CLI2->>SRV: 建立 bridge WebSocket(携带密文通道) SRV-->>CLI2: 就绪,开始接收转发 CLI2-->>CLI: 打印 https://ROUTE(随机16位).ID.opentunnel.xyz

注意 DNS-01 这一步:它不验证端口 80,不要求你有公网 IP,只验证 DNS 权限。 这是没有固定 IP 的家庭宽带能拿到正规证书的唯一途径。

路由名默认是 16 位随机字符,而且不会出现在公开的证书透明度(CT)日志里 —— 意味着没人能通过查 CT 日志反查出你的隧道地址。

Terminal window
opentunnel route add 4096
# Added route 21992cc9713e5fc5 → 127.0.0.1:4096
# https://21992cc9713e5fc5.3f2a91c4.opentunnel.xyz
安装
Terminal window
curl -fsSL https://opentunnel.xyz/install | sh
brew install anomalyco/tap/opentunnel
yay -S opentunnel-bin # Arch
npm install -g opentunnel
cargo install opentunnel-cli

阶段 B:手机发一条消息(每次)#

sequenceDiagram autonumber participant M as 手机浏览器 participant S as OpenTunnel Server participant C as 本地 opentunnel client participant O as opencode server
127.0.0.1:4096 M->>S: TCP 连接 443,TLS ClientHello
SNI = ROUTE.ID.opentunnel.xyz Note over S: 只解析 SNI,其余字节看不懂也不看 S->>C: 经 bridge WebSocket 转发原始 ClientHello 密文 C->>C: ★ 用本地私钥终结 TLS,拿到明文 HTTP Note over C: 校验证书域名,握手完成 C->>O: HTTP 请求
GET / (Authorization: Basic ...) Note over C,O: 应用看到的是 loopback 对端 O-->>C: 200 + OpenCode Web 界面 C->>S: 用会话密钥加密后回传 S-->>M: 原样转发(浏览器全程以为在直连服务端)
NOTE

最后一步是最舒服的地方:对浏览器来说,它就是访问一个普通的 https 站点。没有证书警告,没有客户端 App,没有 VPN 状态栏。它甚至不知道中间有个隧道。


三、最要命的一步:那个随机 24 位密码#

隧道跑通之后我差点裸奔到公网上。然后我意识到一个很要命的事实:

README 里写得很清楚: “Route names never appear in public certificate logs, so the URL can’t be guessed, but anyone you share it with can reach the service: it is not authentication.”

地址猜不出来 ≠ 有鉴权。地址泄露了 = 完全门户大开。

而 opencode 官方文档也直说了:

If OPENCODE_SERVER_PASSWORD is not set, the server will be unsecured. This is fine for local use but should be set for network access.

所以第一步就是打开 HTTP Basic 认证。用户名默认是 opencode,可以用 OPENCODE_SERVER_USERNAME 改。

密码怎么生成#

密码强度必须对得起”这个地址现在挂在公网上”这件事。 别用 123456、别用手机号生日、别用你其他服务复用过的密码。

Terminal window
# Linux / macOS / WSL —— openssl 生成,18 字节 = base64 后正好 24 个字符
export OPENCODE_SERVER_PASSWORD=$(openssl rand -base64 18)
# 顺手看一眼长度,确认是 24
echo -n "$OPENCODE_SERVER_PASSWORD" | wc -c

18 字节 = 144 bit 熵,base64 编码后是 24 个字符,且没有 = 补位。这个长度刚好能在密码管理器里一眼输完,又足够长到爆破无望。

WARNING

Windows 的 PowerShell 默认没有 openssl.exe(Windows 自带的是 OpenSSH,不是 OpenSSL)。用这个等效实现,长度同样是 24:

Terminal window
$b = New-Object byte[] 18
[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($b)
$env:OPENCODE_SERVER_PASSWORD = [Convert]::ToBase64String($b).TrimEnd('=').Replace('+','-').Replace('/','_')

- 和 _ 的替换是为了避开 + /,这样密码在 URL、配置文件、shell 历史里都不需要转义。

启动#

Terminal window
# 只监听 localhost —— 有隧道就够了,不要在局域网里裸奔
OPENCODE_SERVER_PASSWORD="$OPENCODE_SERVER_PASSWORD" opencode serve --port 4096 --hostname 127.0.0.1
# 手机打开那个 https 网址,弹 Basic Auth
# 用户名:opencode
# 密码:你刚生成的那 24 位

--hostname 保持 127.0.0.1 是个原则性问题:暴露面只留隧道这一个口。你用隧道纯粹是为了穿越 NAT,不是为了让同 Wi-Fi 下的邻居也能连你。

还有一层:--proxy-protocol#

默认情况下你的应用看到的是 loopback 对端,看不到真实来访 IP。OpenTunnel 官方专门写了份 trust-boundary 文档提醒这件事 —— 意思是:

如果你的应用里有任何基于 127.0.0.1 判断”这是本地可信请求”的逻辑,它在隧道后面是失效的。

需要传递真实来访者地址的话,客户端加:

Terminal window
opentunnel route add 4096 --proxy-protocol v2

只在你的应用确实支持 PROXY protocol 时才开。 乱开会往应用里塞它不认识的请求头。


四、串起来的完整命令#

Terminal window
# 1. 生成 24 位密码并记住它
export OPENCODE_SERVER_PASSWORD=$(openssl rand -base64 18)
# 2. 起 opencode,只听 localhost,带 Basic Auth
OPENCODE_SERVER_PASSWORD="$OPENCODE_SERVER_PASSWORD" \
opencode serve --port 4096 --hostname 127.0.0.1 &
# 3. 开隧道
opentunnel route add 4096
# https://21992cc9713e5fc5.3f2a91c4.opentunnel.xyz
# 4. 手机浏览器打开它,用 opencode / 你的密码登录

想两边同时用(电脑上看 TUI,手机上发消息),加一个终端 attach:

Terminal window
opencode attach http://localhost:4096

两个客户端共享同一份 session 状态,不是两个独立实例。


五、几个值得记的点#

为什么是 blind TLS 而不是普通反代#

普通反代(ngrok 类)Blind TLS(OpenTunnel)
服务端能看到明文✅ 能❌ 不能
服务端持有私钥✅ 持有❌ 没有
DNS-01 签发证书依赖服务端依赖客户端自己
被入侵后的损失全部流量泄露只有转发通道,没有内容
延迟多一跳解密再加密只读 SNI,开销极小

这本质上是把信任边界画在了正确的地方 —— 中转节点只负责”路由”,不负责”内容”。

它不是什么#

  • 不是 VPN。没有全局流量接管,你手机上只有这一个网址是”远程”的。
  • 不是端口转发。只支持 HTTP 应用(“任何支持 TLS 的现代浏览器/设备”),不是任意 TCP。
  • 不是零配置的安全方案。地址保密性来自不可猜测,不来自鉴权。密码必须设。
TIP

既然是”限时/白嫖性质”的第三方服务,我的取舍是:可以用它做个人开发工具,但不要把生产系统的面板挂在上面。 它本质上是一个公共的流量中转,服务端不做任何端到端的身份认证 —— 信任链完全建立在你那 24 位密码上。


小结#

把 localhost 变成一个公网 https 地址,难点从来不是”穿透”,而是在不牺牲加密强度的前提下穿透。OpenTunnel 的答案是:让服务端看不懂数据,只让它认门牌号。

  • Blind TLS:服务端只读 SNI,证书私钥和明文永远留在本地
  • DNS-01 签发:不需要公网 IP、不需要域名、80 端口也不用开
  • 16 位随机路由名 + 不进 CT 日志:地址不可枚举
  • 但仍需鉴权:OPENCODE_SERVER_PASSWORD + 24 位随机密码,是这套方案成立的前提
  • --hostname 127.0.0.1:暴露面只留隧道一个入口

现在我出门在地铁上,动动手指就能给家里的 opencode 派活儿了。写完这段话,我马上试一次。

OpenCode + OpenTunnel:让手机直接和家里电脑上的 AI 编程助手对话
https://ycxdev.com/posts/opencode-opentunnel-remote-from-phone/
作者
YCX
发布于
2026-10-10
许可协议
CC BY-NC-SA 4.0