Files
qinglong/docs/adr/ADR-0184-offline-local-plugin-package-quarantine-withdrawal.md
T

10 KiB
Raw Blame History

ADR-0184:本机 Plugin Package 离线隔离与能力撤出

  • 状态:Accepted
  • 日期:2026-07-28
  • 关联:RFC D-133、D-149、D-150、D-169、D-170、D-171、D-172、D-173、 D-174ADR-0149、ADR-0150、ADR-0179、ADR-0180、ADR-0181、ADR-0182、 ADR-0183

背景

ADR-0183 在 compromise proposal durable 后阻断目标 signer 的新 catalog publication 和 queued stage,并在确认后删除 trust key、保存受影响 lock 集合。它刻意没有声称 已撤下 active Package

  • install head 的 activeLockDigest 仍可指向旧 lock
  • Package Task 已成为独立、不可变 TaskDefinition revision 和 current head
  • Tool 已进入 Project active-vector snapshot
  • 已批准的历史 Run/Tool invocation 可能绑定旧 revision/snapshot。

因此只给 install state 增加 quarantined,或只让 source query 忽略一个 lock,都无法 关闭完整执行面。热卸载还会与正在执行的脚本、进程和 Tool adapter 竞态。本机模型应 先停 application,再由短生命周期 Owner authority 原子撤出耐久能力。

提议决策

1. 使用永久 quarantine overlay,不改写安装历史

不扩展 queued|staged|activating|active|failed 的既有 install event history。新增本机 SQLite capability v35,以 append-only QingLong3PluginPackageQuarantineEvents、 immutable QingLong3PluginPackageWithdrawalReceipts 和逐 Task QingLong3PluginPackageWithdrawalTasks 覆盖目标 projectId/packageName/installationId/lockDigest。quarantine target 通过 project/package/installation/lock/install record digest 复合外键冻结到原安装事实, 不另建可变 quarantine head。

plugin-package.quarantine.apply 只接受受管 trust root 中 ADR-0183 的 exact revocation receipt,不接受调用方自报 lock 集合。命令必须绑定 receipt/proposal/ impact digest、当前 install head、Owner、mutation、SecurityAudit 和数据库时间。 quarantine 是永久 tombstone;恢复只能安装由非撤销 key 签名的新 lock,不能把旧 head 改回 active。

queued|staged|activating headoverlay 使 recovery source 不再选中目标 lock, 且既有精确安装事实不能被改写;不新增伪造的 install settled enum。对 active head overlay 进入同一事务的 capability withdrawal。

2. Active withdrawal 必须同时处理 Task、Tool 与准入

一个 BEGIN IMMEDIATE 事务固定执行:

  1. 复验 target lock 仍是 install head 或 head 的 active lock
  2. 插入 exact quarantine event,目标与 mutation 都唯一,重复 mutation 只允许 exact replay
  3. 对仍由该 Package/generation 拥有的 current Task 追加 enabled=false revision 不删除旧 revision、Run 或审计;
  4. 从 Project Tool active vector 排除该 lock,按 Profile 基于其余最多 edge 4 个、 standalone 16 个 active source 生成 immutable snapshot;空 vector 是合法 snapshot
  5. 写 withdrawal receipt,绑定 quarantine、Task revision 列表、新 Tool snapshot、 原/新 active-vector digest 和计数;
  6. 在事务取得写锁后、提交前执行授权 callback;callback 以同一连接复验 credential 未失效、确认者仍是 current Ownerdual-control 时提案者也仍是 current Owner
  7. 提交前再次复验 install、Task、未受影响 source vector 和授权 fence。

任一步冲突或越界整体 rollback。Task/Tool 数据继续使用现有表和 pure planner;仅为 quarantine event/receipt/task 明细只增加三张事实表,不复制一套 Package 安装状态机。

3. 历史 revision 仍可读,但所有新 start 必须事务内失败关闭

仅更新 current Task head 或 current Tool snapshot 不足以阻止一个已经绑定历史事实的 新 start。下列本机 transaction 必须读取同一 quarantine head

  • Run dispatching/running CAS:若 Task reconciliation generation 能回溯到已 quarantine 的 lock,在推进 Run 前拒绝;
  • Tool execution start barrier:从绑定 snapshot 的 source 找到 Package lock,若已 quarantine,在写 barrier/调用 adapter 前拒绝;
  • Package stage/activation/recoveryqueued/staged/activating 的精确目标被 recovery source 排除,外键与 tombstone 阻止安装事实变体重写。

已持久进入 running 的 Run 或已经创建 start barrier 的 Tool invocation 保留原 completion/recovery 语义,不能删除或改写历史来伪造“从未执行”。D-173 的 stop_required 仍是撤出前的操作前置条件;本 ADR 不承诺从 SQLite 证明宿主进程已 被操作系统停止,也不实现任意脚本的安全热杀。

4. Crash recovery 与启动门

quarantine、Task disable、Tool snapshot 和 withdrawal receipt 在同一 SQLite 事务, 所以进程崩溃只能看到全部旧事实或完整新事实。COMMIT response loss 由 mutation/ receipt digest exact replay 返回 existing

application startup 在 Package recovery 前执行 capability v35 readiness relation probe

  • quarantine event 必须精确对应一张 withdrawal receipt
  • Task 明细数量、内容 digest、Project/time 与 receipt 必须一致;
  • active withdrawal 的新 snapshot retained source 数量必须一致,且不得再包含目标 lock;
  • probe 使用 LIMIT 1 找出首个损坏关系并失败关闭,不把全表物化进路由设备内存;
  • readiness 通过后,Package recovery、Task/Tool source 和后续 Run/Tool start 仍各自 执行 transaction fence。

不增加 timer、watcher、poller 或自动 GC。一次 Owner command 的 edge/standalone 目标 lock 上限为 4/16,每个 active withdrawal 的 source 上限也为 4/16,单 receipt 最多 128 个 Task;超限要求人工拆分或停机处置,不允许无界事务。

5. 包边界

继续复用:

  • @qinglong/runtime-core 的显式 quarantine/withdrawal contract subpath
  • @qinglong/local-sqlite 的 migration、repository 和 start fence
  • @qinglong/local-owner-cli 的现有 Package command binary
  • @qinglong/local-application 的 startup recovery composition。

不新增 workspace package 或第三方生产依赖。owner-cli 对 runtime-core 的既有源码 导入从 devDependency 纠正为 production dependency,并由 lockfile/源码边界门精确 允许。Cluster 对等能力必须在 PostgreSQL advisory lock、multi-replica admission、admin/runtime ACL 和 HA promotion 门全部实现 后单独接受,不能把本机 SQLite command 暴露为 Cluster transport。

已知限制与后续风险

  1. 当前 Run 不直接保存 Package lock provenancestart fence 通过 immutable Task reconciliation item/generation 回溯;未来应把 lock provenance 提升为 Run 建单事实;
  2. quarantine 与随后 replacement install 的 previous-active/rollback 语义需要独立 恢复 ADR
  3. 本轮 SIGKILL 覆盖 SQLite transaction window,但真实设备断电、ENOSPC、文件系统 损坏和完整 application 停机仍需要实机演练;
  4. 本 ADR 只接受本机 SQLite 能力;PostgreSQL HA 门通过不代表 Cluster 已具备 quarantine adapter、ACL 或多副本管理 transport。

接受门禁

  • pure contractexact replay/conflict、永久 tombstone、bounded withdrawal receipt
  • SQLite migration/readiness/schema audit 与 capability v35
  • queued/staged/activating 三状态不再被 recovery source 选中;
  • active Package 的 Task 全部 disabled、Tool source 全部排除,事务失败零部分事实;
  • 历史 Task revision/Tool snapshot 可读,但新 Run/Tool start 在写前被 fence
  • application readiness 在 withdrawal relation incomplete/tampered 时失败关闭;
  • 非 Owner、identity drift、impact/receipt 漂移和超限全部在可见变更前拒绝;
  • Edge/Standalone DELETE/FULLWAL/FULL 的 pre-COMMIT/post-COMMIT SIGKILL matrix 和 integrity_check=ok
  • 22-package dependency/edge import、application artifact/RSS 预算不回归;
  • 实现触及 Cluster 时重跑 PostgreSQL 18 physical HA Docker 门,否则不声明 Cluster 能力。

验收结果

  • @qinglong/runtime-core/plugin-package-quarantine 已提供 event/receipt schema、 exact replay/conflict、永久 tombstone、128 Task receipt 上限和确定性 mutation ID runtime-core 349/349 通过;
  • reviewed migration 0069-plugin-package-quarantine0070-capability-v35 已落地,contract v35、62 张 owned table、migration checksum、 typed schema 和 readiness relation probe 一致;
  • @qinglong/local-sqlite/plugin-package-quarantine 在一个 BEGIN IMMEDIATE 中完成 exact install validation、Task consecutive disable、Tool snapshot、event/receipt/ task facts以及提交前授权 callbackexact replay 在返回前再次复验 durable facts
  • Package recovery source、Task/Tool current source、Run CAS 与 Tool start barrier 均已读取 quarantine overlay;历史 revision/snapshot 仍可读;
  • ql3-package-trust revoke-confirm 从 ADR-0183 receipt 的 impacted lock 导出目标, 在 trust snapshot 前逐项 quarantine;新建、已有 receipt 和已完成 snapshot 的精确 重放都会先执行该 hook,关闭“receipt 已存在却跳过撤出”的恢复窗口;
  • credential/Owner 的完整认证在事务外完成,事务内再以同连接执行当前 credential、 confirmer Owner 和 proposer Owner fence,避免共享 authority transaction 的重入 死锁;
  • crash matrix 覆盖 edge DELETE/FULL、standalone WAL/FULL,在 Task disable 后、 event 后、receipt 后、COMMIT 前和 COMMIT 后各 SIGKILL,共 10/108 个 pre-COMMIT 场景完整 rollback 后恢复为 created2 个 post-COMMIT 场景完整保留并重放为 existingintegrity_check=ok、foreign key check 通过;
  • 全量测试通过:runtime-core 349、local-sqlite 136、local-admin 63、 local-application 32、local-owner-cli 22
  • dependency/source boundary 无 findingEdge import closure 为 121 modules;四种 application 产物门通过:edge 4,915,362 bytes/615 files/90 modulesstandalone 4,915,506/615/90edge+AI 5,593,183/659/89standalone+AI 5,593,339/659/89
  • 本轮未实现 Cluster quarantine,但按授权额外重跑 PostgreSQL 18.4 physical HA Docker contractphysical streaming、remote_apply、partition promotion guard、 old-primary fence/pg_rewind rejoin、scheduler/credential/Run/tool-result COMMIT-response-loss convergence 和 optional AI schema promotion gate 全部通过。