feat(ql3): bind private release evidence receipts

This commit is contained in:
whyour
2026-08-18 01:48:27 +08:00
parent fd433c41cd
commit 3e7bfa8087
17 changed files with 1452 additions and 66 deletions
+22
View File
@@ -11,6 +11,28 @@
最新增量证据(2026-08-18):
- D-346/ADR-0438(已接受;首份真实公开收据待实际 release tag):Cluster/All 发布不再只通过 GitHub `needs` 保存“两个私有 evidence job
成功”的瞬时调度事实。Worker management 与 CloudNativePG DR 私有 runner 在 source-aware gate 成功后,各自产生一个
`qinglong/private-release-evidence-receipt@v1`:只投影 exact version/source tag/revision/scope、evidence kind、24 小时 freshness、
原报告 SHA-256、可选静态审计摘要和自身 digest,显式禁止原始报告、路径、credential、token、Kubernetes object 与 transcript 发布,并声明
公开消费者不能在缺少私有报告时重放现场结果。两个 1 天 artifact 均绑定同一 `run_id/run_attempt`,只作为跨 job 交接;长期 authority 升为
`qinglong/release-set@v2`,其 digest 对完整收据对象做闭包。Local 必须零收据且不运行私有 evidence jobCluster/All 必须按固定顺序恰好包含
Worker 与 DR 两份同 identity 收据,缺失、重复、额外 kind、source/scope/freshness/static audit/self-digest 漂移均在 tag promotion 前失败关闭。
OCI artifact/file media type同步升为 `application/vnd.qinglong.release-set.v2+json`,不在 v1 下偷换语义。Barman/cert-manager 静态 lock 继续
保持 `releaseReady=false`:它们只证明供应链选择,某次发布的现场 ready 结论由 freshness-bound 收据链给出。实现不新增 package、生产依赖、
数据库、migration、Pod、controller、listener、timer、设备工具或稳态资源;低配设备仍只消费可信工作站生成的 Local selection 与 immutable
image。定向 receipt/release-set/catalog/部署锁/workflow 契约 134/134;完整 backend 1,362 项为 1,360 pass/2 条件 skip/0 fail
18-package clean build/test 退出 0。12 项 package boundary、Cluster dependency、Edge import、Cluster/Worker/CloudNativePG 部署、backup、
Barman/cert-manager selection、image release、deployment-lock surface 与 release-version 审计全部 compatiblepackage boundary 保持
18 packages、`singleSourcePackages=[]``shallowSourcePackages=[]`。14 档 Local artifact 全部 compatible 且与 D-345 字节基线完全一致:默认
Edge/Standalone 为 2,589,890/2,589,968 bytesadopted 为 2,809,185/2,809,308 bytesapplication 为
3,632,769/3,632,889 bytesapplication-api 为 3,800,322/3,800,466 bytesAI 为 3,069,143/3,069,233 bytes
application+AI 为 4,493,043/4,493,175 bytesMCP 为 7,315,930/7,316,038 bytes。阶段完整性 PostgreSQL HA 在 Docker Engine
恢复后以 18.6/arm64 重跑 142/142 gates、timeline `1→2`,独立 evidence audit compatiblemode-0600 报告 SHA-256 为
`1e7c31cc6c7aa3e1e0398eb51696b6054850d5fc25910a08d6faffc9a75f1c6f`container/network/volume 残留为 0。以上证明实现、
失败关闭与本地/HA 回归边界;第一份线上公开收据及其 catalog 闭包仍只能由实际受保护 `v3` release tag dispatch 产生,不能由单测、
workflow 静态审计或本地 fixture 冒充。
- D-345/ADR-0437(已接受;首份真实公开 catalog evidence 待实际 release tag):Local 发布不再只依赖 image publisher 在完整
release-set/catalog 形成前直接掌握 digest 的 Compose rollout。`release-set` 成功后新增只读
`release-catalog-local-deployment-live`,仅在 `local|all` scope 运行;它从公开 discovery ref 独立消费 immutable catalog,重建并审计
@@ -0,0 +1,83 @@
# ADR-0438:内容无关的私有发布证据收据链
- 状态:Accepted
- 日期:2026-08-18
- 关联 RFCQL-RFC-0001 D-03、D-14、D-333、D-335、D-336、D-346
- 关联 ADRADR-0245、ADR-0264、ADR-0425、ADR-0427、ADR-0428、ADR-0430、ADR-0436、ADR-0437
- SupersedesADR-0427 的 release-set v1 聚合边界,以及 ADR-0428 的 release-set v1 OCI media type
## 上下文
Cluster/All 发布已经要求两个 commit-scoped、24 小时内的私有门成功:Worker management 重新审计五份来源报告,CloudNativePG
disaster recovery 重新审计对象存储 backup/WAL/latest restore/PITR、证书轮换和三项静态供应链锁。publisher 通过 `needs` 只能看到 job
成功或失败,最终公开 release-set 却只记录候选策略中的“这些证据必须存在”,没有绑定本次 workflow attempt 实际审核的报告摘要。
因此 durable catalog 可以证明镜像、source 和 deployment family,却不能证明某个 Cluster release-set 对应哪一组私有发布证据。直接上传
原始报告会泄漏生产环境事实并扩大保留面;只依赖 GitHub job 状态又会在 workflow artifact 过期后丢失可验证的发布闭包。
## 决策
1. 两个私有 evidence job 在既有 release gate 成功后,各自产生一份
`qinglong/private-release-evidence-receipt@v1`。收据精确绑定 release version、40-hex source revision、source tag、`cluster|all`
scope、evidence kind、report SHA-256、观测/审核时间和 24 小时窗口,并具有自身 canonical SHA-256。
2. Worker 收据只投影 source-aware 聚合报告摘要。CloudNativePG 收据额外投影 backup、Barman Cloud 与 cert-manager 三项静态审计结果摘要;
静态 selection lock 继续保持 `releaseReady=false`,因为它自身不携带时效证据。实际某次发布是否 ready 由收据与 release-set 闭包决定,
不能永久翻转一个静态文件来冒充持续有效的现场证据。
3. 收据不得包含原始报告、路径、credential、token、连接信息、私钥、Kubernetes 对象或命令 transcript。它显式声明
`sourceReportsUploaded=false``privateReportContentPublished=false`,并诚实记录公开消费者无法在缺少私有报告时重放外部结果。
4. 私有 job 只上传收据文件,保留 1 天;原报告仍只存在于 commit-scoped ephemeral runner mount,禁止 cache 或 artifact 上传。artifact 名必须
同时绑定 `run_id/run_attempt/evidenceKind`,下载时也必须使用同一模式。
5. release-set 升级为 `qinglong/release-set@v2``local` scope 必须恰好零份收据;`cluster|all` 必须按固定顺序恰好包含
`worker-management``cloudnativepg-disaster-recovery` 两份同 release identity 收据。缺失、重复、额外 kind、source/scope、freshness、
static audit 或任一 digest 漂移均在 tag promotion 和 catalog publication 前失败关闭。
6. release-set v2 把完整 content-free 收据嵌入自身并纳入 `releaseSetDigest`,随后由 file provenance、OCI catalog immutable digest、Cosign 与
GitHub provenance 共同保护。原始 1 天 receipt artifact 只是跨 job 交接,长期 authority 是公开 catalog 中的 release-set v2。
7. OCI artifact/file media type 升为 `application/vnd.qinglong.release-set.v2+json`。不能用 v1 media type 承载新增字段,也不接受 v1
release-set 进入新部署锁或 catalog consumer。
8. 不新增 workspace package、production dependency、数据库、migration、listener、timer、controller、Pod 或设备侧工具。所有新工作只发生在
Cluster/All 的短生命周期 release runnerLocal 发布不等待私有 evidence jobEdge/Standalone 只消费工作站生成的 selection 与 immutable
image。
## 失败与恢复
- 私有报告不新鲜或 source 不匹配:私有 job 失败,不生成收据;重新取得同 source 的现场证据后重跑受保护 tag。
- 收据 artifact 缺失、来自其他 attempt 或目录中出现额外文件:release-set 聚合失败;不能以 job success 或手工 digest 替代。
- 收据已生成但 publish matrix 失败:不 promotion;同一 tag 重跑必须重新审核仍在时效窗口内的证据并产生新收据。
- catalog 消费者可验证收据结构、自摘要和 release-set 闭包,但不能声称重放了私有现场结果;需要审计原始事实时进入受控私有 evidence 环境。
- v1 release-set:3.0 尚未正式发布,不提供隐式兼容;从同一 source 重新执行 release workflow 生成 v2。
## 部署与资源影响
- Local scope 的 release-set v2 收据数组为空,不启动两个私有 runner,不增加路由设备下载、CPU、内存或稳态连接。
- Cluster/All 的公开 release-set 增加两份小型 content-free JSON,节点无需安装证据工具或访问私有报告;可信工作站随 catalog 一并审核。
- 原始证据不离开私有 runner,公开内容仅能做 equality/source/freshness binding,不能反推出 credential 或生产对象内容。
## 被拒绝的替代方案
### 只依赖 GitHub `needs.<job>.result`
拒绝。它是瞬时调度事实,不能进入 release-set digest,也无法在 workflow 记录过期后由部署者独立核对。
### 上传全部私有证据到 release-set job
拒绝。公共 publisher 不需要原始生产事实;扩大跨 runner 传输和保留面违反最小披露边界。
### 把静态 Barman/cert-manager lock 永久改为 release-ready
拒绝。静态供应链选择没有 24 小时 freshness、backup/PITR 或 rotation 事实。ready 是 source-scoped release 结论,不是永久 lock 属性。
### 只在 image record 中加入一个布尔值
拒绝。布尔值无法绑定证据内容,也会把 deployment-family 级事实复制到每个镜像 publisherrelease-set 才是跨镜像与私有证据的唯一终态 authority。
## 验证
- receipt contract 覆盖 Worker/DR exact projection、source/scope/freshness/report/static-lock/self-digest 漂移、私有字段注入、closed CLI、stable
private read、mode `0600` no-replace 和内容无关输出;
- release-set contract 覆盖 Local 零收据、Cluster/All exact-two、缺失/额外/漂移收据、v2 standalone inspection 与 digest 闭包;
- workflow 静态门冻结同 attempt artifact、私有报告不上传、两份收据 create/audit、release-set 下载与 aggregate/audit handoff,以及 v2 OCI media type
- 定向契约 134/134,完整 backend 为 1,360 pass/2 条件 skip/0 fail18-package clean build/test 退出 012 项架构/部署审计和 14 档
Local artifact 全部 compatibleworkspace 保持 18 packages 且没有 single-source/shallow-source package
- PostgreSQL 18.6/arm64 HA 重跑 142/142 gates、timeline `1→2`,独立 evidence audit compatible,报告 SHA-256 为
`1e7c31cc6c7aa3e1e0398eb51696b6054850d5fc25910a08d6faffc9a75f1c6f`container/network/volume 残留为 0
- 完整结果同步记录于 QL-RFC-0001 D-346;首份真实公开收据和 catalog 仍须由受保护 `v3` release tag 产生。
+1
View File
@@ -441,6 +441,7 @@
| [ADR-0435](./ADR-0435-uid-resource-version-fenced-kubernetes-resource-retirement.md) | UID/resourceVersion 围栏的 Kubernetes 资源退役 Ceremony | Accepted |
| [ADR-0436](./ADR-0436-post-publication-catalog-bound-kubernetes-release-gate.md) | 发布后 Catalog-bound Kubernetes Release Gate | Accepted(首份真实公开 catalog evidence 待实际 release tag |
| [ADR-0437](./ADR-0437-post-publication-catalog-bound-local-release-gate.md) | 发布后 Catalog-bound Local Release Gate | Accepted(首份真实公开 catalog evidence 待实际 release tag |
| [ADR-0438](./ADR-0438-content-free-private-release-evidence-receipt-chain.md) | 内容无关的私有发布证据收据链 | Accepted(首份真实公开收据待实际 release tag |
## 规则
+16 -3
View File
@@ -13,6 +13,17 @@ ghcr.io/<owner>/qinglong3-release-catalog:v<version>-<scope>
`ghcr.io/<owner>/qinglong3-release-catalog@sha256:<manifest-digest>`,验证这个 immutable reference 后,再从 JSON 的
`images[].reference` 读取完整 `ghcr.io/<owner>/<repository>@sha256:<digest>`
## 私有发布证据收据链
当前长期 authority 是 `qinglong/release-set@v2``local` scope 的 `evidenceReceipts` 必须为空;`cluster|all` 必须按顺序恰好包含
`worker-management``cloudnativepg-disaster-recovery` 两份 `qinglong/private-release-evidence-receipt@v1`。每份收据绑定同一
version/source tag/revision/scope、24 小时 freshness、私有报告 digest 和自身 digestDR 收据还绑定 CloudNativePG backup、Barman Cloud 与
cert-manager 三项静态审计摘要。
这些收据不包含原始生产报告、路径、credential、token、Kubernetes object 或 transcript。公开 consumer 可以重算收据和 release-set digest
但必须保持 `publicConsumerReplay=not_possible_without_private_reports`;它不能声称重放了私有现场结果。原始报告不上传,只有收据以 1 天 artifact
从私有 job 交给 release-set job,随后完整嵌入 release-set v2 并由 durable catalog 长期保护。
## 发布流水线内置 Local 与 Cluster 下游门
`local|all` scope 在 durable catalog 发布后启动独立 `release-catalog-local-deployment-live`。它与 publisher 权限隔离,从公开 catalog
@@ -393,12 +404,14 @@ SHA-256`receipt.expectedDigest` 是 receipt 内的 `receiptDigest`。审计
1. 只接受已验证 Cosign exact workflow identity 与 GitHub source tag/revision provenance 的 catalog immutable
referencediscovery tag 无 authority。
2. materializer 只能接受完整 `qinglong/release-catalog-consumption-ceremony@v1` bundle,不能接受旧的松散 `--release-set`;其中
`qinglong/release-set@v1``release.version``release.sourceRef``release.sourceRevision``release.scope` 必须与变更单一致。
`qinglong/release-set@v2``release.version``release.sourceRef``release.sourceRevision``release.scope` 必须与变更单一致。
3. 镜像集合必须与上表精确相等;每个 `reference` 必须是 digest reference,且 owner/repository 与部署目标一致。
4. Kubernetes 必须先渲染 overlay,再用离线 post-render materializer 生成和复验 v2 locked manifest;嵌套 overlay 的
4. Local scope 必须为零私有收据;Cluster/All 必须精确包含两份同 source、同 scope、自摘要有效且 freshness 闭合的 content-free 收据。
static lock compatible 不等于现场证据已公开重放,任何 consumer 都必须保留该限制。
5. Kubernetes 必须先渲染 overlay,再用离线 post-render materializer 生成和复验 v2 locked manifest;嵌套 overlay 的
`newName`/digest 不是最终 authority。Local 必须生成并审计 v2 service selection。两族输出都必须绑定同一 catalog manifest、
consumption report 与 release-set digest,并且只能消费 release set 中的 `@sha256:` reference。
5. rollout 前再次确认 catalog receipt/immutable reference 与已检查文件一致。Kubernetes 必须把 locked manifest/report、pinned
6. rollout 前再次确认 catalog receipt/immutable reference 与已检查文件一致。Kubernetes 必须把 locked manifest/report、pinned
kubectl/kubeconfig 和目标 cluster UID 绑定进 preflight/apply receiptversion/source/catalog tag 都只能用于发现,部署始终以
release set 中的镜像 digest 为准。