mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
124 lines
5.5 KiB
Markdown
124 lines
5.5 KiB
Markdown
# ADR-0195:AI-excluded 本机 Application 候选镜像
|
||
|
||
- 状态:Accepted(候选镜像 contract、arm64 build 与 fresh live gate 已实现)
|
||
- 日期:2026-07-29
|
||
- 关联 RFC:QL-RFC-0001 D-42、D-156、D-168、D-175、D-183、D-184、
|
||
D-185
|
||
- 关联 ADR:ADR-0042、ADR-0088、ADR-0167、ADR-0178、ADR-0185、
|
||
ADR-0193、ADR-0194、ADR-0196
|
||
|
||
## 背景
|
||
|
||
D-184 的 Compose 描述符已经拒绝 mutable tag,但仓库此前只有 Cluster
|
||
control/admin 镜像。直接把 workspace mount 到通用 Node image 可以用于测试,
|
||
不能作为本机交付物:它包含开发依赖,缺少独立 production lock,也无法证明 AI
|
||
没有进入默认 Edge/Standalone runtime。
|
||
|
||
为 Edge 与 Standalone 各建一个镜像也没有收益。两者使用相同 application 代码和
|
||
生产依赖,仅由私有配置选择 SQLite journal、页预算和资源 envelope。复制镜像会
|
||
产生两套 lock、SBOM、签名和漏洞响应。
|
||
|
||
## 决策
|
||
|
||
### 1. 一个镜像、两个本机 Profile、零新增 package
|
||
|
||
`deploy/containers/ql3-local-application` 是部署制品目录,不是 workspace
|
||
package。镜像运行时精确包含:
|
||
|
||
- `runtime-core`;
|
||
- `local-sqlite`、`local-admin`、`local-profile`、
|
||
`local-adopted-profile`;
|
||
- `local-command-file`、`local-secret`、`local-process`、
|
||
`local-execution`、`local-application`;
|
||
- `croner@7.0.8` 与 `semver@7.7.4`。
|
||
|
||
Edge/Standalone 继续由
|
||
`qinglong/local-application-process@v2` 私有配置选择。镜像不包含
|
||
`@qinglong/ai`;`ai.deployment` 只能为 D-184 生成的 `excluded`。后续 AI
|
||
镜像若成立,必须作为独立 opt-in 制品和预算评审,不能修改默认镜像闭包。
|
||
|
||
### 2. Builder authority 不进入 runtime
|
||
|
||
Dockerfile 的两处 base 都直接固定为同一 Node 24.18.0 Bookworm Slim OCI index
|
||
digest,不提供 `NODE_IMAGE` build argument。build dependency root 独立锁定
|
||
TypeScript、Node/Semver types 与 Drizzle schema 编译类型;`ql3-ai` 只在
|
||
workspace builder 中满足 application 的静态类型编译。
|
||
|
||
production dependency root 使用另一份 lock,只安装 `croner` 与 `semver`。
|
||
最终 stage 从 builder 仅复制上述十个内部 package 的 `package.json` 与 `dist`;
|
||
删除 npm 自动生成但 entrypoint 不需要的 `.bin` 链接,以及 TypeScript
|
||
`.js.map`/`.d.ts.map` 调试映射;不复制 AI、Drizzle、TypeScript、cache、源码或
|
||
开发类型。所有 `npm ci` 都禁用 lifecycle scripts。
|
||
|
||
### 3. 镜像身份与运行 envelope
|
||
|
||
镜像默认:
|
||
|
||
- `USER 65532:65532`;
|
||
- `NODE_ENV=production`;
|
||
- 唯一 entrypoint 为 `ql3-local-application`;
|
||
- 不声明端口或 health listener;
|
||
- 标记 `edge,standalone` 与 `ai=excluded`。
|
||
|
||
镜像自身不内置可写数据层。D-184 Compose 描述符必须继续固定:
|
||
|
||
- 由 host deployment owner 覆盖 numeric UID:GID;
|
||
- read-only root filesystem;
|
||
- `network_mode: none`;
|
||
- drop all capabilities 与 `no-new-privileges`;
|
||
- 唯一 `/var/lib/qinglong3` bind;
|
||
- 16 MiB noexec tmpfs;
|
||
- Edge 128 MiB/64 PID,Standalone 256 MiB/256 PID。
|
||
|
||
默认 numeric user 是无 Compose 覆盖时的最小权限兜底,不代表它能读取任意 host
|
||
volume。
|
||
|
||
### 4. 候选证明与 release 证明分开
|
||
|
||
静态审计复核 base digest、两份 manifest/lock、build/runtime package 闭包、
|
||
非必要文件删除、禁用 install scripts、默认 user、entrypoint 与 labels,并以
|
||
mutation fixture 拒绝 mutable base、AI runtime copy、额外 production
|
||
dependency、非必要文件回归和 lock 漂移。
|
||
|
||
真实 live contract 使用 D-184 prepare 分别创建 fresh Edge 与 Standalone
|
||
volume,再在 read-only、network-none 容器中以各自 128 MiB/64 PID 与
|
||
256 MiB/256 PID envelope 启动候选镜像。只有观察到 `active`、由
|
||
`docker stop` 触发 `SIGTERM`、`stopped`、exit 0,并在退出后取得 SQLite
|
||
`integrity_check=ok` 才通过。
|
||
|
||
CI 在原生 amd64 与 arm64 runner 重复 build、identity、CycloneDX inventory、
|
||
`--help` 和 fresh live contract。ADR-0196 又把本机 profile 接入共享 attested
|
||
OCI 与 release matrix;当前本机 arm64 成功仍不能替代尚未执行的远端双架构
|
||
结果。
|
||
|
||
本 ADR 不接受以下 release 声明:
|
||
|
||
- 本地 tag 或 Docker image ID 是可分发 digest;
|
||
- 单架构镜像代表双架构 OCI manifest;
|
||
- lock 文件代表 SBOM;
|
||
- Dockerfile 存在代表 provenance/signature;
|
||
- 128 MiB Docker 门代表固定物理路由器的闪存、冷启动或断电证据。
|
||
|
||
## 当前证据
|
||
|
||
- 静态 contract:`findings=[]`;
|
||
- mutation audit:6/6(含非必要文件与双 Profile/双架构 CI contract);
|
||
- runtime inventory:10 个内部 package、2 个外部 package、AI absent,
|
||
611/640 files、4,897,102/5,242,880 bytes、无 symlink/special file;
|
||
- 本机镜像:Linux arm64、默认 `65532:65532`;
|
||
- uncompressed image:251,932,346 bytes;
|
||
- Edge live:128 MiB、0.5 CPU、64 PID、network none、read-only root;
|
||
- Standalone live:256 MiB、0.5 CPU、256 PID、同一镜像与隔离边界;
|
||
- 两个 application:各 19 个低敏事件,active 后 SIGTERM graceful stop;
|
||
- SQLite:`integrity_check=ok`。
|
||
|
||
## 后续发布门
|
||
|
||
- 原生 amd64/arm64 CI 的实际成功记录;
|
||
- ADR-0196 已实现但尚未取得成功记录的双架构 OCI index、逐平台 digest、
|
||
CycloneDX/SPDX、provenance、keyless signature 与远端 registry 回读;
|
||
- npm high/critical 与 SPDX license policy 已实现;base/OS vulnerability
|
||
scanner 仍待完成;
|
||
- 固定低配路由器的存储占用、冷启动、idle RSS、断电、ENOSPC/EROFS;
|
||
- image upgrade/rollback 与 D-184 service activation controller。
|