Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
halo-record — AIエージェント向けの改ざん防止監査証跡:ハッシュチェーン化されたRuntime Records、依存関係なし、誰でも検証可能 | Kitploit
ツール/GitHubGitHub/bkuan001/halo-record
フォレンジックサプライチェーンセキュリティインシデントレスポンスAIセキュリティ
GitHubbkuan001/halo-record

halo-record

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

リポジトリを見るウェブサイト
63610224日前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有
# halo-record

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

計装が捕捉するすべてのアクション(ツール呼び出し、モデル呼び出し、データアクセス、承認)は、追記専用のハッシュチェーンログ内の1つの **Runtime Record** となり、**Runtime Report** はそのチェーンを HTML ページとしてレンダリングしたものです。このページは、読者のブラウザ内でハッシュチェーンを再計算し、検証したレコードから表示内容をレンダリングします(ページ自身のコードに依存しない検証には、JSONL に対して `halo verify` を実行してください)。チェーンのチェックポイントを保持する任意の当事者は、そのチェックポイントまでのレコードが取得以降変更されていないことを、そのレコードを生成した相手を信頼することなく検証できます — このチェックポイントが要となる部分です。チェーン単体では、記録者を運用する当事者以外の全員に対して改ざん検知可能です([LIMITS.md §1](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md))。顧客のセキュリティチームが「あなたのエージェントは我々のデータで何をしたのか?」と尋ねてきたとき、長文の説明ではなくリンクを渡せます。セキュリティレビューではすでに SOC 2 チェックリストの隣で AI に関する質問がなされており、そしてますますそれらの質問は ISO 42001、EU AI Act の記録保持条項、顧客自身のアンケートから来ています。今日では、書面による保証でもまだ通ります。このプロジェクトの背後にある賭けは、それが長くは続かないだろうというものです。

**[Help Net Security](https://www.helpnetsecurity.com/2026/08/31/halo-record-open-source-ai-agent-audit-trail/) で紹介**(2026年8月)。

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

> **halo-record を使っていますか、あるいは検討していますか?** あなたが誰で、何のために使うのか教えてください → [Who's using halo-record?](https://github.com/bkuan001/halo-record/discussions/7)

## 自分で確認する

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

- **ランタイム依存ゼロ。** 標準ライブラリのみ。`pip install halo-record` はちょうど1つのパッケージをインストールします。
- **ネットワーク呼び出しなし**、ただし4つのオプトインを除く — ウィットネスへのアンカリング(サブジェクト ID、レコード数、2つのチェーンフィンガープリント — ヘッドとチェーンルート — を送信)、ウィットネスのチェックポイントの読み戻し(サブジェクト ID を送信)、RFC 3161 タイムスタンプ(チェックポイントの状態ハッシュのみを Timestamp Authority に送信)、そして `halo serve --verify-email`(レポート閲覧者が入力したアドレスへ、`$HALO_SMTP_HOST` で設定したサーバー経由の SMTP で、ワンタイムコードとレポートのサブジェクト名を送信)。いずれも明示的に呼び出さない限りオフです。その他のレコード内容があなたのインフラから出ることはありません。
- **生のツール引数はハッシュ化され、併せて秘匿化されたサマリーが付きます。** 引数は正規化ハッシュとサマリーとして保存されます:既知のシークレットおよび PII パターンをマスクした引数テキストで、200文字に制限されます。どのパターンにも一致しない短い入力はサマリーにそのまま現れます。ハッシュのみモード(`summaries=False`)ではサマリーは一切保持されません。秘匿化はベストエフォートです(一般的なシークレットおよび PII 形式に対する正規表現と、エントロピーによるキャッチオール):多層防御として扱い、保証とは考えないでください。`summary` 以外にあなたが提供する結果フィールドは、同じパターンでスキャンおよびマスクされますが、200文字に切り詰められません(LIMITS §13)。
- **監査できるほど小さい。** レポートページに埋め込まれた JavaScript と CSS を含めて約8,700行の Python(空行、コメント、docstring を除いたコード行数)。1日で全部読めます。
- **Apache-2.0。**
- **文書は第一級。** [LIMITS.md](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md)(チェーンが証明できないこと)、[PRIVACY.md](https://github.com/bkuan001/halo-record/blob/main/PRIVACY.md)(レコードに含まれるものと、あなたのマシンから出るもの)、[RETENTION.md](https://github.com/bkuan001/halo-record/blob/main/RETENTION.md)(保持ポリシーの下での運用)、そして [REVIEWERS.md](https://github.com/bkuan001/halo-record/blob/main/REVIEWERS.md) — 4コマンドの独立検証と、レビュー所見の引用形式。

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

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

**インストール前に実物を見る:** [サンプル Runtime Report](https://bkuan001.github.io/halo-record/sample-report.html) — 架空のデータ、本物のチェーン。開くと、そのスクリプトが埋め込まれたレコードのハッシュチェーンをブラウザ内で再計算します。

## 60秒デモ

エージェントは不要です。[uv](https://docs.astral.sh/uv/) があれば、インストールするものはありません:

```
uvx --from halo-record halo demo --serve
```

または従来の方法:

```
pip install halo-record
halo demo --serve
```

いずれかを実行すると、2社の顧客を持つ架空のサポートエージェントベンダーをスキャフォールドし、チェーンを目撃し(オペレーター外部の1社を代表するローカルwitnessファイルを使用 — [LIMITS.md §1](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md) を参照)、ゲート付きのRuntime Reportsを提供し、オペレーターコンソールをブラウザで開きます。次に改ざんテストを試してください:`.jsonl` ファイルの1つから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 -> HTML that re-verifies in the browser
halo serve ./records --port 8721          # all tenants, gated per customer
```

0.2.45 で最初に配信されたディレクトリをアップグレードしますか? `halo serve` は起動時に以前の各グラントを再読み込みします。レコードに subject id が含まれていないファイル(`agent.jsonl` のようなプレーンな `Recorder` ファイル)に対するグラントは隔離され、起動のたびに、それを再発行する `halo grant` コマンドとともに出力されます — [LIMITS.md §17, "Serving a directory first served by 0.2.45"](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#17-one-chain-file-per-subject) を参照してください。

クイックスタートは、ブラウザで自分のエージェントの 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 mcp import Client   # mcp 2.x
from halo_record import Recorder
from halo_record.integrations.mcp import instrument_client_session

async def run(server):   # an MCPServer, a server URL, or stdio parameters
    async with Client(server) as client:
        instrument_client_session(client, Recorder("audit.jsonl"), server="stripe")
        await client.call_tool("charge", {"customer": "cus_123", "amount": 500})   # recorded
```

ラップされたクライアント上のすべての `call_tool()` が記録され、エラー結果を返すツールは `error` としてシールされる(アダプターは `isError` と mcp 2.x の `is_error` の両方を読み取る)。mcp 1.x では、`Client` の代わりに初期化済みの `ClientSession` を渡す。ラッパーは同じである。

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

```python
from halo_record.integrations.gateway import record_log

record_log(Recorder("audit.jsonl"), {"tool": "gen_ai:gpt-4o", "model": "gpt-4o", "status": 200, "subject": "acme-corp"})
```

OpenTelemetry GenAI スパンを出力するもの(CrewAI、LlamaIndex、および OTel インストルメンテーションを備えたほとんどのエージェントフレームワーク)は、OTel アダプターを通じてチェーンに取り込まれ、[TypeScript パッケージ](https://github.com/bkuan001/halo-record-ts)は Vercel AI SDK と JS エージェントエコシステム向けのネイティブアダプターを提供します。あなたのスタック用のアダプターがありませんか? issue を作成してください。ほとんどのアダプターは約 100 行です。

## コーディングエージェント(Claude Code または Codex)を記録する

Claude Code は、すべてのツール呼び出しの後に `PostToolUse` フックを発火します。これを `halo hook` に指定すると、各アクション — ファイル書き込み、シェルコマンド、MCP コネクター呼び出し — がローカルチェーン内のレコードになります。コード変更は不要で、設定エントリを 1 つ追加するだけです:

```json
{
  "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`)を設定すると、すべての記録をそれを生成したエージェントビルドに紐付けます — 監査人が特定の期間に実行されていたバージョンについて尋ねたとき、エクスポートは記憶ではなく列によって答えます。

Codex CLI は同じライフサイクルフックを同じイベント形状で提供します(フックはデフォルトで有効)。これを `~/.codex/hooks.json` に追加すると、Codex のシェルコマンド、`apply_patch` 編集、MCP 呼び出しが同じチェーンに記録されます:

```json
{
  "hooks": {
    "PostToolUse": [
      {"matcher": ".*", "hooks": [{"type": "command", "command": "halo hook"}]}
    ]
  }
}
```

フックはイベント自体から両者を区別し(Codex は `turn_id` と `model` を追加する)、各レコードに `claude-code` または `codex` のラベルを付ける。`HALO_HOOK_AGENT` を設定すると一方を強制できる。どちらも取り込み層である。`PostToolUse` フックはツールの実行後に発火するため、レコードはハーネスが報告する内容から構築される。

レポートが「この実行はどのようなルールの下で行われたか」に答えられる必要がある場合は、`HALO_AUTHORITY_FILE` にそのセッションの有効な権限の JSON スナップショットを設定する。プライバシーに配慮すること。ハッシュと参照のみとし、生のプロンプト、非公開のポリシーテキスト、シークレット、完全なツールスキーマは含めない — 既知のシークレットおよび個人データ形式はシール時にマスクされるが、ハッシュと参照はそのまま通過し、自由形式のテキストは検出されない(LIMITS §6 を参照)。`snapshot_id` は、基盤となる権限が変更されていない間のみ再利用すること。同じ id で内容が変更されていない連続したレコードは圧縮される — 内容が変更されているのに id を再利用した場合は、通知とともに完全な形で保存される。

```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"]
}
```

```sh
HALO_AUTHORITY_FILE=./authority.json halo hook
```

スナップショットはアクション記録と同じハッシュチェーンに封印される。良いデフォルトは、開始時のセッションレベルのスナップショット1つに加え、ルール、Skills、フック、MCPツールレジストリ、またはコンパクションポリシーが変更されたときの新しいスナップショットである。長いセッションをスリムに保つため、同じ `authority.snapshot_id` を持つ連続するレコードは、最初の完全なスナップショットの後にコンパクションされる。以降のレコードは `{"snapshot_id": "...", "same_as_previous": true}` のみを保持する。ポインタはハッシュチェーンに留まるが、かさばる refs/omissions/stale-if ブロックはアクションごとに繰り返されない。(コンパクションはレコーダープロセス単位である。ツール呼び出しごとに1プロセスを生成するフックスタイルのキャプチャでは、直前の完全なスナップショットが末尾レコードでない限り完全なボディを再保存するため、短命なプロセスは再利用ガードと引き換えにチェーンサイズを犠牲にする。)

SDKユーザーは同じブロックを直接アタッチする — `build(..., authority={...})` または `record_call(..., authority={...})`。ハッシュのみのキャプチャも同じサーフェスである(いずれでも `summaries=False`):

```python
from halo_record import Recorder, record_call

rec = Recorder("audit.jsonl")
with record_call(rec, "crm.lookup", {"account": "acct-9"},
                 authority={"snapshot_id": "auth_1", "rules_hash": "sha256:..."},
                 summaries=False) as call:              # hash-only: no summaries, no excerpts
    call.result = crm.lookup("acct-9")
```

次に、いつものように:

```
halo verify ~/.halo/audit.jsonl
halo report ~/.halo/audit.jsonl -o report.html
```

アクション後のフックを公開する任意のエージェントランタイムは、同じコマンドを供給できます — フックは1つのイベントをstdinでJSONとして読み取り、1つのレコードを追記します。

1つのチェーン、同時に1人の書き手。チェーンは連結リストです。同じヘッドを読み取った2人の書き手がどちらも追記すると、チェーンは分岐し(同じ前任者を主張する2つのレコード)、検証は影響を受けたレコードを名指しします。`Recorder`は自身の追記をサイドカーロック(ここではPOSIX `flock`、TypeScriptパッケージではロックディレクトリ)で直列化し、`halo hook`は`Recorder`を通じて追記するため、上記のフック設定はカバーされています。チェーンファイルに直接書き込むもの — 手作りのフック、並列ワーカー、ログシッパー — は、ヘッドを読んでから追記する一連の処理を通じて同等の排他ロックを保持するか、プロセスごとのチェーンに書き込む必要があります。[LIMITS.md セクション9](https://github.com/bkuan001/halo-record/blob/main/LIMITS.md#9-single-writer-chains)が、言語をまたぐ境界を含め、この点を完全に扱っています。

## 記録が失敗したとき

レコードはアクションの実行後に書き込まれます。Haloは、レコードを書き込めない場合にアクションをブロックしません。アクションの前にレコードを書き込むことは提供されていません。

- **デフォルト: アクションの結果はそのまま通過します。** レコードを書き込めない場合(ディスクフル、権限、ロックタイムアウト)、`trace()`、`record_call`、`@record`、フレームワークアダプター(OpenAI Agents、LangChain、LiteLLM、OpenTelemetry)、MCPラッパー、および`halo hook`は、アクション自身の結果を変更せずに返すか、アクションが失敗した場合はアクション自身の例外を再送出します。stderrへの警告が欠落したレコードを名指しします: `halo-record: FAILED to record '<tool>' (<error>); the action ran, but its record is NOT in the evidence log. ...`(使用可能なstderrがない場合は、`halo_record`ロガーに送られます)。LangChainハンドラーとOTelエクスポーターも損失をカウントします(`lost_records`)。チェーン内の何も、決して書き込まれなかったレコードを示すことはできないため、残りのレコードは依然として検証されます。ウィットネスチェックポイントの周期を監視しているレビュアーには、停止したチェーンが見えます。後続のレコードの間に1つだけ欠落したレコードがある場合は、stderrの行にのみ現れます。
- **strictモードでは例外も送出されます。** `strict_recording=True`を渡すか(`trace`、`record_call`、`@record`、`instrument_client_session`、またはアダプターのコンストラクターに)、`HALO_STRICT_RECORDING=1`を設定すると、同じstderr行が書き込まれた後、記録エラーが送出されます。これは`trace`、`record_call`、`@record`、MCPラッパー、OpenAI Agentsフック、およびLangChainハンドラー(LangChainの`raise_error`を設定します)から呼び出し元に到達します。OTelエクスポーターやLiteLLMロガーからは呼び出し元に到達できません。これらのフレームワークはエクスポーターやコールバックが送出したものをキャッチしてログに記録するため、そこでは両モードともstderr行がシグナルとなります(LIMITS.md セクション18)。エラーが送出される時点でアクションはすでに実行されているため、これを「アクションが失敗した」と解釈してリトライする呼び出し元は、アクションの副作用を繰り返します(返金が二重に発行される)。strictモードは、レコードの欠落がそのリスクよりも悪い場合にのみ使用してください。
- レコーダーなしの`record_call`と`@record`(何も渡されておらず、`trace`によってバインドもされていない)は、ラップされたコードが実行される前に`RuntimeError`を送出します。
- `@record`と`trace()`は`async def`関数を受け付けます。ラッパー自体も`async def`です。関数をawaitし、その後、返されたものまたは送出されたものを記録します(キャンセルはエラーとして記録されます)。`trace()`は本体がawaitされている間、レコーダーをバインドしたままにするため、非同期エージェント内の`record_call`または`@record`ツールはトレースされたチェーンに着地し、並行するエージェントはそれぞれ自身のチェーンを保持します。awaitableを返す通常の関数は、それが返ったときに記録されます。非同期ジェネレーター関数は、それが呼び出されたときに記録され、ジェネレーターオブジェクトがその結果となります。それがyieldするものは記録されません。

## 完全性 vs. 網羅性(この部分を読んでください)

各レイヤーが何を証明するのかを正確に把握してください — それらは異なる主張であり、その違いこそが要点です:

自己保持チェーンは**確立されたヘッドに対する完全性**を証明します。誰かがすでに保持しているチェーンヘッドが与えられれば、その背後にあるレコードのあらゆる編集、並べ替え、削除が検出可能になります。それ自体では — 運用者の外部の誰かがヘッドを見る前には — チェーンは内部の一貫性を証明するのであって、履歴を証明するのではありません。運用者はレコードを削除して再シールでき、新しいファイルは検証に通ってしまいます。チェーンは、そのヘッドが運用者の制御を離れた瞬間に**歴史的にコミット**されます。

それがウィットネスです。運用者の外部の当事者がチェーンの定期的なチェックポイントを保持します — サブジェクトID、レコード数、および2つのチェーンフィンガープリント(ヘッドとチェーンルート)。正確なペイロードはそれだけで、他には何もありません。チェックポイントは、コミットされた履歴の書き換えを検出可能にし、チェックポイントの欠落自体が可視のイベントとなります:

```
halo anchor audit.jsonl witness.jsonl           # anchor a checkpoint to a local witness
halo anchor audit.jsonl witness.jsonl --check   # completeness verdict against it
```

特に *time* については、外部の RFC 3161 タイムスタンプがチェックポイントの自己申告時刻を、運用者の制御外にある Timestamp Authority からの証明に置き換えます —「このチェーンは遅くとも T までにこのヘッドに到達した」という証明であり、ホスト型インフラを必要とせず第三者による検証が可能です。デフォルトの TSA は無料の freetsa.org です(評価には十分)。本番環境では、`--tsa` を運用者の制御外にある TSA(例: DigiCert / Sectigo、または relying party が指定するもの)に向けてください:

```
halo anchor audit.jsonl witness.jsonl --timestamp          # attach a TSA time proof to the checkpoint
halo anchor audit.jsonl witness.jsonl --check              # reads the token's claimed time
```

`--check` はトークンがこのチェーン状態にバインドされていることを確認し、その証明された時刻を読み取りますが、TSA の署名を**検証しません** — これは意図的に標準ツールに委ねられており、レビュアーが当社のコードを一切信頼しなくて済むようにしています。時刻を独立して検証するには(これがセキュリティレビュアーに渡すものです):

```
# tsa.token_b64 lives in the witness log; decode the latest one to a standard .tsr file
python3 -c 'import json,base64; cps=[json.loads(l) for l in open("witness.jsonl") if l.strip()]; t=[c["tsa"] for c in cps if c.get("tsa")][-1]; open("token.tsr","wb").write(base64.b64decode(t["token_b64"])); print(t["digest"])'
curl -s -o tsa-ca.pem https://freetsa.org/files/cacert.pem     # CA for the default TSA (a commercial TSA publishes its own)
```

---

[Read more](https://github.com/bkuan001/halo-record)
ツールをダウンロード