feat: 沙箱规模——/tmp 顺序加严、注册表寻址与失联精确清理、超时边界抽取

- Docker /tmp tmpfs 移到全部 -v 之前(父挂载在前,seed 子挂载在后);
  宿主级 bwrap /tmp 仍宣告未隔离,口径见 tmp_isolation_status()
- workers 表补 host/pid/address(老库幂等补列),心跳透传与保留语义,
  _claim 写 claimed_by_worker;orphaned_containers 精确识别失联容器,
  Worker.cleanup_orphaned_containers 默认只杀 pid_verified 的精确容器名
- docker_run_timeout() 纯函数抽取(行为与原内联一致),README 补边界与
  未验证清单;新增 test_sandbox_scale.py 20 项
This commit is contained in:
host committed 2026-11-04 20:54:33 +00:00
1 parent e079b9b44a
commit b42b650fb7
7 files changed
+897 -48

No files matched your search

+49
View File
@@ -1252,6 +1252,55 @@ interrupted 且终态任务 cancel 返回 400、心跳后 sweep 不扫刚心跳
仍是串行一轮一任务)。两个端点都要 X-API-Key(与其它已认证
端点一致);worker 直接写库注册时不需要 key,HTTP 心跳是给有
key 的外部 worker 用的另一条路。
- 沙箱规模(本轮新增,见 `cloud/tests/test_sandbox_scale.py`):
- /tmp 隔离分层表(唯一口径 `sandbox.tmp_isolation_status()`):
Docker 任务容器 `--tmpfs /tmp` + `--tmpfs /var/tmp`(容器随
`--rm` 消失,/tmp 也随之消失,绝不落宿主 /tmp);且 tmpfs
挂载在全部 `-v` 之前,附件 seed 的 `/tmp/codex-seed:ro` 子挂载
压在 tmpfs 之上(旧顺序把 tmpfs 放最后,本 VM 无 docker 未
证实旧顺序确实遮挡,故写为顺序加严而非已证实 bug)。
宿主级 codex workspace-write(bwrap)仍不隔离:挂载由 codex
二进制内部决定,host.py 只传 sandboxPolicy 类型、无挂载旋钮,
本仓库改不了它;需要 /tmp 隔离的任务请走 docker 执行器,
不要拿 Docker 层的隔离给宿主级任务背书。
- workers 注册表寻址:workers 表新增 host(worker 的
`--host-name`,容器内 worker 绝不能用容器名冒充宿主名)、
pid(worker 进程号)、address(CLI `--worker-address`,显式给
才有,不含任何凭证);老库幂等补列,老行三列为 NULL 按未知
处理。`POST /workers/heartbeat` 同样接受三字段(host 非字符串、
pid 非整数、address 非字符串时 400);心跳未给时保留旧值,
worker 每次心跳都显式刷新本机 host 与本进程 pid,重启复用
同 worker_id 时不残留旧 pid。
- 失联容器精确清理(`workers.orphaned_containers()` +
`Worker.cleanup_orphaned_containers()`,只写识别与杀容器,
不改任务状态):容器名必须精确匹配 `codex-<32 位 hex 任务 id>`,
且任务行存在、状态为 running/interrupted、认领记录
`tasks.claimed_by_worker`(`_claim` 在 CAS 认领时写)指向
心跳过期的 worker,才算失联;基础设施容器(如 codex-postgres)、
非任务名、已完成任务、无认领记录、owner 仍存活的一律排除,
绝不按 `codex-` 前缀删。默认只杀 pid_verified 的容器(本机 host
+ 已知 pid 且 pid 明确已死);远端 host / pid 未知 / 未验证的
只列不杀,显式 `verified_only=False` 才杀(仅单机语义已确认
时开,跨主机场景绝不能默认开)。旧的
`cleanup_stale_containers()`(任务正则兜底、不区分 owner)保留
不改行为,供认领记录缺失的老任务容器用;任务状态本身由
`sweep_stale_tasks`(心跳过期 -> interrupted)负责,两者分工。
本轮只写代码与测试,绝不在本轮动生产容器、不杀任何生产任务。
- 长任务天花板与并行验证的代码支撑(不宣称已大规模实测):
超时边界抽成纯函数 `worker.docker_run_timeout()`(取较小值、
None 语义、no_time_limit 语义、非法值回落,行为与原内联一致);
取消 -> interrupted(执行结果绝不覆盖)、超时 -> failed、
恢复靠断点续跑(goal-resume / run 续跑),三者状态语义不变。
天花板验证方法(待真实大规模跑时按此执行,非本轮已做):
睡眠循环任务(每轮 sleep + 一行回复)测轮次/墙钟上限、
取消与超时各走一遍终态、owner 崩溃后 sweep + 精确清理各走
一遍;多端并行只做 web 自适应跨视口:console 已含 viewport
meta 与小屏 media 查询(单测只守基线),手工验证矩阵为
320 / 375 / 768 / 1024 / 1440 宽度(无横向滚动、导航可收起、
任务列表与详情可读),iOS/Android 原生明确不在本轮范围。
未验证项(诚实清单):真实 40 人并行、真实大规模长任务天花板、
真实 docker 下 tmpfs 顺序是否曾遮挡 seed、宿主级 bwrap /tmp
隔离(本仓库无修复手段,见上)。
- 任务按执行器池派发:tasks 表新增 `executor` 列(可空,老库幂等
补列);`POST /tasks` 接受可选 executor(local/docker/cloud,
非法值 400),未传时为 None 表示任意 worker 可认领;worker