Files
qinglong/docs/adr/ADR-0157-bounded-tool-execution-trace-and-audit-evidence.md
T

6.9 KiB
Raw Blame History

ADR-0157:有界 Tool Execution Trace 与 Audit Evidence

  • 状态:Accepted
  • 日期:2026-07-26
  • 关联:ADR-0049、ADR-0074、ADR-0133、ADR-0154、ADR-0155、ADR-0156 RFC D-03/D-29/D-131/D-148/D-149

背景

ADR-0155 已定义 Tool execution admission 必须绑定耐久 StepRun、Trace 和 Audit evidenceADR-0156 已完成 StepRun aggregate 与双方言 Run fence,但仍缺少可以在崩溃、 响应丢失和多副本重放后验证的 Trace/Audit 事实。

仅在 OpenTelemetry exporter、RunEvent payload 或普通日志中记录 trace/span id 不足以 形成启动门禁:

  • exporter 可以关闭、采样、延迟或失败;
  • RunEvent 不能证明独立 Trace 与审计事实的完整内容和摘要;
  • security_audit_events 在 PostgreSQL runtime 角色下是 insert-only,不能为了重放而 扩大为全表可读;
  • 低配路由器不能为了这项能力引入常驻 collector、额外 workspace package 或新的第三方 依赖。

决策

1. 使用 profile-neutral、digest-bound evidence bundle

@qinglong/runtime-core/tool-execution-evidenceruntime-core 的显式 subpath,不新增 workspace package。它定义:

  • qinglong/tool-execution-trace-anchor@v1
  • qinglong/tool-execution-audit-receipt@v1
  • qinglong/tool-execution-evidence-bundle@v1
  • 32 位小写十六进制 Trace ID、16 位 Span ID,与 W3C Trace Context 的 identity 形状兼容;
  • Project、Run、StepRun、invocation plan、binding、adapter、redaction contract 和 audit contract 的完整摘要绑定;
  • 固定 tool.invoke.startallowed、非空 Policy fence 的 SecurityAuditRecord
  • domain-separated SHA-256 trace、audit record 与 receipt digest。

Bundle 的 canonical JSON 总量上限为 16 KiB,单页最多 128 条。输入、Secret、handler、 异常对象、execute seam 和 Tool 结果都不得进入该 bundle。

2. 两个 durable identity 与 exact replay

repository 同时以 (trace_id, span_id)audit event_id 作为唯一 identity。第一次 prepare 原子写入,完全相同的重放返回 existing;任一 identity 复用到不同内容必须 返回 semantic conflict。按 Run 查询使用 (created_at_ms, trace_id, span_id) 稳定 keyset pagination。

读取时必须重新执行领域规范化,并把 canonical JSON 与每个镜像列逐一比对。数据库内 JSON、digest、identity 或时间镜像发生漂移时失败关闭,不能返回部分可信 evidence。

3. SQLite 使用共享 operation authority

SQLite 0053-tool-execution-evidence 新增:

  • ToolExecutionTraceAnchors
  • ToolExecutionAuditReceipts

0054-capability-v27local-control-core 推进到 v27,并声明 tool_execution_evidence:1。repository 复用 LocalSqliteOperationAuthorityBEGIN IMMEDIATE,只允许绑定同 Project、同 Run、状态为 readywaiting_approval 的 Tool StepRun。SecurityAuditEvent、Trace anchor 和 receipt 在同一事务中提交或回滚。

数据库 CHECK 完整绑定 JSON 与镜像列;foreign key 将 receipt 绑定到既有审计事件、Trace 和同 Run StepRun。

4. PostgreSQL 保持 Audit insert-only

PostgreSQL pg-0029-tool-execution-evidencecontrol-core 推进到 v28,并新增:

  • ql3.tool_execution_trace_anchors
  • ql3.tool_execution_audit_receipts

只有 ql3_runtime 取得两张 evidence 表的 SELECT/INSERT,禁止 UPDATE/DELETE;其他 产品角色保持零权限。ql3_runtimesecurity_audit_events 继续只有 INSERT,没有 SELECT。

因此 PostgreSQL repository 不 JOIN 审计表读取:它在同一个 SERIALIZABLE 事务中先写 immutable SecurityAuditEvent,再写 Trace 和保存完整 audit_json 的 receiptreceipt 以 foreign key 证明审计行存在。运行时没有修改或删除审计行的权限,重放通过 receipt 内的完整规范化 audit 与 digest 校验,不需要扩大低敏审计表的读取面。

事务使用固定 statement/lock/idle timeout、最多三次 serialization/deadlock/并发唯一 冲突收敛,并以共享锁固定同 Project ready Tool StepRun,避免状态检查与提交之间漂移。

5. evidence 仍不是副作用启动授权

双方言显式 tool-execution-evidence subpath 只提供存储能力,root、通用 runtime、 admin、worker ingress 不重新导出 repository。它没有连接真实 Tool adapter,也不会 启动进程、HTTP、MCP、Worker 或任意外部副作用。

production execution 继续不可达,直到一个后续 start-barrier authority 能在同一 可判定事务中绑定或精确证明:

  1. immutable plan/dispatch
  2. ready Tool StepRun
  3. Trace anchor
  4. allowed Audit receipt
  5. handler binding 与 adapter-specific recovery evidence。

低配与集群资源影响

  • 不新增 workspace package、依赖、timer、watcher、socket、线程或常驻进程;
  • 没有 exporter/collector 才能运行的前置条件;
  • 每次 Tool 启动只增加一条 Trace、一条 receipt 和既有一条 Audit
  • 所有读取都有唯一索引或有界 keyset page
  • Edge 与 Cluster 使用同一领域摘要和重放语义,数据库机制和权限模型按 profile 分别实现。

被否决方案

  1. 只依赖 OpenTelemetry:采样和 exporter 可用性不能成为耐久启动证据,拒绝。
  2. 把完整 evidence 塞进 RunEvent:失去独立 identity、retention 和 ACL,拒绝。
  3. 允许 runtime SELECT 全部 Audit:扩大低敏审计读取面,拒绝。
  4. Trace、Audit、receipt 分三个事务提交:产生可见半提交窗口,拒绝。
  5. 只保存 digest,不保存规范化 JSON:无法在恢复时重建并验证完整绑定,拒绝。
  6. 为 Trace/Audit 新拆 workspace package:没有独立部署或依赖生命周期,拒绝。
  7. prepare 时允许 running/terminal StepRun:无法证明 evidence 在副作用开始前 建立,拒绝。

验证

  • runtime-core Tool evidence 定向测试:7/7,完整 runtime-core304/304
  • local-sqlite 完整测试:106/106
  • cluster-postgres package160 pass、1 条件 skip
  • PostgreSQL 18.4 arm64 新增 evidence 真实 integrationmigration/runtime 独立角色 下 1/1
  • PostgreSQL 18.4 arm64 六角色完整 integration40 pass、1 条件 skip
  • PostgreSQL 18.4 arm64 physical HAphysical streaming、remote_apply、旧主隔离、 timeline 1→2 promotion、pg_rewind、同步复制恢复、两代双 control replica gates.passed=true
  • 所有单实例和 HA 临时 Docker 资源均由 gate 清理。

后续门禁

  1. 实现 opaque/encrypted invocation plan 与 redacted preview Artifact
  2. 实现 plan/dispatch、StepRun、Trace、Audit 的双方言同事务 start barrier
  3. 定义 built-in、isolated process、MCP、HTTP 和 Worker adapter 的启动后恢复证据;
  4. 增加 receipt retention/compaction,不得破坏未终结 Run 的恢复可验证性;
  5. 完成物理 Edge 故障与规模证据后,再评审 production adapter composition。