feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,117 @@
# 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、只读文件系统与闪存写放大报告。