跳转至

远程 GPU 后端

在一台机器(GPU 机器、家庭服务器)上运行 VoiceStudio 后端,然后从另一台机器的桌面应用或浏览器驱动它——通过你的 tailnet,推理仍留在性能强大的机器上。

你是通过自己的脚本调用 API,而不是使用桌面应用?请参阅 docs/api-auth.md,这是一份面向使用者的参考文档,涵盖每个认证门禁(共享 PIN、API 密钥、听写 WebSocket、受信网络),并说明确切的请求头、参数以及 401/403/429 的含义。

本页介绍保留的远程后端模式:整个后端及其项目在另一台机器上运行。它与网络共享(Network sharing)不同,后者将当前后端的 UI 提供给局域网内的其他设备。

这是可选功能,默认关闭:未设置 API key 时,后端仍然像以前一样仅允许回环访问。

形态

┌──────────────┐     tailnet (WireGuard)      ┌─────────────────────┐
│ laptop        │  ws/https to MagicDNS URL   │ gpu-box              │
│ VoiceStudio UI  │ ──────────────────────────▶ │ VoiceStudio backend    │
│ (thin client) │  short-lived session/ticket │ OMNIVOICE_API_KEY set │
└──────────────┘                              └─────────────────────┘

桌面应用就是瘦客户端——没有单独的二进制文件。你在设置中输入后端 URL 和 API key。该密钥只兑换一次,获得一个短期会话;普通 HTTP 请求使用该会话,WebSocket 使用绑定路径的一次性票据。主密钥永远不会被存储或放入 URL。

1. 在 GPU 机器上:使用密钥运行后端

生成一个密钥,并以后端已设置该密钥的方式启动:

export OMNIVOICE_API_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(24))')"
export OMNIVOICE_SERVER_MODE=1          # headless: relaxes the loopback admin gate
uv run uvicorn backend.main:app --host 0.0.0.0 --port 3900

Docker 镜像也是同样的思路——传入 -e OMNIVOICE_API_KEY=…。

如果浏览器要从与后端不同的源加载 UI(例如通过该机器的局域网 IP 打开 :3901 上的 Vite 开发服务器),你还需要后端 CORS 允许列表包含该源——参见 来自其他源的浏览器(CORS);服务器模式和受信网络都不涵盖 CORS。

当设置了 OMNIVOICE_API_KEY 时,每个非回环请求都需要被接受的凭据。脚本应使用 Authorization: Bearer <key>。为兼容起见,旧的 ?api_key= 和 ov_key 传递方式仍被接受,但后端不再创建主密钥 cookie,且内置 UI 仅使用短期会话。机器本身上的回环流量仍然不受门禁限制。

2. 通过 Tailscale 访问

在两台机器上都安装 Tailscale(其客户端是 BSD-3 开源;如果你想要完全开源的方案,可以用 headscale 自托管控制平面)。然后,该机器可通过其 MagicDNS 名称访问:

http://gpu-box.your-tailnet.ts.net:3900

对于 TLS(推荐——见下文警告),请将该端口置于机器上的 Tailscale Serve 之后:

tailscale serve 3900
# now reachable at https://gpu-box.your-tailnet.ts.net

Serve 在节点上终止连接并从 127.0.0.1 转发,因此对后端来说,该请求看起来像是回环请求——这正是该路径仍然需要 API key 的原因(bearer 门禁在非本地暴露场景下不依赖来源地址;设置密钥后,它始终适用于受密钥保护的客户端)。Uvicorn 默认信任来自回环的代理头,因此 Serve 转发的 HTTPS scheme 会成为权威的 ASGI scheme,浏览器会话 cookie 会收到 Secure。对于非回环反向代理,请显式配置 Uvicorn 的 --forwarded-allow-ips=<proxy-ip>;应用本身从不信任任意的 X-Forwarded-Proto 头。

不要为此使用 tailscale funnel(公共互联网暴露)。即使有密钥,语音克隆后端也不应出现在开放的互联网上。

3. 在应用中指向远程

设置 → 共享 → 远程后端:

  • 后端 URL:第 2 步中的 MagicDNS URL(如果你没有使用 Serve,则带 :3900;如果使用了,则不带端口)。
  • API key:第 1 步中 OMNIVOICE_API_KEY 的值。
  • 测试连接:不使用凭据请求免认证的 {url}/health,如果健康检查成功,则将输入的密钥兑换为会话。
  • 保存并重新加载:仅存储 URL,并针对远程后端重启 UI。密钥输入在一次兑换后被清空。URL 必须是完整的 http:// 或 https:// URL(单独的 gpu-box:3900 会被拒绝);保存未通过测试连接的 URL 时会先要求确认——错误的基地址会让应用无法访问任何后端,直到你在此处改回来。

将 URL 留空即可回到本地后端。

从浏览器使用(无需桌面应用)

你也可以用普通浏览器驱动远程后端——在 fragment 中带密钥打开一次 URL:

https://gpu-box.your-tailnet.ts.net/#api_key=<key>

刻意使用 fragment(#,而不是 ?):fragment 永远不会发送到服务器,因此密钥不会出现在 GPU 机器或任何反向代理的请求日志中。fragment 会被同步清除,然后密钥兑换一次,获得最长八小时的会话;主密钥不会被存储。如果你的密钥包含 +、&、# 或 =,请进行 URL 编码(例如 #api_key=a%2Bb);来自上述 secrets.token_urlsafe 的密钥无需编码。此后,UI 会正常加载并使用短期会话。跨源的 bearer 会话以标签页为作用域;关闭标签页后需要重新输入。如果某个请求再次返回 401(密钥过期/错误/轮换),系统会提示你重新输入。当访问门禁是网络共享(而非远程密钥)时,同一个门禁会改为显示局域网共享 PIN 提示。

安全说明

  • 明文 HTTP 可被嗅探。 在敌对网络上通过 http:// 传输的 bearer 密钥可能被从线路上读取。对于完全可信局域网以外的任何场景,请使用 Tailscale(WireGuard 加密)或 Tailscale Serve(TLS)。
  • 第一方 UI 从不持久化 OMNIVOICE_API_KEY,从不创建包含它的 URL,也从不将其管理员会话放入 WebSocket URL。WebSocket 票据在 30 秒后过期,并且只能在一个路径上使用一次。
  • API key 和局域网共享 PIN 相互独立:PIN 保护临时共享会话,而密钥是持久化的远程凭据。两者任一都可启用;设置后都会被检查。
  • 除非在机器上设置 OMNIVOICE_SERVER_MODE=1,否则管理路由(/system/*、/api/settings/*)仍受回环门禁限制;在服务器模式下,API key 也是这些路由的访问控制(短共享 PIN 仅用于消费,不门禁管理)——参见下方的凭据规则。
  • 使用 OMNIVOICE_TRUSTED_NETWORKS 信任局域网或反向代理。 如果你在反向代理(nginx、Caddy、NPM)后面运行 VoiceStudio,或者只把它暴露在受信任的局域网/Tailnet 上,请将 OMNIVOICE_TRUSTED_NETWORKS 设置为逗号分隔的 CIDR 列表(例如 192.168.1.0/24,10.0.0.0/8);来自这些网络的客户端随后会被消费门禁(共享 PIN、API key、听写 WebSocket)视为受信任,无需密钥/PIN。转发原始 Host 头的代理(Caddy 和 Nginx Proxy Manager 会这样做)还需要将其主机名加入 OMNIVOICE_ALLOWED_HOSTS,否则没有 API key 的请求会因主机名无法识别而被拒绝;IP 地址、localhost 和 *.ts.net 名称无需条目(参见 主机名)。管理路由(/system/*、/api/settings/*)仍然严格仅限回环——如需无头管理,请使用 OMNIVOICE_SERVER_MODE=1。这是 OMNIVOICE_SERVER_MODE=1(信任所有非回环来源)的细粒度替代方案,也能绕过会剥离 Authorization 头的代理。默认为空——不改变严格的回环默认值。受信任网络成员资格只是消费豁免——它本身永远不会解锁管理路由,即使在服务器模式下也是如此(#1213)。 与 OMNIVOICE_SERVER_MODE=1 结合使用时,未提供任何凭据的受信任网络客户端在管理路由上仍会收到 403(除非完全没有配置凭据,即裸 Docker #261 流程,此时管理路由开放);如果设置了凭据,则只有 API key——而不是共享 PIN——能访问管理路由。完整的双层模型参见 api-auth.md。
  • 密钥以恒定时间比较,且永远不会被记录。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 om/VoiceStudio