feat(ql3): scope deployment-family release candidates

This commit is contained in:
whyour
2026-08-16 08:54:51 +08:00
parent d991deb7ee
commit 7130d77a76
27 changed files with 1332 additions and 303 deletions
@@ -7,6 +7,7 @@
D-184、D-185、D-186
- 关联 ADRADR-0042、ADR-0090、ADR-0128、ADR-0185、ADR-0194、
ADR-0195
- 后续修订:ADR-0425 已取代本 ADR 的固定三镜像矩阵;本 ADR 的 Local image identity、双架构、SBOM、签名与 digest rollout 约束继续有效。
## 背景
@@ -4,6 +4,7 @@
- 日期:2026-08-01
- 关联 RFCQL-RFC-0001 D-235、D-236
- 关联 ADRADR-0128、ADR-0196、ADR-0252
- 后续修订:ADR-0425 已把本 ADR 的私有 Worker evidence gate 收窄到 `cluster|all` 发布族;`local` 发布不得等待该证据,Cluster 的 source/freshness/private-runner 约束继续有效。
## 背景
@@ -28,12 +28,13 @@ attestation service、transparency log、本地 Docker daemon 和短期 GitHub t
`cosign``gh``docker`、短期 token file 和新 report pathmutable tag、branch
ref、owner 漂移、symlink、group/other-writable executable 或已存在输出均失败关闭。
2. 三个外部工具按绝对路径直接执行,不经 shell 或 ambient `PATH`。GitHub token
必须来自 current-owner `0600` bounded file,只注入`gh attestation verify`
必须来自 current-owner `0600` bounded file,只注入`gh attestation verify`
子进程;不得进入 argv、`cosign`/`docker` 环境、报告或失败输出。工具在执行前后
复验 device/inode/size/SHA-256,降低 ceremony 中途替换风险。
3. ceremony 精确执行一次 keyless signature 验证,以及绑定 release workflow、
source digest、source tag、非 self-hosted runner 和 OCI bundle 的 provenance、
CycloneDX、OS-vulnerability 三类 GitHub attestation 验证。随后拉取同一 digest,
CycloneDX、OS-vulnerability、source-derived release-candidate contract 四类 GitHub
attestation 验证。随后拉取同一 digest,
要求本地 image inspection 的 Linux `amd64|arm64` `RepoDigests` 包含精确输入。
4. ceremony 使用固定、非敏感、单条 `run_read` redacted evidence vector 检验最终
release image 内的第 11 个 `evidence-verify` 产品命令。该容器使用 non-root
@@ -41,12 +42,12 @@ attestation service、transparency log、本地 Docker daemon 和短期 GitHub t
128 MiB、0.25 CPU 与 32 PIDs,只读挂载 vector;输出必须与独立 verifier 的
exact no-authority result 一致,vector inode/size/mtime/digest 前后不变。
5. 成功只新建一个 current-owner `0600`、two-space canonical JSON 报告。报告保留
public release identity、工具 SHA-256/size、步 argv/stdout/stderr digest 与字节数、
public release identity、工具 SHA-256/size、步 argv/stdout/stderr digest 与字节数、
verification/isolation 结果和自身 canonical SHA-256,不保留原始工具输出、token、
executable path 或 workstation identity。它明确声明
`reportAttestation=none``actionAuthority=none`
6. 独立 offline audit 使用 no-follow stable read 校验报告 canonical encoding、exact
shape、expected release identity、工具与步 transcript digest、一致的 isolation/
shape、expected release identity、工具与步 transcript digest、一致的 isolation/
limitation 以及顶层 digest。其结果固定为 `externalResults=not_replayed`;离线审计
不能证明外部命令确实运行,也不能重放某一历史时点的 registry、GitHub 或
transparency-log 状态。
@@ -0,0 +1,120 @@
# ADR-0425:按部署族冻结 3.0 Release Candidate,并闭合 Worker 发布集合
- 状态:Accepted(实现与静态/变异门已完成;公开 tag、GHCR digest 和远端证明结果待实际发布)
- 日期:2026-08-16
- 关联 RFCQL-RFC-0001 D-01、D-03、D-05、D-14、D-42、D-61、D-186、D-257、D-333
- 关联 ADRADR-0088、ADR-0128、ADR-0196、ADR-0253、ADR-0254、ADR-0255、ADR-0281、ADR-0420、ADR-0424
## 背景
原唯一 image release workflow 有两项结构问题。第一,version 只由 dispatch input 与 tag 字符串相互校验,
没有把 18 个 QL3 workspace manifest、容器 runtime manifest、Node ABI、Dockerfile version、部署 Profile、
镜像集合与双架构矩阵冻结为同一份可证明契约。第二,Local、Control、Control AI 与 Admin 四个 image 被一个
固定矩阵发布,且无条件等待 Worker management 和 CloudNativePG 两个私有集群证据。这让只使用
Edge/Standalone 的路由器/NAS 发布也依赖集群 HA 基础设施;反过来,真实 Cluster 部署需要的
`qinglong3-worker` 已有 Dockerfile、锁文件、Kubernetes manifests 和 live rollout,却完全不在发布矩阵中。
这不是测试数量问题,而是产品集合和发布 authority 不一致:轻量用户被过度阻塞,集群用户又拿不到完整制品。
## 决策
### 1. 唯一 workflow 支持三个封闭部署族
`.github/workflows/ql3-image-release.yml` 继续是唯一 image publication authority,并只接受显式
`workflow_dispatch` 到 exact protected `v3` tag。新增必选 `release_scope`
- `local`:仅 `qinglong3-local-application`,服务 Edge/Standalone
- `cluster``control`、可选 AI control、Admin、Worker
- `all`:同时发布两族,但不得弱化任一族的门禁。
scope 不接受自由文本、额外 repository 或运行时拼接。matrix 只能来自下一节的 source-derived contract
workflow 内不再维护第二份 image 清单。
### 2. Source-derived release-candidate contract 是矩阵唯一来源
根级 `ql3-release-candidate-contract.cjs` 接受 exact QingLong 3 SemVer、40-hex commit、匹配的完整 tag ref 和
closed scope,随后从受审源码推导 no-replace canonical JSON
- 18 个 workspace 必须全部通过 package-boundary audithard cap 仍为 18,且无 single/shallow package
- 每个 workspace version 必须等于 tag versionNode engine 必须为 `>=24.18.0 <25`
- 每个所选 image 的 production manifest、Dockerfile Node 24.18.0 与 OCI version label 必须相同;
- 平台固定 `linux/amd64``linux/arm64`
- Local profile 固定 `edge|standalone` 且不要求 Cluster private evidence
- Cluster profile 固定 `cluster|worker-edge|worker-node`,必须要求 Worker management 与 CloudNativePG evidence
- legacy 根 package 的 2.x version 只作为兼容事实记录,并明确排除出 3.0 release identity。
报告携带对自身 unsigned exact JSON 的 SHA-256。publisher 从同一 checkout 重新生成并独立 exact-audit,不能直接
信任 job output 中的任意 repository/pathjob output 只传递 contract 派生的有界 matrix 和 cluster evidence bit。
### 3. Local 不再被 Cluster HA 证据阻塞
Local scope 的两个私有 evidence job 必须为 skippedpublisher 仍无条件依赖 release-candidate 与 native OS scan
并继续对 pushed Local digest 执行 Edge/Standalone 两个真实 compose rollout。Cluster/all scope 才能把 private
evidence bit 置为 truepublisher 使用显式 `always()` 条件,只在 candidate/OS 成功,且 cluster scope 的两个
私有 job 均成功时取得写权限。skipped 不能被当作 Cluster successfailed/cancelled 也不能通过条件表达式旁路。
### 4. Cluster 发布集合必须包含 Worker
Worker 加入与其他 image 相同的 native amd64/arm64 build-once、Trivy 0.70.0 OS-only HIGH/CRITICAL、扫描证据、
OCI merge、production dependency audit、CycloneDX、Cosign keyless、GitHub attestation、远端 manifest 回读和
验证后 tag promotion。Worker production SBOM 当前为 27 components24 external、3 internal)和 28 dependency
nodes;唯一新增 license allowlist 项是锁中 `asn1js` 的 BSD-3-Clause。OCI config 固定 non-root `65532:65532`
唯一 Worker process entrypoint、`io.qinglong.profile=worker``edge,node` capacity profiles 与 exact 3.0 version。
Control/Admin Dockerfile 同步补 exact 3.0 version label,使 tag、workspace、runtime manifest 与所有 OCI config
首次共享同一 release identity。
### 5. Candidate contract 必须成为 digest-bound 第四类证明
每个发布 digest 除 SLSA、CycloneDX、OS-vulnerability 外,再以
`https://qinglong.dev/attestations/release-candidate-contract/v1` 附加 candidate predicate,并以 repository、
workflow、source digest、source ref、非 self-hosted builder 和 OCI bundle 远端回读。Admin image 内的
`verify-release.sh`、外部 workstation ceremony 与 offline report auditor 同步从三类/七步升级为四类/八步;
否则“生成了 contract”不能算发布者或部署者实际验证过。
## 资源与权限边界
- 不新增 workspace package、npm production dependency、数据库、migration、SQL、role、Pool、connection
- contract 与 audit 只在显式 release job 短生命周期运行,不进入 Local/Worker/Control/Admin runtime filesystem
- 不增加 Edge/Standalone timer、watcher、listener、queue、cache 或常驻进程;
- Local scope 不接触 self-hosted private evidence runnerCluster scope 不得把 skipped 私有证据解释为成功;
- release tag 仍只在所有 digest verification 完成后 promotiontag 本身不成为部署 authority。
## 失败与恢复
- tag/version/workspace/container version 任一漂移:修正源码并重新创建 tag,不手改报告;
- package boundary 不兼容或出现第 19 个 package:先独立评审边界,不扩大 candidate hard cap
- Worker SBOM/license/config 漂移:更新锁与供应链 ADR 后重跑,不能从 Cluster scope 静默删除 Worker
- Local scope 意外等待 Cluster evidence:视为发布拓扑回归;
- Cluster scope 的 private job skipped/failedpublisher 不启动;
- candidate attestation 缺失或远端 source binding 不匹配:不得 promotion version/source tag
- 公开发布尚不存在:只报告 implementation-ready,不用 fixture、stub 或本机 tag 冒充 GHCR 成功。
## 被拒绝的替代方案
### 为 Local 和 Cluster 复制两套 workflow
拒绝。它会复制 OIDC identity、action pins、scanner、copier、签名和 tag promotion 逻辑,形成安全策略漂移。
### 继续发布固定 all matrix
拒绝。低配用户会被无关 HA 证据阻塞,同时无法表达独立修补 Local image 的发布意图。
### Cluster 不发布 Worker,让运维现场自行 build
拒绝。部署 manifest 已把 Worker 作为产品制品;现场 build 绕过统一 SBOM、OS scan、签名与 provenance。
### 只校验 tag,不持久化 candidate predicate
拒绝。tag 不能证明 workspace、容器、Profile、平台和 gate 集合,也不能让部署端在 digest 上独立回读。
## 验证
- release candidate create/audit、scope/version/source/report mutation7 项;
- Worker SBOM/OCI、OS policy、共享 release workflow、Admin verifier/ceremony/distribution 定向总计 105/105
- backend 1,246 pass/2 条件 skip/0 fail18-package clean build/test 退出 0
- package boundary 返回 18 packages、hard cap 18、single/shallow 均为空;dependency、Edge import、Cluster/Worker deployment、image release、OS policy、Console/distribution 均 compatible
- 14 档 Local artifact 全部 compatible;默认 Edge/Standalone 为 2,589,890/2,589,968 bytesapplication+AI 为 4,493,043/4,493,175 bytesMCP 为 7,315,930/7,316,038 bytes
- Cluster Admin npm pack 为 250 files、271,238-byte tarball、1,690,196-byte unpacked;四个 runtime dependency root 的离线缓存审计为 0 vulnerability
- 本 Gate 无 schema、migration、SQL、role、Pool 或连接/HA 拓扑变化,因此不重复 PostgreSQL 门,复用 D-331 PostgreSQL 18.6 arm64 physical HA 142/142、timeline `1→2` 基线;
- 公开 tag 后再记录 GHCR 五镜像 digest、四类 attestation 与外部 Admin ceremony,不提前宣称完成。