feat(ql3): close release tags after catalog verification

This commit is contained in:
whyour
2026-08-18 03:53:25 +08:00
parent dbd57b27d0
commit a99f1508f7
12 changed files with 1336 additions and 77 deletions
+20
View File
@@ -11,6 +11,26 @@
最新增量证据(2026-08-18):
- D-350/ADR-0442(已接受;首份真实 GHCR 部分 promotion/replay 证据待实际 release tag):正式 image tag 不再在完整
release-set 审计后、durable catalog 建立前提前公开。`versionTag/sourceTag` mutation 现位于 release-set provenance、catalog
immutable round-trip、catalog signature/provenance 验证、catalog receipt 审计及其 attestation 全部成功之后。新的
`qinglong/release-publication-plan@v1` 联合复验 exact release-set、catalog plan/raw manifest/manifest digest/receipt,并只列出每个
image 的 immutable source 与两个目标 tag。publisher 对每个已存在 image repository 取得最大 1 MiB 的 canonical tag inventory
严格检查 OCI tag 字符集与无重复;auth/network/registry error 不再被任意 `image digest` 非零退出降级成 absent。所有 immutable source
和既有目标 tag 必须在任何 mutation 前完成全量预检,冲突保持零 tag 写;随后只 copy absent tagexact tag 用于 response-loss 重放。
最终逐 tag 回读形成 `qinglong/release-publication-tag-observation@v1`,再与上游闭包组成自摘要
`qinglong/release-publication-closure-receipt@v1`,本地复审并独立 attested 后才上传 deployment bundle。receipt 诚实保留
`crossRepositoryAtomicity=false``registryTagCas=false`immutable catalog digest 仍是唯一部署 authority。实现不新增 package、
生产依赖、数据库、migration、Kubernetes object、设备工具或常驻资源;所有 inventory 与收据只存在于短生命周期 release runner
Edge/Standalone/路由设备和 Cluster 节点稳态成本均为零。阶段门已重跑:专项 closure/workflow 96/96、完整 backend 1382 pass +
2 条条件 skip、18-package clean build/test 全通过、14/14 静态审计与 14/14 artifact 档位通过;artifact 实际字节依次为基础
Edge/Standalone `2589890/2589968`、adopted `2809185/2809308`、application `3632769/3632889`、application-api
`3800322/3800466`、AI `3069143/3069233`、application+AI `4493043/4493175`、MCP `7315930/7316038`,均低于各自硬上限。
PostgreSQL HA Docker 门以 PostgreSQL 18.6/arm64 完成 142/142、timeline `1→2`,私有报告 SHA-256 为
`c8c5cad7a7feb6db066b14efcc241f33ed574c3d80b909014bc894d8f9cb7cbf`,证据复审通过且临时容器/卷/网络残留均为零。Barman
live object-store 恢复与 cert-manager 在线轮换仍诚实保持外部 release blocker。真实 GHCR 部分 promotion/response-loss 仍只能由
受保护 `v3` tag 或受控 release repository 演练证明。
- D-349/ADR-0441(已接受;首份真实 GHCR conflict/reuse 证据待实际 release tag):关闭 durable catalog discovery tag 的覆盖窗口。
之前 workflow 虽声明 `v<version>-<scope>` 无部署 authority,却直接对该 tag 执行 `artifact put`;已有不同 digest 会先被覆盖,
response-loss 重跑也没有“相同复用、冲突拒绝”的可执行分支。catalog plan/receipt 现升级为 v2publisher 必须先在 runner 私有
@@ -0,0 +1,84 @@
# ADR-0442Catalog-ready 的终态 Release Tag 发布与闭合收据
- 状态:Accepted
- 日期:2026-08-18
- 关联 RFCQL-RFC-0001 D-03、D-14、D-336、D-349、D-350
- 关联 ADRADR-0427、ADR-0428、ADR-0439、ADR-0441
- SupersedesADR-0427 中“完整 release-set 审计后即可 promotion”的最早发布顺序
## 上下文
QingLong 3 的 image publisher 先写入无 tag 的 immutable digest,完成逐镜像签名、SBOM、漏洞与 provenance 验证,再由
release-set job 聚合所有镜像。ADR-0427 因此允许在完整 release-set 审计后 promotion `versionTag/sourceTag`
后续 ADR 又增加 release-set file provenance、durable OCI catalog、catalog signature/provenance、manifest round-trip 和 catalog receipt。
原有 promotion 步骤却仍位于这些 Gate 之前。如果 catalog 发布、签名、attestation 或 receipt 失败,公开 image tag 已经可见,但可部署的
immutable catalog authority 尚未闭合。旧 promotion 还把任意 `regctl image digest <tag>` 非零退出都当作 tag absent;网络、认证或 registry
错误可能因此被错误降级成“可以写入”。
## 决策
1. `versionTag/sourceTag` mutation 移到以下事实全部成功之后:完整 release-set 已审计并 attestedcatalog 已按 immutable digest
round-tripcatalog signature 与 GitHub provenance 已验证;catalog receipt 已生成、审计并 attested。
2. 新的 `qinglong/release-publication-plan@v1` 必须重新读取并联合审计 exact release-set、catalog plan、raw manifest、manifest
digest 与 catalog receipt。计划只含 release identity、上游摘要、每个 image 的 immutable reference/digest、两个目标 tag 和固定策略;
catalog receipt 之前不能生成该计划。
3. promotion 在任何 tag mutation 前,对每个已存在的 image repository 执行一次完整 tag inventory:最大 1 MiB、canonical line、OCI
tag 字符集、无重复。inventory 读取失败、超限或畸形全部失败关闭,不能再把任意 digest lookup 错误解释为 absent。
4. 预检首先验证所有 immutable source digest。inventory 中已存在的所有目标 tag 必须逐个解析为计划 digest;任一冲突时尚未发生任何
tag mutation。只有全量预检成功后才依固定顺序 copy absent tagexact tag 不重写,用于 response-loss 恢复。
5. copy 阶段完成后,必须重新读取所有 `2 × imageCount` 个 tag,并用
`qinglong/release-publication-tag-observation@v1` 固化 exact ordered mapping。缺失、额外、重排、重复或 digest 漂移均不能闭合。
6. `qinglong/release-publication-closure-receipt@v1` 同时绑定 publication plan、release-set、catalog plan/receipt/manifest、最终 tag
observation 和固定策略,并具有自身 digest。plan、observation、receipt 必须一起进入 90 天 deployment bundlereceipt 必须再次本地
审计并单独 attested,使下载者可以离线重放 closure audit。
7. receipt 诚实保留 `crossRepositoryAtomicity=false``registryTagCas=false`。它证明 workflow 观察到 catalog-ready 后的完整终态,
不声称 GHCR 提供跨 repository 事务或 tag CAS。发布中途失败时不删除正确 tag;同 protected source tag 重跑只能复用 exact digest
任何不同 digest 都失败。
8. deployment consumer 仍只信任已签名/attested 的 immutable catalog digest。最终 image tag 和 closure receipt 是发布可见性与运维证据,
不是部署 authority 的替代品。
## 失败与恢复
- catalog receipt attestation 前失败:没有正式 image tag mutation;修复后从同一 protected source tag 重跑。
- repository inventory 读取不确定:立即停止,不能把 auth/network/registry error 当作 absent。
- 任一既有 tag 指向其他 digest:全量预检阶段停止,不写任何本轮目标 tag。
- promotion 中途 response loss:可能已有部分 exact tag;重跑重新取得全部 inventory,复用 exact tag,只补 absent tag,最后重建相同
observation 与 closure receipt bytes。
- promotion 后竞争:最终逐 tag digest 回读会阻止 closure;稍后外部改写也无法改变已 attested receipt 绑定的 immutable digestconsumer
仍不会信任 mutable tag。
## 部署与资源影响
- Edge/Standalone/路由设备不执行该协议,不安装 Node、regctl、Cosign 或 GitHub CLI,不增加 RSS、磁盘写、timer、listener、watcher、
updater 或常驻进程。
- Cluster 节点、Kubernetes object、CloudNativePG、数据库、migration、SQL、Pool、Worker 与运行时镜像均无变化。
- 新工作只在短生命周期 GitHub-hosted release runner:每个 image repository 一个最大 1 MiB inventory、一个小型计划/观察/收据文件和
最终 tag 回读。不新增 workspace package、生产依赖或部署服务。
## 被拒绝的替代方案
### 继续在 release-set 审计后立即 promotion
拒绝。完整镜像集合不等于 durable catalog 已经可验证;后续 catalog 失败会留下过早公开的 tag。
### 继续把 `image digest` 任意失败解释为 absent
拒绝。不存在、无权限、网络中断和 registry 故障不能共享同一 mutation 决策。
### 先写 tag,再用 closure receipt 记录结果
拒绝。收据只能证明终态,不能修复错误的发布前置顺序;catalog-ready 必须是 mutation 的真实前置条件。
### 声称 closure receipt 提供跨 repository 原子性
拒绝。OCI registry 没有该事务语义。精确重放和终态闭合能收敛部分成功,但不能伪造原子 commit。
## 验证
- publication closure contract 覆盖 Local/Cluster/All 计划、确定性 receipt、缺失/重排/额外/digest 漂移 tag、上游 catalog 脱离、
self-digest tamper、closed CLI 与 no-replace 输出;
- workflow 静态门固定 `catalog receipt attestation → publication plan → bounded inventory/preflight → tag mutation → exact observation →
closure audit/attestation → bundle upload` 顺序,并拒绝缺失 inventory、closure 或独立 attestation
- 发布链与完整仓库验证结果记录于 QL-RFC-0001 D-350;首份真实 GHCR 部分 promotion/response-loss 重放仍须由受保护 `v3` release tag
或受控 release repository 演练产生。
+1
View File
@@ -445,6 +445,7 @@
| [ADR-0439](./ADR-0439-deterministic-private-evidence-receipts-and-release-set-replay.md) | 确定性私有证据收据与 Release-set 重放 | Accepted(首份真实线上重放待实际 release tag |
| [ADR-0440](./ADR-0440-release-set-closure-private-evidence-freshness.md) | Release-set 闭合时私有证据 Freshness 重验证 | Accepted(首份真实线上闭合待实际 release tag |
| [ADR-0441](./ADR-0441-no-overwrite-release-catalog-discovery-publication.md) | Release Catalog Discovery Tag 无覆盖发布 | Accepted(首份真实 GHCR conflict/reuse 证据待实际 release tag |
| [ADR-0442](./ADR-0442-catalog-ready-terminal-release-tag-publication.md) | Catalog-ready 的终态 Release Tag 发布与闭合收据 | Accepted(首份真实 GHCR 部分 promotion/replay 证据待实际 release tag |
## 规则
+10 -4
View File
@@ -437,10 +437,16 @@ immutable image reference 写入 compose/rollout。
## 发布失败与恢复
GHCR 不提供跨 repository tag 事务,release set 明确记录 `crossRepositoryAtomicity=false`如果 promotion 中途
失败,不删除已经正确的 tag,也不重新构建镜像。使用原 source tag/revision 重跑 release workflow:它会先验证
每个 source digest 和既有 tag;既有 tag 指向同一 digest 时继续,指向其他 digest 时立即失败。只有
release-set、catalog immutable digest、两类 provenance 与 receipt 全部生成并验证后,才能宣布该 deployment family
GHCR 不提供跨 repository tag 事务,release set 与最终 closure receipt 都明确记录 `crossRepositoryAtomicity=false`正式
`versionTag/sourceTag` 只能在 release-set file provenance、catalog immutable digest、catalog signature/provenance、catalog receipt
及其 attestation 全部成功后开始。publisher 随后必须为每个 image repository 取得不超过 1 MiB 的完整 tag inventory;读取失败、非
canonical line、非法 OCI tag 或重复项均失败关闭,不能把任意 `image digest` 错误当作 tag absent。它会在任何 mutation 前验证全部
immutable source 与全部既有目标 tag,任一不同 digest 都使本轮零 tag 写入。
如果 promotion 中途失败,不删除已经正确的 tag,也不重新构建镜像。使用原 source tag/revision 重跑 release workflow:它会复用 exact
tag,只补 absent tag;全部 tag 最终回读后生成 `qinglong/release-publication-tag-observation@v1`
`qinglong/release-publication-closure-receipt@v1`。closure receipt 绑定 release-set、catalog plan/manifest/receipt 和每个最终 tag
与 plan、observation 一起进入 90 天 bundlereceipt 再次审计、attest 后,下载者可离线重放 closure audit。只有这条闭合链全部生成并验证后,才能宣布该 deployment family
可部署。对于 `cluster|all`,还必须等待只读 catalog consumer 与 catalog-bound K3s deployment/retirement Gate 成功;publisher 成功而
consumer 失败时不能宣布 Cluster release。不要给 consumer 临时增加写权限“修复”可见性或 tag,应修正 GHCR package visibility/retention
或发布配置后,对同一受保护 tag 重跑完整工作流并重新验证 exact digest。