Files
qinglong/docs/adr/ADR-0393-generation-bound-plugin-package-secret-binding-ledger.md
T

6.1 KiB
Raw Blame History

ADR-0393:按 Generation 固定的 Plugin Package Secret 绑定账本

  • 状态:Accepted
  • 日期:2026-08-13
  • 关联 RFCQL-RFC-0001 D-305
  • 关联 ADRADR-0216、ADR-0221、ADR-0392

问题

Plugin Package Manifest 已能声明逻辑 Secret 需求,但 materialization 仍会拒绝任何 Secret requirement。仅把 Secret 名称传入执行器会留下三类不确定性:Secret rotation 会让同一 generation 在不同时间解析到不同值,跨 Project 引用可能越权,安装确认与实际执行也缺少可审计的同一事实。

QingLong 3.0 同时面向低配路由设备与集群节点。解决方案不能为 Local/Edge 引入 Secret watcher、常驻缓存、额外数据库连接或独立 daemon,也不能在集群数据库中向宽权限角色暴露 Secret 绑定事实。

决策

  1. 引入 qinglong/plugin-package-secret-binding@v1。一个不可变 binding 精确绑定一个 Package resource generation、Manifest digest 与 lock digest,并完整覆盖 Manifest 声明的所有 Secret requirement。
  2. binding 只保存 qlsecret:// 引用,不保存 Secret 明文。引用必须与 Package installation 位于同一 Project,并固定显式 versionrequired requirement 不允许为空,optional requirement 可以显式记录 null
  3. binding 必须携带 approved-action-executionlocal-owner-confirmation authority kind 及其 evidence digest。D-305 只定义并持久化 authority 事实,不新增绕过既有审批或 owner-confirmation 的入口。
  4. 发布时必须证明目标仍是当前 active installation head,且 installation、Project、Package、lock、generation 与 Manifest digest 全部一致。相同 generation 的完全相同内容幂等返回 existing;不同内容冲突并失败关闭。
  5. binding 使用 domain-separated SHA-256 摘要,持久 JSON 上限 64 KiB,最多沿用 Manifest 的 64 个 Secret requirement 上限。SQLite 与 PostgreSQL 分别追加同构 ledger,不改写历史 migration。
  6. Local 使用既有单写者 SQLite authorityCluster 仅向 ql3_package_executor 授予 ledger 的 SELECT, INSERTruntime、admin、package manager 与 worker ingress 均无权限。实现不新增 package、进程、timer、watcher、连接或常驻缓存。
  7. ledger 是历史事实,不直接成为 Package lifecycle 的 active-consumer blocker。D-306 在消费 binding 并产生实际 materialization/执行依赖时,再定义 Secret resolution、撤回与 rebinding 的生命周期规则。

方言映射

项目 Local / Edge / Standalone Cluster
migration 0091-plugin-package-secret-bindings + 0092-capability-v46 pg-0059-plugin-package-secret-bindings
capability plugin_package_secret_binding@1contract v46 plugin_package_secret_binding@1contract v58
写入串行化 既有 SQLite 单写者 authority PostgreSQL 唯一约束与 active-head 条件写入
资源增量 一个有界 append-only 表、三个索引 一个有界 append-only 表、三个索引
常驻开销 无新增进程、连接、timer、watcher、cache 无新增 Pod、worker、连接、timer、watcher、cache

明确不在 D-305 中完成

  • 不读取或解密 Secret 明文,不把 Secret 注入 Task 环境。
  • 不提供面向用户的 bind/rebind 命令或远程管理 API。
  • 不声明 Secret rotation 自动改变既有 generation;新版本必须由后续显式授权和新事实消费。
  • 不改变现有“Manifest 含 Secret requirement 时 materialization 拒绝”的执行边界。

替代方案

执行时解析浮动 Secret 名称

拒绝。它让同一 generation 的行为随 rotation 漂移,无法复现,也无法把授权证据与实际版本绑定。

把加密 Secret 值复制进 Package ledger

拒绝。它扩大密文托管面、备份恢复面与轮换复杂度,也不必要地增加路由设备存储和集群权限面。

只在产品层保存临时绑定

拒绝。崩溃、重启或主库切换后无法证明执行消费的是安装时确认的同一绑定。

影响

  • 相同 Package generation 的 Secret 选择成为可复现、可摘要、可审计的不可变事实。
  • Secret 明文仍留在既有 Secret custody 边界内;ledger 泄露只暴露有界引用元数据,不暴露值。
  • D-305 建立的是消费前置条件,不代表 Secret-aware Package 已可运行;产品授权与 runtime consumption 仍属于 D-306。
  • Local 与 Cluster 保持同一领域契约、不同存储 authority,低配设备不承担集群组件成本。

验证

  • core contract:精确 Manifest coverage、required/optional、同 Project、固定 version、canonical order、authority/digest/shape drift 与 generation/Manifest 重校验。
  • SQLite:幂等、冲突、active generation fencing、tamper fail-closed、migration checksum、typed schema、readiness 与 rollout safety。
  • PostgreSQL:同构 repository contract、migration checksum、schema/readiness 与 package-executor 最小权限。
  • 阶段完成前运行完整 18-package build/test、backend 回归、package/dependency boundary 审计和 PostgreSQL 18 physical-streaming HA gate。

当前证据

  • runtime-core 全量 509/509local-sqlite 全量 232/232。
  • cluster-postgres 317 项:316 pass、1 条仅在未提供独立测试数据库 URL 时条件 skip、0 fail。
  • 完整 18-package clean build/test 退出 0Worker 133/133AI 209 pass/3 条件 skipCluster Admin 302 pass/2 条件 skipLocal Owner 163 pass/5 条件 skip,其余 package 同样 0 fail。
  • backend 1,190 项:1,188 pass、2 条件 skip、0 fail。package boundary、cluster dependency boundary、edge import、service bridge import 与 local image 五项审计均为 0 findingworkspace 仍为 18 package、无 single-source/shallow-source packagePostgreSQL 60 个与 SQLite 93 个 migration 文件继续作为显式 ordered_ledger 管理。
  • PostgreSQL 18.4 arm64 physical-streaming HA 125 项 gate 全绿,timeline 1→2,旧主完成 fence 与 pg_rewind 只读重入;报告 SHA-256 为 acf0fea7ca7699989dfe70f5dd0061cdf5fb1968c691094331fea06ce01b96dc