Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

分发插件

你构建了一个插件;现在它需要离开你的机器,而不必要求安装它的人盲目信任你。ZeroClaw 的分发方案有两个独立层面:Ed25519 清单签名(是谁发布的)和注册表安装(它如何到达那里)。本页涵盖这两者,并结合 crates/zeroclaw-plugins/src/signature.rssrc/plugin_registry.rs 以及 host.rs 中的安装路径进行核对。

签名

什么是已签名的

签名涵盖规范清单字节。主机会解析 TOML,仅移除根级别中完全匹配的 signaturepublisher_key 条目,保留文档的其余部分,并去除末尾空行(signature.rs 中的 canonical_manifest_bytes)。这使签名可以自嵌入:在不包含这些根字段的情况下对清单签名,然后添加这些字段;验证时会先移除它们再进行检查。

值得了解的两个后果:

  • .wasm 组件本身受签名保护。签名所证明的是清单:发布者所担保的名称、版本、能力和权限。当制品在传输过程中需要保证完整性时,请将其与注册表 sha256 摘要(如下)配对使用。
  • 名称类似于 config_schema.properties.signature 的嵌套字段,以及同样带此前缀的根字段(如 signature_algorithm),仍会被纳入签名。对保留内容重新格式化或重新排序会使签名失效。请在清单最终确定后再进行签名。
  • 因此,诸如 config_schema.properties.api_token.x-secret 之类的配置暴露标记属于受签名保护的策略。在公开配置暴露和受作用域限制的 secrets.get 访问之间切换工具或通道属性,需要重新构建并重新签名。
  • 由之前的基于前缀的规范化器签名的软件包,仅当它们依赖于其中某个边界情况,或依赖于附加在已移除字段上的 TOML 装饰时,才需要重新签名。普通清单的签名内容保持不变。

键和进程

签名使用 Ed25519,并通过主机用于验证的同一 ring 基元实现。签名采用 base64url(无填充);公钥使用十六进制编码。该 crate 暴露了完整工具链(signature.rs):generate_signing_key 生成一个 PKCS#8 密钥对及其十六进制公钥,sign_manifest 对规范化字节生成 base64url 签名,而 public_key_hex 则从已存储的私钥中恢复公钥。当前没有用于签名的 CLI 封装;发布者会在其发布流水线中通过一个简短的 Rust 辅助程序来驱动这些函数。

签名后的清单随后会携带两个额外的 根级字段:signature(base64url 值)和 publisher_key(你的十六进制公钥)。将二者都放在第一个表头(包括 [config_schema])之前;根据 TOML 规则,在表头之后追加它们会使其成为该表的成员,主机将看到一个未签名的清单。

name = "my-plugin"
version = "0.1.0"
signature = "<base64url-signature>"
publisher_key = "<hex-public-key>"

[config_schema]
type = "object"
properties = {}
additionalProperties = false

希望信任您的运维人员会将该十六进制密钥添加到其 plugins.security.trusted_publisher_keys 列表中:

zeroclaw config set plugins.security.signature_mode strict
zeroclaw config set plugins.security.trusted_publisher_keys '["<your-key-hex>"]'

验证的行为如何

验证在发现和安装时都会运行(enforce_signature_policyhost.rs 调用);发现阶段会跳过失败的插件并记录日志,安装阶段则返回错误。从操作者角度看,模式矩阵如下:

模式无符号已签名,但密钥不受信任已签名,签名无效已签名并受信任
disabled加载已加载,未检查已加载,未检查已加载,未检查
permissive加载时带有警告加载时带有警告加载时带有警告已加载,已验证
strict已拒绝已拒绝已拒绝加载

注意 strict 对你作为发布者的含义:严格模式下的运营方只有在你的精确 key 位于其受信任集合中清单字节校验通过时才会加载你的插件。任何签名后的清单修改,无论是你自己还是分发路径中的任何人所做,都会导致安装失效。这就是其目的。

注册发布

安装路径是本地插件目录;registry 只是一个在命令执行时查询的 JSON 索引(zeroclaw plugin search / install)。这两个命令仅存在于编译时包含插件宿主的二进制文件中(参见 build features);预构建的发布二进制文件不包含此功能。默认索引为 zeroclaw-labs/zeroclaw-plugins 仓库的 registry.json;私有 registry 只需提供一个 URL 即可使用(每条命令可通过 --registry <url> 指定,或使用 ZEROCLAW_PLUGIN_REGISTRY_URL 环境变量,按照 src/plugin_registry.rsregistry_url 所定义的优先级顺序解析)。

一个注册表条目(crates/zeroclaw-plugins/src/registry.rs 中的 PluginRegistryEntry)包含:nameversion、可选的 descriptionauthorcapabilities、归档 url,以及 zip 的可选 sha256 摘要。

归档契约

zeroclaw plugin install <name> 会解析入口,下载 zip,在存在摘要时验证摘要,安全解压,并将解压后的目录交给本地安装所使用的同一 PluginHost::install 路径。解压过程在设计上具有防御性(src/plugin_registry.rs),你的归档必须能通过它:

  • zip 必须包含一个根级别的 manifest.toml,或者恰好一个包含该文件的嵌套插件目录。没有 manifest 或者多于一个都会导致归档被拒绝。
  • 带有路径穿越、绝对路径或 Windows 驱动器前缀的条目名称会被拒绝。
  • 下载在流式传输时受限(50 MiB),因此一个不提供 Content-Length 的服务器无法强制进行无限缓冲;解压也受同一上限限制,因此 zip bomb 无法无限展开。

版本解析:当安装程序收到一个裸名称时,它会在索引中选择最后一个匹配的条目;固定的 name@version 会精确选择该版本。请有意地在你的注册表中按顺序排列重复的名称,最旧的放在前面。

搜索不是信任边界

zeroclaw plugin search 是对索引的未认证发现;它绝不会安装、启用或执行任何内容。安全性发生在安装时:摘要校验、安全解压、清单验证,以及操作员的签名策略,与本地路径安装完全相同。因此应相应地发布:在安装之前,默认认为一切都是不受信任的传输。

发布者清单

[!IMPORTANT] 编译后的 .wasm.cwasm 文件是二进制构件,通常每个都有数 MB。不要在没有 Git LFS 的情况下将它们提交到 git 源树中:每次重建都作为普通 blob 提交,会永久膨胀仓库历史,并且 git diff/审查工具会因此卡住。把它们当作其他构建输出一样处理:将 target/*.wasm/*.cwasm 添加到 .gitignore,并改为通过发布构件或插件注册表归档分发。如果某个构件确实必须留在树中,请在首次提交之前使用 LFS 跟踪该模式(git lfs track "*.wasm")。

  1. 完成清单:名称、版本、功能,以及代码使用的最小权限集。
  2. 构建组件;对于 skill bundles,验证每个 SKILL.md 的 frontmatter(发现会强制要求 namedescription)。
  3. 签名:生成或加载你的 Ed25519 密钥,对规范化的 manifest 字节进行签名,嵌入 signaturepublisher_key
  4. 压缩插件目录(一个清单,无路径技巧,低于 50 MiB)。
  5. 计算 zip 的 SHA-256 并发布带有该摘要的 registry 条目。
  6. 将你的公钥十六进制值发布在某个地方,以便操作者可以独立于 registry 进行验证(你的 repository、你的网站)。在 strict 模式下,操作者信任的是密钥,而不是 registry。
  7. 每次发布时:提升 version,重新签名(版本行位于规范字节中),重新生成摘要,将新条目追加到旧条目之后。