Files
qinglong/docs/adr/ADR-0196-local-image-attested-multiarch-release-contract.md
T

158 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0196:本机镜像证明化多架构发布契约
- 状态:Accepted(共享发布契约、CycloneDX、许可证/漏洞门与 CI 接线已实现;
本机双架构 OCI 和远端 GHCR 发布记录尚未取得)
- 日期:2026-07-29
- 关联 RFCQL-RFC-0001 D-14、D-42、D-61、D-126、D-128、D-175、
D-184、D-185、D-186
- 关联 ADRADR-0042、ADR-0090、ADR-0128、ADR-0185、ADR-0194、
ADR-0195
## 背景
ADR-0195 已证明同一个 AI-excluded image 能在 Edge 128 MiB/64 PID 和
Standalone 256 MiB/256 PID envelope 中完成真实 SQLite 生命周期,但候选
image ID 不是发布 authority。仓库已有 control/admin 的双架构、SBOM、
provenance、签名和远端回读流程;为本机镜像复制一套 privileged workflow
会产生独立 action pin、OIDC identity、漏洞策略和验证漂移。
本机 build manifest 还比 production manifest 多一个只用于 Drizzle schema
类型编译的 `drizzle-orm`。直接套用“两个 manifest dependencies 完全相同”的
Cluster 假设会迫使 build-only dependency 进入 runtime,或让 SBOM 隐藏真实
差异。
## 决策
### 1. 三个镜像共用一个发布 authority
`.github/workflows/ql3-image-release.yml` 是 QingLong 3.0 唯一 backend image
发布 workflow,精确矩阵固定为:
- `control → qinglong3-cluster-control`
- `admin → qinglong3-cluster-admin`
- `local → qinglong3-local-application`
每项分别绑定 exact Dockerfile、production dependency root 和最终 digest。
不得增加第四个 repository、让三个 profile 共用 digest,或为本机镜像复制
第二套 OIDC workflow。原 `audit:cluster-image-release:ql3` 作为兼容命令保留,
新的主命令是 `audit:image-release:ql3`;这不增加 workspace package。
ADR-0253 已收紧入口:发布只接受显式 dispatch 到与输入 QingLong 3 SemVer 完全一致的 protected `v3` tag
不再由 tag push 自动发布。私密 Worker management source-aware gate 先在受保护 ephemeral runner 只读执行;
registry/OIDC/attestation 写权限只存在于依赖该 gate 的 GitHub-hosted publisher。workflow 没有 `pull_request`
trigger;所有第三方 action继续固定完整 commit SHA。
### 2. 本机 SBOM 明确区分 runtime root 与 build-only root
本机 build/runtime manifest 共享制品身份
`@qinglong/local-application-image@3.0.0-alpha.0`。production root 只允许
`croner@7.0.8``semver@7.7.4`build root 必须精确等于 production root 加
受审的 `drizzle-orm@1.0.0-rc.4`,其他差异失败关闭。
共享 CycloneDX 1.5 生成器只从 production lock 遍历运行时可达图,再在 builder
lock 中从相同 production roots 复算并比较。因此 build-only Drizzle、TypeScript、
types 与 AI 均不能进入本机 SBOM。当前本机图固定为:
- 2 个外部 component
- 10 个内部 component
- 12 个 component、13 个 dependency node
- root 为
`pkg:npm/%40qinglong/local-application-image@3.0.0-alpha.0`
实际 read-only image inventory 必须与 12 个 component 精确对账,并继续通过
ADR-0195 的 640 files/5 MiB、无 symlink/special file 门。
### 3. 许可证、漏洞和容量都必须失败关闭
每个 runtime component 必须携带且只能携带一个受审 SPDX license ID。共享
allowlist 当前为 `0BSD``Apache-2.0``BSD-2-Clause``ISC``MIT`
`Python-2.0``Unlicense`;新增或缺失 license 必须先修改 ADR 与变异测试。
本机实际闭包只使用 Apache-2.0、ISC、MIT。
每个 release matrix 项在 build/push 前对自己的 production lock 执行
`npm audit --omit=dev --audit-level=high`。安全公告服务不可用、high 或
critical advisory 都使发布失败。ADR-0254 已补充独立 OS/base image CVE scanner:三个 image 的两个 native
architecture 都在 publish 前以 digest-pinned base、Trivy 0.70.0、HIGH/CRITICAL、OS-only、unfixed 不忽略执行,
例外必须经过最多 30 天的 owner/ticket/purl/image-scoped 生命周期。
本机 OCI 每个平台的 compressed layer 总预算固定为 128 MiBCluster 既有
512 MiB 门保持不变。该值是 registry/传输门,不是路由器实际闪存容量承诺。
### 4. 原生 image gate 与 attested OCI gate分离
原生 amd64/arm64 `local-image` job 继续执行候选镜像 identity、12-package
inventory、CycloneDX 对账、router stress 以及 Edge/Standalone fresh live
contract。共享 `image-oci` job 则以 `control|admin|local` 三项 exact
Dockerfile matrix 生成 `linux/amd64,linux/arm64` OCI layout,并要求:
- 每个平台 config 固定 Linux、`65532:65532`、唯一 local application
entrypoint、`NODE_ENV=production`
- `edge,standalone``ai=excluded` labels、source revision 和 version
精确匹配;
- 每个平台恰有一个 digest-bound attestation manifest
- attestation 同时包含 SPDX-2.3 与 SLSA provenance v1
- SPDX 中 `/opt/qinglong/node_modules` 的 npm purl 与 12-component
CycloneDX 完全相同;
- provenance 只允许 source revision 和不含 credential 的标准 proxy args。
### 5. 远端发布只以 digest 为 subject
每个 profile 的 Buildx push digest分别接受:
1. BuildKit SBOM
2. BuildKit maximum provenance
3. Cosign GitHub OIDC keyless signature
4. GitHub SLSA provenance attestation
5. GitHub CycloneDX attestation。
发布后同一 job 必须从 GHCR 回读 `IMAGE@DIGEST`,复验双平台/双 attestation
manifest,随后以精确 `.github/workflows/ql3-image-release.yml@GITHUB_REF`
certificate identity 验证 Cosign,并以 repository、workflow、source commit、
source ref 和 OCI bundle 分别验证 SLSA 与 CycloneDX。tag 仍只是发现入口,
D-184 Compose 私有输入必须使用 digest。
## 当前证据
- 本机 CycloneDX12 components、13 dependency nodes
- 实际 arm64 image
`sha256:b99e61e90c84fc113b03d8d8d237ee3f5cefc3f800d73708b30ed591ec4a94b0`
`65532:65532`、251,932,346 uncompressed bytesinventory 为
611 files/4,897,102 bytes,并与 CycloneDX 精确一致;
- SBOM/许可证变异测试:10/10
- OCI layout 结构/config/attestation 变异测试:9/9
- release workflow 变异测试:19/19
- D-185 image contract6/6
- control/admin/local 三份 production lock 的联网 npm audit
0 vulnerabilities
- 共享 CI/release 静态审计:
`images=[control,admin,local]`、amd64/arm64、inventory/attestation 全开启。
本机真实双架构 OCI 本轮未取得。临时 `docker-container` builder 已确认同时支持
amd64/arm64,但 BuildKit 在执行项目 Dockerfile 前,从 Docker Hub OAuth endpoint
获取 `docker/buildkit-syft-scanner:stable-1` token 超时。该外部失败与
ADR-0128 已记录的 admin 证据缺口相同,不能记作 OCI 成功;临时 builder、
BuildKit container、隔离 Docker config 与未完成 OCI 输出均已删除。
## 尚未完成的 Release Gate
- GitHub-hosted `image-oci` 三 profile 的真实成功记录;
- GHCR 中可回读的本机双架构 manifest digest
- 对该 digest 的 Cosign、SLSA、CycloneDX 三类远端 verify 记录;
- GitHub-hosted 六矩阵 base image/OS package scan 的真实成功记录;
- 固定低配路由器的下载大小、解压占用、冷启动、断电与升级/回滚实证;
- D-184 service activation controller 使用新 digest 的升级与回退闭环。
## 验证
- `pnpm sbom:local-image:ql3`
- `pnpm audit:image-release:ql3`
- `pnpm audit:local-image:ql3`
- `node --test test/back/ql3ClusterImageSbom.test.cjs`
- `node --test test/back/ql3ClusterOciLayoutAudit.test.cjs`
- `node --test test/back/ql3ClusterImageReleaseAudit.test.cjs`
- `node --test test/back/ql3LocalImageAudit.test.cjs`
- 在实际 image 内执行
`ql3-cluster-image-sbom.cjs --image=local --inventory-root=...`
- 在可获取 BuildKit scanner 的环境执行 CI `image-oci` 和受保护 release
workflow,并保存最终远端 verify 记录。