Files
codex-app-server/chv-guest/README.md
T
host 2ad29eeb1f feat(chv-guest): M4 Chromium 单独钉死 + /app 收紧为最小集
- Chromium 钉死:CHROMIUM_HEADLESS_SHELL_REV(纯数字修订号)
  设即钉死,buildinfo.py 校验读回的 ms-playwright 缓存目录名
  须恰好是该修订号,否则构建失败;generate/check 的 --verify
  也复核;未设时只记录、构建输出明说未钉死,BUILDINFO 记 null
- /app 最小集:build.sh 不再把整个 $REPO_DIR/cloud/ 拷进客机,
  只拷 host.py(只用标准库,不依赖 cloud/);REPO_DIR 预检
  不再要求 cloud/ 存在
- generate_lockfile.py 只读 BUILDINFO.versions 一事已在 to10 轨
  改写,本棒以合成 BUILDINFO 干跑复核(假版本 9.9.9 进 lockfile,
  宿主 git 1:2.43.0 未混入)并补钉死复核;真实 lockfile 产出
  仍待 V-9 真机构建,不编造版本号
2026-11-06 18:41:56 +00:00

166 lines
11 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.
# Cloud Hypervisor 客机镜像(chv-guest)——内容清单与版本化构建说明
> 本目录有清单与真实构建步骤(build.sh,补缺口轨已实做),
> 但镜像本身不在本轨内实际构建发布。原因写在前面,不藏着:
> 当前没有带 /dev/kvm 的宿主可以构建并实测 VM 镜像(生产
> 主机是 CVM、无嵌套虚拟化;本 VM 容器内同样无 /dev/kvm)。
> 构建与实测见 `docs/chv-production-readiness.md` 的“需镜像
> 构建 / 需 KVM 宿主实测”项,绝不在本轨声称镜像已建成、
> 已发布、已跑通。
## 1. 镜像定位
- 基础:Ubuntu Minimal 24.04 官方云镜像(轻量口径,2026-11-06
用户定:停掉 5.5G 全量版,不再对齐 Manus,不做三面板/
多租户/生产级全套;只装 codex + Python + SSH + cloud-init
+ 薄客机 agent + git + gt + 最小中文字体)。
- 载体:Cloud Hypervisor 直接内核启动(vmlinux)+ raw 磁盘
镜像,执行器用 `CHV_KERNEL` / `CHV_ROOTFS` 指向它们。
- 客内 host.py 位置:`/app/host.py`(与 Docker 的 `/app:ro`
同款约定,只是载体从容器只读挂载换成 VM 镜像内置代码;
代码版本 = 构建时仓库 commit,见 §3 版本化)。
- 绝不进镜像的东西:任何模型 key、系统 key、用户账号数据、
生产凭证。模型 key 由客机 cloud-init 预置的运行环境变量名
注入(与 Docker 的 `-e <名>` 同款惯例),不写进镜像层。
## 2. 内容清单(轻量口径,2026-11-06 用户定)
| 类别 | 内容 | 来源/版本口径 |
| --- | --- | --- |
| 语言与工具链 | Python 3.12、git、curl、ca-certificates、xz-utils、fontconfig、openssh-client(Node.js 不装:Playwright 驱动是 Python playwright,见 PLAYWRIGHT=1 分支;任务镜像 Dockerfile 里也没有 Node.js,本清单不虚构它) | 构建时钉小版本并记入构建日志;apt 用 --no-install-recommends(B-21:默认装 Recommends 会连带拉入 gcc 全套工具链顶满根分区) |
| 文档/图片工具 | 已裁剪:pandoc、poppler-utils、latexmk、texlive-latex-base、python-docx、openpyxl、python-pptx、PyMuPDF、Pillow 不在轻量镜像内 | 全量版曾照抄 `cloud/docker/Dockerfile` 的 apt/pip 清单,轻量版按用户决定砍掉 |
| agent 引擎 | codex、codex-code-mode-host、codex-resources/bwrap(成套,约 367M,缺 sibling 直接构建失败) | codex 0.164.0-alpha.9(与生产任务镜像一致);`CODEX_BIN_DIR` 由构建方提供,bin/ 不进仓库 |
| 浏览器 | Playwright + chromium-headless-shell(默认装,不要完整 Chromium) | Python playwright 装当时的 headless-shell,版本以 ms-playwright 缓存目录名记入 BUILDINFO.versions.chromium;浏览器在系统路径 /usr/local/share/ms-playwright(B-22:virt-customize 里 HOME=/,默认缓存会落到根目录 /.cache),agent 运行时须设 PLAYWRIGHT_BROWSERS_PATH 指向它;单独钉死见 §3:CHROMIUM_HEADLESS_SHELL_REV 设为纯数字修订号即钉死,未设时只记录、不强制 |
| 客机基础 | openssh-server(SSH 通道用)、cloud-init(qemu-guest-agent 已裁剪,CHV 不用它) | sshd 只允许 key 登录,禁止密码登录——build.sh 写入 /etc/ssh/sshd_config.d/99-chv.conf 并经 sshd -t + grep 断言(feat-chv-secfix,M8,此前只是文档声称) |
| Gitea 同步 | gt 1.0.0(官方 install.sh + SHA256 校验)、chv-sync(启动时 clone、显式 pull/push 的同步脚本) | 凭证走 seed 盘 /etc/chv/guest-env(0600)投递,不进镜像;见 §7 |
| 任务代码 | 仅 `/app/host.py` 一个文件(host.py 只用标准库,不依赖 cloud/;旧版把整个 cloud/ 拷进 /app/,客内可读全套 server/worker 代码,M4 收尾已收紧为最小集) | 版本见 §3(repo_commit),不挂载宿主代码目录(VM 与宿主无共享路径) |
中文 LaTeX 说明:与 Docker 镜像同款口径——xelatex + CJK
字体会再增数百 MB,本清单不默认包含;需要时单独一版镜像,
不在本轨悄悄加。字体只留最小的 fonts-wqy-microhei(中文能
看),不要全套 fonts-noto-cjk。
## 3. 版本化
镜像 tag 格式(构建时三项缺一即失败,不生成无版本镜像):
```
chv-guest-<ubuntu 版本>-<codex 版本>-<仓库 commit 短 hash>
```
例如(示例格式,非已建成):`chv-guest-24.04-0.164.0-alpha.9-aebc623`。
构建产物清单(构建方必须输出,执行器用它做 preflight 之外的
版本核对,v1 执行器暂不校验 tag,见就绪清单):
- `vmlinux`(与 Ubuntu 云镜像配套的内核)
- `disk.raw`(根文件系统镜像)
- `BUILDINFO.json`:`{ubuntu, codex_version, repo_commit, built_at,
contents: [清单逐项名]}`——contents 只有名字列表;逐项版本
在 `versions` 里(B-02:dpkg + pip freeze 从客机读回,
chromium 以 ms-playwright 缓存目录名记,gt 以 gt_version 记),
`generate_lockfile.py` / `check_lockfile.py` 以它为唯一来源。
Chromium 单独钉死:构建时设 CHROMIUM_HEADLESS_SHELL_REV
为纯数字修订号(如 1248),buildinfo.py 校验读回的缓存
目录名须恰好是 `chromium_headless_shell-<rev>`,否则构建
失败,BUILDINFO.chromium_pinned_rev 记该值;未设时记 null、
只记录不强制,构建输出明说“未钉死”。真实 lockfile 产出
须等 V-9 真机构建(本机无 /dev/kvm,版本号绝不编造)。
## 4. 构建脚本(补缺口轨:已从骨架改成真实步骤)
见 `build.sh`:参数校验 → 下载 Ubuntu Minimal 云镜像(SHA256
必填)→ qemu-img 转 raw(轻量版不扩盘,底座根分区 2.4G 够用;
全量版 B-16 的 +12G 扩盘已删除)→ virt-customize 装 §2 清单、
拷入 codex 成套二进制与 host.py(仅此一个文件,不拷 cloud/)
到 /app、装 gt 1.0.0、拷入
chv-sync 并写 cloud-init 启动钩子、写 host key 不重生成配置 →
virt-get-kernel 取内核 → 生成并校验 BUILDINFO.json。
`./build.sh --dry-run out/` 只打印命令、不执行、不要求
KVM;全量模式在无 /dev/kvm 的机器上第一步就明确退出
(`CHV_GUEST_BUILD needs a KVM host`),绝不假装构建成功。
全量版曾在 WSL KVM 宿主(ajpkmq6i)上跑过四轮真机构建,
轻量版沿用同一宿主与流程,构建记录见 `~/workspace/chv-prod-audit/`。
## 5. Host key 首启注入硬性要求(补缺口轨新增)
执行器的 per-VM 预置方案(见 `cloud/chv/hostkeys.py`)能
跑通的前提是客机镜像必须满足以下全部,否则预置在真机
上跑不通,执行器会 fail-fast,不回落成弱方案:
- 装 cloud-init 且支持 NoCloud seed(seed 磁盘(CIDATA ISO,feat-chv-fix 起挂进 VmConfig
第二块只读盘)由执行器在 VM 启动前生成,含 meta-data 与 user-data);
- 首启时先应用 seed 再启动 sshd,seed 里的 user-data
把该 VM 的 host key 对写进 /etc/ssh/(write_files,
0600);seed 里绝不能带 `runcmd:`——user-data 的
runcmd 会整体替换镜像 `/etc/cloud/cloud.cfg.d/` 里
的 runcmd(cloud-init 不跨配置类型合并),旧版 seed
的 restart ssh 两行就这样顶掉了 `99-chv-sync.cfg`
的 `chv-sync boot` 钩子(sync-test 首轮实测);sshd
重启由镜像自己的 runcmd(`99-chv-sync.cfg`,cloud-final
阶段)做,见 `cloud/chv/hostkeys.py` 根因禁令;
- 镜像里已钉死 `ssh_deletekeys: false`(build.sh 已写),首启
不得无条件重新生成 host key——重生成会使预置的 known_hosts
立即失效,SSH 钉死直接拒绝,这是设计,不是 bug;无预置 key
时首启 bootcmd 只在缺 key 时 `ssh-keygen -A` 生成(B-23:
曾写 `ssh_genkeytypes: []` 彻底关掉首启生成,客机 sshd 起
不来,已删,绝不再写);
- sshd 只允许 key 登录,禁止密码登录(build.sh 写入
/etc/ssh/sshd_config.d/99-chv.conf:PasswordAuthentication no、
PermitRootLogin prohibit-password、ChallengeResponseAuthentication no、
KbdInteractiveAuthentication no、PubkeyAuthentication yes,并经
sshd -t + grep 断言;feat-chv-secfix,M8);
- seed 磁盘是 per-VM 敏感物,执行器在 VM 关闭/
回收时删除,镜像构建方不得把任何一把 host key 固化
进镜像(所有 VM 同 key 是安全事故)。
## 6. 与执行器的对接
- 执行器 preflight 只校验内核/镜像文件存在且可读,不校验镜像
内容版本——内容版本核对(BUILDINFO)是镜像发布流程的责任,
在就绪清单里标为“需镜像构建”项,不在本轨假装已做。
- 每任务磁盘:执行器 copy 模式下把 `disk.raw` 复制为每任务
磁盘,任务之间文件系统独立;reuse 模式仅单租户实验,preflight 拦截(须同时设 CHV_REUSE_SINGLE_TENANT_EXPERIMENT=1 并审计放行,feat-chv-fix)。
- 快照基准:`CHV_SNAPSHOT_URL` 指向的快照必须由本镜像的干净
启动状态制成(pause 后 snapshot),预热池用它做干净 VM 来源。
## 7. Gitea 双向同步(用户工作区口径,2026-11-06 追加)
主角是用户,不是 agent:用户在 Gitea 上的仓库在 VM 启动时
clone 到 `~/workspace`,用完再同步回 Gitea;VM 是用户的工作
区,Gitea 是权威远端。同步口径沿用 Git Workspace Platform
已定的规则:分叉或冲突时失败关闭,不强制覆盖、不自动
rebase、不静默丢提交。
- 凭证:seed 盘投递 `/etc/chv/guest-env`(0600,root 所有),
含 `GITEA_URL` / `GITEA_USER` / `GITEA_TOKEN` / `SYNC_REPO`
/ 可选 `SYNC_BRANCH` / 可选 `GITEA_EMAIL`。token 绝不进镜像、
代码、日志;`chv-sync boot`(root,cloud-init runcmd 调)把
token 写进 `/home/chv/.git-credentials`(0600,git credential
store 标准机制),再以 chv 身份 clone;`pull` / `push` /
`status` 以 chv 身份跑,只用已落盘的 credential,不再读
guest-env。无 guest-env 的启动静默不同步,VM 照常用。
- 行为:`boot` 只在未 clone 时 clone,已 clone 则静默退出,
不自动 pull(避免重启时覆盖进行中的工作);`pull` 要求
工作树干净且只接受 fast-forward;`push` 无 upstream 时
`push -u origin HEAD`,否则普通 push,非 fast-forward 即
失败,绝不 `--force`。
- gt 1.0.0 随镜像装好(官方 install.sh + SHA256 校验),登录
不 baked:运行时用 `GITEA_TOKEN` + `GITEA_CLI_HOST` 环境变量
即可用(`gt repo list` 等),或在 VM 里 `gt auth login`。
- 验收(用户视角):VM 启动后仓在 `~/workspace` 且是最新
HEAD;VM 内改文件 commit push 后 Gitea 网页可见该提交;
Gitea 上改一次后 VM 内 pull 能同步回来。双向都通才算集成通。
- 集成(2026-11-07,第二轮第一棒):执行器生成 seed 时自动写
guest-env 已做(`cloud/chv/gitea_sync.py` + `wake_vm`):
GITEA_TOKEN 进密钥集(只进 seed,绝不进 argv),其余 5 键
也随 seed 进 guest-env;来源是宿主 `CHV_GITEA_*` 环境变量
或任务 `gitea_sync` dict;必填 4 键缺失时起 VMM 前
fail-closed(`gitea_sync_incomplete`),无同步变量时静默
不同步。客机用户与静态网络由 seed 附加项供给:
`CHV_SEED_USER_DATA_EXTRA_FILE`(含 `users:` 块的文件)与
`CHV_SEED_NETWORK_CONFIG_FILE`(NoCloud network-config),
路径配了但文件读不出时 wake 前 fail-closed
(`seed_extra_missing`);附加 user-data 里禁 `runcmd:`。
专用 token 与测试仓名须用户给,本轨不自编 token、不动
生产 Gitea 配置。