
halo-record v0.2.42
面向AI智能体的防篡改审计追踪:哈希链式运行时记录,零依赖,任何人可验证。
halo-record
防篡改 AI 代理运行时记录:供应商运行但无法编辑的审计追踪。
代理的每一个动作(工具调用、模型调用、数据访问、审批)都会成为追加式哈希链日志中的一条记录。任何持有该链检查点的一方都能验证其背后的记录从未被更改,无需信任产生这些记录的一方。当客户的安全团队询问“你的代理用我们的数据做了什么?”时,你交给他们的是一个链接,而不是一段文字。安全审查已经在 SOC 2 清单之外开始提出 AI 相关问题,而如今书面保证依然能过关。这个项目所押注的信念是:这种情况不会持续太久。
记录格式是开放的,任何人都可以自由实现。本包是参考实现:记录器、验证器、见证客户端和报告服务器。
为什么你可以信任这段代码
你被要求将记录器放入你的代理中。你不应该仅凭信任就接受这一点:
- 零运行时依赖。 仅使用标准库。
pip install halo-record只安装一个包。 - 无网络调用,见证服务除外,且它是可选加入的,只接收记录计数和链指纹。记录内容永远不会离开你的基础设施。
- 原始输入永远不会进入记录。 参数经过哈希处理,仅以脱敏摘要的形式存储——绝不存储原始值。脱敏是尽力而为(基于常见密钥和 PII 格式的正则匹配):请将其视为纵深防御,而非绝对保证。
- 体量小到足以审计。 约 4,300 行 Python。一个下午就能全部读完。
- Apache-2.0。
60 秒演示
无需代理。使用 uv,无需安装任何东西:
uvx --from halo-record halo demo --serve
或者用传统方式:
pip install halo-record
halo demo --serve
两种方式都会搭建一个虚构的客服代理供应商(含两个客户),对链进行见证,提供受门控的 Runtime Report,并在浏览器中打开操作员控制台。然后试试防篡改测试:从某个 .jsonl 文件中删除一行并重新加载。报告会捕捉到这一点。
记录你自己的代理
在边界处加一行:
from halo import trace
agent = trace(run_my_agent, profile="my-agent", log="audit.jsonl") # wraps your entrypoint; records every tool call to ./audit.jsonl
如果不带 log=,记录会写入 ~/.halo/my-agent.jsonl(每个代理一条链)。或者使用适配器接入你已在运行的系统(见下方矩阵)。然后渲染报告:
halo report audit.jsonl -o report.html # one chain -> self-verifying HTML
halo serve ./records --port 8721 # all tenants, gated per customer
当你在浏览器中看到自己代理的 Runtime Report 时,快速入门就算完成了。如果你拿到了 JSONL 文件却没有报告,说明有问题:请提交 issue。
接入你已在运行的系统
| 在边界处捕获 | 从现有遥测数据摄取 |
|---|---|
原生记录器(from halo import trace) | OpenTelemetry GenAI spans |
| MCP 拦截器 | LiteLLM 回调 |
| LangChain / LangGraph 回调 | Langfuse 导出 |
| OpenAI Agents SDK 钩子 | 任意网关 / 反向代理日志 |
| Claude Code / Claude Agent SDK 钩子 |
每条记录都带有 source 标签,因此报告会披露每条证据是如何收集的。捕获的记录和摄取的记录位于同一条链中。
任何能发出 OpenTelemetry GenAI spans 的系统(CrewAI、LlamaIndex,以及大多数带有 OTel 插桩的代理框架)都能通过 OTel 适配器进入链中,而 TypeScript 包 为 Vercel AI SDK 和 JS 代理生态提供了原生适配器。缺少适合你技术栈的适配器?提交 issue。大多数适配器大约只有一百行代码。
记录你的编码代理
Claude Code 在每次工具调用后都会触发 PostToolUse 钩子。将其指向 halo hook,每个动作——文件写入、shell 命令、MCP 连接器调用——都会成为本地链中的一条记录。无需修改代码,只需一条 settings 配置:
{
"hooks": {
"PostToolUse": [
{"matcher": "*", "hooks": [{"type": "command", "command": "halo hook"}]}
]
}
}
将其添加到 ~/.claude/settings.json,记录就会写入 ~/.halo/audit.jsonl(可用 $HALO_LOG 覆盖)。不接触数据、网络或外部状态的纯编排工具会被跳过——链记录的是信任边界动作,而非思考过程。设置 HALO_HASH_ONLY=1 可只记录内容哈希而不记录摘要。设置 HALO_AGENT_VERSION(以及可选的 HALO_AGENT_MODEL)可将每条记录绑定到产生它的代理构建版本——当审计员询问某个时间窗口内运行的是哪个版本时,导出结果按列即可回答,而非凭回忆。
如果你需要报告回答“这次运行是在什么规则下发生的?”,请将 HALO_AUTHORITY_FILE 设置为会话有效权限的 JSON 快照。请保持隐私安全:只包含哈希和引用,不包含原始提示词、私有策略文本、密钥或完整工具模式。
{
"snapshot_id": "auth_2026_07_08T1100Z",
"captured_at": "2026-07-08T11:00:00Z",
"scope": "session",
"workspace": {"path_hash": "sha256:...", "git_commit": "abc1234"},
"refs": [
{"kind": "project_rules", "id": "CLAUDE.md", "hash": "sha256:...", "loaded": true, "truncated": false},
{"kind": "mcp_tool_registry", "id": "filesystem", "hash": "sha256:..."}
],
"omissions": [{"kind": "private_policy", "reason": "customer_secret", "hash": "sha256:..."}],
"stale_if": ["project_rules_hash_changed", "mcp_tool_registry_hash_changed"]
}
HALO_AUTHORITY_FILE=./authority.json halo hook
该快照与动作记录一起被封入同一条哈希链。一个好的默认做法是:在开始时创建一个会话级快照,并在规则、Skills、钩子、MCP 工具注册表或压缩策略发生变化时创建新快照。为保持长时间会话的精简,连续的具有相同 authority.snapshot_id 的记录会在首个完整快照之后被压缩:后续记录只保留 {"snapshot_id": "...", "same_as_previous": true}。指针依然保持哈希链连接,但庞大的 refs/omissions/stale-if 块不会在每个动作上重复。然后,像往常一样:
halo verify ~/.halo/audit.jsonl
halo report ~/.halo/audit.jsonl -o report.html
任何暴露了动作后钩子的代理运行时都可以接入同一条命令——该钩子从 stdin 读取一个 JSON 事件并追加一条记录。
完整性与完备性(请阅读这部分)
要精确理解每一层证明了什么——因为它们是不同的主张,而差异正是关键所在:
自持链证明的是相对于已确立链头的完整性:给定一个某人已经持有的链头,其背后记录中的任何编辑、重排或删除都会变得可检测。单独来看——在操作者之外的任何人看到链头之前——一条链证明的是内部一致性,而非历史:操作者可以丢弃一条记录并重新封链,新文件依然能通过验证。当链头离开操作者控制的那一刻,这条链便在历史上定格。
这就是见证者:操作者之外的一方,持有链的周期性指纹(仅一个计数和一个链头哈希,别无其他)。检查点使重写已定格历史的行为可被检测,而错过检查点本身就是一个可见事件:
halo anchor audit.jsonl witness.jsonl # anchor a checkpoint to a local witness
halo anchor audit.jsonl witness.jsonl --check # completeness verdict against it
还有一个边界,需要直说:无论是链还是见证服务,都不能证明每个现实世界中的动作都经过了记录器。那是捕获完备性——它取决于记录器在技术栈中的位置(原生插桩、钩子、网关摄取),而非任何哈希。记录带有 source 标签正是出于这个原因。
| 主张 | 自持链 | + 外部检查点 | + 可信捕获 |
|---|---|---|---|
| 检测对既有工件的编辑 | ✔ | ✔ | ✔ |
| 检测对已定格历史的重写 | — | ✔ | ✔ |
| 检测缺失/迟到的检查点 | — | ✔(约定节奏) | ✔ |
| 证明每个动作都被记录 | — | — | 取决于捕获边界 |
任何人都可以运行见证服务。你自己运行的见证服务将历史承诺给你;要将其承诺给你的客户,则需要一个他们有理由信任的见证服务。无论哪种方式,协议都是开放的。
由托管的、被认可的见证服务是这个项目自我维持的方式。早期接入请联系:[email protected]。
这在合规体系中的位置
halo-record 是一个证据层,而不是认证。它产出的工件正是评估框架们一直在用不同措辞要求的东西:
- 安全问卷和 SOC 2 审查: 用可验证的 Runtime Report 回答 AI 相关章节,而不是截图和文字描述。
- AIUC-1: 产生该标准问责控制所要求的防篡改日志(E015.4)和含授权事件的完整执行链记录(E015.2)——持续性的运行时证据,而非审计时才重建。
- OWASP(GenAI 安全项目): OWASP Agentic Applications Top 10 2026 和 LLM Top 10 中代理行为风险背后的运行时证据——目标劫持、工具滥用、身份与权限滥用——记录为代理实际做了什么、使用了哪些工具和数据。
- AARM(CSA): 产生 AARM 规定的防篡改动作回执(R5/R6)——链式连接并由外部独立见证。halo-record 是回执层;搭配执行网关即可构成完整的 AARM 系统。参见
AARM.md。 - Agentic Trust Controls: ATC 证据控制背后的运行时记录——防篡改动作日志(RBM-03)和权限证明(AID-05)合并在同一条链式记录中,见证层覆盖两者之上。参见
ATC.md。 - EU AI Act: 高风险 AI 系统的日志和记录保存义务。
- ISO 42001 / NIST AI RMF: 管理体系控制背后的操作性证据。
这些本身都不构成任何认证。它只是给你的评估人员一些可验证的东西去看。边界——halo-record 刻意不做什么,以及当审查者问起时该如何回答——都记录在 LIMITS.md 中。
CLI
halo verify validate schema + hash chain (non-zero exit on failure; CI-friendly)
halo report render a chain as a self-verifying HTML Runtime Report
(--from/--to: a date-windowed report covering only the review period)
halo serve serve per-tenant reports over HTTP, access-scoped per customer
halo grant designate a report recipient (email or domain)
halo anchor witness a chain head, or --check completeness
halo demo scaffold the full vendor demo (record -> witness -> gated report)
halo export date-bounded evidence export: CSV + manifest tied to the chain head
halo sample emit a valid example log
halo hash canonical sha256 of a JSON value
halo hook Claude Code PostToolUse hook
完整性模型
要计算一条记录的哈希:取该记录,排除 integrity.hash,将 integrity.prev_hash 设为前一条记录的哈希;使用 RFC 8785(JSON 规范化方案)进行规范化;对字节进行 SHA-256。第一条记录的 prev_hash 是 64 个零。验证会重新计算每个哈希并检查每个链接。无需任何密钥;这正是关键所在。
你认为你能在验证器察觉不到的情况下篡改链吗?尝试与结果请看这里。
完整字段参考:halo-record.schema.json。
TypeScript
相同的记录器也面向 Node 发布:halo-record-ts。相同的链格式,相同的见证协议。用任一语言写入的记录都能用任一验证器验证。
贡献
欢迎提交 issue、参与讨论和发起 pull request——基本规则见 CONTRIBUTING.md(简而言之:必须带测试、PR 要小、schema 变更先讨论)。
许可证
Apache-2.0