Files
qinglong/docs/adr/ADR-0088-linux-resource-envelopes-and-physical-device-evidence.md
T

26 KiB
Raw Blame History

ADR-0088:Linux 资源档位与物理设备证据边界

  • 状态:Accepted(原生 x64/arm64 CI 门禁,以及物理候选、idle/故障/TaskDefinition/Compose storage/native application start 补充协议已定义;固定设备报告与集群容量证据仍待完成)
  • 日期:2026-07-22
  • 关联 RFCQL-RFC-0001 D-05、D-06、D-14、D-16、D-37、D-62、D-86、D-87、D-88
  • 关联 ADRADR-0006、ADR-0040、ADR-0042、ADR-0062、ADR-0063、ADR-0066、ADR-0087

上下文

QingLong 同时部署在资源很小的路由设备、普通 NAS/单机和集群节点。单一“最低内存”数字会混合至少三种不同问题:代码在极限预算下是否出现明显回退、某类真实设备是否可被产品支持、集群控制面在给定负载下需要多少容量。

旧 CI 只在 1 CPU、256 MiB memory+swap、128 PID 的容器内运行 Edge Executor。它没有从容器内部证明 cgroup 实际值、swap、OOM、只读挂载、非 root、seccomp 或 NoNewPrivs,也在 Node 20/24 × x64/arm64 backend matrix 中重复运行。os.totalmem() 在容器内还可能返回宿主机内存而不是 cgroup 限额,因此不能作为容器资源证据。

反方向上,把路由器预算强加给 cluster-control 也不合理。Cluster 需要独立验证控制面 import/disabled activation 不会意外打开 PostgreSQL 或装配本地任务栈,但 512 MiB 的空载 CI 门禁不能回答生产副本数、连接池、claim 吞吐、故障转移或队列容量。

决策

1. 三种 CI 资源档位

Node.js 固定为 24.18.0,只在原生 Linux x64/arm64 runner 上运行,不用 QEMU 生成资源数字。容器使用 Debian slim 基线,并从容器内部严格验证 cgroup v2 与运行边界。

档位 容器限额 执行内容 证据语义
router-stress-ci 0.5 CPU、128 MiB、0 swap、64 PID Edge Executor、Node SQLite 小批量、真实两步 Workflow、active Package Prompt execute/replay、16 次 Workflow admission 写锁、两组各 16 点 Workflow crash、14 点 ModelInvocation crash 与 20 点 Prompt 外层事务 crash 仅证明极限余量,supportedMinimum=false
edge-release-ci 1 CPU、256 MiB、0 swap、128 PID Edge Executor、10000 行输出、取消、Node SQLite、真实两步 Workflow、active Package Prompt execute/replay、32 次 Workflow admission 写锁、两组各 16 点 Workflow crash、14 点 ModelInvocation crash 与 20 点 Prompt 外层事务 crash 发布回归门禁,仍不是物理设备支持证据
cluster-control-ci 2 CPU、512 MiB、0 swap、256 PID cluster-control 模块加载与 disabled activation 控制面空载/禁用路径门禁,不是集群容量规划

三个档位都必须证明:

  • 进程为非 root,根文件系统和 /workspace 只读,只有有界 /tmp tmpfs 可写;
  • NoNewPrivs=1、seccomp filter 生效;
  • memory.maxmemory.swap.maxcpu.maxpids.max 与档位精确相等;
  • 工作前后 memory.eventsmax/oom/oom_kill/oom_group_kill 增量为零;
  • 架构与原生 runner matrix 声明一致。

门禁失败必须 fail closed。Docker CLI 参数、os.totalmem()、宿主机规格或“进程退出码为零”均不能替代上述容器内证据。

Edge 两档的 /tmp 使用 64 MiB bounded tmpfs,以满足正式 Edge Artifact policy 要求的 32 MiB free reserve + 4 MiB single-Attempt quota。该数字是 tmpfs 上限, 不是预留或已占用内存;实际占用仍计入同一 cgroup。资源门直接运行 local-application 的两步 Workflow product test,避免额外 wrapper 重复驻留模块; product test 发布自身进程 peak RSS,顶层同时以 memory.peakmemory.events 约束整个进程树。SQLite 写锁门只调用正式 Workflow admission Repository,并验证每个 Workflow 恰好一个 BEGIN IMMEDIATE、一个 commit、零 rollbackcrash 门在 Edge DELETE/FULL 与 Standalone WAL/FULL 下分别执行 admission COMMIT 和 conclusive-stop/control-terminal 两组独立的 8 点/profile SIGKILL → reopen → exact replay 矩阵,共 32 个场景。

同一 Edge 门还直接运行 Local AI production composition 的 active Package Prompt:从正式 Package install/materialized revision/automation publication 启动,创建一个 Run、一个 StepRun.kind=model、零 RunAttempt,经同一 SQLite ModelInvocation authority 调用一次 进程内假 provider,并证明 exact replay 返回 content-free receipt 而不再次调用 provider。 报告同时发布进程 peak RSS、数据库 logical/allocated growth、SQLite integrity 和数据库文件 私有输入/输出排除结果。ModelInvocation start/completion 的 7 点/profile、14 场景 SIGKILL → reopen → exact replay 也进入两档资源门。Prompt admission/finalization 又按 每 profile 各 5 点覆盖事务开始、Run/Event/Mutation/fact 写入与 COMMIT,共 20 场景;16 个 COMMIT 前 crash 必须全回滚,4 个 COMMIT 后 crash 必须 durable exact replay,并持续证明 content-free、integrity 与 foreign key。物理断电仍明确为未证明。

这组 crash evidence 只证明进程在 COMMIT 前后被内核 SIGKILL 后的 SQLite 原子性、完整性和重放语义,报告固定 physicalPowerLossProven=false。它不证明电源瞬断、存储控制器 cache、文件系统 barrier、NAND/FTL 或整机重启,不能关闭物理断电 Gate。

2. Profile 预算不能互相推导

  • router-stress-ci 的 128 MiB 是 regression stress,不是 QingLong 3.0 的最低支持内存,也不能写入安装器的兼容判断。
  • edge-release-ci 的 256 MiB 只是模拟发布门禁。当前 Edge 产品候选目标仍是 1 CPU、256 MiB RAM、1 GiB 可用持久空间,推荐 512 MiB;只有物理设备证据完成后才能接受或修改该目标。
  • cluster-control-ci 只证明 disabled Profile 不打开数据库、不装配 runtime stack,并约束模块加载 RSS/启动时间。不得据此宣称 512 MiB 可以承载生产集群。
  • standalone、worker 后续必须拥有自己的工作负载与证据,不能借用 Edge 或 Cluster 的通过结果。

3. 物理 Edge 发布证据

Edge 支持声明必须来自固定设备清单,至少覆盖目标 CPU/SoC、架构、RAM、存储介质、文件系统、内核、libc 与发行版。每份证据记录:

  1. 冷启动、首次 ready、空闲 RSS、CPU 唤醒和后台写入;
  2. migration 时间、额外磁盘峰值、100/1000/10000 TaskDefinition 开销;
  3. 单任务启动、持续日志、输出截断、取消与重启恢复;
  4. 低磁盘、真实 ENOSPC、只读文件系统、进程崩溃、断电重启和时钟跳变;
  5. 原始命令、版本/commit、配置摘要、内核/cgroup/挂载事实和结果摘要。

证据必须版本化归档并能关联 release candidate。共享 CI、Docker Desktop、虚拟机或 QEMU 只能作为前置回归证据,不得标记 physical-device evidence。

4. Cluster 容量证据

Cluster 容量报告独立记录副本数、PostgreSQL 版本与角色、连接池、数据库延迟、候选/claim/ACK/completion 吞吐、重复率、queue depth、failover 与恢复时间。控制面空载 RSS 门禁不能替代这些负载结果,路由器物理设备结果也不能限制 Cluster 的合理资源申请。

被否决的替代方案

  1. 把 128 MiB 作为最低支持配置:压力测试没有证明真实闪存、内核、libc、后台服务与断电语义,拒绝。
  2. 继续只执行 256 MiB Executor 容器:没有验证实际隔离,也遗漏 SQLite 与 Cluster,拒绝。
  3. 所有 Profile 使用同一资源预算:把低资源优化与集群容量规划混为一谈,拒绝。
  4. 使用 QEMU 扩大资源数字覆盖:仿真开销和原生 I/O/调度不可比,只保留功能 smoke,不作为性能证据。
  5. os.totalmem() 判断容器内存:它可能返回宿主机可见值,拒绝。

验收证据

  1. scripts/ql3-linux-resource-gate.cjs 定义三个固定档位,并从 cgroup v2、/proc 与 mount table 读取实际证据;未知参数、非 Linux、非原生目标架构、root、宽松限额或安全边界缺失均拒绝。
  2. scripts/ql3-local-workflow-resource-benchmark.cjs 使用正式 Repository 记录 Workflow admission 写锁;product/crash tests 由顶层门直接执行,避免 128 MiB 档重复 Node wrapper。
  3. scripts/ql3-cluster-control-benchmark.cjs 验证 disabled activation 的数据库打开次数和 runtime assembly 次数均为零,且 stop 收敛。
  4. test/back/ql3LinuxResourceGate.test.cjsql3LinuxResourceWorkflow.test.cjs 固定档位、解析、失败关闭、原生双架构矩阵和精确 Docker 限额。
  5. 本地 arm64 Node 24.18.0 的最新实测中,128 MiB/0.5 CPU 档 memory.peak=128229376、Workflow process peak RSS 85725184、active Prompt process peak RSS 92282880、Prompt SQLite file/allocated growth 均为 016 次 admission 写锁 p95 57.325 ms256 MiB/1 CPU 档 memory.peak=129253376、 Workflow process peak RSS 85499904、active Prompt process peak RSS 90951680、 Prompt SQLite file/allocated growth 均为 032 次 admission 写锁 p95 1.372 ms。两档都证明 Prompt provider 恰好一次、exact replay、content-free durable facts、零 RunAttemptWorkflow admission 与 conclusive-stop/control-terminal 两组各 16 点、ModelInvocation start/completion 14 点,以及 Prompt admission/finalization 外层 20 点 crash matrix、数据库 integrity/foreign-key 检查和所有 gate 均通过,memory.eventsmax/oom/oom_kill/oom_group_kill 增量为零。 这些 Docker 数字仍不是物理设备支持声明;x64 由同一 GitHub Actions matrix 生成独立证据。

物理设备记录协议

物理 Edge 节点先准备一个只含设备声明的 exact-shape manifest

{
  "schemaVersion": 1,
  "evidenceClass": "physical_edge_candidate",
  "profile": "edge",
  "deviceId": "router-a1",
  "deviceModel": "Example Router A1",
  "soc": "Example SoC",
  "storageMedium": "emmc",
  "expectedArchitecture": "arm64",
  "memoryBytes": {
    "minimum": 251658240,
    "maximum": 335544320
  },
  "expectedFilesystem": "ext4"
}

在 Node 24 构建完成后,以真实数据盘中的专用目录运行:

pnpm build:back
pnpm run build:packages:ql3
pnpm evidence:physical-edge -- \
  --manifest=/etc/qinglong/device-evidence.json \
  --data-path=/opt/qinglong/evidence-scratch \
  --output=/opt/qinglong/evidence/edge-rc1.json \
  --json

记录器拒绝 macOS、容器/VM 指示、架构/内存/文件系统漂移、symlink、根目录数据路径、已有输出和非 canonical 路径;SQLite 基准的临时库实际位于声明的数据文件系统。输出以 0600、no-replace、fsync 发布,并绑定 SHA-256。storageMedium、设备型号和 SoC 仍是 operator-declared provenanceSHA-256 也不是签名,因此报告始终为 supported=false

基础记录器现在还在同一真实数据文件系统的私有 scratch 中运行 plugin_package_failed_upgrade_edge_candidate:在 fresh production migration SQLite 上先激活 generation 1,再持久化一个包含循环 Workflow 的 generation 2 staged 升级,并通过正式 recovery coordinator、正式候选资源物化 prerequisite 和正式 SQLite repositories 恢复。报告只有在 generation 2 精确进入 failed(activation_fact_conflict)、旧 activeLockDigestpreviousActiveLockDigest 均保留、activation publisher 零调用、候选 materialized revision 零行、SQLite integrity_check=ok,且耗时、RSS delta、logical/allocated 数据库增长均处于固定上限内时才通过。相同 workload 同时进入 128 MiB router stress 与 256 MiB Edge release cgroup 门,前者仍只是 CI stress;只有由本记录器在无虚拟化 指示的固定设备和声明数据盘上生成时,才属于物理候选证据。两条路径都明确保留 physical_power_loss_not_proven,不能推出断电安全或正式最低配置。

补充 idle 证据

idle sampler 只观察一个已经启动的 QingLong Node 进程,采样窗口为 30 至 3600 秒,间隔为 1 至 60 秒且必须整除窗口。manifest 必须绑定 device、PID 与期望 executable;采样期间 PID、boot ID、进程 start ticks、UID、executable 或命令摘要任一漂移都失败关闭:

{
  "schemaVersion": 1,
  "evidenceClass": "physical_edge_idle_candidate",
  "profile": "edge",
  "deviceId": "router-a1",
  "processId": 1234,
  "expectedExecutable": "/usr/local/bin/node",
  "durationSeconds": 300,
  "sampleIntervalMs": 5000
}
pnpm evidence:physical-edge-idle -- \
  --manifest=/etc/qinglong/idle-evidence.json \
  --output=/opt/qinglong/evidence/edge-rc1-idle.json \
  --json

报告只证明该进程的 RSS 分位数、CPU ticks、page faults、context switches 和 /proc/<pid>/io 字节/系统调用增量。它不证明整机 CPU wakeups、整机闪存写放大、冷启动或首次 ready;不得从进程计数推导这些结论。

专用文件系统故障证据

故障探针接受 enospc_filesystemread_only_filesystem,但探针路径必须是非根、无 symlink、文件系统类型精确匹配且总容量不超过 256 MiB 的专用 mountpoint。脚本不会写满文件系统:operator 必须预先准备已满的专用挂载或只读挂载,探针只执行一次 no-replace 小文件写入并要求得到精确 ENOSPCEROFS,随后验证探针项不存在。

{
  "schemaVersion": 1,
  "evidenceClass": "physical_edge_fault_candidate",
  "profile": "edge",
  "deviceId": "router-a1",
  "fault": "read_only_filesystem",
  "probePath": "/mnt/ql3-fault-ro",
  "expectedFilesystem": "ext4",
  "maximumFilesystemBytes": 268435456
}
pnpm evidence:physical-edge-fault -- \
  --manifest=/etc/qinglong/fault-ro-evidence.json \
  --output=/opt/qinglong/evidence/edge-rc1-fault-ro.json \
  --json

该报告只证明独立小文件写入在专用故障挂载上返回目标 errno;它不证明主数据文件系统已经经历故障、不证明 QingLong application recovery,也不证明断电恢复。

TaskDefinition 规模证据

规模记录器要求 TaskDefinition 正式 schema、Repository 和读取路径已构建。它在真实数据文件系统内创建私有 scratch database,运行 28 条 reviewed migration,并且只通过正式 Repository 追加 100、1000、10000 条定义;每档使用最多 40 页、每页 256 条的稳定 cursor 完整扫描,同时记录追加/扫描耗时、RSS/peak RSS 和数据库/journal/WAL/SHM 的 logical/allocated bytes。

{
  "schemaVersion": 1,
  "evidenceClass": "physical_edge_task_scale_candidate",
  "profile": "edge",
  "deviceId": "router-a1",
  "expectedArchitecture": "arm64",
  "expectedFilesystem": "ext4",
  "sampleCounts": [100, 1000, 10000]
}
pnpm evidence:physical-edge-task-scale -- \
  --manifest=/etc/qinglong/task-scale-evidence.json \
  --data-path=/opt/qinglong/evidence-scratch \
  --output=/opt/qinglong/evidence/edge-rc1-task-scale.json \
  --json

该报告只证明 fresh SQLite v14 上正式 TaskDefinition Repository、内建 qinglong/command@v1 写入语义门禁的三档存储/读取成本,不证明 Trigger/Run scheduler 吞吐、2.x adopted migration 峰值、整机闪存写放大、非 command spec 语义或 TaskDefinition→execution compilation。

基础记录器可导入一份 idle 报告、一份 TaskDefinition 规模报告和最多两份不同 fault 类型报告:

pnpm evidence:physical-edge -- \
  --manifest=/etc/qinglong/device-evidence.json \
  --data-path=/opt/qinglong/evidence-scratch \
  --idle-evidence=/opt/qinglong/evidence/edge-rc1-idle.json \
  --task-scale-evidence=/opt/qinglong/evidence/edge-rc1-task-scale.json \
  --fault-evidence=/opt/qinglong/evidence/edge-rc1-fault-enospc.json \
  --fault-evidence=/opt/qinglong/evidence/edge-rc1-fault-ro.json \
  --output=/opt/qinglong/evidence/edge-rc1.json \
  --json

导入要求所有报告为当前用户拥有的私有 0600 普通文件,摘要有效,且 device、Profile、boot ID、Linux、架构和文件系统与基础记录完全一致;规模报告还必须绑定相同 data path。重复 fault 类型、缺少任一规模档位或跨启动周期拼接一律拒绝。导入后基础报告仍为 candidate;只有被导入且通过的精确条目才从 remaining evidence 中移除。

Compose storage 跨启动候选证据

ADR-0203 增加独立的两阶段 recorder。它要求同一 data path 是专用、0700、真实 /dev/* block device 的 mountpointprepare 在脚本自己的可丢弃 Edge scratch deployment 内创建 3 份真实 rollout backup,把最老候选留在 durable collection stage,然后由 operator 在脚本外重启。resume 只接受不同 boot ID 和同一 UID/device/architecture/filesystem,使用 production collection commit 与 exact replay 验证 tombstone、SQLite 完整性、保留底线和 allocated bytes 回收:

pnpm evidence:physical-edge-compose-storage -- prepare \
  --manifest=/mnt/ql3-evidence/manifests/compose-storage.json \
  --data-path=/mnt/ql3-evidence \
  --session=/mnt/ql3-evidence/reports/compose-storage-session.json \
  --json

# 由 operator 在工具之外执行正常设备重启;不要在同一 boot 运行 resume。

pnpm evidence:physical-edge-compose-storage -- resume \
  --manifest=/mnt/ql3-evidence/manifests/compose-storage.json \
  --session=/mnt/ql3-evidence/reports/compose-storage-session.json \
  --output=/mnt/ql3-evidence/reports/compose-storage-report.json \
  --json

它只报告 prepare/resume 各自 boot 内的 partition sectors-written 操作上界。计数 不得跨 boot 相减,报告不证明 reboot 本身的写入、真实突发断电、NAND/FTL 写放大 或闪存寿命,也不覆盖 MTD/UBI/UBIFS。后者必须使用独立设备计量适配器。基础 recorder 可用 --compose-storage-evidence=/mnt/ql3-evidence/reports/compose-storage-report.json 导入;导入后 power_loss_restart 和 whole-device flash evidence 仍保持未完成。

Native application 跨启动首次 Active 候选证据

ADR-0204 针对没有 Docker 的低配 Edge 节点增加 native recorder。先对最终 AI-excluded production installation root 执行 inspect,用输出的 artifact digest/files/bytes 与 Node digest 填充 manifestprepare 使用正式 deployment API 创建 fresh OpenRC-compatible scratch deployment,随后由 operator 外部重启并在 manifest boot-age 窗口内 resume

pnpm evidence:physical-edge-application-start -- inspect \
  --artifact-root=/opt/qinglong3-release \
  --json

pnpm evidence:physical-edge-application-start -- prepare \
  --manifest=/opt/qinglong/evidence/application-start-manifest.json \
  --data-path=/opt/qinglong/evidence-scratch \
  --artifact-root=/opt/qinglong3-release \
  --session=/opt/qinglong/evidence-scratch/application-start-session.json \
  --json

# operator 在工具外重启,并在 manifest maximumBootAgeMs 内执行:

pnpm evidence:physical-edge-application-start -- resume \
  --manifest=/opt/qinglong/evidence/application-start-manifest.json \
  --session=/opt/qinglong/evidence-scratch/application-start-session.json \
  --output=/opt/qinglong/evidence-scratch/application-start-report.json \
  --json

该报告只裁决 warm Node runtime 下正式 native application closure 到唯一 active 事件的 monotonic 上界,以及 sampled RSS/proc I/O、SIGTERM graceful stop 和 SQLite v35。它不证明 cold Node/dynamic linker、cold artifact metadata、exclusive page cache、firmware/service-manager latency、Compose 或 release signature。 基础 recorder 用 --application-start-evidence=<absolute-report> 导入后,仍保留 power_on_cold_node_and_service_manager_start_to_first_ready

Init-managed application 首次 Active 候选证据

ADR-0205 继续拆解上述 broad Gate。它使用 systemd/OpenRC 正式 deployment renderer,但将 evidence-only POSIX wrapper 作为 service main process。wrapper 在 Node 加载前从 /proc/uptime 记录 kernel clock 起点,在同一进程中逐行保留 application JSON,并把 active uptime 绑定到 event ordinal。prepare 只生成 exact descriptor/install/enable planoperator 必须在工具外安装、enable 和正常重启:

pnpm evidence:physical-edge-service-start -- inspect \
  --artifact-root=/opt/qinglong3-release \
  --json

pnpm evidence:physical-edge-service-start -- prepare \
  --manifest=/opt/qinglong/evidence/service-start-manifest.json \
  --data-path=/opt/qinglong/evidence-scratch \
  --artifact-root=/opt/qinglong3-release \
  --session=/opt/qinglong/evidence-scratch/service-start-session.json \
  --json

# operator 审查并安装 exact descriptor、enable 随机 service,再正常重启。

pnpm evidence:physical-edge-service-start -- resume \
  --manifest=/opt/qinglong/evidence/service-start-manifest.json \
  --session=/opt/qinglong/evidence-scratch/service-start-session.json \
  --output=/opt/qinglong/evidence-scratch/service-start-report.json \
  --json

resume 重新验证 installed descriptor、manager binary、systemd loaded/active/running/enabled + MainPID/FragmentPath,或 OpenRC active + default runlevel,并复验 wrapper→Node PPid、UID、start ticks、exact executable/cmdline、 artifact 和唯一 official active。基础 recorder 用 --service-start-evidence=<absolute-report> 导入。

通过只增加 kernel_boot_to_init_managed_native_application_active。Linux /proc/uptime 不含 firmware/bootloader,因此仍保留 firmware_and_bootloader_power_on_to_linux_kernel_clock;证据 wrapper 也不是最终 直连 Node release unit,因此仍保留 direct_release_unit_without_evidence_wrapper。报告始终 supported=false,工具不自动安装、enable、重启、stop、disable 或清理 service。

ADR-0206 又增加直连 release unit 候选门:

pnpm evidence:physical-edge-direct-service-start -- inspect \
  --artifact-root=/opt/qinglong3-release \
  --json

pnpm evidence:physical-edge-direct-service-start -- prepare \
  --manifest=/opt/qinglong/evidence/direct-service-start-manifest.json \
  --data-path=/opt/qinglong/evidence-scratch \
  --artifact-root=/opt/qinglong3-release \
  --session=/opt/qinglong/evidence-scratch/direct-service-start-session.json \
  --json

# operator 审查并安装 exact direct descriptor、enable 随机 service,再正常重启。

pnpm evidence:physical-edge-direct-service-start -- resume \
  --manifest=/opt/qinglong/evidence/direct-service-start-manifest.json \
  --session=/opt/qinglong/evidence-scratch/direct-service-start-session.json \
  --output=/opt/qinglong/evidence-scratch/direct-service-start-report.json \
  --json

production local application 在 Linux active 边界发布一个最大 4 KiB 的 current startup receipt;它每次激活只原子替换一次,不是 append log。resume 将 receipt boot/PID/start ticks 与实时 /proc、exact Node argv、systemd MainPID/monotonic timestamp 或 OpenRC supervise-daemon parent 交叉核对。基础 recorder 以 --direct-service-start-evidence=<absolute-report> 导入;通过后只 移除 direct_release_unit_without_evidence_wrapperfirmware/bootloader、 whole-device 写放大、断电、签名和其它未采集门不变。

Release archive 外部签名证明

ADR-0399 将 release archive Gate 设计为 QingLong verify-only 的两阶段协议。基础物理 报告必须已导入通过的 direct release start evidenceprepare 把 source repository、 40 位 Git revision、物理设备/boot、release archive digest、实机 artifact tree 与 Node identity 写入 canonical signing payloadoperator 在 QingLong 外部使用 Ed25519 私钥签署, finalize 再用固定 SPKI 公钥复算 fingerprint 并验证 detached signature

pnpm evidence:physical-edge-release -- prepare \
  --physical-report=/opt/qinglong/evidence/physical.json \
  --release-archive=/opt/qinglong/releases/qinglong3-edge.tar.gz \
  --repository=https://github.com/whyour/qinglong.git \
  --revision=<40-lowercase-git-revision> \
  --payload=/opt/qinglong/evidence/release-payload.json

# operator 外部签署 exact payload bytes

pnpm evidence:physical-edge-release -- finalize \
  --physical-report=/opt/qinglong/evidence/physical.json \
  --release-archive=/opt/qinglong/releases/qinglong3-edge.tar.gz \
  --payload=/opt/qinglong/evidence/release-payload.json \
  --signature=/opt/qinglong/evidence/release-payload.sig \
  --trusted-public-key=/etc/qinglong/release-ed25519.pub \
  --expected-repository=https://github.com/whyour/qinglong.git \
  --expected-revision=<40-lowercase-git-revision> \
  --output=/opt/qinglong/evidence/physical-release-evidence.json

验签通过只关闭 release_archive_signature。公钥分发/撤销、真实签发 ceremony、固定设备 采集、firmware/bootloader、whole-device flash、adopted migration 与断电证据继续独立; 输出永久保持 supported=false。私钥不得进入 QingLong 仓库、设备数据目录、环境变量或命令。

后续约束

物理设备 candidate recorder、进程 idle sampler、专用文件系统 fault probe、正式 TaskDefinition 规模记录、Compose storage、warm-Node native application start、wrapper init-managed、direct release init-managed 与外部 release attestation 候选协议已实现;真实 Local Workflow 也已进入 128/256 MiB CI 资源门,但下一切片仍需补齐 firmware/bootloader clock、整机 wakeup/FTL 写放大、2.x adopted migration、application recovery、受控突发断电,并在固定设备执行现有 recorder 与真实 signing ceremony,而不是继续降低容器内存数字。只有固定 x64/arm64 设备矩阵完成后,才能把某一档位从 ci_* 提升为产品支持证据;任何阈值调整都必须说明硬件、内核、文件系统、工作负载和历史基线,Cluster 扩容结论继续走独立容量测试。

首次 active recorder 已绑定最终可发布 application artifact 与正式 readiness contractwrapper init-managed recorder 把 Node 前 kernel uptime、systemd/OpenRC 和 live process tree 纳入同一 bootdirect recorder 再以生产 startup receipt 关闭 wrapper 与最终 release unit 的结构差异。但 Linux uptime 仍不含 firmware,当前所有开发机/Docker 结果也未替代固定物理设备报告,所以仍不能替代完整 power-on Gate。当前 Executor benchmark 或一次模块 import同样不能替代。TaskDefinition 持久化 schema、Repository 与读取路径现已由 ADR-0089 冻结,100/1000/10000 规模记录器也已接入基础报告,但尚未采集固定设备结果。其 fresh schema migration 计时不得替代 2.x adopted database migration,后者仍须记录可审计的额外磁盘峰值采样精度。未完成的边界继续保留在 remaining evidence。