Files
codex-app-server/docs/chv-muse-p9-gap.md
T
host c06ba50983 feat-chv-muse-p9: 推理与遥测受限通道(Sentinel 预审 + 遥测最小化 + 训练退出开关)
- 受限推理通道:cloud/chv/inference_channel.py(origin 解析、
  allowlist、上限、Sentinel 预审),执行器 _run_with_vm 起 VM 前
  必过 _inference_gate(allow 才继续,deny/ask 审计 + fail-closed,
  任务级 extra_env 合并进预审);host.py 第二道防线(_server_command
  的 origin 结构校验 + Server.request 超时/重试走 env)。
  origin 规则:https 公网网关(只 443);http 只接受本地中继
  (字面环回/私网 IP,如 http://172.17.0.1:28084,端口保留,
  无 DNS、无重绑定,allowlist 与单次能力照常);未知 provider
  标签走默认网关(与 _server_command 一致)。
- sentinel.py:从 P5(25bcd35)取的超集版本 + 推理连接器条目
  (POST /v1/responses、GET /v1/models;scheme/端口只 https/443,
  本地中继不走 evaluate,不把 http 加进表)。
- 遥测最小化:cloud/chv/telemetry.py(字段白名单、确定性采样、
  落盘失败不影响任务、token 未回传记 null)+ 训练退出开关
  (默认开,本项目决定;关后轨迹不再产生;损坏 fail-closed;变更审计)。
- 访问边界:docs/chv-access-boundary.md(运营访问口径 + 审计钩子,
  不冒充技术不可访问;Confidential VM 明确未做)。
- 测试:cloud/tests/test_chv_muse_p9.py 51 组全过;5 个旧文件
  的 make_exec 配假 origin + 假解析器(新 fail-closed 门要求的
  测试配置);全量 cloud 套件 1918 OK(skipped=8)。
  详见 docs/chv-muse-p9-gap.md(含自定清单与真机待验证清单)。
2026-11-06 23:03:06 +00:00

158 lines
15 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.
# 对齐 Muse P9 差距表:推理与遥测受限通道
> 先写表再动手。依据:`~/workspace/research_notes/meta-muse-ai-architecture-20261101-0755/report.md`
> (Meta 官方 security.muse.ai 与设计文 2026-09-08 为准;第三方拆解只作参考,不得把逆向细节写成官方架构)。
> 基线:`06c3e17`。分支:`feat-chv-muse-p9`。本地做,不推、不合、不部署。
## 1. 现状 vs Muse 官方要求
| # | Muse 官方要求(security.muse.ai / 设计文) | 现状(基线 06c3e17) | 本轨做法 |
|---|---|---|---|
| 1 | 推理与遥测代理是通向外部基础设施的**受限通道** | 模型调用无收敛点:`host.py` 起 `codex app-server`,provider=`sub2api` 时 `-c` 覆盖 base_url(取 `SUB2API_BASE_URL`);provider 为空时 codex 用自身 config.toml 默认网关(xcmapi 路径),执行器侧没有任何出口校验 | 新模块 `cloud/chv/inference_channel.py`:单一出口点 + 执行器 `_run_with_vm` 起 VM 前预审(Sentinel 评估);`host.py` 的 `_server_command` 做结构校验(https、无 userinfo、主机在 allowlist 内)当第二道防线 |
| 2 | VM 发送**有限数据**用于推理与遥测(安全文**未给字段级清单**) | 无任何遥测:任务元数据只进宿主日志与审计 spill,无字段清单、无采样、无发送路径 | 新模块 `cloud/chv/telemetry.py`:字段级白名单(只含任务级元数据,见 §2);采样率可配;落盘 `<state_dir>/telemetry.jsonl`,发送失败只记日志、不影响任务 |
| 3 | 推理轨迹(对话往返、工具调用、子代理交接)在**去除关键个人身份信息后**可用于训练新检查点;用户可**一键退出训练** | 无训练上传、无开关 | 训练退出开关(`training_opt`,见 §2):状态持久化在 `<state_dir>/training-opt.json`,关后轨迹类事件不再产生;开关变更进审计。**默认值开是本项目决定,非 Meta 官方值**(报告注明帮助中心原文未逐字确认默认开启) |
| 4 | 当前 VM 的 Meta 访问边界(官方明确,非营销口径):运营政策限制 Meta 人员访问用户数据,但**并不阻止** Meta 在必要时为支持、保障或运营而访问;“连 Meta 也看不到”是未来 Confidential VM 的目标 | 无边界说明 | `docs/chv-access-boundary.md`:只写文档 + 审计钩子,不冒充技术上不可访问;Confidential VM 明确列为未做 |
| 5 | 广告隔离:不把用户对话或 VM 数据分享给广告系统;但浏览互联网时表现为用户活动、可能间接影响广告 | 无口径 | 只记为口径(见 §4),不用写代码 |
| 6 | Sentinel 是连接器动作与全部网络出口的唯一权限机构 | P3 已做 Sentinel 最小审批、P5 已做纵深(本基线**都没有**;P3 在 `feat-chv-muse-p3` 分支 a90cf78,P5 在 `feat-chv-muse-p5` 分支 25bcd35) | 推理通道走 Sentinel 审批,不重复造审批:`cloud/chv/sentinel.py` 从 **P5 的 25bcd35 拷入**(P5 版已是 P3 的超集,顶层仍只用标准库),再加 P9 的 inference 条目(三张表里加 `inference`:scheme https、端口 443、路径 `POST /v1/responses` + `GET /v1/models`);合入时以本文件为准(它是 P3+P5+P9 的并集) |
## 2. 本项目自定清单(非 Meta 官方值,逐项注明)
### 2.1 遥测字段白名单(`telemetry.py`)
只含任务级元数据,**绝不含**对话原文、文件内容、密钥、个人身份信息:
| 字段 | 含义 | 来源 |
|---|---|---|
| `task_id` | 任务 id | 任务 dict |
| `vm_id` | VM 名 | 执行器 |
| `provider` | 模型 provider(`sub2api` / `xcmapi`) | 通道决策 |
| `model` | 模型名 | 任务 dict |
| `status` | `completed` / `failed` / `interrupted` / `timeout` | 运行结果 |
| `started_at` / `ended_at` | 起止时间戳(秒) | 执行器 |
| `duration_ms` | 耗时毫秒 | 执行器 |
| `tokens_in` / `tokens_out` / `tokens_total` | token 数 | host.py 回传的 usage(未回传时记 `null`,**不编造**) |
| `turns` | 回合数 | 运行结果 |
| `tool_calls` | 工具调用次数 | 运行结果 |
| `sampled` | 是否被采样命中 | 采样器 |
不在白名单里的键一律丢弃;键名含 `key` / `token` / `secret` / `password` / `prompt` /
`content` / `text` / `message` / `email` / `name` 的一律丢弃(防误带)。采样率
`CHV_TELEMETRY_SAMPLE_RATE`:0.0–1.0,默认 `1.0`(全量,任务级元数据量小);按
`task_id` 确定性采样(同一任务每次判定一致,可测试)。
### 2.2 轨迹事件(训练用)
轨迹类事件只含回合级元数据(回合数、工具名、耗时),**不含任何文本**;去标识化
= 只出元数据、不出内容。退出开关关后,轨迹类事件**不再产生**(不是产生后丢弃)。
### 2.3 推理通道上限(自定)
| 项 | 环境变量 | 默认 | 范围 | 说明 |
|---|---|---|---|---|
| 单次请求超时(秒) | `CHV_INFERENCE_TIMEOUT` | `120` | 10–600 | 与 `host.py` 的 `Server.request` 默认超时一致;非法值即 ValueError |
| 重试上限(次) | `CHV_INFERENCE_MAX_RETRIES` | `1` | 0–5 | `Server.request` 的 BrokenPipe 重连次数上限;默认 `1` 即**保持现有行为不变**(现为 `range(2)`);模型回合级重试仍是 worker 的 `max_retries` 语义,本轨不动 |
### 2.4 训练退出开关默认值
默认**开**(轨迹可用于训练)。这是**本项目决定,非 Meta 官方值**:报告注明帮助中心原文未逐字确认
默认开启。开关状态持久化、变更进审计;关后立即生效(后续轨迹不再产生)。
### 2.5 推理出口 allowlist 与 origin 规则
- provider=`sub2api`:origin 取 `SUB2API_BASE_URL`(去尾斜杠);未知 provider 标签与
provider 为空一样走默认网关(与 host.py `_server_command` 一致——标签本身不决定
origin,origin 永远只取 operator 配置的 env)。
- provider 为空或未知标签(xcmapi 路径,codex 用自身 config.toml 默认网关):执行器
**看不到** origin,改取 `CHV_INFERENCE_ORIGIN`(operator 显式配置,如 `https://xgapi.lol`);
未配即 `ask`——审计 + 任务停止(fail-closed),**不静默放行**。部署迁移:现网 xcmapi 部署
须补设 `CHV_INFERENCE_ORIGIN`,否则任务会在预审处停下(这是有意行为,见 §3)。
- scheme 规则(2026-11-07 全量套件复核时修正——初版只认 https 是错的):
- https:公网网关(如 xgapi.lol),端口只接受 443(或缺省);
- http:**只接受本地中继**——字面环回/私网 IP 主机(如 cloud/README.md 的 sub2api 网关
文档形状 `http://172.17.0.1:28084`,Docker 桥接 IP),端口保留;http + 公网主机名/IP、
http + 主机名一律拒绝。主机名一律不算本地中继(DNS 结果不可信)。
- 本地中继分支:字面 IP 无 DNS、无重绑定,跳过 Sentinel 的 DNS/SSRF 步;allowlist 与
单次能力(P3 默认档)照常执行;解析器不被调用(测试用必错解析器钉住)。Sentinel 表里的
inference scheme/端口仍只写 https/443——本地中继不走 `evaluate`,不要“顺手”把 http
加进表(那会放宽公网路径)。
- 路径规则(`inference` 连接器):只放 `POST /v1/responses` 与 `GET /v1/models`;
`/v1/models` 是否必需**待真机核实**(codex 启动时是否拉模型列表),若非必需则收紧为只放
`/v1/responses`。公网路径:解析到私网 IP 即 deny(SSRF,复用 Sentinel);解析失败即 deny。
- 任务级 env 合并:门的 origin 取任务级 `extra_env`(网关配置随任务来,如 worker 的
`_gateway_env`)覆盖执行器进程 env;origin 非法(http 公网、带 userinfo、https 非 443
端口)记 deny,不抛裸 ValueError。报错/审计原因不带 origin 原值(http URL 里可能含
userinfo,见 `resolve_inference_origin`)。
## 3. 改什么(文件清单)
| 文件 | 动作 |
|---|---|
| `cloud/chv/sentinel.py` | **新增**:从 P5 分支 `25bcd35` 拷入(P5 版已是 P3 的超集,顶层仍只用标准库),再加 P9 的 inference 条目(三张表:路径、scheme、端口);合入时以本文件为准(P3+P5+P9 的并集) |
| `cloud/chv/inference_channel.py` | **新增**:受限推理通道(origin 解析、allowlist、上限校验、Sentinel 预审、`InferenceChannelDenied` 之外的 Decision 返回) |
| `cloud/chv/telemetry.py` | **新增**:遥测事件(字段白名单、采样、落盘、永不抛错)+ 训练退出开关(持久化、审计) |
| `cloud/chv/executor.py` | **改动**:`_run_with_vm` 起 VM 前调推理预审(`_inference_gate`);allow 通过、deny/ask 审计 + `ChvFailure` 停止,不起 VM。`execute()`(只读 status,不跑模型回合)不设门 |
| `host.py` | **改动**:`_server_command` 的 sub2api origin 结构校验(https、无 userinfo、主机在 allowlist 内);`Server.request` 默认超时与重连上限改走 `CHV_INFERENCE_TIMEOUT` / `CHV_INFERENCE_MAX_RETRIES`(默认 120 秒 / 1 次,即现有行为) |
| `cloud/tests/test_chv_muse_p9.py` | **新增**:全部覆盖(见 §5) |
| `docs/chv-muse-p9-gap.md` | **新增**:本文件 |
| `docs/chv-access-boundary.md` | **新增**:运营访问边界说明 |
**不碰的**:`cloud/chv/config.py`(通道读 `env` 字典,不进 VM 配置片段;架构测试无“env 必须进片段”的规矩)、
P3 的 authd、P5 的 eBPF 污点(P5 已结论:本 VM 的 bpf() 被 seccomp 挡,不可行;本轨不重复探)、
P6 的确定性检查层(审计复用执行器已有的 `_emit`,不另造审计接口)、worker 的 `max_retries`、
guest 镜像(`host.py` 是标准库单文件,`/app/host.py` 拷入方式不变——本轨对 `host.py` 的改动只用标准库)。
## 4. 诚实边界
- 本轨是**决策层 + 预审**,不是网络层强制:客内 codex 直连的强制仍靠 Tinyproxy 白名单 + 宿主防火墙
(第三棒落地中)。不要把本轨写成“出口已全部经 Sentinel”——P3 的模块 docstring 同款声明。
- provider 为空时执行器看不到 codex config.toml 里的 origin:本轨用 `CHV_INFERENCE_ORIGIN`
显式配置 + 未配即 ask 兜底;这不是“读到了 config”,部署时须补 env(见 §2.5)。
- 遥测目前只落盘 `<state_dir>/telemetry.jsonl`,**没有**向外部遥测端点发送的路径;“发送失败不影响任务”
指落盘失败也不影响任务。向外部发遥测是后续工作,需另批。
- 训练轨迹目前**没有**上传路径:开关管的是“轨迹类事件是否产生”;上传通道本身不存在,开关是为
上传通道预留的门禁,不是已接通的上传。
- token 数只取 host.py 回传的 usage;codex 未回传时记 `null`,不估算、不编造。
- `GET /v1/models` 是否必需未验证(见 §2.5),真机核实前不收紧、也不放宽。
- `sentinel.py` 是从 P5 分支(25bcd35)原样拿来的超集版本:其中 `capability_for` 的非 one-shot
分支在运行时 import `cloud.chv.capabilities`——该模块是 P5 的文件,本轨基线 06c3e17 里没有。
本轨只走 one-shot 默认档,运行时不触及该分支(新测试 51/51 证明);mypy 会报 1 处
`import-not-found`,待 P3/P5/P9 集成轨把 capabilities.py 带进来后消除,不在本轨另造一份。
其余 mypy 报错与基线一致:`executor.py:1396/1401`(`out` 未注解)与 `marker_tools.py:142`
是 P3 已记录的基线问题,本轨未碰。
## 5. 测试清单(`test_chv_muse_p9.py`,全部用标准库与 mock,不联网、不启动真实 VM)
- origin 解析:合法 origin 通过;缺 `SUB2API_BASE_URL` 即错;http 公网主机名/IP 被拒;
http 本地中继(字面环回/私网 IP,如 `http://172.17.0.1:28084`)通过且端口保留;
URL 带 userinfo 被拒;尾斜杠被剥掉;未知 provider 标签走默认网关(不 deny)。
- allowlist:`CHV_INFERENCE_ALLOW_HOSTS` 显式覆盖生效;默认即 origin host。
- 上限校验:超时/重试非法值(非整数、越界)即 ValueError;默认值 120/1。
- 预审决策(假解析器,不联网):allow(公网 IP + 白名单 + 路径内);deny(解析到私网 IP、
origin 非法);ask(主机不在白名单、provider 为空且未配 `CHV_INFERENCE_ORIGIN`);
路径越界(`GET /v1/admin`)即 deny;能力绑定(单次、300 秒);本地中继 allow(不查 DNS,
用必错解析器钉住)与本地中继 ask(allowlist 不含该主机)。
- 旁路:直连(非通道)调模型无入口——执行器 `_run_with_vm` 前必过预审(mock 执行器验调用顺序:
预审 deny 时 `wake_vm` 未被调用);任务级 `extra_env` 的 origin 被合并进预审(worker 网关
路径),非法 origin 转 deny 而不是裸 ValueError。
- 遥测:白名单外键丢弃;`prompt`/`content`/`api_key` 类键丢弃;采样确定性(同一 task_id 多次判定一致;
采样率 0 全丢、1 全留);落盘失败(state_dir 不可写)不抛错、不影响任务;token 未回传记 `null`。
- 训练开关:默认开;关后轨迹事件不再产生、任务级遥测不受影响;开关变更持久化 + 审计有记录;
状态文件损坏时 fail-closed(当作关处理,记日志)。
- host.py:`_server_command` 的 origin 结构校验(http 公网被拒、本地中继通过、allowlist 不匹配
被拒、合法通过);`Server.request` 默认超时与重试上限走 env(mock 验默认值 120/1 与 env 覆盖)。
- 旧测试 harness:5 个文件的 `make_exec` 配了假 origin(`SUB2API_BASE_URL` 与
`CHV_INFERENCE_ORIGIN` 均为 `https://xgapi.lol`)+ 假公网解析器——这是新 fail-closed 门
要求的测试配置,不是放宽门;门的 deny/ask 行为由本轨新测试钉住。
## 6. 真机待验证清单(排队,测试机空闲后;第三棒 → round-11 → P4/P5/P6 → 本轨)
1. `GET /v1/models` 是否必需:真机跑一轮 turn,确认 codex 是否调它;必需则保留,不必需则收紧为只放 `POST /v1/responses`。
2. 预审对真实 xgapi 主机的判定:`https://xgapi.lol` 解析后 IP 是否干净(不在 SSRF 挡截网段内);若解析到被挡网段,记录真实判定、不绕过。
3. `CHV_INFERENCE_ORIGIN` 未配时 xcmapi 任务是否如实停在预审(ask + 审计),配上后是否放行。
4. 本地中继真机:`SUB2API_BASE_URL=http://172.17.0.1:28084` 的任务预审是否放行(本地中继分支)、
真实 turn 是否走通该中继;同时确认 http 公网 origin 在真机仍被拒。
5. 真实 turn 的 usage 是否回传:遥测里的 token 字段是否从 `null` 变成实数。
6. 遥测落盘文件在真实任务后是否存在、内容是否只有白名单字段。
7. 训练开关在真机上的持久化:开关翻转后重启执行器,状态是否还在。
7. 超时/重试上限在真机上的表现:长 turn 是否在 `CHV_INFERENCE_TIMEOUT` 处超时(只记录、不主动造超时)。