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

144 lines
6.9 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.
# 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-evidence``runtime-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.start``allowed`、非空 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-v27``local-control-core` 推进到 v27,并声明
`tool_execution_evidence:1`。repository 复用 `LocalSqliteOperationAuthority`
`BEGIN IMMEDIATE`,只允许绑定同 Project、同 Run、状态为 `ready`
`waiting_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-evidence``control-core` 推进到 v28,并新增:
- `ql3.tool_execution_trace_anchors`
- `ql3.tool_execution_audit_receipts`
只有 `ql3_runtime` 取得两张 evidence 表的 SELECT/INSERT,禁止 UPDATE/DELETE;其他
产品角色保持零权限。`ql3_runtime``security_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。