GAP / gxai.dev

GAP (GX AI Protocol) Specification v0.1

Informative translation. The Japanese text is the normative specification. Where this translation and the Japanese text disagree, the Japanese text governs. The machine-readable schemas and test vectors are language-independent and normative in either case.

Status
Draft / reference implementation exists
Wire version
gap/0.1
Normative
Japanese specification
Schema
machine-readable bundle
License
Undetermined (confers no guarantee of the right to implement)
Source
github.com/kokubee/gxceed@19f2540 · tree a77935e

0. What this is #

A wire protocol for delivering a company's ESG data as raw activity data, signed at source, straight to an investor's AI agent over API and MCP — without passing through a PDF disclosure report.

The conventional structure:

core systems -> staff/consultants (aggregation, embellishment) -> PDF report -> analyst (re-keying) -> assessment

The GAP structure:

core systems/sensors -> [signature] -> GAP Node -> [MCP/REST] -> investor AI agent (computation and verification on the receiving side)

The three gaps in the name #

GAPWhat it isHow v0.1 closes it
Disclosure GAPDivergence between disclosed figures and primary field dataOnly activity data signed with the source's private key circulates, making alteration in transit mathematically detectable
Time GAPAnnual reporting against investors' real-time decision-makingContinuous ingest into an append-only stream (continuous auditing). Any window can be computed on demand
Agent GAPUnstructured documents written for humans against the deterministic interfaces agents requireDeterministic computation and verification exposed as MCP tools. The same arguments always yield the same figure and hash

The design in one line #

GAP does not carry emissions. It carries activity data, and the evidence that it is genuine.

Emissions are activity data times an emission factor, and the choice of factor is a judgement that varies by regime, standard and year. Letting the company settle that judgement first is where embellishment begins. GAP separates factor application out as an explicit choice on the receiving side, and asks the company for nothing but what it measured.

1. Layers #

1. Semantic data layer (standardising meaning) #

A single reading of activity data is expressed by the following payload. This is the byte string that gets signed.

{
  "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" }
}

Schema: reading-payload.schema.json

Rules:

  • Exactly one timestamp format: YYYY-MM-DDTHH:MM:SSZ (second precision, UTC, no milliseconds). Variation would make the signed byte string irreproducible, so millisecond precision and offset notation violate the specification.
  • quality is mandatory. Using estimated or corrected values is legitimate in itself, but the moment they are mixed in unlabelled, an unembellished stream becomes indistinguishable from an embellished one.
  • seq increments from 1 per source. A missing number remains as evidence of a deletion or a missed reading.
  • Zero-Embellishment Constraint: a payload whose metric, or any key under meta, contains a term denoting derived emissions (co2, co2e, ghg, emission, scope1|2|3, tco2, carbon_footprint) must not be accepted. GAP carries activity data only.
  • No unit conversion, rounding or correction may be performed in transit. The value the instrument produced is the value that travels.

2. Query and execution layer (MCP native) #

The MCP tools exposed to investor-side agents (all read-only and idempotent):

ToolRole
list_sourcesDiscover primary data sources, including the granularity visible to the calling key
get_activity_dataRaw activity data up to the permitted granularity, with provenance hashes
list_emission_factorsApplicable factors, each with a citation URL and publication date
compute_emissionsActivity data times an explicitly specified factor, computed deterministically
verify_provenanceRecomputes signatures and the hash chain (returns no values)
detect_anomaliesStatistical detection of gaps, missing seq, outliers and flatlines

Context budget optimization

An agent's context should be spent on "the figure plus the grounds that it is correct", not on hauling raw logs.

  • Responses are capped (1000 points by default); beyond that the node returns too_many_points rather than silently truncating, forcing the caller to coarsen granularity.
  • verify_provenance returns only booleans and hashes — carrying correctness without carrying values.
  • Aggregated responses carry a bucket_hash (SHA-256 over the constituent payload hashes), making it verifiable which raw data a figure was built from without revealing the values.

Where responsibility for the computation sits

compute_emissions returns no figure when the factor cannot be uniquely identified; if several candidates match it returns factor_ambiguous with the list. This is specification, not inconvenience — if a node could silently pick a factor, "whose judgement produced this number" would become unclear again.

3. Trust and identity layer (proof and permission) #

Canonicalization: a subset of RFC 8785. Object keys in ascending order, no whitespace, non-finite numbers forbidden, -0 normalised to 0.

Hash: payload_hash = SHA-256(canonical_json) (lowercase hex).

Chain: prev_hash points at the preceding reading's payload_hash, forming an append-only link; seq increases by one. Substituting or quietly deleting past readings shows up as a broken link.

Signature: Ed25519, over the UTF-8 bytes of the canonical JSON, attached as base64. The public key is handed to the node at source registration (its SHA-256 fingerprint is published).

Obligation to recompute: a node must not trust the verification flag recorded at ingest. compute_emissions re-derives signatures and hashes immediately before computing, and if even one reading is inconsistent it returns provenance_compromised instead of a figure. (An implementation that trusts only the ingest-time flag will serve embellished figures while claiming they are verified, if the node's storage is written to directly.)

Granular access control (the confidentiality boundary)

disclosure_levelWho may read itGranularity
publicEvery approved API keyCoarsened to min_bucket_seconds before disclosure (e.g. hourly metering disclosed daily)
engagementOnly keys holding raw_access_grantsA grant may permit finer granularity
restrictedNode internal only

A factory's real-time electricity draw can be confidential, bearing directly on utilisation and unit cost. Rather than forcing a choice between hiding and showing everything, GAP coarsens the time granularity while preserving verifiability after the fact (bucket_hash). An engagement source is still visible as existing to keys without a grant (access.granularity = "none"), because hiding it would remove the entry point for engagement.

2. Endpoints #

MethodPathAuth
GET/.well-known/gap.jsonNone (node discovery)
GET/api/v1/gap/schemaNone (JSON Schema bundle)
GET/api/v1/gap/sources[/:id[/readings]]API key
GET/api/v1/gap/factorsAPI key
GET/api/v1/gap/computeAPI key
GET/api/v1/gap/verifyAPI key
GET/api/v1/gap/anomaliesAPI key
POST/mcp/gapAPI key (MCP Streamable HTTP)
GET/api/gap/ingest/headNode-issued ingest bearer
POST/api/gap/ingestIngest bearer plus a per-reading Ed25519 signature

Ingest authentication has two stages so that a leaked token still cannot rewrite the past. The token establishes who may connect; the signature establishes that the data is genuine.

3. Errors (normative) #

codeMeaning
embellishment_rejectedPayload contains derived emissions (violates the Zero-Embellishment Constraint)
signature_invalidCannot be verified with the registered key
chain_broken / seq_out_of_orderDoes not connect to the head of the chain
provenance_compromisedStored data disagrees with its signature (computation refused)
factor_ambiguousThe factor is not uniquely determined (candidates included)
factor_not_found / unit_mismatch / factor_metric_mismatchInvalid factor selection
granularity_deniedRequest finer than the permitted disclosure granularity
grant_required / source_restrictedOutside the disclosure scope
too_many_points / window_too_largeResponse exceeds the cap (change granularity or window)

Batch ingest is all-or-nothing. If even one reading is broken the whole batch is refused, and both acceptances and refusals are recorded in the audit log (raw_ingest_log).

4. Reference implementation #

The reference implementation lives in a private repository, kokubee/gxceed, and is not publicly accessible at this time. The paths below describe its structure; they are not links.

LocationContents
workers/data-api/src/raw/provenance.tsCanonicalization / hashing / chaining / signature verification
workers/data-api/src/raw/ingest.tsSignature-verifying ingest (implements the Zero-Embellishment Constraint)
workers/data-api/src/raw/store.tsRetrieval, aggregation, computation, verification, anomaly detection
workers/data-api/src/mcp-gap.tsMCP server (six tools plus a spec resource)
workers/data-api/src/routes/gap.tsREST bindings (same logic as MCP)
scripts/gap/gap-sign.mjsSigning library for the source side (no dependencies, Node 22)
scripts/gap/demo-factory-meter.mjsDemo data generation for a factory smart meter
scripts/gap/e2e-local.mjsLocal end-to-end run (32 checks)
scripts/health/gap-selftest.mjsCanonicalization, hashing and signature vector verification
node scripts/health/gap-selftest.mjs   # self-check of the provenance layer
node scripts/gap/e2e-local.mjs         # sign -> ingest -> compute -> verify -> MCP, end to end

The vectors are in test-vectors.json. The signing key is the published RFC 8032 §7.1 TEST 1 key, deliberately a value that cannot be used in production.

5. Deliberately out of scope in v0.1 (open questions for v0.2) #

  • Zero-knowledge proofs / differential privacy: confidentiality currently extends only to coarsening time granularity, and not as far as proving validity without revealing the raw data.
  • Key rotation procedure: public_key_hash is recorded on each reading so past readings can be re-verified, but the rotation protocol itself is undefined.
  • Cross-verification between nodes: input_hash allows identical inputs to be matched to identical results, but there is no inter-node audit protocol.
  • Scope 3 / supply-chain chaining: referencing another company's node is undefined; v0.1 is closed over the sources within a single node.
  • Streaming subscriptions: polling only. Push via webhook or SSE is undefined.
  • Regime mapping (CSRD / SSBJ / GHG Protocol): GAP only separates activity data from factors; mapping to a given regime's line items is the recipient's responsibility.

6. The point of standardising #

If individual companies each implement their own MCP server, the integration cost on the investor side grows in proportion to the number of companies. GAP fixes only the following four things, leaving everything else to each implementer:

  1. The byte string that gets signed (the canonicalization rules)
  2. How the chain is formed (prev_hash + seq)
  3. The names and input/output semantics of the tools (six tools)
  4. The constraint that what leaves the company is activity data, not emissions

With those four in place, a single agent implementation on the investor side can connect to, verify against and compare across any company's GAP node using the same code.

Machine-readable schemas #

Each schema's $id points at this site, and $ref resolves here too. The provenance of the distribution and the SHA-256 of each file are in manifest.json.

FilePurpose
reading-payload.schema.jsonThe signed unit of the protocol: one measurement period of raw activity data
ingest-request.schema.jsonBatch envelope sent from a primary source to a node
source.schema.jsonPublic descriptor of a data source: what it measures, how often, under which key, at what disclosure granularity
emission-factor.schema.jsonA citable factor, kept deliberately outside the signed stream
compute-result.schema.jsonShape of a computed figure, including everything needed to reproduce it independently
test-vectors.jsonCanonicalization, hashing, chaining and signature vectors

Changing canonicalization, hashing or the chain rule invalidates every signature ever produced under this version. Such a change requires a new spec value (v0.2), not an edit here.