feat(ql3): materialize auditable local trial kits

This commit is contained in:
whyour
2026-08-26 09:58:29 +08:00
parent 2253b99066
commit 2620be0587
11 changed files with 1026 additions and 81 deletions
@@ -1,6 +1,6 @@
# ADR-0503:可下载的 Local Alpha 试运行套件
- 状态:Proposed实现完成,原生 Linux x64/arm64 CI 与实际归档待验收
- 状态:Proposed(原生 Linux x64/arm64 已通过,实际可下载归档待维护者授权
- 日期:2026-08-26
- 决策:D-408
- 关联:ADR-0193、ADR-0195、ADR-0196、ADR-0425
@@ -84,4 +84,4 @@ Docker Desktop 的 bind mount 根目录可能把宿主当前 UID 映射为容器
- `ql3 --version``ql3 setup --help` 在 read-only、network none、128 MiB、0.5 CPU、32 PID 下通过;
- Docker Desktop 完整旅程因 mount root UID 非等价失败,临时 credential/pepper 目录已删除;未把该结果记为通过。
转为 Accepted 前必须取得同一提交的原生 Linux amd64/arm64 完整旅程成功记录,并重新执行 package、backend、artifact、dependency 和 release workflow 审计。实际双架构 trial-kit archive 仍需维护者明确授权手动生成;Public Release Set 是否把 operator 纳入正式签名/catalog,留给后续独立 release-set schema 决策。
提交 `2253b99066e0c221e11dc01384f496ec2a50e4bd` 的 CI run `32918632202` 已在原生 Linux amd64/arm64 同时通过完整 fresh Owner、Edge/Standalone lifecycle 和 SQLite integrity40 个 required job 全部成功且无重试。实际双架构 trial-kit archive 仍需维护者明确授权手动生成;转为 Accepted 前还必须记录两个可下载 artifact 的 digest 与离线复核结果。Public Release Set 是否把 operator 纳入正式签名/catalog,留给后续独立 release-set schema 决策。
@@ -0,0 +1,70 @@
# ADR-0504Local Alpha Trial Kit 单一物化与离线审计
- 状态:Accepted
- 日期:2026-08-26
- 决策:D-409
- 关联:ADR-0193、ADR-0195、ADR-0196、ADR-0503
## 背景
ADR-0503 已定义 Local Application 与短生命周期 operator 组成同架构 Trial Kit,并由原生 Linux x64/arm64 门验证完整 fresh 用户旅程。此前手动 artifact 步骤仍在 workflow shell/heredoc 内直接执行 `docker image save`、复制 SBOM 并拼装 manifest。该实现能产生文件,但本地维护者无法复用同一逻辑,下载者也没有一个拒绝额外文件、checksum 漂移或 SBOM 替换的离线审计入口。
阶段产物如果只有 CI 内联命令,没有唯一可执行的物化协议,仍可能出现“CI 声称通过、实际下载目录无法独立复核”的分叉。
## 决策
### 1. 使用一个仓库内 materializer 作为唯一写 authority
新增 `ql3-local-alpha-trial-kit-bundle.cjs`,固定提供两个闭合模式:
- `create`:验证 release identity、完整 source revision、Tier-1 架构、两个镜像的 ID/OS/架构/non-root user/OCI label,以及两份受审 CycloneDX SBOM;随后通过一次 `docker image save` 生成去重 archive
- `audit`:不调用 Docker、不访问网络,只验证 manifest exact shape、闭合文件集、每个文件的 byte length/SHA-256、`SHA256SUMS`、两份 SBOM 的 profile/version 身份和两镜像 ID 的分离。
GitHub Actions 和本地阶段产物必须调用同一入口。workflow 不再拥有独立的 heredoc manifest 实现。
### 2. 套件是闭合目录,不是松散文件集合
每个架构的目录只允许六个 regular file
1. 一个同时包含 Application/operator 的 Docker archive
2. Application CycloneDX SBOM
3. operator CycloneDX SBOM
4. 面向部署者的 `README.md`
5. canonical `manifest.json`
6. 覆盖前五个文件的 `SHA256SUMS`
symlink、子目录、credential、keyring、数据库、日志、未声明证据或任意额外文件都失败关闭。创建目标必须是未使用的 canonical absolute path;任何失败都会删除本次半成品目录,既有目录不会被覆盖。
### 3. 离线验证不等同于重新证明 CI live gate
manifest 中的 `verification` 是该 artifact 生成位置之前已通过的 workflow gate 声明。离线 auditor 证明目录内容未漂移、身份相互一致,不伪称在低配设备上重新执行 fresh Owner 或 lifecycle 门。实际加载后的设备 smoke 和生产发布签名仍是不同层级的证据。
普通 push/PR 继续只运行构建和 live gate,不上传大 archive。`workflow_dispatch + produce_alpha_artifacts=true` 仍需要维护者显式授权。
## 被拒绝的替代方案
### 保留 workflow heredoc,另写一个只读 auditor
拒绝。写入与读取协议分离会形成两个 schema authority,测试只能证明 auditor 接受样例,不能证明 CI 实际写出的内容来自同一实现。
### 每个镜像各自生成 archive 和 checksum
拒绝。它会在低容量设备上重复保存共享 Node layer,并破坏 ADR-0503 的单 archive 边界。
### 把 materializer 做成 workspace package
拒绝。它是发布期仓库工具,不是运行时领域能力;新增单文件 package 会扩大 18-package 边界而不带来部署隔离。
## 影响
- 阶段产物可以在 CI 之外按同一协议生成并离线复核;
- 低配设备只需要 `sha256sum` 和 Docker 即可先验证文件再加载,不增加常驻运行时依赖或 RSS;
- manifest 从松散的顶层 image 字段收敛为 `archive/images/sboms/readme/verification` 的 exact shape;该 schema 尚未公开发布,因此不承担旧 artifact 兼容承诺;
- Cluster native image artifact 暂不复用此脚本,其单镜像/多角色发布语义与 Local Trial Kit 不同。
## 验证
- 单元测试覆盖正常物化/审计、错误 source revision、archive 篡改、额外文件和 SBOM 替换;
- Local operator 静态审计要求 workflow 同时调用 `create``audit`
- 当前提交的真实 arm64 Application/operator 必须由该入口生成本地私有 Trial Kit,并再次执行离线审计和加载后最小 smoke;
- 正式 downloadable 双架构 artifact 仍由维护者授权的手动 workflow 生成。
+2 -1
View File
@@ -506,7 +506,8 @@
| [ADR-0500](./ADR-0500-short-lived-cluster-security-administration-command.md) | 短生命周期 Cluster Security Administration 产品命令 | Accepted |
| [ADR-0501](./ADR-0501-opt-in-kubernetes-security-administration-job.md) | 可选的一次性 Kubernetes Security Administration Job | Accepted |
| [ADR-0502](./ADR-0502-bounded-cluster-api-credential-pepper-keyring.md) | 有界 Cluster API Credential Pepper Keyring | Accepted |
| [ADR-0503](./ADR-0503-downloadable-local-alpha-trial-kit.md) | 可下载的 Local Alpha 试运行套件 | Proposed实现完成;原生 Linux 双架构与实际归档待验收 |
| [ADR-0503](./ADR-0503-downloadable-local-alpha-trial-kit.md) | 可下载的 Local Alpha 试运行套件 | Proposed(原生 Linux 双架构已通过;实际可下载归档待维护者授权 |
| [ADR-0504](./ADR-0504-canonical-local-alpha-trial-kit-materialization.md) | Local Alpha Trial Kit 单一物化与离线审计 | Accepted |
## 规则
+9 -11
View File
@@ -26,7 +26,7 @@
该实物保存在工作区忽略目录,不进入 Git,也尚未上传 GitHub。公开下载仍需维护者明确授权上传。它只含 headless Application,没有可下载的 `ql3 setup/owner/task/...` 管理制品;因此它足以证明 runtime 工程可用性,但不能独立完成部署用户旅程。此前“单架构内部试运行材料”的表述按 D-408 收紧为“运行时工程候选”。
ADR-0503 已增加独立的 `qinglong3-local-operator`:它复用现有统一 `ql3` CLI,每次执行一个 command-file 命令后退出,不进入常驻 Application。后续手动 Alpha run 会把 Application 与 operator 通过一次 `docker image save` 写入同一架构的去重 archive;只有原生 Linux amd64/arm64 都完成 fresh setup、首 Owner ceremony、Application active/stop 和 SQLite integrity 后,才能升级为 Local Alpha Trial Kit
ADR-0503 已增加独立的 `qinglong3-local-operator`:它复用现有统一 `ql3` CLI,每次执行一个 command-file 命令后退出,不进入常驻 Application。提交 `2253b99066e0c221e11dc01384f496ec2a50e4bd`原生 Linux amd64/arm64 已同时通过 fresh setup、首 Owner ceremony、Application active/stop 和 SQLite integrityCI run `32918632202` 为 40/40。ADR-0504 又把一次 `docker image save`、manifest、SBOM、README、`SHA256SUMS` 和离线审计收敛为同一个 materializer;下一项未完成的外部里程碑是维护者授权生成并保留两个可下载 archive
## 生成
@@ -40,9 +40,9 @@ ADR-0503 已增加独立的 `qinglong3-local-operator`:它复用现有统一 `
Local artifact 含:
- 一个包含 Application 与短生命周期 operator 的 `qinglong3-local-trial-kit-<arch>.docker.tar`;共享 Node 基础层在 archive 中去重;
- schema 为 `qinglong/alpha-local-trial-kit@v1``manifest.json`,绑定版本、完整 source commit、架构、两个 image tag/image ID、共同 archive SHA-256 与已通过 gate
- schema 为 `qinglong/alpha-local-trial-kit@v1``manifest.json`通过 `archive/images/sboms/readme/verification` 绑定版本、完整 source commit、架构、两个 image tag/image ID、文件长度/SHA-256 与已通过 gate
- 与实际只读镜像 inventory 对账过的 CycloneDX SBOM
- 本说明
- 面向 Local 用户的 README 与覆盖全部内容文件的 `SHA256SUMS`
Cluster artifact 仍是每个角色一个 native Docker archive 和各自 manifest。
@@ -53,17 +53,15 @@ Cluster artifact 仍是每个角色一个 native Docker archive 和各自 manife
在同架构 Linux Docker 主机上进入解压后的 artifact 目录:
```sh
archive="$(find . -maxdepth 1 -name '*.docker.tar' -type f -print -quit)"
expected="$(node -p "require('./manifest.json').archiveSha256")"
actual="sha256:$(sha256sum "${archive}" | cut -d ' ' -f 1)"
test "${actual}" = "${expected}"
sha256sum --check SHA256SUMS
archive="$(node -p "require('./manifest.json').archive.file")"
docker load --input "${archive}"
image="$(node -p "require('./manifest.json').image")"
expected_id="$(node -p "require('./manifest.json').imageId")"
image="$(node -p "require('./manifest.json').images.application.reference")"
expected_id="$(node -p "require('./manifest.json').images.application.id")"
test "$(docker image inspect --format '{{.Id}}' "${image}")" = "${expected_id}"
operator_image="$(node -p "require('./manifest.json').operator.image")"
operator_expected_id="$(node -p "require('./manifest.json').operator.imageId")"
operator_image="$(node -p "require('./manifest.json').images.operator.reference")"
operator_expected_id="$(node -p "require('./manifest.json').images.operator.id")"
test "$(docker image inspect --format '{{.Id}}' "${operator_image}")" = "${operator_expected_id}"
docker run --rm --read-only --network none --cap-drop ALL \
--security-opt no-new-privileges "${image}" --help
@@ -0,0 +1,69 @@
# QingLong 3.0 Local Alpha Trial Kit
本目录是绑定一个 QingLong 3.0 源码提交和一个 Linux 架构的阶段试运行套件,不是公开 release 或生产升级承诺。它同时包含常驻 Application 镜像和短生命周期 operator 镜像;两者共享的 OCI layer 只在同一个 Docker archive 中保存一次。
## 适用范围
- `amd64``arm64` Linux Docker 主机;
- 低配路由/NAS 的 Edge profile,或资源较充足单机的 Standalone profile
- fresh、隔离的测试数据目录;
- 离线导入、设备兼容验证和 3.0 Alpha 用户旅程验证。
不要把它直接用于生产数据、2.x 唯一数据目录或生产 Secret。Cluster/Kubernetes 节点应使用 Cluster Integration Candidate;本套件不包含 PostgreSQL HA、Worker 或 Cluster Admin。
## 离线验收
先在解压目录中执行不依赖 Node.js 的文件校验:
```sh
sha256sum --check SHA256SUMS
```
`manifest.json` 必须满足:
- `schema``qinglong/alpha-local-trial-kit@v1`
- `sourceRevision` 是你准备试用的完整 40 位 commit;
- `architecture` 与主机相同;
- `maturity``alpha_candidate_not_public_release`
如果同时持有 QingLong 源码和 Node.js 24,可执行严格的闭合文件集、manifest、SBOM 和 checksum 审计:
```sh
node scripts/ql3-local-alpha-trial-kit-bundle.cjs \
--mode=audit \
--bundle=/absolute/path/to/this-directory
```
任一校验失败都不要加载或运行 archive。
## 加载与最小 smoke
`manifest.json.archive.file` 找到 archive 后加载:
```sh
docker load --input qinglong3-local-trial-kit-<arch>.docker.tar
```
以 manifest 中 `images.application.reference``images.operator.reference` 为准,分别核对 `docker image inspect` 返回的 image ID。然后执行无网络、只读 smoke:
```sh
docker run --rm --read-only --network none --cap-drop ALL \
--security-opt no-new-privileges \
<application-image> --help
docker run --rm --read-only --network none --cap-drop ALL \
--security-opt no-new-privileges \
<operator-image> --version
docker run --rm --read-only --network none --cap-drop ALL \
--security-opt no-new-privileges \
<operator-image> setup --help
```
## Fresh 试运行边界
完整 fresh setup、首 Owner ceremony、Application active、SIGTERM drain 和 SQLite integrity 已在同一架构的原生 Linux CI 中验证。实际部署时仍必须使用独立目录,并让 operator 以最终数据文件 POSIX owner 的 UID/GID 运行;operator 默认无网络且每次只执行一个命令后退出,不应作为 sidecar 或 daemon 常驻。
Edge 的验证上限为 Application 128 MiB、0.5 CPU、64 PIDStandalone 为 256 MiB、0.5 CPU、256 PIDoperator 为 128 MiB、0.5 CPU、32 PID。这里的数值是试运行门,不是所有 workload 的容量承诺。
停止并删除 Alpha 容器即可回退 fresh 测试环境。若触碰 2.x 数据或进行迁移,必须使用项目既有 reconciliation/cutover/rollback 流程,不能只替换镜像。