Files
qinglong/docs/adr/ADR-0194-replay-safe-local-deployment-preparation.md

5.2 KiB
Raw Permalink Blame History

ADR-0194:可重放的本机部署准备与 Supervisor 描述符

  • 状态:Acceptedsystemd、OpenRC、Compose 部署准备器与真实 fresh setup 已实现)
  • 日期:2026-07-29
  • 关联 RFCQL-RFC-0001 D-05、D-06、D-40、D-62、D-175、D-183、 D-184
  • 关联 ADRADR-0063、ADR-0066、ADR-0086、ADR-0088、ADR-0185、 ADR-0193

背景

ADR-0193 已关闭 fresh 数据库与常驻 application 的语义缺口,但操作员仍需手工创建 八个私有目录、复制两份 JSON、选择资源上限并自行拼接 supervisor 命令。手工步骤 容易产生宽权限目录、host/container 路径混用、可变镜像 tag、错误的停止信号或把 初始化命令误当成常驻进程。

直接在 CLI 中调用 systemctl enablerc-updatedocker compose up 又会引入 宿主机全局副作用。服务管理器调用没有与 SQLite mutation 相同的事务和精确重放 语义,也会迫使测试以 root 权限修改真实系统状态。

决策

1. 部署准备仍属于现有短生命周期 Owner CLI

@qinglong/local-owner-cli/local-deploymentql3-local-deploy 负责部署文件 准备,不新增 workspace package。入口只接受:

ql3-local-deploy prepare --command-file /absolute/private-command.json

command file 继续复用 @qinglong/local-command-file 的 canonical、当前 UID、 0600、no-follow 协议。命令必须是 exact local.deployment.prepare,固定 Profile、instance、deployment root、D-183 mutation identity 和三种 service 之一。

实现保留在同一个 package 中,并按 contract validation、文件发布事务、 descriptor rendering 与薄 orchestration 四个内部模块组织。内部模块不是独立 发布或依赖边界,避免为了文件数量继续拆 workspace package,也避免部署协议、 文件事务和 supervisor 模板重新堆积为单个巨型源文件。

2. 文件事务无覆盖且可恢复

准备器以当前 POSIX UID/GID 创建并复核固定的 0700 deployment、pepper、 backup、receipt、Artifact、Plugin staging/activation 与 service 目录。随后:

  1. 复用 D-183 executeLocalSetup 收敛 migration 与 key authority
  2. 生成 exact qinglong/local-application-process@v2 fresh 配置;
  3. 生成 systemd、OpenRC 或 Compose 中唯一一种描述符;
  4. 使用同目录 deterministic stage、完整写入、fsync、hard-link no-replace、 directory fsync 和 stage cleanup 发布;
  5. 已有目标只能在 owner、mode、link count、size 和完整字节全部一致时返回 existing,否则失败关闭。

在执行 setup 前先检查已有 config/descriptor 与遗留 stage;已存在内容漂移不会先 改变数据库或密钥。崩溃发生在 stage、link 或 cleanup 窗口时,同一命令可以继续 收敛;不得生成新 mutation ID 来重试。

3. 三种描述符是显式 Profile contract

systemd/OpenRC 只接受 canonical regular Node executable 与 application entrypoint 文件必须由 root 或当前 UID 拥有且不能 group/world writable。描述符固定 SIGTERM、30 秒停止预算、0077 umask 和前台监督:

  • Edge128 MiB、64 PID、1024 fd
  • Standalone256 MiB、256 PID、4096 fd。

Compose 只接受完整 @sha256 image reference,并固定:

  • numeric current UID:GID
  • read-only root filesystem
  • bind mount 唯一 deployment root
  • network_mode: nonefresh 默认 AI excluded);
  • drop all capabilities 与 no-new-privileges
  • 16 MiB noexec tmpfs、30 秒停止预算及同一 Profile memory/PID 上限。

container application config 使用 /var/lib/qinglong3 内部路径;host setup 仍对 bind source 执行。两套路径不能混写。

若当前 UID 为 0,命令必须显式写 allowRootService: true;非 root 必须为 false。这只是明确风险,不把 root 执行宣传为推荐配置。

4. 准备不等于系统启用

准备器不执行以下行为:

  • 不复制文件到 /etc
  • 不调用 service manager 或 Docker daemon
  • 不拉取或构建镜像;
  • 不签发、claim 或恢复 Owner
  • 不启用 AI、网络、Plugin online fetch
  • 不修改已存在的不同配置。

operator 必须检查 bundle,再显式安装和启动。发布镜像、系统包、签名、升级回滚与 真机 supervisor evidence 具有独立供应链和权限责任。

验收

  • systemd fresh deployment 首次 prepared、原命令 existing
  • 八个目录均为当前 UID 0700application/unit 为 0600
  • 真实 SQLite integrity_check=ok
  • OpenRC 使用 supervise-daemon、TERM/KILL 有界停止和 0077
  • Compose 使用 digest image、read-only、network none、cap-drop、no-new- privileges 与 Profile 上限;
  • command widening、mutable tag、root 未确认、权限或内容漂移均拒绝;
  • CLI stdout/stderr 不包含路径、image、digest、material、token 或 secret
  • local-owner-cli package test 全量通过。

未包含

  • 正式可发布的本机 OCI image 与 SBOM/provenance/signature
  • deb/rpm/apk/opkg 等系统包;
  • systemctl、OpenRC、Compose 的真实安装/升级/回滚控制器;
  • systemd/OpenRC 多发行版、rootless container 与 Linux x64/arm64 live matrix
  • 路由器断电、ENOSPC、只读文件系统与闪存写放大报告。