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

118 lines
5.2 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-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 enable``rc-update``docker compose up` 又会引入
宿主机全局副作用。服务管理器调用没有与 SQLite mutation 相同的事务和精确重放
语义,也会迫使测试以 root 权限修改真实系统状态。
## 决策
### 1. 部署准备仍属于现有短生命周期 Owner CLI
`@qinglong/local-owner-cli/local-deployment``ql3-local-deploy` 负责部署文件
准备,不新增 workspace package。入口只接受:
```text
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: none`fresh 默认 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 `0700`application/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、只读文件系统与闪存写放大报告。