Files
codex-app-server/docs/chv-muse-p12-gap.md
T
host d4cef29cdb round-12: migrate P8 registries + P12 audit to AppStateStore
- app_state.py: host_state_db_path() (host-level DB, distinct from
  per-VM state_db_path); kv_get/kv_put/kv_delete on meta (namespaced
  keys; kv_put skips the no-secrets guard by design — registry docs
  hold user prompts, documented); audit_trail table (P12 record
  shape + hash chain, seq explicit); counts()/backup() gain
  audit_trail.
- swarm.py / presentation.py / browser_tasks.py: JSON-file backends
  replaced by KV rows (p8-swarm / p8-ledger / p8-browser-tasks);
  legacy JSON imported once on first load (renamed .migrated);
  corrupt row / unreadable store returns empty (fail-safe, same
  semantics as before; next successful save self-heals).
- audit_trail.py: StoreAuditTrail (same record shape, hash chain,
  filters, export as JSONL version, backed by audit_trail table);
  migrate_jsonl_to_store() moves a chain verbatim (broken chain
  refused, divergent seq refused, re-run idempotent). JSONL class
  kept for reading old files.
- P12 dict contracts now run against real P3/P5 code
  (ApprovalQueue.record -> build_approval_card ->
  card_matches_channel; CapabilityIssuer.issue -> describe ->
  PermissionRegistry.record_grant).
- P6->P3 local protocol test: real PrivsepClient + real worker
  server + real git status; worker git argv carries the real P3
  helper + useHttpPath; no token in argv/env/audit. runuser + HTTP
  pull stay real-machine-only (see gap docs).
- Gap docs updated: P8 migration done, guest-side browser follow-up
  explicitly left for the real-machine track (no task_id in the
  browser protocol yet; lifecycle needs a real VM to verify);
  P6 wiring + P12 migration marked done; production-readiness.md
  gains §20 (round-12 integration).
- Tests: new cloud/tests/test_chv_muse_round12.py, 24/24 OK;
  test_chv_muse_p8.py ledger corrupt test rewritten for the KV
  backend (+ legacy-import test). P7+P8: 95+1 tests OK.
2026-11-07 00:36:25 +00:00

102 lines
6.3 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 P12 差距表:审计轨迹与活动视图
依据:`~/workspace/research_notes/meta-muse-ai-architecture-20261101-0755/report.md`
(官方新闻室、安全文 2026-09-08、设计文为准;第三方拆解只作参考,
不得把逆向细节写成官方架构)。基线:06c3e17。
## 1. Muse 官方要求(报告原文口径)
| # | 要求 | 来源 |
|---|------|------|
| R1 | 完整审计轨迹(audit trail)展示已做与计划做的事 | 新闻室、设计文(报告 §2.2 第 7 步) |
| R2 | 头像下方显示正在做什么的摘要,点头像可见完整活动日志与已批准权限 | 设计文(任务简报口径;报告未逐字展开,列为设计文要求) |
| R3 | 确定性 UI:结构化审批卡(明确接受/拒绝动作)与凭证安全存储 | 设计文(报告 §2.6) |
| R4 | 审批请求由 Sentinel 直接发到客户端 UI(不是对话内),用户答复直接回 Sentinel | 安全文(报告 §2.6) |
| R5 | 用户可自由检查、编辑、下载 VM 内所有文件,包括 Muse 关于用户的记忆 | 安全文(报告 §3 凭证与数据政策) |
## 2. 现状(基线 06c3e17)
- 审计是散的:`cloud/auth.py` 的 `AuthStore.audit()` 只追加
`(user_id, project_id, action, detail, created_at)` 到 SQLite
`audit_log` 表,`list_audit()` 只按项目倒序取;`cloud/worker.py`
的 `Worker._audit()` 是 best-effort 包装;`worker_adapter.py` 的
`make_chv_audit_callback` 把 CHV 事件转成 `(project_id,
"chv_vm_" + event, detail)`。没有统一记录模型,没有“已做/计划
做”汇总,没有活动视图,没有已批准权限清单,没有导出。
- 审批侧:P3(分支 feat-chv-muse-p3,提交 a90cf78)有
`ApprovalQueue`(内存态 pending 记录:目标描述 + 原因 + ts);
P5(分支 feat-chv-muse-p5,提交 25bcd35)有对话外审批通道
`approval_channel.py`(nonce + 15 分钟过期 + HMAC-SHA256 签名,
答复只能用一次)与 `ScopedCapability.describe()`(能力绑定描述)。
但 P3/P5 都不在本基线里,本轨不能 import 它们,只能按 dict
契约接线,集成时由 round-11 合并。
- 状态存储:P7(feat-chv-muse-p7)正在做状态分离,本轨审计轨迹
的持久化先落独立 JSONL 文件 + 明确标注待迁移到 P7 存储,
不重复造 P7 的库。
## 3. 本轨改什么
- `cloud/chv/audit_trail.py`(新,纯标准库):统一审计记录模型
(时间/主体/动作/对象/结果/原因/关联任务/关联审批/项目);
追加写 JSONL + SHA-256 哈希链(改写/删除/调序可被 `verify()`
发现);字段级白名单清洗(密钥、token 值、对话原文绝不进
审计,否定测试钉住);事件分类覆盖任务派发、VM 生命周期、
同步、审批、支付(P10)、配额(P11)、记忆操作(P7 的忘记)、
后台任务(P8)、推理/遥测(P9);`from_legacy_row()` 把现有
`audit_log` 行转成统一记录(标 `legacy: true`,无哈希链);
导出选 JSON(不做 CSV,见 §5)。
- `cloud/chv/activity.py`(新,纯标准库):计划登记(已做 vs
计划做:pending/running/done/cancelled,非法流转拒绝);
已批准权限登记(接 P5 `describe()` 形状:连接器/主机/用例/
档位/有效期/次数余量/撤销);`activity_summary()` 纯函数拼出
正在做摘要 + 近期已做 + 计划清单 + 已批准权限数。
- `cloud/chv/approval_card.py`(新,纯标准库):结构化审批卡
数据(动作/目标/原因/风险分级/接受与拒绝两个明确动作/五档
授权类型选项,永久档标注须显式 opt-in);`card_matches_channel()`
校验卡数据与 P5 通道载荷一致(pending_id + 目标字段全等)。
- 前端视图不做:本轨只给后端 + 数据接口,客户端 UI 是另一侧的
工作,契约见各模块 docstring。
## 4. 诚实边界
- 哈希链是**篡改可发现**,不是防篡改:能写文件的人重算整条链
就能掩盖改动。真防篡改要外部锚定(远端日志/签名时间戳),
不在本轨。
- 审计清洗是字段名黑名单 + `key=value` 模式脱敏,不是内容理解:
密钥若藏在自由文本字段里且不带 `token=`/`password=` 等标记,
清洗器看不出来。调用方仍须只传描述性字段(P3/P5 的口径)。
- 活动视图的“正在做什么”来自计划登记与审计记录,不是实时
进程探针;VM 内真实执行状态以执行器为准。
- 审批卡只做数据,不做签发:档位可选项由 Sentinel 决定(P5 口
径),本轨只呈现调用方传进来的档位列表。
## 5. 决定记录
- 导出选 JSON 不选 CSV:JSON 保留哈希链与嵌套结构,CSV 会丢
字段;任务简报允许二选一。
- 审计持久化先用独立 JSONL 文件(`audit_trail.jsonl`,0600),
待 P7 状态库就绪后迁移,迁移接口是 `from_legacy_row()` 同款
转换(本文件行 → P7 记录)。**迁移已完成(round-12)**:
`StoreAuditTrail` 走宿主级 AppStateStore 的 `audit_trail` 表,
与 JSONL 版同一记录形状与哈希链;`migrate_jsonl_to_store()`
原样搬链(坏链拒绝、分叉拒绝、重跑幂等);JSONL 版保留读旧
文件,新写一律走 Store 版。测试钉在
`cloud/tests/test_chv_muse_round12.py`。
- project_id 强制:`list()`/`export()` 必须传 project_id,只返
回该项目记录;跨项目读取返回空,不抛错(与现有 `list_audit`
行为一致,不泄露“存在别的项目记录”这一事实)。
## 6. 真机待验证清单(未做,排队在 round-11 之后)
1. 审计轨迹(round-12 起走 StoreAuditTrail / SQLite,不再是
JSONL)在真实 VM 生命周期(wake/pause/resume/shutdown)
下的追加与 `verify()` 表现。
2. 活动摘要与真实任务派发/完成事件的对应(正在做是否及时变
更、计划状态流转是否与 worker 终态一致)。
3. 已批准权限清单与 P5 五档能力在 wake/同步路径下的真实表现
(签发→使用→过期→撤销全程)。
4. 审批卡数据与 P5 对话外通道在真机上的端到端一致(卡上点的
接受/拒绝与通道答复同一 pending_id)。
5. 审计库落盘权限(0600)与多进程追加(worker + host.py)
下的完整性(SQLite WAL + 30 秒 busy 超时)。