# GAP (GX AI Protocol) Specification v0.1 — full text Source: https://github.com/kokubee/gxceed · docs/design/gap-spec-v0.1.md Blob: 3bb90cc762631cab3140da9b90ceb456374ba8e3 · last commit touching this path: 19f2540926c57dbb6051730ac84faea261d83454 Canonical HTML: https://gxai.dev/spec/gap/v0.1 (Japanese, normative) Informative English translation: https://gxai.dev/en/spec/gap/v0.1 License: undetermined. Status: Draft. This file is generated verbatim from the normative Japanese specification; relative repository links are rewritten to absolute gxai.dev URLs. --- # GAP (GX AI Protocol) Specification v0.1 **Status**: Draft / リファレンス実装あり(`workers/data-api`) **Wire version**: `gap/0.1` **機械可読スキーマ**: [`spec/gap/v0.1/`](https://gxai.dev/spec/gap/v0.1/) **配布想定**: gxai.dev --- ## 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 で表現する。これがそのまま**署名対象のバイト列**になる。 ```json { "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`](https://gxai.dev/spec/gap/v0.1/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. リファレンス実装 | 場所 | 内容 | |---|---| | `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` | 正規化・ハッシュ・署名のテストベクタ検証 | ```bash node scripts/health/gap-selftest.mjs # 証跡レイヤーの自己診断 node scripts/gap/e2e-local.mjs # 署名 → ingest → 算定 → 検証 → MCP を一気通し ``` テストベクタは [`spec/gap/v0.1/test-vectors.json`](https://gxai.dev/spec/gap/v0.1/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 点だけで、それ以外は各社の自由に任せる: 1. 署名対象のバイト列(正規化規則) 2. チェーンの張り方(`prev_hash` + `seq`) 3. Tool の名前と入出力の意味(6 tools) 4. 「排出量ではなく活動量を出す」という制約 この 4 点が揃っていれば、投資家側の 1 つの Agent 実装が、 どの企業の GAP Node に対しても同じコードで接続・検証・横断比較できる。