mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): close release tags after catalog verification
This commit is contained in:
@@ -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 tag,exact 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 现升级为 v2,publisher 必须先在 runner 私有
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# ADR-0442:Catalog-ready 的终态 Release Tag 发布与闭合收据
|
||||
|
||||
- 状态:Accepted
|
||||
- 日期:2026-08-18
|
||||
- 关联 RFC:QL-RFC-0001 D-03、D-14、D-336、D-349、D-350
|
||||
- 关联 ADR:ADR-0427、ADR-0428、ADR-0439、ADR-0441
|
||||
- Supersedes:ADR-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 已审计并 attested;catalog 已按 immutable digest
|
||||
round-trip;catalog 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 tag;exact 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 bundle;receipt 必须再次本地
|
||||
审计并单独 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 digest,consumer
|
||||
仍不会信任 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 演练产生。
|
||||
@@ -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) |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
@@ -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 天 bundle;receipt 再次审计、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。
|
||||
|
||||
Reference in New Issue
Block a user