内容提要
Remote Codex 是一个轻量远程控制台,用户可通过手机 PWA 远程操作本机 Codex。其设计遵循三条边界:代码、登录凭据和执行环境保留在电脑上;手机仅运行界面;外网访问通过电脑主动发起的 Cloudflare 出站隧道实现,不开放路由器端口。桥接层使用零依赖 Node.js 将浏览器 HTTP 请求转换为 codex app-server 的双向 JSON-RPC,并通过方法白名单、随机配对令牌、常量时间校验和 CSP 限制权限。事件推送在局域网使用 SSE,远程因 Quick Tunnel 不支持 SSE 而改用带序号游标的长轮询,配合内存事件缓冲和会话恢复处理断线。当前版本面向单人临时使用,尚未实现多用户、令牌轮换、限速和持久化。
延伸解读
为什么用出站隧道而不是端口映射
文章明确对比了两种外网访问方式:路由器端口映射会把能改代码、执行命令的入口直接暴露在公网,风险过高;而 Cloudflare Quick Tunnel 由电脑主动向外建立长连接,公网请求经 Cloudflare 边缘转回本机 127.0.0.1,无需公网 IP 或改路由器。远程模式下服务强制只监听回环地址,连同一局域网的其他设备也无法直连,所有外部流量必须经过隧道入口,这从网络层收窄了暴露面。
白名单与人工审批是两道独立防线
桥接层对 /api/rpc 做方法白名单过滤,未列出的方法一律返回 403,且 app-server 升级新增的方法默认关闭,这挡住了协议层的越权调用。但文章强调白名单替不了人工判断:手机点下“本次允许”时,电脑上会真实改文件或跑命令。因此审批卡片会展示原因、命令、工作目录或改动文件列表,用户仍需看清内容再点。两层边界各管一摊,不能互相替代。
远程模式为何改用长轮询
局域网模式用 SSE 推送事件,但文章指出 Cloudflare Quick Tunnel 目前不支持 SSE,导致远程模式下首页能开、任务却看不到流式输出。解决方案是抽出共用的内存事件缓冲,为事件编号并保留最近 1000 条;远程前端改用带 after 游标的长轮询,服务端有新事件或满 20 秒超时就返回。进程重启后 eventSession 变化会让旧游标失效并重新对齐,避免拿着上一轮序号干等。
当前版本的明确限制与处置方式
文章坦承 0.1.0 面向单人临时使用,尚未实现多用户隔离、令牌轮换、单设备撤销、按 IP 失败限速和持久化事件库。配对链接和二维码本身就是开门钥匙,一旦泄露,在进程存活期间他人即可控制会话。文章给出的处置方式很直接:怀疑链接泄露时按 Ctrl+C 停掉进程,临时隧道和当次随机令牌一并作废。需要固定域名或长期在线时,文章建议改用命名 Cloudflare Tunnel 或 Tailscale 组网。
Q&A
Remote Codex 是怎么让手机在外网控制电脑上的 Codex 的?
电脑主动通过 cloudflared 建立到 Cloudflare 的出站隧道,把本机 127.0.0.1:4173 的桥接服务暴露为临时 HTTPS 地址;手机 PWA 访问该地址,桥接层再把 HTTP 请求转换为 codex app-server 的 JSON-RPC,从而远程操作本机 Codex。整个过程不需要公网 IP,也不开放路由器入站端口。
Remote Codex 为什么不用端口映射,而是用 Cloudflare 隧道?
因为 Codex 能改本机代码、执行本机命令,直接做端口映射等于把这个高权限入口挂到公网上,安全风险太高。Cloudflare 隧道由电脑主动向外发起连接,源站无需开放任何入站端口,手机流量经 Cloudflare 边缘节点转回本机,既避开运营商 NAT 和动态公网地址,也降低了暴露面。
手机端能调用 Codex 的哪些接口,有没有限制?
桥接层维护一个方法白名单,只放行 model/list、thread 系列、turn/start、turn/steer、turn/interrupt 等会话管理方法;不在白名单内的请求一律返回 403。像 thread/shellCommand、command/exec 以及删除历史会话这类高风险接口都没有对浏览器开放,app-server 后续新增的方法默认也处于关闭状态。
任务运行中想临时补充要求,手机端会怎么处理?
前端会区分空闲发送和运行中插话:没有活跃轮次时调用 turn/start;检测到 state.busy 且存在 activeTurnId 时自动改调 turn/steer,并附带 expectedTurnId。这样即使发送瞬间上一轮恰好结束,服务端也能拒绝错位插话,不会把补充说明误塞进别的上下文。
手机锁屏或断网重连后,还没处理的审批请求会丢吗?
不会。Codex 发出审批请求时,桥接层会把原始请求按 id 存入内存 pendingServerRequests 表并广播事件;手机端重连后调用 GET /api/pending 就能重新拉回尚未处理的审批,不会因为错过推送而把任务卡死。用户提交审批后经 /api/respond 写回子进程,同时广播 requestResolved 让其他页面同步收起弹窗。
为什么远程模式下事件推送从 SSE 改成了长轮询?
因为 Cloudflare Quick Tunnel 目前不支持 SSE,远程模式下手机收不到流式输出。桥接层因此抽出共用的内存事件缓冲 EventBuffer,局域网仍用 SSE 推送,远程模式改用带序号游标的长轮询 GET /api/events/poll?after=42,配合 20 秒超时和 eventSession 校验处理服务重启后的旧游标失效问题。
配对令牌是怎么生成和保护的?链接泄露了怎么办?
每次启动进程会用 randomBytes(24) 生成 192 位随机令牌,拼在配对地址的 # 片段后,避免出现在 HTTP 请求行和访问日志里;手机扫码后令牌存入 localStorage 并清掉地址栏,之后请求统一用 Bearer 头携带,服务端用常量时间比较校验。0.1.0 还没有令牌轮换和单设备撤销,怀疑链接泄露时最直接的办法是按 Ctrl+C 停掉进程,让临时隧道和当次令牌一起作废。
Remote Codex 0.1.0 有哪些已知限制?
当前版本只面向单人临时远程使用,没有多用户隔离、令牌轮换、按 IP 的失败限速、持久化事件库、审计日志和自动故障转移;手机端也还没做图片附件、语音输入与系统级推送通知。Quick Tunnel 每次启动域名都会变,也不提供可用性承诺,需要固定域名和长期在线时应改用命名 Cloudflare Tunnel 或 Tailscale。