Hermes Mobile 通过短命 opaque 配对码而非 QR 码内置 API 密钥的方式配对手机与本地 Agent,避免密钥泄露风险。
本地 AI Agent 的远程控制带来一个棘手的安全问题:手机需要足够的信息来发现并认证到电脑,但 QR 码并不适合存放一个长期有效的 API Key。QR payload 可能会出现在截图、相机历史、调试输出或复制后的 deep link URL 中。
我开发了 Hermes Mobile,一个用于控制运行在本地电脑上的 Hermes Agent 的 Android 和 iOS 客户端。当前的配对流程使用一个短期有效的、不透明的配对码,而不是将网关密钥直接嵌入 deep link。以下是这一实现模式、其权衡取舍,以及一种可复现的验证方式。
手机运行移动客户端。Mac 或其他电脑在 8642 端口运行 Hermes 网关。Wi-Fi 和 Tailscale 只在两者之间提供网络路由:
手机客户端 -> 私有 Wi-Fi 或 Tailscale -> 电脑网关 :8642
这一区别在设备发现阶段非常重要。电脑选择器应当列出响应为 Hermes 网关的机器,而不是 tailnet 上所有可见的设备。手持设备自身的 Tailscale 地址会被故意过滤掉,以免应用把自己提供给自己连接。
设置链接携带两段配对信息:
hermes://setup?pairCode=HTYH6PSW&pairServer=http%3A%2F%2F100.x.y.z%3A8765
pairCode 是一个不透明的一次性值。pairServer 则标识了可以兑换它的私有端点。长期有效的网关凭证并非此路径中的查询参数。
在手机端,交换过程刻意保持精简。简化到其核心操作,就是:
const response = await fetch(
`${pairServer}/pair-exchange?code=${encodeURIComponent(pairCode)}`
);
if (!response.ok) return null;
const gatewayConfig = await response.json();
交换成功后,Hermes Mobile 将返回的密钥通过平台安全存储(由 Android Keystore 或 iOS Keychain 提供支持)保存下来。此后手机使用返回的网关 URL 和密钥进行认证的健康检查和会话。
电脑端在内存中维护配对码映射表,包含过期时间和已消费标志。一次有效的交换会消费该码。第二次使用相同码的请求会失败。未知的和过期的码也会失败。
当前的配对页面展示的码最大生命周期为二十分钟,以便用户有时间在页面间切换,但页面每分钟会刷新一次显示的码。重要的特性不仅是码本身很短,而是首次交换成功后即拒绝重放。
这一方案并不能神奇地让不可信网络变得安全。交换端点仍然只能通过你控制的路由访问,例如同一私有 Wi-Fi 网络或你的 Tailscale tailnet。如果配对服务器暴露在公网,或者电脑本身已被入侵,不透明的码并不能提供完整的防御。
你可以在不打印真实凭证的情况下测试这个模式:
Hermes 包含确定性测试用例,覆盖自对等过滤、一次性拒绝、未知码拒绝和过期场景。这些测试很有用,因为仅靠一个绿色的连接指示灯无法证明凭证没有泄露到 QR payload 中,也无法证明重放保护真正生效。
同样的设计适用于本地开发工具、家庭实验室仪表盘、自托管助手和设备控制应用。将发现与授权分离:发现一个 IP 地址并不等于授权它,在 Tailscale 上看到一台设备也不能证明它运行的是你需要的服务。
用一个不透明的、会过期的能力进行配对;通过私有路由交换一次;验证服务和凭证;然后将结果密钥存入平台凭证存储。这是一个小型协议,但它从设置流程中去除了一个出乎意料地常见的凭证泄露。
如果你想使用这个工作流而不是自己构建移动客户端,Hermes Mobile 是一个付费的 Android 应用。