GAP (GX AI Protocol) Specification v0.1
0. これは何か #
企業のESGデータを、PDFの開示レポートを経由せず、発生源で署名された生の活動量のまま、 API と MCP で投資家のAI Agentに直結するための通信規約。
従来の構造:
基幹システム → 担当者/コンサル(集計・化粧) → PDF報告書 → アナリスト(再入力) → 評価
GAP の構造:
基幹システム/センサー → [署名] → GAP Node → [MCP/REST] → 投資家 AI Agent(算定・検証は受け手側)
名前が指す 3 つの GAP #
| GAP | 中身 | GAP v0.1 が閉じる手段 |
|---|---|---|
| Disclosure GAP | 開示された数値と現場の一次データの乖離 | 発生源の秘密鍵で署名された活動量だけを流通させ、途中の書き換えを数学的に検知可能にする |
| Time GAP | 年1回の報告と、投資家のリアルタイムな意思決定の時間軸のズレ | 追記専用ストリームへの継続的 ingest(Continuous Auditing)。任意の窓を都度算定できる |
| Agent GAP | 人間向け非構造化文書と、Agentが要求する決定論的インターフェースの断絶 | MCP Tool として決定論的な算定・検証を提供。同じ引数なら常に同じ数値とハッシュ |
設計上の 1 行 #
GAPは排出量を運ばない。活動量と、それが本物である証拠だけを運ぶ。
排出量は「活動量 × 排出係数」であり、係数の選択は制度・基準・年度によって変わる判断である。 その判断を企業側で先に済ませてしまうことが「化粧」の入口になる。GAP は係数の適用を 受け手側の明示的な選択として分離し、企業側には「測ったもの」だけを出させる。
1. レイヤー構成 #
① セマンティック・データレイヤー(意味の標準化) #
活動量 1 件は次の payload で表現する。これがそのまま署名対象のバイト列になる。
{
"spec": "gap/0.1",
"source_id": "jp-demo-plant-chiba-meter-01",
"seq": 1,
"metric": "electricity_kwh",
"period_start": "2026-07-01T00:00:00Z",
"period_end": "2026-07-01T01:00:00Z",
"value": 812.4,
"unit": "kWh",
"quality": "measured",
"prev_hash": null,
"meta": { "line": "A", "tariff": "peak" }
}
スキーマ: reading-payload.schema.json
規定:
- 時刻は 1 形式のみ:
YYYY-MM-DDTHH:MM:SSZ(秒単位・UTC・ミリ秒なし)。 表現ゆれがあると署名バイト列が再現できないため、ミリ秒付きやオフセット表記は仕様違反。 qualityは必須。推計値・修正値を使うこと自体は正当だが、 ラベルなしで混ぜた瞬間に「化粧していないストリーム」と区別がつかなくなる。seqは source ごとに 1 から連番。欠番は削除・欠測の証跡として残る。- Zero-Embellishment Constraint(非化粧制約):
metricおよびmetaのキーに派生排出量を示す語 (co2/co2e/ghg/emission/scope1|2|3/tco2/carbon_footprint)を 含む payload は受理してはならない。GAP が運ぶのは活動量に限る。 - 単位変換・丸め・補正を転送経路で行ってはならない。機器が出した値をそのまま運ぶ。
② クエリ&実行レイヤー(MCP Native) #
投資家側 Agent 向けの MCP Tool(すべて read-only / idempotent):
| Tool | 役割 |
|---|---|
list_sources | 一次データ源の発見。呼び出し側の鍵で見える粒度も返す |
get_activity_data | 生の活動量(許可粒度まで)。証跡ハッシュ付き |
list_emission_factors | 適用可能な係数の一覧(出典 URL・公表日つき) |
compute_emissions | 活動量 × 明示指定の係数を決定論的に算定 |
verify_provenance | 署名・ハッシュチェーンの再計算(値は返さない) |
detect_anomalies | 欠測・seq 欠番・外れ値・フラットライン等の統計的検知 |
Context Budget Optimization
Agent のコンテキストは「数値 + それが正しい根拠」に使わせ、生ログの運搬には使わせない。
- 応答点数は上限(既定 1000 点)で頭打ちにし、超えたら黙って切り詰めず
too_many_pointsを返して粒度を粗くさせる。 verify_provenanceは真偽値とハッシュのみを返す(値を運ばずに正しさだけを運ぶ)。- 集計して返す場合も
bucket_hash(内訳 payload_hash の SHA-256)を添え、 値を明かさずに「どの生データから作ったか」を検証可能にする。
算定ロジックの責任の所在
compute_emissions は係数を一意に特定できないと数値を返さない。
候補が複数あれば factor_ambiguous として候補一覧を返す。
これは実装の不便さではなく仕様である — ノードが暗黙に係数を選べてしまうと、
「誰の判断でこの数値になったのか」が再び曖昧になる。
③ トラスト&アイデンティティレイヤー(証明と権限) #
正規化: RFC 8785 のサブセット。オブジェクトのキーは昇順、空白なし、
非有限数は禁止、-0 は 0 に正規化。
ハッシュ: payload_hash = SHA-256(canonical_json)(hex 小文字)
チェーン: prev_hash が直前 reading の payload_hash を指す追記専用リンク。
seq は 1 ずつ増加。過去の差し替え・こっそりした削除はリンク切れとして露見する。
署名: Ed25519。canonical JSON の UTF-8 バイト列に対する署名を base64 で付す。 公開鍵は source 登録時にノードへ渡す(fingerprint = SHA-256 が公開される)。
検証の再計算義務: ノードは ingest 時の検証結果フラグを信用してはならない。
compute_emissions は算定の直前に署名とハッシュを取り直し、
1 件でも不整合があれば数値を返さず provenance_compromised を返す。
(ingest 時のフラグだけを信じる実装は、ノードのストレージを直接書き換えられた場合に
「検証済み」と称して化粧後の数値を配ってしまう。)
Granular Access Control(機密境界)
| disclosure_level | 誰が読めるか | 粒度 |
|---|---|---|
public | 承認済みの全 API key | min_bucket_seconds まで粗くして開示(例: 時間計測 → 日次開示) |
engagement | raw_access_grants を持つ鍵のみ | grant 側でより細かい粒度を許可可能 |
restricted | ノード内部のみ | — |
工場のリアルタイム電力は生産稼働率・原価に直結する機密になり得る。
GAP は「隠す / 全部見せる」の二択にせず、時間粒度を落として開示し、
落とした後も検証可能性を保つ(bucket_hash)ことで折り合いをつける。
engagement の source は grant がない鍵にも存在だけは見せる
(access.granularity = "none")。隠すとエンゲージメントの入り口が消えるため。
2. エンドポイント #
| メソッド | パス | 認証 |
|---|---|---|
| GET | /.well-known/gap.json | なし(ノード発見) |
| GET | /api/v1/gap/schema | なし(JSON Schema バンドル) |
| GET | /api/v1/gap/sources[/:id[/readings]] | API key |
| GET | /api/v1/gap/factors | API key |
| GET | /api/v1/gap/compute | API key |
| GET | /api/v1/gap/verify | API key |
| GET | /api/v1/gap/anomalies | API key |
| POST | /mcp/gap | API key(MCP Streamable HTTP) |
| GET | /api/gap/ingest/head | ノード発行の ingest bearer |
| POST | /api/gap/ingest | ingest bearer + reading ごとの Ed25519 署名 |
ingest の認証が 2 段なのは、トークンが漏れても過去の書き換えはできないようにするため。 トークンは「誰が接続してよいか」、署名は「そのデータが本物か」を担保する。
3. エラー(規定) #
| code | 意味 |
|---|---|
embellishment_rejected | 派生排出量を含む payload(非化粧制約違反) |
signature_invalid | 登録鍵で検証できない |
chain_broken / seq_out_of_order | チェーン先端に繋がらない |
provenance_compromised | 保存済みデータが署名と一致しない(算定を拒否) |
factor_ambiguous | 係数が一意に定まらない(候補を同梱) |
factor_not_found / unit_mismatch / factor_metric_mismatch | 係数の選択が不正 |
granularity_denied | 開示粒度より細かい要求 |
grant_required / source_restricted | 開示範囲外 |
too_many_points / window_too_large | 応答が上限超過(粒度か窓を変える) |
バッチ ingest は all-or-nothing。1 件でも壊れていれば全体を拒否し、
受理も拒否も監査ログ(raw_ingest_log)に残す。
4. リファレンス実装 #
リファレンス実装は非公開リポジトリ kokubee/gxceed にあり、現時点で外部からは参照できない。
以下のパスは実装の構成を示すためのもので、リンクではない。
| 場所 | 内容 |
|---|---|
workers/data-api/src/raw/provenance.ts | 正規化 / ハッシュ / チェーン / 署名検証 |
workers/data-api/src/raw/ingest.ts | 署名検証つき ingest(非化粧制約の実装) |
workers/data-api/src/raw/store.ts | 参照・集計・算定・検証・異常検知 |
workers/data-api/src/mcp-gap.ts | MCP Server(6 tools + spec resource) |
workers/data-api/src/routes/gap.ts | REST バインディング(MCP と同一ロジック) |
scripts/gap/gap-sign.mjs | 発生源側の署名ライブラリ(依存なし・Node 22) |
scripts/gap/demo-factory-meter.mjs | 工場スマートメーターのデモデータ生成 |
scripts/gap/e2e-local.mjs | ローカル一気通し(32 チェック) |
scripts/health/gap-selftest.mjs | 正規化・ハッシュ・署名のテストベクタ検証 |
node scripts/health/gap-selftest.mjs # 証跡レイヤーの自己診断
node scripts/gap/e2e-local.mjs # 署名 → ingest → 算定 → 検証 → MCP を一気通し
テストベクタは test-vectors.json。
署名鍵は RFC 8032 §7.1 TEST 1 の公開テスト鍵で、実運用には使えない値を意図的に使っている。
5. v0.1 の意図的な非対応(v0.2 以降の論点) #
- Zero-Knowledge Proof / 差分プライバシー: 現状の機密対応は時間粒度の粗視化まで。 「生データを明かさずに正当性のみ証明する」までは踏み込んでいない。
- 鍵ローテーションの手続き:
public_key_hashを reading に記録しており 過去分の再検証はできるが、ローテーション自体のプロトコルは未定義。 - 複数ノード間のクロス検証:
input_hashにより同一入力からの同一結果は照合できるが、 ノード間の相互監査プロトコルはない。 - Scope 3 / サプライチェーン連鎖: 他社ノードの活動量を参照する連鎖構造は未定義。 現状は 1 ノード内の source に閉じる。
- ストリーミング購読: 現状はポーリング。webhook / SSE による push は未定義。
- 制度マッピング(CSRD / SSBJ / GHG Protocol): GAP は活動量と係数を分離するだけで、 どの制度のどの項目に対応するかの写像は持たない。写像は受け手側の責務とする。
6. 標準としての狙い #
個別企業がばらばらの MCP を実装すると、投資家側 Agent の統合コストが企業数に比例して増える。 GAP が固定しようとしているのは以下の 4 点だけで、それ以外は各社の自由に任せる:
- 署名対象のバイト列(正規化規則)
- チェーンの張り方(
prev_hash+seq) - Tool の名前と入出力の意味(6 tools)
- 「排出量ではなく活動量を出す」という制約
この 4 点が揃っていれば、投資家側の 1 つの Agent 実装が、 どの企業の GAP Node に対しても同じコードで接続・検証・横断比較できる。
機械可読スキーマ #
各スキーマの $id はこのサイトの URL を指しており、$ref もここで解決する。
配布物の由来と各ファイルの SHA-256 は manifest.json にある。
| ファイル | 役割 |
|---|---|
reading-payload.schema.json | 署名の単位。正規化したこのオブジェクトがハッシュ・署名対象のバイト列そのもの |
ingest-request.schema.json | 発生源からノードへ送るバッチのエンベロープ |
source.schema.json | データ源の公開記述子(何を・どの頻度で・どの鍵で・どの開示粒度で) |
emission-factor.schema.json | 出典を辿れる係数。署名対象のストリームの外に意図的に置く |
compute-result.schema.json | 算定結果の形。独立に再現するのに要る情報を含む |
test-vectors.json | 正規化・ハッシュ・チェーン・署名のベクタ |
正規化・ハッシュ・チェーン規則を変更すると、このバージョンで作られた署名がすべて無効になる。
その種の変更はここへの編集ではなく、新しい spec 値(v0.2)を要する。