15 KiB
ADR-0481:Committed Legacy Data Receipt 的本机部署 Lineage
- 状态:Accepted
- 日期:2026-08-21
- 关联 RFC:QL-RFC-0001 D-05、D-06、D-17、D-64、D-87、D-184、D-259、D-388
- 关联 ADR:ADR-0194、ADR-0309、ADR-0310、ADR-0313、ADR-0314、ADR-0362、ADR-0476、ADR-0477、ADR-0478、ADR-0479、ADR-0480
背景
ADR-0480 已把 Legacy config/db/ssh.d prepared model 原子提交到 Local SQLite,并在 transformation root
留下内容无关、可精确验证的 commit.json。但是现有部署链只绑定 SQLite activation、Legacy silence
commitment、Application config、进程 identity 与 service/Compose outcome。D-387 的 commitDigest 和数据库
receiptDigest 尚未进入 systemd、OpenRC 或 Compose 的启动事实。
这不是日志字段缺失,而是 authority 缺口:现有 adopted Application v3 不认识 data application receipt,部署者 可以先完成 D-387,再继续用一个只绑定 SQLite activation 的 v3 config 启动目标;相反,若把 D-387 apply 与 service start 合并,又会让数据迁移 credential 获得 activation 权限,并破坏 ADR-0480 已明确的职责分离。
Compose 还有一个更早的产品缺口。fresh v2 固定把 deployment root 映射到 /var/lib/qinglong3;SQLite activation
却绑定宿主机 canonical source/target path digest。直接把 adopted config 的路径改成容器路径会让真实 Application
校验失败,继续依赖测试中的合成 mount evidence 也不能称为受审 adopted Compose create/config。
本决策只处理本机 Edge/Standalone。Cluster 必须使用 PostgreSQL、separation-of-duty、控制面 rollout 与集群专用 fencing;不能复用本机私有文件或 Owner credential ceremony。
决策
1. Receipt 是启动前置事实,不是启动授权
D-387 的 commit.json 证明一个 prepared transformation 已经完成数据库提交和明文逻辑回收。它不表示:
- 配置、SSH binding、Task 或 Trigger 已启用;
- Legacy 已静默;
- systemd/OpenRC unit 已安装、enable 或 start;
- Compose target 可以
up; - target 可以回退,或 Legacy 可以重新启动。
因此 data apply、deployment bundle prepare、service/Compose activation 和 rollback 继续是四个独立命令与 durable 事实。任何一步成功都不隐式调用下一步。
2. Application v4 精确绑定 committed application
新增只允许 storage.mode=adopted 的
qinglong/local-application-process@v4。它保留 v3 的 storage 与 cutover,并增加 exact 字段:
{
"legacyDataApplication": {
"commitPath": "/absolute/private/transformation/commit.json",
"expectedCommitDigest": "64-hex",
"expectedReceiptDigest": "64-hex"
}
}
Application 在取得 signal、SQLite、Secret、Plugin Package、AI 或 lifecycle authority 前,按以下顺序失败关闭:
- 解析 exact v4 config,要求所有 authority path 规范、非 root、互异且有界;
- 使用 no-follow descriptor 有界读取私有
commit.json,复核 owner、mode、link count、inode 与读前后 stat; - 校验 D-387 exact schema、
state=committed、reclamation 三项固定值与 payload-domain digest; - 同时匹配 config 的
expectedCommitDigest、expectedReceiptDigest、Profile,以及 storage activation 所绑定的 transformation/target 事实; - 再验证既有 Legacy silence commitment,然后才进入 adopted storage startup。
v2 fresh 行为不变。v3 仍表示“只接管 SQLite、没有声明完整 data-directory application”的兼容模式;完成 D-387 并要把其结果作为部署前置事实的路径必须使用 v4,部署 prepare 不得为该路径生成 v3。
3. Canonical commit codec 下沉到既有 SQLite adoption domain
commit.json 的 payload 本来由 SQLite adoption receipt 派生。canonical type、exact normalizer 与 digest 计算放在
现有 @qinglong/local-sqlite/data-directory-adoption 子路径,由 D-387 cleanup、Application startup gate 和 Owner
deployment consumer 共同复用。文件身份读取仍由各自 authority owner 负责。
这不是新 package,也不让 Local SQLite 读取 deployment journal、调用 init/Docker 或获得 service authority;它只 共享内容无关的纯数据 contract,避免 Application 与 Owner 各自维护一份易漂移的 schema。
4. 新增 adopted bundle prepare/verify,但不激活服务
既有 ql3-local-deploy 增加 exact、私有 command-file 操作:
local.deployment.adopted.prepare
local.deployment.adopted.verify
命令精确绑定 Profile、instance/cutover、deployment root、SQLite source/target/recovery/manifest/activation、Legacy silence commitment、D-387 commit/receipt,以及 systemd、OpenRC、Compose 三选一 service contract。prepare 在发布 任何文件前验证全部源证据,随后 no-replace 发布 Application v4 config、描述符和一个内容无关 bundle receipt;verify 只验证终态,绝不修复、install、enable、start、stop、pull image 或连接 socket。
该操作不执行 local.setup,因为 target SQLite、Owner pepper、Secret keyring 和 D-387 receipt 已存在。fresh
local.deployment.prepare 保持 v2/local.setup 语义,避免把两种 authority 混成大量 optional 字段。
5. systemd/OpenRC 保持 Owner → root bridge → Owner consumer
Application v4 config 的文件 SHA-256 继续进入 service-manager intent,因此短生命周期 root bridge 可传递绑定而无需 解析 D-387 schema、打开 SQLite 或读取 transformation root。root bridge 仍只执行固定 manager/argv 并发布 outcome。
Owner consumer 必须重新读取 v4 config、commit 和既有 activation/commitment/data evidence。新的 cutover journal 版本显式记录:
legacyDataApplicationCommitDigest;legacyDataApplicationReceiptDigest;- 既有
applicationConfigDigest、activationDigest与commitmentDigest。
restart/stop 必须保持同一 commit/receipt binding;发生漂移时不能调用 manager。Legacy rollback preparation 可以读取
该 lineage,但 commitDigest 绝不替代既有 stopped evidence、双阶段授权、barrier 和 readiness proof。
6. adopted Compose 使用 identity-preserving bind
fresh v2 Compose 继续把 deployment root 映射到 /var/lib/qinglong3。adopted v4 不复用这一映射,而要求:
- target、recovery、manifest、activation、cutover commitment、D-387 commit 与运行时可写目录均位于私有 deployment root;
- deployment root 以 source 与 target 相同的 canonical absolute path 绑定进容器;
- Legacy SQLite source 作为唯一额外 bind,同样映射到相同 canonical absolute path;
- 描述符固定 read-only rootfs、无网络、cap-drop、no-new-privileges、UID:GID 和 Profile memory/PID 上限;
- adopted target 不使用隐式
unless-stopped绕过 generation/Legacy reproof。
这样 Application 看到的 path 字节与 activation 中的 path digest 相同,不需要重写已签定的证据,也不扩大为任意 caller-supplied mount 表。Compose config inspection 必须精确验证两条 mount、config path、restart policy、image digest、 generation 和 mutation labels。
Compose preflight 在任何 compose up 前验证 v4 commit/receipt,并把二者加入 rollout receipt。apply 的 exact replay、
失败回收、restore 与 evidence collection 必须继承同一 binding;restore 仍只恢复目标 SQLite generation,不删除 D-387
提交、不重新创建已回收明文,也不自动启动 Legacy。
7. 低配与大节点使用同一协议、不同预算
本能力不新增 workspace package、production dependency、daemon、timer、watcher、listener、数据库连接或后台 retry。 Application 常驻路径只增加一次不超过 64 KiB 的私有 JSON 读取和 SHA-256;Owner/Compose 验证均为人工触发的一次性 操作,不扫描历史。
Edge 保持 128 MiB/64 PID 描述符预算,Standalone 保持 256 MiB/256 PID。两档共享同一 receipt/lineage schema,避免 低配设备成为弱协议;Cluster 节点不因此获得本机模式的规模或高可用声明。
故障与重放语义
- commit 在 prepare 前漂移:不发布 bundle;
- prepare 在文件发布中崩溃:原命令只按 deterministic content/no-replace 收敛,未知额外文件失败关闭;
- service/Compose activation 前漂移:不调用 manager/Docker;
- manager/Docker side effect 后响应丢失:沿用既有 barrier 与 inspect-only recovery,不因 receipt 重新执行副作用;
- Application 读取 commit 后、storage 前 commit 被替换:descriptor identity/stat 复核失败;
- 已 active 后 commit 被删除或漂移:下一次 restart/stop consumer 失败关闭并进入既有人工诊断路径;当前进程不引入 watcher,因此不冒充运行期间持续 attestation;
- rollback:必须执行既有显式 prepare/authorize/consume 或 Compose restore ceremony,receipt 本身没有回退权限。
被拒绝的替代方案
只把 commitDigest 加进 stdout 或 service intent
拒绝。Application 仍可用 v3 直接启动,root/Compose side effect 也可能发生在 Owner 复核前。
D-387 apply 成功后自动 start
拒绝。数据迁移 credential 会获得 deployment authority,且 COMMIT-response-loss 会让自动副作用无法安全重放。
Application 导入 Owner CLI 或直接调用 systemd/Docker
拒绝。会把短生命周期部署 authority 带入低配常驻闭包,并破坏 root bridge 的最小解析面。
adopted Compose 继续映射到 /var/lib/qinglong3
拒绝。会改变 activation 已绑定的 path identity;重新计算 digest 等于伪造一份没有原 ceremony 支持的新证据。
为 receipt 建立新 workspace package或常驻 watcher
拒绝。canonical codec 已有 SQLite adoption owner;watcher 既不能回滚外部 side effect,也会为路由设备增加常驻成本。
用 receipt 自动允许 Legacy rollback
拒绝。receipt 证明新数据已提交,反而意味着回退更需要数据 reconciliation;它不证明 target stopped 或 Legacy ready。
验收条件
- v4 config、commit schema/digest/Profile/receipt/path 任一漂移,Application 在 signal/storage 前失败;v2/v3 既有语义不变。
- adopted prepare/verify 对 systemd、OpenRC、Compose 均可 exact replay,且不产生 install/start/socket/network 副作用。
- systemd/OpenRC root bridge 不解析 commit;Owner cutover journal 显式绑定 commit/receipt,restart/stop 漂移失败。
- adopted Compose 真实 config 使用 identity-preserving mount,preflight/apply/restore/evidence lineage 保留 commit/receipt。
- activation 与 rollback 仍需既有独立命令;D-387 apply 或 bundle prepare 单独成功不会改变服务状态。
- 覆盖成功、exact replay、commit/config/mount 漂移、发布崩溃、manager/Docker 响应丢失、ENOSPC 与低配资源边界。
- 完整 package/backend、架构、distribution、artifact 门通过;workspace package 数、浅包审计和常驻依赖闭包不退化。
实现与验收证据
- Application v4、canonical data application commit codec、adopted bundle、systemd/OpenRC Owner consumer 与 Compose
lineage 均已落地。Compose rollout receipt 为 v3,restore/evidence collection receipt 为 v2,三者携带相同
applicationConfigDigest/activationDigest/commitmentDigest/legacyDataApplicationCommitDigest/ legacyDataApplicationReceiptDigest,fresh receipt 使用同一 schema 但 adopted 字段为null。 - adopted bundle 聚焦套件
12/12通过:systemd、OpenRC、Compose prepare/verify exact replay,不产生 service intent; commit/config/mount 漂移失败关闭;真实docker compose config --format json证明 identity-preserving 双 bind;Dockerupresponse loss 只 inspect;restore 保持 activation 已绑定的 target device/inode;evidence collection 继承同一 lineage。新增发布故障矩阵覆盖“stage 已 fsync、target 尚未 link”与“target 已 link、stage 尚未清理”,原命令均以 no-replace bytes 收敛且不激活服务。 - SQLite rollout safety 聚焦套件
8/8、完整 Local SQLite239/239通过。adopted restore 在同一 inode 内有界改写, ENOSPC/部分写保留 exact source stage 与 old-snapshot recovery evidence,重放后恢复原字节并清理中间材料;fresh restore 继续使用原 replace 语义。 - 完整 Local Owner 为
222 total / 217 pass / 5 root-service conditional skip / 0 fail。tracked backend 在新增 release contract 回归后为1540 total / 1538 pass / 2 conditional skip / 0 fail;loopback/TLS 用例在允许本机 listener 的环境运行。18 个 QL3 workspace package 均完成 clean build 与逐包测试。 - package boundary schema v6 保持
workspacePackageCount=18、singleSourcePackages=[]、shallowSourcePackages=[];Local Owner 为135 source / 134 nested / 1 root binary entry。没有新增 workspace package、production dependency、daemon、timer、watcher、listener 或数据库连接。 - 十四档 Edge/Standalone artifact audit 全 compatible。基础 Edge/Standalone 为
2,611,978 / 2,612,056bytes、319 files、58 loaded modules;Adopted 为2,831,713 / 2,831,836bytes、339 files、59 modules;Application 为3,669,436 / 3,669,556bytes;Application+AI 为4,529,710 / 4,529,842bytes;MCP 为7,337,910 / 7,338,018bytes,均保留预算 headroom。Edge/Standalone Compose 继续分别固定128 MiB/64 PID与256 MiB/256 PID。 - release live preflight/rollout 不再硬编码过期 SQLite v44/v37,而从已构建 Local SQLite contract 读取 v50;最终
deployment-readiness 不复制实现版本号,只要求有界正整数并要求 Edge/Standalone 对同一 selection 完全一致。catalog
selection、deployment readiness、publication closure 与 tag finalizer 相关回归
28/28通过,image release、local image、package boundary、cluster dependency、Edge import、service bridge、deployment lock 与 cluster deployment 审计均 compatible。 - PostgreSQL/Cluster 语义未由本 ADR 修改,也不由本地 receipt 重新声明。独立 PostgreSQL 18.6 arm64 HA Docker 门仍以
timeline
1 → 2、146gates 和无 finding 的 evidence audit 通过,用于证明本切片没有破坏既有集群基线,而不是把 Local lineage 当作 Cluster authority。
未包含
- Cluster/PostgreSQL/Kubernetes 的 prepared-model application 与 rollout;
- 运行期间持续文件 attestation 或 tamper remediation;
scripts/upload的最终启用、Task/Trigger activation、SSH host-key 审核;- 数据产生后的自动 reconciliation 或 Legacy restart;
- 固定物理 Edge/NAS 的断电、FTL 写放大和加密卷销毁证明。