host
06c3e172fe
fix(chv): restore 路径补 H7 net 校验+登记,seed 暂存文件走 atomic_file
...
restore_vm 此前既不校验 net 冲突、也不登记 net.json:
已登记 VM 的 tap/mac/ip 会被恢复出的 VM 静默复用,且
恢复出的 VM 自身不登记、后来的 wake 也看不到它,两边
都漏判。现与 wake 同款:_validate_restore 在起 VMM 前
校验冲突(net_tap_conflict + vm_failed 审计),
_restore_boot 在 restore 成功后登记 net.json,登记失败
审计 net_registry_write_failed + 拒绝恢复(P2-19 语义,
stop_vm 清半恢复 VM)。
hostkeys.write_seed/_apply_seed_extras 的 meta-data、
user-data、network-config 改走 atomic_file(mkstemp 0600
→ 写 → fsync → rename):user-data 含 host 私钥明文,
直接写 + 事后 chmod 会在 chmod 前以 umask 权限短暂可读;
半截文件本身到不了 ISO(唯一消费者 genisoimage 只在
write_seed 返回后跑),原子写同时堵住权限窗口与半截残留。
回归测试 6 个:restore 冲突拒起/恢复后登记/登记失败
拒起/无 net_json 不登记;seed 走原子写(0600、无 tmp
残留)/写失败不留半截文件。CHV 全套件 395 测试通过。
2026-11-06 19:32:41 +00:00
host
f448ecb74b
docs(chv): L4/M6 取舍定案 + 部署核对清单
...
- L4:保持警告不改硬拒(CHV_NET 是手动直接启动命令透传值,
与 API 的 CHV_NET_JSON 是两条路径;硬拒会断手动运维,
误配代价只是 API 模式忽略 CHV_NET);写清兼容影响与
将来改硬拒的前置条件;部署核对只设 CHV_NET_JSON
- M6:确认声明 + 审计就是本项机制,不假装已校验;部署核对
加人工项(部署记录写明已核对租户数 = 1 与项目名一致,
并复核 disk_reuse_experiment_allowed 审计)
- §11.4 加环境变量核对与残留尾巴审计抽查项
(net_registry_write_failed / dirty_marker_write_failed /
vm_net_registry_delete_failed 任一出现即先修宿主再启用)
2026-11-06 18:41:56 +00:00
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
host
c39715659e
fix(chv): P2-20/P2-19/P2-17/P2-18 残留收尾 + NO_PROXY 合并
...
- P2-20:_guest_env_delivered 加专用锁,读写只走
_delivered_secrets/_mark/_clear 三个入口,并发 hammer 测试钉死
- P2-19:_record_net_entries 失败发 net_registry_write_failed
独立审计并返回 False;_boot_vm 见 False 即拒起(wake 随后清半醒 VM)
- P2-17:net.json 删失败发 vm_net_registry_delete_failed 审计;
_write_dirty_marker 失败发 dirty_marker_write_failed 审计并返回
False,stop 不抛的契约不变
- P2-18:新增 cloud/chv/atomic_file.py(mkstemp 随机名 + 文件
fsync + rename + 目录 fsync),net.json/known_hosts/
snapshots.json/pool registry/脏标记/spill 全部走它,固定 .tmp 清零
- H1 附带:NO_PROXY 实测确有覆盖 bug(env 重复赋值以后者为准,
调用方 NO_PROXY 被 egress 的 localhost 值静默覆盖),改为合并
单条发出;egress 已设时调用方代理变量(含小写)一律不发
- 测试:新增 cloud/tests/test_chv_residual.py(23 项),旧 closeup
两处钉死旧语义的测试按新语义更新
2026-11-06 18:41:50 +00:00
host
ba5df980bf
fix(chv): known_hosts 22 端口写裸 host 行,修 provisioned 钉死失效
...
OpenSSH 默认端口只查裸 host 名、非默认端口只查
'[host]:port':known_hosts_line 对 22 端口也写
'[192.168.10.10]:22',ssh 永远匹配不上,报 Host key
verification failed、wake fail-closed 关 VM。keyscan
TOFU 输出裸行,lite9 没暴露。非 22 端口仍用括号形式。
2026-11-06 17:59:53 +00:00
host
4f3a176771
fix(chv): CHV 54 VmConfig 形态——payload.kernel/cmdline 纯字符串 + 磁盘显式 image_type Raw
...
真机 CHV 54.0.0 实测:旧顶层 kernel:{path}/cmdline:{args}
create 时 204 照放、vm.boot 400 'No bootitem provided';
补 payload 后又报 'Image type required for disk'(raw 盘
无魔数、探测失败),磁盘须显式 image_type,枚举原名
Raw(JSON 写小写 raw 报 unknown variant)。
2026-11-06 17:59:53 +00:00
host
5b6514d019
fix(chv): api_client 接受 204 无 Content-Length 响应
...
WSL 真机实测(CHV 54.0.0):vm.create 成功回 204 No Content,
且不带 Content-Length、无体;严格校验(P1-6)把合法成功当
失败拒掉,wake_vm 卡在 vm.create。204/304 按 HTTP 语义体长
恒为 0,不要求长度、不读体(keep-alive 下读会等到超时);
其它状态仍严格(无长度/截断/chunked 均拒绝)。
测试:test_chv_cover100 新增 3 项(204 成功、200 无长度仍拒、
_parse_status)
2026-11-06 17:43:54 +00:00
host
d7cc482ed5
feat(chv-guest): seed 附加项供客机用户与静态网络(wake 路径前置)
...
wake_vm 在 WSL 台架上跑通需要客机身份与网络:镜像本身不建
用户、tap 无 DHCP,缺 users 块的 VM 登不进去、缺 network-config
的 VM 拿不到静态 IP。最小补法(复用 write_seed 机制):
- hostkeys.write_seed 新增 user_data_extra(原样追加进
user-data,供 users: 块)与 network_config(写 seed
network-config 文件,0600);附加项含 runcmd: 即 ValueError
- config _seed_fragment:CHV_SEED_USER_DATA_EXTRA_FILE /
CHV_SEED_NETWORK_CONFIG_FILE(只存路径)
- executor._seed_extras 在 wake 起 VMM 前读入,缺失即
ChvFailure seed_extra_missing;provision_host_key 透传
- README §7 残留更新:guest-env 集成已做
- 测试:test_chv_gitea_sync 新增 5 项(写盘/禁 runcmd/
配置映射/wake 带附加项/缺失 fail-closed)
2026-11-06 16:58:27 +00:00
host
e7f699c62b
feat(chv-guest): Gitea 同步变量经 seed 进 guest-env(执行器集成)
...
- 新增 cloud/chv/gitea_sync.py:6 键白名单,三源合并
(宿主 CHV_GITEA_* < 任务 gitea_sync < extra_env),
必填 4 键缺失即 fail-closed,空即不同步
- ssh_transport:GITEA_TOKEN 进密钥集(argv 拒绝),其余
5 键进 guest-env 集(split_guest_env)
- hostkeys.write_seed:guest_env 与 secret_env 合并写同一
条目,仍无 runcmd、0600
- config:_gitea_fragment 产 gitea_sync_env(函数内 import,
叶子模块顶层只许标准库)
- executor.wake_vm:起 VMM 前合并+校验(gitea_sync_incomplete),
guest-env 随 seed 投递;provision_host_key 透传 guest_env
- worker:_run_attempt 仅对 CHV 执行器把同步变量并进 gateway_env
- 测试:cloud/tests/test_chv_gitea_sync.py 20 项
- 边界:tasks 表无 JSON 列,DB 往返丢任务 gitea_sync;
生产主来源是宿主 CHV_GITEA_* 环境变量
2026-11-06 16:55:32 +00:00
host
67ca0b86cd
fix(chv-guest): seed user-data 不再自带 runcmd,防顶掉镜像钩子
...
sync-test 首轮实测:seed 的 runcmd 整体替换了镜像
/etc/cloud/cloud.cfg.d/99-chv-sync.cfg 的 runcmd(cloud-init
不跨配置类型合并,merge_how 救不了 conf.d),chv-sync
boot 钩子静默不跑、启动 clone 没跑。
- hostkeys.build_seed_user_data 不再发 runcmd;chmod 600
已由 write_files 0600 保证;sshd 重启搬到镜像自己的
runcmd(cloud-final,sshd 首次启动之后)。
- build.sh:99-chv-sync.cfg 的 runcmd 增加 restart ssh
一行(在 chv-sync boot 之前)。
- README §5 同步硬性要求;回归测试 2 项(无 runcmd、
带 secret 仍单 write_files 列表无 runcmd)。
- 针对性 147 项全过(test_chv_to10/gaps/secfix/fix/
arch95plus)。
本地提交,未推未部署。
2026-11-06 16:35:14 +00:00
host
cbd5ab7c0b
fix(chv-guest): 首启缺 key 时才生成 host key(B-23)
...
Minimal 底座 cloud.cfg 不含 ssh 模块,build.sh 又写
ssh_genkeytypes: [] 彻底关掉首启生成:客机无 host key、
sshd -t 失败、ssh.service 连败 5 次,SSH 全程不通(真机
冒烟 131s 未通,console 已到 login、ping 通、tap 正常)。
修正:99-chv-hostkey.cfg 改写首启 bootcmd,只在缺
ed25519 key 时 ssh-keygen -A;ssh_deletekeys: false 保留,
绝不重生成已预置的 per-VM key(bootcmd 跑在 seed
write_files 之前,预置 key 随后覆盖生成值)。README §5
同步修正;新增 BuildScriptTest 回归断言。
2026-11-06 12:05:43 +00:00
host
dcb0824f78
fix(chv-guest): 浏览器装系统路径(B-22)
...
Round 7 构建本身成功,但 BUILDINFO.versions.chromium 为空:
virt-customize 的客机 shell 里 HOME=/,Playwright 默认缓存
~/.cache/ms-playwright 落到根目录 /.cache(实测浏览器在
/.cache/ms-playwright/chromium_headless_shell-1248),而
build.sh 读的是 /root/.cache/ms-playwright(不存在),ls 读回
空文件;buildinfo.py 当时不校验 chromium 非空,静默放过,
事后 check_lockfile / generate --verify 兜底报未覆盖(exit 1)。
改:安装与读回都走 PLAYWRIGHT_BROWSERS_PATH=
/usr/local/share/ms-playwright(agent 运行时也须设同一变量,
否则 chv 用户在家目录找不到浏览器);buildinfo.py 加
playwright_browsers_path 字段,并校验 playwright=1 时
versions.chromium 非空;README 同步。
2026-11-06 10:55:02 +00:00
host
4c8e4eac7b
fix(chv-guest): apt 用 --no-install-recommends(B-21)
...
Round 6 真机构建在 Playwright 步 ENOSPC 失败:--install 默认装
Recommends,python3-pip 连带拉入 python3-dev → gcc/g++/
build-essential 全套,根分区 2.4G 用到 2.2G(95M 空闲),
headless-shell 下到 70% 写满。改走 --run-command apt-get
install --no-install-recommends,必需项显式列出(fontconfig、
openssh-client),两轮 apt 后 apt-get clean + 清 lists 收尾。
buildinfo contents 与 README 同步加 openssh-client/fontconfig。
2026-11-06 10:29:59 +00:00
host
5206dfbc43
fix(chv-guest): gt 用绝对路径调用(B-20)
...
Round 5 真机构建在 gt 安装步失败:install.sh 已装好
/usr/local/bin/gt,但 virt-customize 的客机 sh 里 PATH 不含
/usr/local/bin,同一 --run-command 链里的 gt version 报
command not found,整个 virt-customize 退出非 0。
改两处为 /usr/local/bin/gt version(安装断言 + 版本读回文件)。
install.sh 自身的 SHA256 校验与二进制下载均已通过,不是下载问题。
2026-11-06 10:09:02 +00:00
host
4d09518338
feat(chv-guest): 轻量镜像 + Gitea 双向同步(停全量版,改 Ubuntu Minimal)
...
- build.sh 改轻量:Ubuntu Minimal 底座、apt 精简(git/ssh/cloud-init/
python3/pip/curl/ca-certificates/xz-utils/fonts-wqy-microhei)、
Playwright + chromium-headless-shell、去 B-16 +12G 扩盘;
加 git + gt 1.0.0(官方 install.sh + SHA256 校验,GITEA_CLI_VERSION
钉 1.0.0)与 chv-sync 启动钩子(cloud-init runcmd)
- B-19(新发现):dpkg freeze 的 ${Package} 会被客机 shell 展开
成空,284 行全空白;加反斜杠转义,探针实测已出真实版本
- buildinfo.py:轻量 contents、chromium-headless-shell、gt_version
- generate/check_lockfile.py:gt/chv-sync 声明来源、headless-shell 映射
- chv-sync.sh(新):boot/pull/push/status,分叉/脏树失败关闭,
凭证走 seed 盘 guest-env,不进镜像
- README §1/§2/§3/§4 改轻量口径,新增 §7 Gitea 双向同步
- tests:ChvSyncTest 6 项(本地 bare 仓当远端);针对性测试 144 项全过
2026-11-06 09:47:54 +00:00
host
9e60d5dfaa
fix(chv-guest): B-18 virt-get-kernel -o 是目录不是文件
...
- 第三轮真机构建:virt-customize 全程通过(Playwright 步已过),
随后 virt-get-kernel -a disk.raw -o $OUT/vmlinux 失败;手动
查 --help 确认 -o 是 output directory——旧写法会在 OUT 里
建出 vmlinux 目录,后面的 test -s 文件校验必然失败,即使
下载成功构建也会失败。
- 修复:-o 改为 $OUT,下载 vmlinuz-<ver> 与 initrd.img-<ver>
后把 vmlinuz-* 移为 vmlinux 文件;initrd.img-* 留在 OUT 里
作配套产物(与任务产物清单一致)。
- 同轮里 virt-get-kernel 首跑另报过一次 "file receive
cancelled by daemon",同一盘手动重跑即成功(瞬时故障),
此处失败重跑一次,与 B-05/B-06 重试口径一致。
2026-11-06 09:01:20 +00:00
host
b5e75f156d
fix(chv-guest): B-17 Playwright 安装加 --break-system-packages
...
- 第二轮真机构建实测:单次 virt-customize 在 playwright 步
失败,pip3 install --no-cache-dir playwright 在 Ubuntu 24.04
上报 PEP 668 externally-managed-environment 而拒绝安装;
随后 python3 -m playwright 报 No module named playwright。
此前 PLAYWRIGHT 默认 0,该步从未在默认构建里跑过,缺口
被默认开启暴露出来。
- 修复:playwright 的 pip 安装与第一处 pip 安装同款口径
(--break-system-packages,失败回落无 flag 再试),且只有
pip 成功才跑 python3 -m playwright install --with-deps
chromium(括号分组 + &&,失败即停,不再以 ; 带病续跑)。
- 第二轮中间证据:B-10 步已过(sshd -t 完成、99-chv.conf 与
cloud-init hostkey 配置已写入、客机 /etc/ssh 无残留 host
key;dpkg/pip freeze 已写 876/92 行)。
2026-11-06 08:34:14 +00:00
host
368bccd16e
fix(chv-guest): B-16 真机构建扩根分区(云镜像根分区仅 2.4G 会写满)
...
- 首轮真机构建实测:单次 virt-customize 在 playwright
--with-deps 装 g++/make 时报 No space left on device,
根分区 /dev/sda1 仅 2.4G、已用 97%。
- 修复:qemu-img resize +12G 后 sgdisk 只扩 1 号分区
(起始扇区实测取,不写死;14/15/16 不动),再 guestfish
resize2fs;实测根分区 2.4G→14G、可用 12G、编号仍为 sda1。
- 不用 virt-resize:实测它重写分区表、把根分区从 1 号重编号
为 4 号,而执行器默认 cmdline 是 root=/dev/vda1,重编号后
无法启动。工具检查清单加 sgdisk、guestfish。
2026-11-06 07:59:29 +00:00
host
3b41f864e8
test(chv-guest): 构建脚本测试夹具跟进 B-09 新契约
...
- 假 codex 二进制须报出版本号(codex --version 核对)
- 假仓库改为真 git 仓库,REPO_COMMIT 取其实测短 hash
(旧 test123 非 hex,B-03 格式校验本应拒绝)
- 干跑行补 BUILDINFO.json 字样(断言依赖)
另:79ba56b(并行轨)已把 Playwright 改默认开启、单次
virt-customize、补 fonts-noto-cjk,本提交只补测试与干跑
文案,不动构建逻辑
2026-11-06 15:34:06 +08:00
host
79ba56bd51
feat(chv-guest): Playwright 默认开启、单次 virt-customize、补 fonts-noto-cjk
...
- virt-customize 3 次调用合并为 1 次(VC_ARGS 数组按序执行;
PLAYWRIGHT=0 时仍可显式关掉,freeze 步骤保留兜底)。
- PLAYWRIGHT 默认 0→1:浏览器常驻复用;最终 freeze 在
Playwright 安装之后(B-08),BUILDINFO.versions.pip 含
playwright,chromium 目录名照常记录。
- apt 清单加 fonts-noto-cjk(浏览器 CJK 渲染字体,不是 LaTeX;
xelatex 仍不装)。
- 针对性测试:to10+gaps+secfix 94 项,91 过;3 项失败为
7f18c2d 已有 fixture 陈旧(REPO_COMMIT=test123 非 hex),
与本次改动无关,改前改后一致。
2026-11-06 07:25:15 +00:00
host
7f18c2d13b
fix(chv-guest): 客机镜像构建缺口补齐(B-02/B-03/B-04/B-05/B-06/B-07/B-08/B-09/B-10/B-13/B-15)
...
- B-10(实测):virt-customize chroot 里 sshd -t 必败,双因:
无 host key(exit 1)+ 缺 /run/sshd(exit 255);修为
mkdir -p /run/sshd + ssh-keygen -A 临时生成,断言通过后
立删临时 key,镜像仍不带 host key(已在 WSL 真机预演验证)
- B-08:Playwright 装在第二次定制,须重写 pip freeze 否则
BUILDINFO 里无 playwright;Chromium 以 ms-playwright 缓存
目录名记录进 guest-versions-chromium.txt
- B-09:codex --version 须含 CODEX_VERSION;REPO_COMMIT 须
等于 HEAD 短 hash;工作树脏即拒绝构建
- B-02/B-13:BUILDINFO 生成抽成 buildinfo.py(可单元测试),
补 disk.raw/vmlinux sha256+大小、ubuntu 镜像 sha、cmdline、
playwright 开关、image_sha256 字段
- generate_lockfile.py 改写:版本唯一来源改为
BUILDINFO.versions(客机读回),codex 三件套取
codex_version、/app/host.py 取 repo_commit、chromium 取
versions.chromium;不再查构建宿主 dpkg/pip(旧方法不成立)
- check_lockfile.py 补强:条目格式、contents 全覆盖、冻结
清单双向比对
- B-03/B-04 输入格式校验、B-05 curl 重试、B-06 哈希不符
删文件重下、B-07 磁盘预检、B-15 set -euo pipefail
- 同步改 test_chv_to10.py:generate 不再查宿主,改断言客机
版本来源 + 不回落宿主
2026-11-06 15:13:23 +08:00
host
8c8d62f70d
test(chv): 补齐 chv 模块剩余覆盖到 100%(生产与实验分开计分)
2026-11-01 07:00:21 +00:00
host
e718f0f326
test(chv): 修复 isWorkerAlive 边界测试抖动(精确边界改确定性字段测)
2026-11-01 00:44:12 +08:00
host
54f20dfe55
feat-chv-closeup:收尾(executor 覆盖到 100%、合并/灰度准备,堆叠于 #264)
...
- 后端新增 cloud/tests/test_chv_closeup.py 85 项:唤醒失败
(vm.create/vm.boot/API 超时/guest 就绪超时/host key 不匹配/
provisioned 缺钉死)、恢复与快照失败、取消与清理(残留
socket/登记/磁盘/密钥、脏标记)、中途死亡分类、只读
execute 归类、入口校验、preflight 事后改写、防御性
except、worker_adapter 剩余分支;executor.py 覆盖 100%。
- 前端新增 src/api/chv.closeup.test.ts 13 项:显式 false、
undefined、可用性优先级、Workers 旧 bug 文案不复发、
120 秒边界;控制按钮与新页面仍不做。
- 新增 tools/chv_merge_precheck.py(只读):逐个核 base/head/
mergeable/CI/链衔接,实测 11 个 PR 全 NOT-READY(CI 全
FAIL、mergeState 全 UNSTABLE),退出码 1,绝不合并。
- docs/chv-production-readiness.md 新增 §14:审计落盘运维
runbook(定位/轮转/读取/清理,ADR-14 取舍不变)、mypy
默认口径 2 错评估(沙箱验证过的标注修法,本轨不改
其它链文件)、灰度三阶段可执行清单、V-1~V-9 检查表
(全部未验证)、逐项评分。
- 安全语义不变:egress、key seed 投递、磁盘删除、默认
provisioned、tap 校验、前端不绕后端白名单均未放宽。
- 只到 PR OPEN,不合并、不部署;CI 账单仍是硬门禁。
2026-10-31 12:15:56 +00:00
host
2f3ff8b3ec
feat-chv-to10:往 10 分方向提升(类型/lint 清零、worker 适配抽取、审计落盘、lockfile 生成脚本、前端四态与统一文案、就绪清单 §13)
2026-10-31 10:20:42 +00:00
host
df8c64bd64
feat(chv): 架构/可维护性/前端只读侧推进——生产实验分离、配置片段化、VM 只读展示
...
架构:WarmPool 从 cloud/chv/pool.py 移到 experimental_pool.py,
生产残留清理彻底分文件到 cloud/chv/orphans.py(facade 直接读
registry 文件清理,不构造池、不碰 prewarm/acquire/release);
cloud/chv 与 cloud/sandbox 不再导出 WarmPool,旧路径一律
ImportError(CHV 未合并、无生产用户,兼容保留已无保留理由);
worker 改走 orphans facade,不再持有 _warm_pool;#215 对齐从
接口形状推进到 REPLACEMENT_POINTS 替换清单(R1/R2 可机械替换、
R3 永不替换,#215 模块当前不存在由测试钉住)。
可维护性:build_chv_config 拆成 base/ssh/network/quota/disk/
timeout 六片段(组装只做 dict 合并,键冲突即报错;38 字段集
与取值不变,单项非法报错文案逐字一致);ADR-12 模块职责图
与禁 import 清单(AST 钉住);cloud/chv/ 公开 API 全量类型
标注、无 Any 逃逸,并修正三处旧标注错误;死代码扫一遍
(删实验池已无调用的 _quarantine 委托与未用 _LOGGER)。
前端(只读侧,不做控制按钮):chv.ts 新增纯逻辑谓词与文案
(执行器精确匹配、附件守卫、VM 状态文案、列表徽标);列表
行新增 VM 已报告死亡徽标;详情对全部 CHV 任务显示 VM 行
(已报告死亡/未报告死亡,不猜 live);WorkersSection 与成本
卡沿用已有数据,不新开池管理/成本页;服务端未新增第二个
生命周期字段(持久化事实只有三项,chv_vm_dead 已是完整派生),
只把派生抽成 _chv_vm_dead_from_row 并补 goal 回落形态测试。
2026-10-31 06:55:26 +00:00
host
42de20da68
feat(chv): 架构与代码质量 9.5+ 提升——长函数清零、WarmPool facade、#215 对齐、审计计数、只读 VM 字段、ADR-8~11
2026-10-31 06:20:12 +00:00
host
c908a0147a
feat(chv): 架构与代码质量提升——WarmPool 定案、长函数提取、严格测试替身、Docker 同步守卫、配置统一校验、类型注解与 ADR
...
堆叠于 #246(feat-chv-secfix),只到 PR OPEN,未合并、未部署。
安全语义不变:egress、key seed 投递、磁盘删除、默认 provisioned、
tap 校验一律保留;前端零改动。
- WarmPool 定案为实验性(ADR-5):dispatch 永远冷启动,
wired_to_dispatch 恒 False,acquire/resume 只保留实验接口,
worker 仍调 reconcile_orphans 清理残留。
- 长函数提取:preflight 126→31、wake_vm 135→36、__init__ 69→31、
build_chv_config 159→58;错误文案逐字不变。
- 严格测试替身:fake API 校验 VmConfig(任务盘+seed 盘、net
无重复),fake SSH 校验 known_hosts 钉死;新增 5 项测试。
- Docker 同步守卫:8 个真实标记用例下 Docker 内联版与正典
parse_goal_result 状态全等,改分叉即 fail。
- 配置统一校验:preflight 以 config_invalid 统一兜底端口/配额/
超时/快照/host-key 模式(构造时已校验正常值,preflight 兜底
事后改写);CHV_NET 与 CHV_NET_JSON 双设仍警告不硬拒。
- 类型与 lint:chv 叶子模块公开 API 补类型注解;stdlib 扫描
揪出 executor 内 1 个真死 import(host_key_paths)并删除。
- ADR:docs/chv-architecture.md(ADR-1 至 ADR-7)。
测试:CHV 163 项全过;cloud 全量 1593 项三跑结果为 8 errors
(首跑详细日志因 tail 截断丢失)、全过、1 失败(浏览器代理
时序测试,与 CHV 无关),全量本身存在时序偶发,CHV 套件
确定性全过。
2026-10-31 05:50:56 +00:00
host
baf26beb78
feat(chv): 安全审计修复 High/Medium/Low(堆叠于 #239)
...
审计报告 ~/workspace/chv-security-audit/REPORT.md(No-Go,
Critical 0 / High 7 / Medium 8 / Low 5)代码层修复,mock
测试覆盖,真机动态验证一律未做、只列清单(见
docs/chv-production-readiness.md §11)。
High:
- H1 egress 代码门:代理地址校验、直连显式允许+审计、
每 VM 独立 tap/mac/ip 校验、net 字段白名单;宿主防火墙
列为部署清单,代码门+防火墙缺一不可。
- H2 网关 key 移出 argv:provisioned seed 盘客内文件
/etc/chv/guest-env(0600)投递,传输层密钥进 argv 直接
ValueError;keyscan 模式密钥投递 fail-closed。
- H3 任务盘删除:stop/recycle/cleanup 删 disk.raw(保留期
0)+ 残留盘 GC,删失败审计+脏标记。
- H4 默认值改 provisioned:keyscan 只作显式 opt-in。
- H5 seed.iso 生成即 0600;宿主私钥 seed 盘建好后即删;
崩溃残留扫描 cleanup_stale_secrets。
- H6 state_dir 默认出 /tmp(~/.local/share/.../chv-vms,
preflight 保 0700);api-socket 组/其它可写即 fail-fast。
- H7 tap 隔离校验:net.json 登记+stop 释放,共享即拒绝审计。
Medium:M1 shlex 远程转义;M2 清理失败即拒起(脏标记);
M3 配额默认 50GB + 快照数/总量/保留期上限;M4 SHA256 必填
+ BUILDINFO versions 实测记录(事前 lockfile 列清单);
M5 host key/ preflight/失败审计补齐;M6 reuse 须声明单租户
项目;M7 容量计数只认连通 socket;M8 sshd 加固进 build.sh
断言。Low:L1 链接不跟随;L2 路径白名单;L3 失败审计;
L4 双变量警告+字段白名单;L5 预热盘/任务盘对应关系定义。
旧测试:实验室默认值与生产默认值区分标注;行为已变的旧
测试按新语义更新(容量死 socket、快照白名单、磁盘路径
白名单、网关 argv、reuse 租户声明)。
测试:新 test_chv_secfix 39 项;旧 117 项 + 2 项新增断言 =
158 项;worker 相关 129 项;前端 vitest 9 项;cloud 全量
1588 项(skipped 4)。
2026-10-31 03:02:10 +00:00
host
003ea357f9
feat(console): CHV production-grade frontend — worker availability, attachment guard, queue hint, build:console fix, vitest
2026-10-31 02:35:38 +00:00
host
036323e620
feat(console): CHV executor option in dispatch/env selectors + experimental note in detail/list
...
- DispatchSection: add cloud-hypervisor option (labelled experimental,
default off); default stays docker; hint states a KVM-host CHV worker
must claim the task (otherwise it stays queued; attachments are
rejected by the backend at creation) and that the frontend only passes
the executor value through, never bypassing the backend allowlist.
- EnvSection: same option, because the backend env-create allowlist
(local/docker/cloud-hypervisor) does allow it; note states env
allowlist excludes cloud.
- TaskListSection: add cloud-hypervisor to the executor filter so CHV
tasks are findable; list rows already showed executor.
- TaskDetailPanel: pure display note for cloud-hypervisor tasks
(experimental, default off, no VM controls); detail already showed
executor from the task API.
- README: document the CHV console entry, experimental marking, and
verbatim backend-error display.
- Rebuilt cloud/console.html from src (tsc + vite single-file build);
no backend change: executor is already returned by _task_to_dict.
2026-10-31 02:25:21 +00:00
host
445376743d
feat: CHV 审核修复 P2(范围恢复为全做)
...
P2-1:preflight reuse/CPU/内存校验块小提取为
_preflight_config_checks(纯移动,深耦合处如实停)。
P2-2:build_vm_command 降级为私有 _build_vm_command;
Docker goal 内联解析顶部加正典分叉说明 + 同步检查清单。
P2-3:gaps grep 文本断言改成真实 Worker._run_attempt
行为断言。P2-4:README/build.sh 口径对齐(章节顺序、
BUILDINFO 名字列表、Node.js 不装写实)。P2-5:stop_vm
删除失败记审计。P2-6:CPU/内存校验统一进 preflight。
测试:原 84 + 新增 33 = 117 项,mock,无真 VM。
2026-10-31 02:01:03 +00:00
host
9771ed9e8c
feat: CHV 审核修复(P0+P1,堆叠于 #230)
...
P0:seed 磁盘 CIDATA ISO 挂进 VmConfig 第二块只读盘;
restore provisioned 下复用钉死 known_hosts、缺失 fail-fast
绝不 keyscan;preflight 拦截 CHV_DISK_MODE=reuse(显式
实验二次确认 + 审计放行);SSH 目标选客机 IP 方案(显式
host > net 静态 ip > 显式转发宣称回环,三者皆无 fail-fast)。
P1:网关 extra_env 经 SSH env 前缀注入、worker 守卫收窄到
Local/CHV;取消/清理/看门狗接 CHV 通用路径;WarmPool
acquire 内 resume;容量门持久化计数;磁盘配额总量+余量
与已存在磁盘校验重拷;API 按 Content-Length 验完整性;
快照 file:// 验落盘再 shutdown;execute 死端口归类
vm_not_running。
P2 全部排除未做(用户范围收窄令,留待以后一轨)。
测试:原 84 + 新增 29 = 113 项,mock,无真 VM。
2026-10-31 01:54:13 +00:00
host
22b83f118a
feat: CHV gap fill — worker breakpoint wiring, pool reconcile, per-VM host keys, egress validation, guest build script
...
Stack: #216 -> #221 -> this. Worker breakpoint branch now calls any
executor providing goal_resume_breakpoint_task (Local keeps its
original path, gateway tasks still loud-fail); WarmPool
reconcile_orphans is called on CHV worker startup and close with
audit-on-failure; per-VM SSH host keys are provisioned before VMM
start via cloud-init NoCloud seed (keyscan kept only as weak
fallback, CHV_HOST_KEY_MODE=provisioned|keyscan); CHV_NET_JSON is
validated in preflight and explicit direct egress is audited;
chv-guest/build.sh is now real steps with --dry-run and fails fast
without /dev/kvm. Tests: new test_chv_gaps.py (18), cloud full
1514 OK (skipped=4). No real VM verification: no KVM host.
2026-10-31 01:30:52 +00:00
host
fde7bf4c3e
feat: CHV 执行器优化(拆模块/去重复/SSH 钉死/池持久化/中途死亡分类)
...
- 拆模块:CHV 从 cloud/sandbox.py 巨类拆到 cloud/chv/(executor/
pool/config/api_client/ssh_transport/disk/errors),sandbox 只留
兼容 re-export,旧导入路径与新路径是同一对象
- 去重复:goal 解析、host 顶层参数、runner 超时抽成共享 helper,
Local 与 CHV 在矩阵输入下结论全等(Docker goal-resume 变体
有意保留内联版,见 PR 描述)
- 安全:SSH host key 按 VM 钉死(state_dir/<vm>/known_hosts,
ssh-keyscan 首次捕获 TOFU),废除 no + /dev/null;CHV_SSH_USER
空串与 CHV_SSH_PORT 越界直接拒绝,不静默 fallback
- execute 语义:只读已运行 VM,未运行报 vm_not_running,不为
status 起 VM
- 预热池:registry 落盘、重启载入只当孤儿、reconcile 清理、
损坏 quarantine;条目仍一次性,绝不复用脏 VM
- 长任务:VM 中途死亡判 interrupted(断点可续跑);中途心跳
经评估不做(无客内进度信号可采)
- #215 未合并:只列复用清单,不复制模块
测试:原 42 项 + 新 24 项全过;相关旧套件与 cloud 全量 1496 项
全过(skipped=4)。验证仍是 mock + 干跑 + 单元测试,未做真机、
压测、安全审计;堆叠于 #216 之上,base 为 feat-cloud-hypervisor。
2026-10-30 19:45:33 +00:00
host
c371bbdf1e
feat: Cloud Hypervisor 执行器生产级 v1(cloud-hypervisor)
...
单任务单 VM:API 唤醒(preflight→vmm.ping→vm.create→vm.boot→SSH 就绪)、
暂停/快照/休眠等价/关闭/回收、预热快照池、每任务磁盘 copy、容量与磁盘配额门、
受控 egress fail-closed、VM 事件审计与分类失败;worker/server/autoscaler 接线
(executor=cloud-hypervisor,任务内 LLM 不可自选);默认关闭,不进生产路由。
未做真实 VM 验证:生产主机与本 VM 均无 /dev/kvm、无嵌套虚拟化,验证为 mock +
干跑命令 + 单元测试;真 VM、快照恢复、压测、安全审计见
docs/chv-production-readiness.md。CI 账单故障仍是硬门禁,本提交只到 PR OPEN。
2026-10-30 18:54:16 +00:00
openqj
aebc6238a6
Merge pull request #201 from openqj/feat-platform-auto-resume
...
feat: auto-resume platform-interrupted non-goal tasks
2026-10-30 15:51:43 +08:00
host
4428a721a6
feat: auto-resume platform-interrupted non-goal tasks
...
Deploy restarts kill running tasks: the worker marks them
interrupted (worker_shutdown on graceful exit, sweep on stale
reclaim), and auto_resume_interrupted used to accept goal tasks
only, so every non-goal task killed by a deploy stayed
interrupted forever unless a human retried it (the 2026-10-30
demo task died this way).
Now worker_shutdown/sweep interruptions are re-queued for
non-goal tasks too: the platform killed the task, so the
platform re-queues it. The re-queue is a fresh run, not a
breakpoint resume: goal_resume_pending is forced to 0 and any
stale resume_message is cleared, so the new run cannot be
hijacked by the goal-resume path or an old continue message;
files in the same worktree persist. All existing guards stay:
user pause, coordinator orphans, quota (still goal-only),
backoff, the 3-strike stall guard (non-goal tasks have no
checkpoints, so each platform re-queue counts as no-progress),
and the auto_resume_max cap. Other non-goal reasons (NULL,
quota, user cancel) still do not auto-resume.
2026-10-30 15:39:55 +08:00
openqj
d6369154c2
Merge pull request #198 from openqj/feat-automation-edit
...
feat(console): automation panel edit and richer list
2026-10-30 15:38:59 +08:00
host
2d644f901b
feat(console): automation panel edit and richer list
...
- AutomationPanel rows now show title, prompt content (160-char preview with expand/collapse), repo_url, last/next run, run_once badge and cancelled time.
- Active automations get an inline edit form (name/prompt/interval/repo) that PATCHes only changed fields via the existing automation_update endpoint; backend semantics unchanged (no last_run_at reset, cancelled stays terminal).
- Rebuilt cloud/console.html with tsc + vite (build green); automation python tests 18/18 OK.
2026-10-30 15:31:21 +08:00
openqj
e2ce04ce4f
Merge pull request #197 from openqj/feat-stream-waiting
...
feat: browser stream waits through round gaps instead of disconnecting
2026-10-30 15:29:17 +08:00
openqj
92157ef421
Merge pull request #195 from openqj/feat-shared-home-mcp-sync
...
feat: sync shared-home MCP scripts on control-plane startup
2026-10-30 15:19:26 +08:00
host
962bb50a1c
feat: browser stream waits through round gaps instead of disconnecting
...
Stale bridge state used to end the stream on the first stale read,
so every round gap and every worker restart killed the watch (the
2026-10-30 demo watch died this way when a 15:03 deploy restarted
the worker). Now a stale state switches to a waiting heartbeat
every 5s while the task is still alive (running/queued/paused/
interrupted), and frames resume when the next round's MCP starts
polling. The stream ends only on a terminal task state, a missing
task row, or 10 minutes of continuous staleness. The panel shows
a waiting status and keeps the last frame. Also force-close the
SSE connection on stream end: HTTP/1.1 keep-alive otherwise left
terminal streams hanging with no EOF.
2026-10-30 15:14:37 +08:00
host
b977a9dee8
feat: sync shared-home MCP scripts on control-plane startup
...
租户 config 的 mcp_servers.* 指向共享 home 路径,但宿主共享
home 里原先没有 MCP 脚本,宿主 app-server 启动 MCP 即文件
不存在、握手断掉,面板 mcp-servers-list 三服务器 toolsError
全是 handshake failed;14:43 的手工拷入能修一次,但下次
MCP 脚本改动后面板又会跑旧版。
本批只在服务端 main() 启动时同步一次:清单唯一定义在
cloud/shared_home_mcp.py(7 文件:playwright_mcp/browser/
remote、websearch_mcp/websearch、imagegen_mcp/imagegen,含
同目录兄弟模块),解析优先用 host-config-host.json 的
codex_home,兜底用 tenants_root 父目录/codex-home;缺失
拷入、不同刷新(原子替换)、相同不动;源缺失/目标不可写
只记 warning,绝不阻塞启动。create_server() 不做同步,
避免单测写向生产共享路径。
被否决的落点:worker _tenant_dirs(只在下个任务派发时跑,
面板要等下一个任务才更新,且不修共享路径本身);deploy
host_deploy.sh(会在 shell 里复制第二份清单,且重启不
部署时不刷新);面板请求时同步(把读请求变成写请求,
失败会把面板读数变成 502 风险)。
2026-10-30 07:08:51 +00:00
openqj
671b4bedef
Merge pull request #194 from openqj/feat-project-tree
...
feat: project directory tree + first-round snapshot
2026-10-30 15:00:11 +08:00
openqj
4f035f0449
Merge pull request #186 from openqj/feat-console-llm-status
...
前端:任务详情 LLM 进度与失败原因标识
2026-10-30 14:46:24 +08:00
host
ce7fb3afd6
feat: project directory tree + first-round snapshot
...
- cloud/tree.py: structured worktree tree (dirs first, ignore
.git/node_modules, symlinks not followed, entry/depth
truncation flags, text render for injection)
- GET /tasks/{id}/tree with /files auth + worktree resolution,
max_depth 1..5, max_entries 1..500, escape 400, missing 404,
cloud/cross-host 409
- MCP get_task_tree translating to the new endpoint
- worker first-round injection of a repo-checkout top-level
snapshot (2 layers, 120 entries) after repo resolution,
labelled as checkout snapshot not worktree; skipped for
coordinator/resume/fork, silent when no host source
- docs/project-tree.md; tests for tree, endpoint, injection,
MCP translation
2026-10-30 14:46:11 +08:00
openqj
85f280e5e7
Merge pull request #193 from openqj/feat-software-engineering-github
...
feat: software-engineering skill GitHub guarantees
2026-10-30 14:40:30 +08:00
host
70ad8d7d09
feat: 任务详情 LLM 进度与失败原因标识(列表失败原因摘要)
2026-10-30 14:38:23 +08:00
openqj
b3f4597d3f
Merge pull request #192 from openqj/feat-chromium-flags-quality
...
perf: Chromium 启动 flag 微调 + screencast 画质 q60→q50
2026-10-30 14:34:56 +08:00