6.9 KiB
ADR-0186:Cluster PostgreSQL Plugin Package 隔离与能力撤出
- 状态:Accepted
- 日期:2026-07-28
- 关联:RFC D-137、D-149、D-152、D-154、D-158、D-174、D-175、D-176; ADR-0125、ADR-0137、ADR-0152、ADR-0154、ADR-0158、ADR-0184、ADR-0185
背景
D-174 已为 Edge/Standalone 建立 SQLite quarantine overlay,但 Cluster 仍只有 Package 安装、物化、Task reconciliation 与 Tool snapshot 的 PostgreSQL事实。若 Cluster 只在 某个 control replica 内保存 denylist,重启、水平扩容或主备提升都会重新暴露受影响 能力;若只改变 install state,则已发布的 Task revision 和历史 Tool snapshot 仍能被 新的 Run/StepRun 引用。
隔离必须同时满足:
- event、Task disable、Tool source withdrawal 和 receipt 原子提交;
- 多副本、COMMIT 响应丢失、同步复制与主备提升后语义不变;
- runtime 只能获得启动判定,不能读取隔离证据;
- 已经 durable running 的工作保留 completion/recovery,不伪装成安全热杀;
- 不为这一个领域再拆 workspace package,也不把 Cluster 代码带入路由设备 Profile。
决策
1. control-core v36 使用永久 overlay
pg-0037-plugin-package-quarantine 将 capability 从 v35 推进到 v36,并新增:
plugin_package_quarantine_events:绑定 revocation receipt、impact、精确 install record、双人或 break-glass 主体、reason、mutation 和事件时间;plugin_package_withdrawal_receipts:绑定目标、前后 active vector、新 Tool snapshot、Task 撤回数量和数据库提交时间;plugin_package_withdrawal_tasks:绑定每个 Task 的前一 revision 与 disabled revision。
事实表只追加,不删除或改写安装历史、旧 Task revision、旧 Tool snapshot、Run、 StepRun、Artifact 与 Completion。
2. package-executor 持有唯一提交 authority
commit_plugin_package_quarantine(jsonb,jsonb,jsonb,jsonb) 是
SECURITY DEFINER,只授予 ql3_package_executor:
- 锁定 Project 和 exact install;
- 校验 event/receipt target、时间关系、active head 与 materialized revision;
- 对 Package 当前拥有且仍 enabled 的 Task 追加 disabled revision;
- 重新计算并校验排除目标 lock 后的完整 active source 集;
- 发布 immutable Tool snapshot;
- 写 event、receipt 和 Task withdrawal relation。
任一 identity、source、Task head、snapshot 或 authorization fence 漂移都回滚整个 事务。event digest 与 target 唯一;COMMIT 响应丢失后通过 durable read-after-write 收敛为 exact replay。
3. runtime 只获得同 Project 启动栅栏
ql3_runtime 对三张隔离表没有 SELECT/INSERT/UPDATE/DELETE,只能执行:
plugin_package_run_start_allowed(project,task,revision);plugin_package_tool_start_allowed(project,definitionRef,digest)。
guard 通过 Task reconciliation 或历史 Tool snapshot source 回溯 Package lock,并对
Project 取 FOR SHARE;quarantine commit 对同一 Project 取 FOR UPDATE。因此新
Run dispatch 与 Tool barrier 不可能越过并发隔离事务。同步副本缺失且
synchronous_commit=remote_apply 时,行锁 WAL 会使启动型 mutation fail closed;
不得为提高切换窗口可用性绕过该 fence。
Run 已经 durable 处于 dispatching/running 且 Task revision 未变化时,不重新解释为 新 start,允许既有 completion/recovery 收敛。Package install recovery、Task reconciliation recovery 和 Tool snapshot recovery 都排除已有 quarantine event 的 target/source,避免重启复活。
4. 管理入口是短生命周期 subpath
@qinglong/cluster-admin/plugin-package-quarantine 组合 readiness、PostgreSQL
repository 和事务内授权复验:
- 每批 1–128 个 dense、event digest/target 唯一的 event;
- 每个 repository transaction 在写入前和 COMMIT 前调用授权复验;
- 使用独立 package-executor credential,完成后关闭数据库;
- 不进入 cluster-admin root,不新增常驻 HTTP route、listener、timer 或 workspace package。
它接收的是已经绑定 revocationReceiptDigest + impactDigest + exact lock 的事件。
Cluster 当前安装事实没有保存 OCI publisher signature provenance,因此本 ADR 不允许
从 revoked key、package 名或 registry 路径猜测受影响 lock。自动影响分析与事件生产
必须先在后续 D-177 建立不可变 signer provenance。
不采用方案
新增 quarantine workspace package
该能力与现有 cluster-postgres storage adapter、cluster-admin 短 authority 同生命周期, 新增包没有独立交付或依赖隔离收益,并违反 22-package hard cap。
给 runtime 隔离表读取权限
runtime 只需要布尔启动判定。直接 SELECT 会暴露管理主体、reason、impact 与安装历史, 也会让各消费者自行实现不一致 join。
跨多个事务撤出 Task 和 Tool
会出现 Task 已禁用而 Tool 仍可启动、或 event 已存在但 receipt 不完整的可见窗口, 并使 recovery 无法判断 winner。
在没有 provenance 时自动按 key 隔离
无法证明某个 install lock 确由该 signer 发布;猜测影响集会产生漏隔离或误隔离, 比显式承认缺口更危险。
验收证据
@qinglong/cluster-postgres:190 pass,1 条需要外部 live URL 的 integration 条件 skip;migration checksum、schema contract、Drizzle、readiness、恢复与 barrier 单元门通过;@qinglong/cluster-admin:101 pass,1 条真实 Kubernetes API 条件 skip;新增 batch service 覆盖双次授权、dense/limit/duplicate target 和显式 subpath;- PostgreSQL 18.4 Debian arm64 physical-streaming HA Docker contract:
- 构造一个真实 Task+Tool Package,隔离前 Run/Tool guard 均为 true;
- COMMIT 响应丢失后 durable replay 收敛,event/receipt 各 1、Task withdrawal 2;
- 新 Tool snapshot retained source 为 0,runtime 隔离表访问返回 42501;
- 隔离后及 timeline 1→2 promotion、
pg_rewind、同步 rejoin 后双 guard 均为 false; - 分区提交未被确认、旧主先 fence、同步副本恢复后才重新开放 mutation;
- 最终
pluginPackageQuarantine.*证据全为 true,gates.passed=true。
真库门发现并修复了 JSON 运算符优先级、active install/head join、JSON text 到 UUID
显式转换和 ECONNRESET durable retry 四类静态 mock 未覆盖的问题。
后续
- D-177/ADR-0187 已完成 Cluster Package signer provenance、durable revocation receipt/impact、receipt-first Run/Tool deny 与有界 quarantine producer;
- 将 ADR-0187 producer 接入正式双人/break-glass Cluster 管理 transport;在该 ceremony 完成前保持 subpath 为内部短 authority,不发布“任意请求按 key 隔离”的 产品能力;
- 为多 Project 批次补充显式并发/限流策略,但单事务仍只处理一个 Project target, 不扩大数据库锁范围。