# NovaScale 中的远程 Codex

Canonical: https://galaxnet.dev/zh-hans/nova/articles/remote-codex-in-novascale/
Language: zh-Hans

NovaScale 如何通过私有 Tailnet 上的 Codex app-server，把远程 Codex 工作流带到 iOS App 中。







## 背景

有时我在收到自托管 Grafana 告警后，需要直接用手机处理服务器运维问题。我可能需要检查某个服务、搜索错误原因，或者写一个小脚本。使用 AI 聊天 App 会有帮助，但我一直在想，NovaScale 是否可以提供一种更接近 agent 的界面，而不只是聊天界面。

这个想法在脑子里停留了一段时间，但当时还没有清晰的形态。

四月底，我决定取消 Anthropic Claude 订阅，因为 Codex 已经开始覆盖我大部分开发工作流。尤其是 Codex 不会因为五小时限制而打断正在进行的会话，这一点对我很重要。

后来我在 X 上看到有人提到 Codex 的 `app-server` 功能，于是开始研究。我阅读了[官方 app-server 文档](https://developers.openai.com/codex/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 文档](https://developers.openai.com/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 空间很宝贵。

## 底层工作方式

![NovaScale 与 Codex app-server 交互](/nova/remote-codex-app-server.svg?v=3)

### Codex app-server

在第一个原型阶段，我可以用这样的终端命令启动监听：

```bash
codex app-server --listen ws://100.64.x.y:14500
```

这已经足够让 NovaScale 连接到 Tailnet 内的一台主机，并与 Codex app-server 通信。

后来，在更新 Codex CLI 后，当我尝试在没有认证的情况下使用非 loopback WebSocket 监听时，看到了更严格的启动错误：

```text
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 文档](https://developers.openai.com/codex/app-server)描述了两种 WebSocket 认证模式：

- `capability-token`，基于 token 文件或 SHA-256 verifier
- `signed-bearer-token`，基于共享密钥和 JWT 风格的 bearer token

对 NovaScale 来说，实际流程是：

1. 在主机上安装 Codex CLI。
2. 在 Tailnet 地址上以服务形式启动 `codex app-server`。
3. 为监听启用 WebSocket 认证。
4. 将主机 URL 和 token 导入 NovaScale。
5. 使用 NovaScale 创建或恢复线程，并流式接收 turn 事件。

现在让 agent 写一个 macOS LaunchAgent 或 Linux systemd unit 已经很容易，但我希望有一条可重复的路径。所以我把设置流程包装成了自动化脚本：

[NovaScale Codex bootstrap script on GitHub](https://github.com/GalaxNet-Ltd/nova-codex-bootstrap)

脚本完成后，如果系统中有可用的 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](https://developers.openai.com/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 留下评论。我会认真阅读这些反馈，并用它们指导下一轮工作。


