Files
qinglong/docs/adr/ADR-0186-cluster-postgresql-plugin-package-quarantine.md
T

139 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0186Cluster 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`
1. 锁定 Project 和 exact install
2. 校验 event/receipt target、时间关系、active head 与 materialized revision
3. 对 Package 当前拥有且仍 enabled 的 Task 追加 disabled revision
4. 重新计算并校验排除目标 lock 后的完整 active source 集;
5. 发布 immutable Tool snapshot
6. 写 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 和事务内授权复验:
- 每批 1128 个 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 pass1 条需要外部 live URL 的 integration 条件
skipmigration checksum、schema contract、Drizzle、readiness、恢复与 barrier
单元门通过;
- `@qinglong/cluster-admin`101 pass1 条真实 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 为 0runtime 隔离表访问返回 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 未覆盖的问题。
## 后续
1. D-177/ADR-0187 已完成 Cluster Package signer provenance、durable revocation
receipt/impact、receipt-first Run/Tool deny 与有界 quarantine producer
2. 将 ADR-0187 producer 接入正式双人/break-glass Cluster 管理 transport;在该
ceremony 完成前保持 subpath 为内部短 authority,不发布“任意请求按 key 隔离”的
产品能力;
3. 为多 Project 批次补充显式并发/限流策略,但单事务仍只处理一个 Project target
不扩大数据库锁范围。