# ============================================================ # 审计 v1(moduleId: core-audit, 可重生 ✅) # plan.md §3.6 的最小实现:5W1H 事件 + 链式哈希(prev_hash → hash) # M1 简化:锚定/WORM/保留策略后置;事件存于世界状态 auditEvents 表 # ============================================================ from __future__ import annotations # 前向类型引用 import hashlib # SHA-256 哈希链 import json # 规范化序列化 from datetime import datetime # 时间戳 from typing import Any # 类型标注 from server.timeutil import fmt_dt # 时间格式化 # 世界状态类型别名 World = dict[str, Any] def write_audit(world: World, next_id, *, actor: str, category: str, action: str, target: dict[str, Any], power: str, rationale: dict[str, Any], result: str = "SUCCESS") -> dict[str, Any]: """写入一条审计事件(append-only;调用方负责随后 save)。 权力等级:P0(审计本身只追加,不改业务数据)。 Args: world: 世界状态(事件追加到 auditEvents) next_id: 发号函数 actor: 谁(用户/agent 标识) category: 事件大类(§3.6.2:SESSION/PLAN/MODEL/TOOL/GATE/WORLD_WRITE/...) action: 具体动作名(如 schedule.publish) target: 作用对象 {type, id, ...} power: 权力等级 P0-P3 rationale: 依据 {intent/evidence/confirmId/approver...} result: 结果 SUCCESS/DENIED/FAILED Returns: 写入的事件对象(含哈希) """ events = world.setdefault("auditEvents", []) # 审计表(缺失自动补) prev_hash = events[-1]["hash"] if events else "GENESIS" # 链头:上一事件哈希或创世标记 at = fmt_dt(datetime.now()) # 单事件统一时间戳 event: dict[str, Any] = { # 5W1H 事件体(§3.6.1 的 M1 子集) "id": next_id("audit"), # 事件 ID "at": at, # When(前端契约字段) "ts": at, # 兼容旧数据字段 "actor": actor, # Who "category": category, # What(大类) "action": action, # What(动作) "target": target, # Where(对象) "power": power, # 权力等级 "rationale": rationale, # Why(依据) "result": result, # 结果 "prevHash": prev_hash, # 链式哈希:前驱 } # 本事件哈希 = SHA256(前驱哈希 ‖ 事件体规范化 JSON)——任何中间篡改都会断链 payload = prev_hash + json.dumps(event, ensure_ascii=False, sort_keys=True) event["hash"] = hashlib.sha256(payload.encode("utf-8")).hexdigest() # 计算并挂载 events.append(event) # 追加入链(append-only) return event # 返回事件(供响应引用)