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 文字 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 文件描述了從另一台裝置使用 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 空間很寶貴。
底層工作方式
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 編碼器,它會嘗試渲染 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,或在
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 留下評論。我會認真閱讀這些回饋,並用它們指導下一輪工作。