集群管理与 vphone-agent
集群管理与 vphone-agent
vphone 集群让你从一台 Mac 的 vphone-manager 界面管理多台 Mac 上的虚拟 iPhone。每个节点运行一个 headless 守护进程 vphone-agent,以 HTTP API(Bearer token 认证)暴露本机的 VM 库与生命周期操作;控制端(vphone-manager)通过 Cluster 面板连接并管理这些节点。
架构速览
agent 本身不触碰 Virtualization.framework、不需要私有 entitlements——VM 由它拉起的签名 vphone-cli 子进程承担,因此 agent 可以正常签名分发到任意 Mac 上运行。agent 重启也不影响其托管的 VM(重新通过 lsof 发现)。
构建与启动
构建
make agent_build
# 产出 .build/release/vphone-agent(release,纯用户态 daemon)
启动(节点侧)
# 默认:127.0.0.1:8740,token 自动生成到 ~/.config/vphone-agent/token(0600)
.build/release/vphone-agent
# 跨机管理:必须显式绑定全部接口
.build/release/vphone-agent --bind 0.0.0.0 --port 8740 --token <your-token>
⚠️ 关键注意事项:--bind 默认值 127.0.0.1 是真 loopback——其他机器完全无法连上。要做跨机管理,必须显式加 --bind 0.0.0.0。该参数同时作用于主 HTTP 端口与屏幕流 / SSH / 事件推送等临时 TCP 端口。
认证 token
- 所有 API 请求都要求
Authorization: Bearer <token>,缺失或错误一律返回 401。 - 三种提供方式:
--token <token>显式指定;--token-file <path>;或缺省时自动生成并落盘~/.config/vphone-agent/token(32 字节随机数的 hex,权限 0600)。 - 一个 token 即拥有该节点全部 VM 的操作权限(单操作者假设),请按此粒度分发与保管。
冒烟验证
在任意机器上用 curl 验证节点可用:
TOKEN=$(cat ~/.config/vphone-agent/token)
curl -H "Authorization: Bearer $TOKEN" http://<node-ip>:8740/api/v1/status
# → {"ok":true,"apiVersion":1,"protocolVersion":"2","hostname":"...", "vmCount":...}
curl -H "Authorization: Bearer $TOKEN" http://<node-ip>:8740/api/v1/vms
# → 节点上的 VM 列表
status 响应里的 protocolVersion 是协议契约版本(当前 "2"),控制端与节点不匹配时会拒绝通信并提示升级。
控制端:Cluster 面板
在 vphone-manager 中打开工具栏的 Cluster:
- 点 Add Node…,填入节点 IP/主机名、端口(默认 8740)、token。
- Test Connection 验证连通与认证通过后保存。
- 节点卡片会显示在线状态、VM 列表、链路质量(RTT / 带宽)与 agent 版本徽标(有更新时提示,可用工具栏 Sync Agents… 把内置 agent 二进制推送到节点完成远程升级)。
除手动添加外,同一局域网内运行 agent 的节点会通过 mDNS(_vphone-node._tcp)自动出现在发现列表中,也可用 6 位配对码免手填 token 完成配对。
添加节点后,远程 VM 会与本机 VM 一起出现在 Library 侧栏与 Gallery 图库中,Start/Stop、Console 屏幕流、SSH、文件、快照等操作与本地 VM 体验一致。

节点掉线与自愈
控制端对每个节点做掉线记账,解决节点彻底离线后侧栏 / 图库残留幽灵 VM 的问题:
- 标记离线缓存:连续 3 次轮询失败(默认 15 s 周期下约 45 s)→ 节点进入 stale 状态,其 VM 显示「离线缓存」amber 徽标(数据仍是缓存值,节点恢复即消失)。
- 清空缓存:连续 6 次失败(约 90 s)→ 该节点的 VM 缓存列表整体清空,幽灵 VM 从侧栏与图库消失。
- 一次成功即恢复:轮询成功或事件流任意一帧到达都会清零计数并回填缓存。
控制端还内置瞬时网络错误重试:幂等 GET 遇超时 / 断连 / DNS 失败等瞬时错误自动重试(最多 2 次、250 / 500 ms 退避);POST / PUT / DELETE 有副作用,绝不重试。
典型 API 一览
| 类别 | 端点示例 |
|---|---|
| 节点状态 | GET /api/v1/status / health / stats / logs |
| VM 列表 | GET /api/v1/vms |
| 生命周期 | POST /api/v1/vms/{name}/start / stop / force-stop / reset |
| 配置读写 | GET / PUT /api/v1/vms/{name}/config、proxy-config |
| 创建 VM | POST /api/v1/vms/create(异步任务,可轮询事件与取消;installKeyboard/extraIPAs 可带键盘开关与附加 IPA) |
| IPA 暂存上传 | PUT /api/v1/staging/ipa/{sha256}——远程创建前把附加 IPA 上传到节点(header X-Expected-SHA256 须与路径一致),返回的 stagedPath 填进创建请求的 extraIPAs,任务结束后节点自动清理 |
| vphoned 热升级 | PUT /api/v1/vms/{name}/vphoned——把新签名 vphoned 二进制暂存到节点上该 VM 的 .vphoned.signed(X-Expected-SHA256 头门禁;运行中 / 已停止均可);仅 regular 变体 VM 放行——内置二进制以 regular entitlements 签名,推给其他变体会覆盖变体专属守护进程 |
| 快照 | POST /api/v1/vms/{name}/snapshots、…/snapshots/{snap}/restore |
| 端口转发 | POST /api/v1/vms/{name}/forward、forward/remove、GET /api/v1/vms/{name}/forwards——把远程 VM 的 guest 端口映射到控制端本机回环(仅 standalone agent,内嵌 agent 返回 404) |
| 固件 | GET /api/v1/firmware/entries、POST /api/v1/firmware/download/{id} |
安全模型与注意事项
- 授权粒度:一个 token = 节点全权。没有 per-VM / per-command 的细分授权。
- 传输加密:裸 HTTP 只适用于可信内网。跨站点部署请使用
--tls-cert/--tls-key(PEM,成对提供)启用 TLS,或走 Tailscale/WireGuard 隧道。 - 临时端口:屏幕流、SSH 代理、事件推送与端口转发 proxy 使用按需分配的临时 TCP 端口,各自有独立的 per-port token 握手鉴权(与 Bearer token 同信任级),绑定地址跟随
--bind。 - fail-fast 版本纪律:协议版本不匹配的节点会在配对/连接阶段被明确拒绝,而不是静默降级。
- 传输层加固:并发连接上限 256;分相位空闲超时——请求读取连续 30 s 无新字节(慢速半包攻击防护)或 keep-alive 空闲 120 s 即断开(事件流等长连接豁免);token 轮换运行期即时生效,无需重启 agent。
深入阅读
- 完整路由表、配对协议与事件推送细节:仓库
docs/cluster.md - 控制端 GUI(vphone-manager)整体说明:仓库
docs/manager.md