# NovaScale 中的遠端 Codex

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

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 文字 frame 傳送。
- 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 路徑使用文字 frame，因此二進位檔案需要 base64 編碼。對於小圖片或文件這沒問題，但它不能替代大檔案傳輸。

## OpenAI 自己的遠端 Codex

OpenAI 現在也有自己的遠端 Codex 方案。官方 [Remote Connections 文件](https://developers.openai.com/codex/remote-connections)描述了從另一台裝置使用 Codex 的方式，包括透過 ChatGPT 手機 App 控制已連接的 Mac 或 Windows 主機，以及透過 Codex App 連接 SSH 主機上的專案。

這個產品方向是合理的。被連接的主機提供檔案、憑證、本機工具、插件、MCP server、瀏覽器存取和安全設定，而手機負責傳送 prompt、核准和後續訊息。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 編碼器，它會嘗試渲染 QR code。它也會列印一個可複製貼上到 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 程序的同一個 runtime。如果另一個程序裡有待核准事項，NovaScale 無法核准那個程序記憶體中的狀態。NovaScale 可以顯示和管理透過自己 app-server 連接啟動的 turn 的核准，但不同 runtime 仍然是不同 runtime。

好消息是執行緒仍然可以恢復。我可以從 NovaScale 在同一個執行緒上開始新的 turn，詢問目前狀態，並繼續工作。

另一個限制是，本機 Codex CLI 不會自動成為 NovaScale 關聯 app-server 中變更的即時鏡像。當我回到主機畫面時，通常會執行 `codex resume` 來接上執行緒。

這還不理想。我想繼續探索 NovaScale 是否最終可以與其他客戶端共享同一個 app-server runtime，或者採用一種更接近 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 留下評論。我會認真閱讀這些回饋，並用它們指導下一輪工作。


