アップデート一覧に戻る
New releaseSep 9, 2026

halo-record v0.2.42

AIエージェント向けの改ざん防止監査証跡:ハッシュチェーン化されたRuntime Records、依存関係なし、誰でも検証可能

共有

halo-record

改ざん検知可能なAIエージェントの監査証跡 — ハッシュチェーン化されたRuntime Recordを、顧客自身が検証できるRuntime Reportとしてレンダリングします。

エージェントが実行するすべてのアクション(ツール呼び出し、モデル呼び出し、データアクセス、承認)は、追記専用のハッシュチェーンログ内の1つのRuntime Recordとなり、Runtime Reportはそのチェーンを自己検証可能なHTMLページとしてレンダリングしたものです。チェーンのチェックポイントを保持する当事者は誰でも、それを生成した者を信頼することなく、その背後にあるレコードが改変されていないことを検証できます — このチェックポイントが要となる部分です。チェーン単体では、レコーダーを運用する当事者以外のすべてに対して改ざん検知可能です(LIMITS.md §1)。顧客のセキュリティチームが「あなたのエージェントは我々のデータで何をしたのか?」と尋ねてきたとき、長々とした説明の代わりにリンクを渡すことができます。セキュリティレビューではすでにSOC 2チェックリストの隣でAIに関する質問がなされており、そしてますますそれらの質問はISO 42001、EU AI Actの記録保持条項、顧客自身のアンケートから来ています。今日では書面による保証でもまだ通ります。このプロジェクトの背後にある賭けは、それが長くは続かないだろうというものです。

Help Net Security で紹介されました(2026年8月)。

レコード形式はオープンで、自由に実装できます。このパッケージはリファレンス実装です:レコーダー、検証器、ウィットネスクライアント、レポートサーバー。

halo-recordを使っていますか、あるいは検討していますか? あなたが誰で、何のために使うのか教えてください → Who's using halo-record?

自分で確認する

あなたはエージェント内にレコーダーを組み込むよう求められています。それを鵜呑みにするべきではありません:

  • ランタイム依存関係ゼロ。 標準ライブラリのみ。pip install halo-record はちょうど1つのパッケージをインストールします。
  • ネットワーク呼び出しなし、ただし3つのオプトインを除く — ウィットネスへのアンカリング(サブジェクトID、レコード数、2つのチェーンフィンガープリント — ヘッドとチェーンルート — を送信)、ウィットネスのチェックポイントの読み戻し(サブジェクトIDを送信)、およびRFC 3161タイムスタンプ(チェックポイントの状態ハッシュのみをTimestamp Authorityに送信)。これらはすべて、あなたが呼び出さない限りオフです。レコードの内容があなたのインフラから出ることはありません。
  • 生のツール引数はハッシュ化され、編集済みのサマリーが併記されます。 引数は正規化ハッシュとサマリーとして保存されます:既知のシークレットおよびPIIパターンをマスクした引数テキストで、200文字に制限されます。どのパターンにも一致しない短い入力は、サマリーにそのまま表示されます。ハッシュのみモード(summaries=False)ではサマリーは一切保持されません。編集はベストエフォートです(一般的なシークレットおよびPII形式に対する正規表現と、エントロピーによるキャッチオール):これは多層防御として扱い、保証とは考えないでください。summary 以外にあなたが指定する結果フィールドは、そのままの形で封印されます(LIMITS §13)。
  • 監査できるほど小さい。 約5,300行のPython(コード行数、空行とコメントを除く)。午後のひとときで全部読めます。
  • Apache-2.0。
  • ドキュメントは第一級です。 LIMITS.md(チェーンが証明できないこと)、PRIVACY.md(レコードに含まれるものと、あなたのマシンから出るもの)、RETENTION.md(保持ポリシーの下での運用)、そして REVIEWERS.md — 4つのコマンドによる独立したチェックと、レビュー所見の引用形式。

各レイヤーが何を証明するか — このプロジェクトにおける要となる区別(LIMITS.md §1):あなた自身が保持するチェーンは、すでに誰かが保持しているヘッドに対して、レコードが編集されていないことを証明します。運用者の外部で保持されるチェックポイントのみが、削除されていないことを証明します。そして、すべてのアクションが捕捉されたことを証明するハッシュは存在しません。

主張自己保持チェーン+ 外部チェックポイント+ 信頼されたキャプチャ
確立された成果物への編集を検出✔✔✔
コミット済み履歴の書き換えを検出—✔✔
欠落/遅延チェックポイントを検出—✔(合意されたケイデンス)✔
すべてのアクションが記録されたことを証明——キャプチャ境界に依存

インストールする前に実物を見る: サンプルRuntime Report — 架空のデータ、本物のチェーン、そしてあなたが見ている間にブラウザ内で自己再検証します。

60秒デモ

エージェントは不要です。uv があれば、インストールするものは何もありません:``` uvx --from halo-record halo demo --serve

または従来の方法:```
pip install halo-record
halo demo --serve

どちらかを選ぶと、2社の顧客を持つ架空のサポートエージェントベンダーをスキャフォールドし、チェーンを目撃し(オペレーター外部の1社を代行するローカルwitnessファイルについてはLIMITS.md §1を参照)、ゲート付きのRuntime Reportsを提供し、ブラウザでオペレーターコンソールを開きます。次に改ざんテストを試してください:.jsonlファイルの1つから行を削除してリロードします。レポートがそれを検出します。

自分のエージェントを記録する

境界での1行:```python from halo_record import trace

agent = trace(run_my_agent, profile="my-agent", log="audit.jsonl") # wraps your entrypoint; records the run boundary to ./audit.jsonl — add record_call() or a framework adapter at each tool boundary to capture individual calls

`from halo import ...` という便利なシムも同梱されていますが、PyPI 上の `halo` という名前は無関係なターミナルスピナーパッケージのものであり、そのパッケージがインストールされている場合はそちらがインポートで優先されます。`halo_record` は曖昧さがないため、例ではこちらを使用しています。

`log=` を指定しない場合、レコードは `~/.halo/my-agent.jsonl` に書き込まれます(エージェントごとに1チェーン)。ラッパーは実行境界を封止し、証拠は呼び出しごとのレコードに残ります。これらはフレームワークアダプター(下のマトリックス)でキャプチャするか、明示的にキャプチャします。後者は委譲リンクの仕組みも示します:```python
from halo_record import Recorder, record_call

rec = Recorder("audit.jsonl")

with record_call(rec, "crm.lookup", {"account": "acct-9"}) as call:            # one sealed record per tool call
    call.result = crm.lookup("acct-9")

with record_call(rec, "payments.refund", {"amount": 120},
                 parent_id=rec.last_record_id()) as call:                      # child links to the action that spawned it
    call.result = payments.refund(120)

次にレポートをレンダリングします:``` 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を開いてください。

### 検証ブロック

ガードレールまたはポリシーレイヤーがアクションをチェックした場合、その判定結果をレコードに載せることができます — ゲートが何を決定したかを記録し、他のすべてのフィールドと同様にハッシュチェーンに封印されるオプションのブロックです:```python
from halo_record import build

build("tool_call", "security", tool="payments.refund",
      verification={"status": "allowed", "verifier": "gate/1.2",
                    "policy_ref": "sha256:1f3a...",
                    "checked_at": "2026-08-01T12:00:00Z"})

1つの命名に関する注意点: このパッケージは record 関数(デコレータ)をエクスポートしており、これがパッケージオブジェクト上の halo_record.record モジュールをシャドウします。その内部を利用したい場合は、import halo_record.record as record ではなく、モジュールパスから直接インポートしてください — from halo_record.record import build。

これは次のようにしてレコードに封印されます:```json "verification": {"status": "allowed", "verifier": "gate/1.2", "policy_ref": "sha256:1f3a...", "checked_at": "2026-08-01T12:00:00Z"}

`record_call(...)` は同じ `verification=` キーワードを受け付けます。`status` はブロック内で必須です。`verifier`、`policy_ref`、`checked_at` は任意です。各ステータスの意味は次のとおりです。

| ステータス | ゲートが報告する内容 | アクションは実行されたか? |
|---|---|---|
| `allowed` | アクションを許可した | はい — アクションは続行された |
| `blocked` | アクションを拒否した | 統合側によって決まるもので、このフィールドによるものではない — レコードが結果を保持している場合もあり、ブロックだけでは未実行を証明しない |
| `modified` | 実行前にアクションを変更した — `action.input` は変更後の、**実行されたまま**のアクションを記述する | はい、変更された形で |
| `unverified` | 実行された(または参照された)が判断を下さなかった — ブロックが存在しない場合とは異なる。後者は検証の主張が一切なされなかったことを意味する | はい — アクションは判定なしで続行された |

このブロックは運用者の統合コードによって提供され、ゲートが述べたと報告する内容を記録します — `principal` と同じ信頼姿勢です([LIMITS](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#11-verification-status-is-the-gates-report-not-halos-finding) を参照)。シーリングはステータスが事後に編集されていないことを証明しますが、チェックが行われたこと、判定が正しかったこと、またはブロックされたアクションが実行されなかったことを証明するものではありません。これは独立した検証ではありません。

`policy_ref` を証拠として使用可能にするには、ルールセットのコンテンツハッシュを使用し、ルールセットの成果物を保持してください — 解決できないラベルはこのフィールドを飾りにするだけです。

## すでに運用しているものに接続する

| 境界でキャプチャ | 既存のテレメトリから取り込み |
|---|---|
| ネイティブレコーダー(`from halo_record import trace`) | OpenTelemetry GenAI スパン |
| MCP インターセプター | LiteLLM コールバック |
| LangChain / LangGraph コールバック | Langfuse エクスポート |
| OpenAI Agents SDK フック | 任意のゲートウェイ / リバースプロキシログ |
| Claude Agent SDK フック | Claude Code および Codex CLI の `PostToolUse` フック(ツール実行後に発火) |

フレームワークアダプターと取り込みパスは各レコードに `source` タグを付与するため、レポートは各証拠がどのように収集されたかを開示します。キャプチャされたレコードと取り込まれたレコードは同じチェーンに存在します。

LangChain / LangGraph の場合、それはコールバックハンドラーです:```python
from halo_record import Recorder
from halo_record.integrations.langchain import HaloCallbackHandler

recorder = Recorder("audit.jsonl")
result = my_chain.invoke(inputs, config={"callbacks": [HaloCallbackHandler(recorder)]})   # every tool call becomes a record

MCP の場合、1 回の呼び出しでクライアントセッションをラップし、その後は どの MCP を使用するエージェントでも、どのフレームワークが駆動していても、すべてのツール呼び出しに対してレコードを出力します:```python from halo_record.integrations.mcp import instrument_client_session

instrument_client_session(session, Recorder("audit.jsonl"), server="stripe") # every session.call_tool() is now recorded

ゲートウェイまたはプロキシのログ(Cloudflare AI Gateway、Portkey、モデルの前段のnginx)については、ログ行をチェーンにマッピングする — 境界でキャプチャされたものではなく、取り込み済みとして明示的にタグ付けする:```python
from halo_record.integrations.gateway import record_log

カテゴリ