返回博客

Tauri 2 多构建变体 ACL 报错:remote.urls 是权限门,不是标签

桌面应用要同时发两个构建变体:一个指向线上域名,一个指向局域网地址。本地开发一切正常,但给 LAN 构建包装好一发,一启动就报:

Command sidecar_call not allowed by ACL

启动页卡死,什么 UI 都不显示。换回 prod 包,同一台机器,同一个代码库,一切正常。

现象

  • tauriInvoke('sidecar_call', …) 在 prod 构建下正常返回
  • LAN 构建下同样的调用被 ACL 拦截,错误信息 Command sidecar_call not allowed by ACL
  • 启动时需要 sidecar 的 bootstrap 页面一进入就进 catch,界面一片空白
  • RUST_LOG=tauri=debug 跑,日志里能看到只有 lan capability 对当前窗口生效

根因

Tauri 2 的 capability 文件里,remote.urls 字段是一个源门(origin gate):这份 capability 的所有权限,只在当前 webview 加载的 URL 匹配其中某个 glob 时才激活

重点:多个 capability 文件的权限不会跨文件自动合并

典型的误解配置长这样:

// capabilities/default.json
{
  "identifier": "default",
  "windows": ["main"],
  "remote": { "urls": ["https://app.example.com/*"] },
  "permissions": [
    "core:default",
    "shell:allow-execute",
    "allow-sidecar-call"
    // ...完整权限集
  ]
}
// capabilities/lan.json
{
  "identifier": "lan",
  "windows": ["main"],
  "remote": { "urls": ["http://192.168.x.x/*"] },
  "permissions": [
    "updater:default"   // 只加了一条「看起来 LAN 才需要的」权限
  ]
}

LAN 构建加载的是 http://192.168.x.x,不匹配 default.jsonremote.urls,所以 default.json 对这个窗口完全无效。allow-sidecar-call 没有被任何激活的 capability 声明,ACL 自然拒绝。

坑:以为 capability 是「叠加标签」,实际是「独立开关」

每一个 capability 文件只在其 remote.urls 命中当前 webview 来源时才生效。 不同来源的 capability 之间权限不互通,必须在每个文件里重复声明所有需要的权限。 失败无声——没有「此来源权限不足」的提示,只有 ACL 拒绝指令名称。

正解

方案 A — 在每个来源的 capability 里重复完整权限集(小项目推荐)

default.json 里的完整权限列表原封不动复制到 lan.json

// capabilities/lan.json
{
  "$schema": "https://schema.tauri.app/config/2",
  "identifier": "lan",
  "windows": ["main"],
  "remote": { "urls": ["http://192.168.x.x/*"] },
  "permissions": [
    "core:default",
    "core:window:default",
    "core:webview:default",
    "core:event:default",
    "core:app:default",
    "updater:default",
    "process:default",
    "process:allow-restart",
    "shell:allow-execute",
    "allow-sidecar-call"
  ]
}

方案 B — 拆分「常驻权限」与「来源白名单」(权限集较大时推荐)

新建一个不带 remote.urls 的 capability 文件,只放通用权限;各来源文件只声明 remote.urls,不重复权限列表。没有 remote.urls 的 capability 对所有窗口的本地内容生效,配合来源文件共同覆盖所有场景。

注意:如果 capability 文件是构建脚本从模板渲染生成的(输出文件被 .gitignore), 要改模板,不要直接改生成文件——否则下次构建会被覆盖。

正解:每个来源的 capability 必须自带完整权限集

修完之后记得完整重新构建——capability JSON 在构建时编译进二进制,运行时修改磁盘上的文件对已安装的包无效。 用 RUST_LOG=tauri=debug 启动,日志会打印哪些 capability 对当前窗口生效,可以直接验证。

排查备忘

  1. RUST_LOG=tauri=debug 启动,搜日志里的 capability,确认哪些文件命中了当前窗口
  2. 对比命中的 capability 的 remote.urls glob 和实际加载的 URL(协议、主机、端口、路径都要精确匹配,大小写敏感)
  3. 检查错误出现的 capability 文件里是否声明了对应的 allow-<command-name> 权限

一句话外卖

Tauri 2 的 remote.urls 是每个 capability 自己的激活开关,多源应用必须在每个来源的 capability 里重申全量权限,没有「继承」也没有「合并」。