NovaScale 中的远程 Codex
NovaScale 如何通过私有 Tailnet 上的 Codex app-server,把远程 Codex 工作流带到 iOS App 中。
背景
有时我在收到自托管 Grafana 告警后,需要直接用手机处理服务器运维问题。我可能需要检查某个服务、搜索错误原因,或者写一个小脚本。使用 AI 聊天 App 会有帮助,但我一直在想,NovaScale 是否可以提供一种更接近 agent 的界面,而不只是聊天界面。
这个想法在脑子里停留了一段时间,但当时还没有清晰的形态。
四月底,我决定取消 Anthropic Claude 订阅,因为 Codex 已经开始覆盖我大部分开发工作流。尤其是 Codex 不会因为五小时限制而打断正在进行的会话,这一点对我很重要。
后来我在 X 上看到有人提到 Codex 的 app-server 功能,于是开始研究。我阅读了官方 app-server 文档,也看了开源的 Codex CLI 代码。它的实现是开源的,这让这类集成更容易理解。
我当时确认了几个关键点:
codex app-server是 Codex 用来支持富客户端的接口,包含认证、会话历史、审批和流式 agent 事件等深度集成。- 它支持 JSON-RPC 2.0 风格的双向通信。使用 WebSocket 传输时,每条 JSON-RPC 消息会作为一个 WebSocket 文本帧发送。
- TCP WebSocket 传输在文档中仍被标记为实验性且不受支持,因此应当把它视为可能变化的集成点。
- CLI 可以生成 app-server API 的 TypeScript 或 JSON Schema 定义,生成的 schema 与用于生成它的 Codex 版本相匹配。
- 对外暴露非 loopback WebSocket 监听前,应当使用 WebSocket 认证保护。
由于 WebSocket 监听可以绑定到非 loopback 地址,我意识到可以把它绑定到主机的 Tailscale 地址上。我以前做 Open vSwitch 时已经熟悉 JSON-RPC,虽然之前没有大量使用 WebSocket,但结合我的 L3/L4 网络背景,这个传输方式足够直接。
我很快在 NovaScale 里做了一个原型,app-server 路径也足够稳定,后来成为 1.5.0 版本的一部分。我还发现它可以支持小文件上传。这带来了一个意外的工作流:如果我看到有趣的植物或花,可以用手机拍照,把照片发送到 Mac 上的 Codex app-server,然后让 Codex 描述它。图片会落在同一次任务创建的工作区里。
这里也有实际限制。WebSocket 路径使用文本帧,因此二进制文件需要 base64 编码。对于小图片或文档这没问题,但它不能替代大文件传输。
OpenAI 自己的远程 Codex
OpenAI 现在也有自己的远程 Codex 方案。官方 Remote Connections 文档描述了从另一台设备使用 Codex 的方式,包括通过 ChatGPT 手机 App 控制已连接的 Mac 或 Windows 主机,以及通过 Codex App 连接 SSH 主机上的项目。
这个产品方向是合理的。被连接的主机提供文件、凭证、本地工具、插件、MCP server、浏览器访问和安全设置,而手机负责发送提示、审批和后续消息。OpenAI 还记录了用于可信设备的安全 relay 层,避免把 app-server 传输直接暴露到公网。
NovaScale 并不是想替代它。我的目标更窄:为已经把机器放在私有 Tailnet 中的用户提供一个 Tailscale-native 工作流,让他们可以从同一个 iOS 运维 App 里使用 SSH、监控、内网 Web 访问和 Codex。
我仍然把 NovaScale 的 Codex 集成标记为实验性。它现在已经有用,但 app-server 的 WebSocket 传输在文档中仍是实验性的,而且我希望用户在不需要时可以移除这个 tab。iOS 上的 tab 空间很宝贵。
底层工作方式
Codex app-server
在第一个原型阶段,我可以用这样的终端命令启动监听:
codex app-server --listen ws://100.64.x.y:14500
这已经足够让 NovaScale 连接到 Tailnet 内的一台主机,并与 Codex app-server 通信。
后来,在更新 Codex CLI 后,当我尝试在没有认证的情况下使用非 loopback WebSocket 监听时,看到了更严格的启动错误:
Error: refusing to start non-loopback websocket listener 100.64.x.y:14500 without auth; configure `--ws-auth capability-token` or `--ws-auth signed-bearer-token`
这是正确的方向。即使 Tailscale 已经在底层用 WireGuard 保护了我的 Tailnet,非 loopback 服务也仍然应该有自己的认证层。这也推动我在发布 NovaScale 1.5.0 前,从临时终端命令切换到更明确的部署方式。
官方 app-server 文档描述了两种 WebSocket 认证模式:
capability-token,基于 token 文件或 SHA-256 verifiersigned-bearer-token,基于共享密钥和 JWT 风格的 bearer token
对 NovaScale 来说,实际流程是:
- 在主机上安装 Codex CLI。
- 在 Tailnet 地址上以服务形式启动
codex app-server。 - 为监听启用 WebSocket 认证。
- 将主机 URL 和 token 导入 NovaScale。
- 使用 NovaScale 创建或恢复线程,并流式接收 turn 事件。
现在让 agent 写一个 macOS LaunchAgent 或 Linux systemd unit 已经很容易,但我希望有一条可重复的路径。所以我把设置流程包装成了自动化脚本:
NovaScale Codex bootstrap script on GitHub
脚本完成后,如果系统中有可用的 QR 编码器,它会尝试渲染二维码。它也会打印一个可复制粘贴到 App 中的 NovaScale URI。
我也把这个流程直接加入了 NovaScale。如果你已经为 Tailnet 中的某台主机配置了 SSH,可以在 App 里选择该主机。NovaScale 会通过 SSH 连接,安装服务,并导入生成的 Codex 主机配置。
App 集成
App 侧比较简单,因为 NovaScale 已经内置了 Tailscale 连接能力。
NovaScale 会直接通过私有 Tailnet 访问 codex app-server 暴露的 API。App 发送初始化、线程操作和 turn 启动等 JSON-RPC 请求。服务器会流式返回事件,因此 UI 可以展示消息、工具进度、审批提示和完成状态。
更难的不是传输层,而是让聊天和 agent 体验在 iOS 上足够快速、沉浸和原生。
NovaScale 1.5.0 只是开始。我会在实际使用中继续改进交互模型。
目前缺失和不理想的地方
任何方案都有取舍。NovaScale 目前为每个配置的主机使用一个独立的 app-server 服务。该服务可以加载 Codex 状态、列出或恢复线程、启动 turn、流式返回事件,并在 App 内展示审批提示。
限制在于,它并不是用户可能同时打开的另一个 Codex CLI 或 Codex Desktop 进程的同一个运行时。如果另一个进程里有待审批事项,NovaScale 无法审批那个进程内存中的状态。NovaScale 可以显示和管理通过自己 app-server 连接启动的 turn 的审批,但不同运行时仍然是不同运行时。
好消息是线程仍然可以恢复。我可以从 NovaScale 在同一个线程上开始新的 turn,询问当前状态,并继续工作。
另一个限制是,本地 Codex CLI 不会自动成为 NovaScale 关联 app-server 中变更的实时镜像。当我回到主机屏幕时,通常会运行 codex resume 来接上线程。
这还不理想。我想继续探索 NovaScale 是否最终可以与其他客户端共享同一个 app-server 运行时,或者采用一种更接近 Codex 文档中远程模式的架构。
待改进工作
NovaScale 目前会在 turn 需要注意时使用 App 内通知和 iOS 本地通知,例如需要审批时。
这可以工作,但对于长时间运行的后台任务还不够可靠。iOS 可能会在远程 Codex 主机继续工作时挂起或终止 NovaScale。
更可靠的做法是使用 Apple Push Notification service。我看到两种可能设计:
- 运行一个 push notification service,让用户配置 Codex hooks,或在
AGENTS.md中写入持久说明,在重要事件发生时通知该服务。 - 用一个开源 daemon 替代原始 app-server 服务。这个 daemon 包装 app-server,在 NovaScale 和 Codex 之间代理流量,监听 turn 进度,并在需要用户注意时发送 push notification。
我目前更倾向第二种方案。它让主机侧集成保持明确,而 daemon 仍然可以通过私有 Tailnet 直接与 NovaScale 通信。
结语
现在远程使用 Codex 已经有很多方式。对于希望在 iOS 运维 App 中获得 Tailscale-native 工作流的用户来说,NovaScale 可以成为一个有用的选择。
我会继续改进它。如果你有建议或发现 bug,欢迎给我们发邮件,或在 App Store 留下评论。我会认真阅读这些反馈,并用它们指导下一轮工作。