Disclosure GAPReported figures drift from field data
Only activity data signed with the source's private key circulates, so any alteration in transit is mathematically detectable.
gap/0.1
A wire protocol for delivering corporate ESG data as raw activity data, signed at source, straight to an investor's AI agent over MCP and REST — with no PDF disclosure report in between.
GAP does not carry emissions. It carries activity data, and the evidence that it is genuine.
This English site is an informative translation. The Japanese specification is normative — where the two disagree, the Japanese text governs.
What changes
In conventional disclosure, a figure passes through aggregation, presentation and re-keying before it reaches an investor. What survives is a number — with no way for the recipient to check which measurement it came from.
Conventional
GAP
Emissions are activity data times an emission factor, and choosing the factor is a judgement that varies by regime, standard and year. Letting the reporting company settle that judgement first is where embellishment begins. GAP separates factor selection out as an explicit choice made by the recipient, and asks the company for nothing but what it measured.
What has been measured
By the stated purpose in the introduction to ISO 32000-2:2020, PDF exists for "preservation of document fidelity independent of the device, platform and software", and its production stage is characterised as "the final-state form for distribution to end users" [S4]. It keeps position precisely; what that position means is not something the format carries by default. What happens downstream has already been measured.
| What was measured | Result | Source |
|---|---|---|
| Table detection F1 across 400 financial documents (annual reports, SEC filings) | 0.06 pdfplumber · 0.10 Camelot ·
0.24 Tabula · 0.79 TATR In the same comparison, body-text extraction reaches 0.95–0.99 |
Adhikari & Agarwal (2024) |
| Relating a KPI to its value across 81 hand-annotated real 10-K filings | Best model reaches Relation F1 22.68% | Deußer et al., ICMLA 2022 (KPI-EDGAR) |
| Extraction from the 2022 ESG reports of 166 HKEX-listed companies | Best configuration (GPT-4 + RAG) is 76.9% accurate | Deng et al. (2023), ESGReveal |
| Time to transcribe one figure (practitioner's own account) | HTML ~20 min · PDF ~30 min · image ~50 min. From XBRL, 1–2 sec | XBRL US ESG Working Group |
| Table candidates in one 462-page sustainability report | Raw parser verdict 460 pages → mechanically filtered on task-specific conditions, 62 pages | Authors' own JASAG v3.1 record unpublished; not exhaustively checked by hand |
The shape of the failure is what matters. On the same corpus, body text comes out at 0.95–0.99 while tables collapse to 0.06–0.24 [S17]. The extractors are not simply bad at their job — nothing in the format says that a number is part of a table. So GAP does not try to parse PDFs better. It puts the signature one layer earlier, before anything is flattened into a document.
Full citations and the extent to which each was checked are in
evidence-sources.json. They are drawn from the
evidence ledger of chapter 7 ("What PDF disclosure loses: provenance, coordinates, computation") of a book in
progress, Non-Financial Information Without Evidence. That book is unpublished.
The three gaps in the name
Only activity data signed with the source's private key circulates, so any alteration in transit is mathematically detectable.
Continuous ingest into an append-only stream (continuous auditing). Any window can be computed on demand.
Computation and verification are exposed as MCP tools. The same arguments always yield the same figure and the same hash.
Zero-Embellishment Constraint
GAP carries activity data only. 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 by a node.
{
"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" }
}
A measurement in kWh. What it gets multiplied by, and what the result is called, is the recipient's decision.
embellishment_rejected{
"spec": "gap/0.1",
"source_id": "jp-demo-plant-chiba-meter-01",
"seq": 1,
"metric": "scope2_co2e_t",
"period_start": "2026-07-01T00:00:00Z",
"period_end": "2026-07-01T01:00:00Z",
"value": 0.351,
"unit": "tCO2e",
"quality": "measured",
"prev_hash": null,
"meta": { "line": "A", "tariff": "peak" }
}
The factor has already been chosen. Whose judgement produced this number has been erased.
For the same reason, no unit conversion, rounding or correction may happen in transit. The value the
instrument produced is the value that travels. Using estimated or corrected readings is legitimate in itself,
but the moment they are mixed in without a quality label, an unembellished stream becomes
indistinguishable from an embellished one.
Layers
One reading's payload is the byte string that gets signed. Timestamps take exactly one form,
YYYY-MM-DDTHH:MM:SSZ — variation would make the signed bytes irreproducible.
seq increments from 1 per source, so a gap in the sequence remains as evidence of a deletion or a missed reading.
Six read-only, idempotent tools. Responses are capped (1000 points by default); beyond that the node
returns too_many_points rather than silently truncating.
compute_emissions refuses to return a figure when the factor is not uniquely determined,
returning factor_ambiguous with the candidates instead.
Canonicalization is an RFC 8785 subset (sorted keys, no whitespace); the hash is SHA-256; the signature
is Ed25519; the chain is prev_hash plus a monotonic seq per source.
A node must not trust the verification flag recorded at ingest — it re-checks immediately before computing.
MCP tools
All read-only and idempotent. The same logic is also exposed over REST at /api/v1/gap/*.
| Tool | Role |
|---|---|
list_sources | Discover primary data sources, including the granularity the calling key may see |
get_activity_data | Raw activity data, up to the permitted granularity, with provenance hashes |
list_emission_factors | Applicable factors, each with a citation URL and publication date |
compute_emissions | Activity data times an explicitly specified factor, computed deterministically |
verify_provenance | Recomputes signatures and the hash chain (returns no values) |
detect_anomalies | Statistical detection of gaps, missing seq, outliers and flatlines |
Verifiability
Fixed vectors for canonicalization, hashing, chaining and signing are distributed with the spec. The signing key is the published RFC 8032 §7.1 TEST 1 key — deliberately a value no real source can use.
Canonical JSON (the exact byte string that gets signed)
{"metric":"electricity_kwh","period_end":"2026-07-01T01:00:00Z","period_start":"2026-07-01T00:00:00Z","prev_hash":null,"quality":"measured","seq":1,"source_id":"jp-demo-plant-chiba-meter-01","spec":"gap/0.1","unit":"kWh","value":812.4}
payload_hash = SHA-256(canonical_json)
5f42e6df31af80d66e1ee0b906ab172bd9b7cb41c34864d853ee57c3b84a5484
Self-check in the reference implementation
node scripts/health/gap-selftest.mjs # canonicalization, hashing and signature vectors
node scripts/gap/e2e-local.mjs # sign -> ingest -> compute -> verify -> MCP, end to end
Vectors: test-vectors.json
Granular access control
A factory's real-time electricity draw can reveal utilisation and unit cost. GAP settles this by
coarsening the time granularity while keeping the disclosure verifiable (bucket_hash).
| disclosure_level | Who may read it | Granularity |
|---|---|---|
public | Every approved API key | Coarsened to min_bucket_seconds (e.g. hourly metering disclosed daily) |
engagement | Only keys holding raw_access_grants | A grant may permit finer granularity |
restricted | Node internal only | — |
An engagement source is still visible as existing to keys
without a grant (access.granularity = "none") — hiding it would remove the entry point for engagement.
Where it bites
The phenomena collapsed into the single word "greenwashing" separate into at least eight kinds [B1]. GAP reaches three of them, and in every case only along one path: a record already committed to the chain being quietly rewritten afterwards. Two more are partial, and the remaining three lie outside what any wire protocol can reach [B2].
| Kind | What happens | Effect of GAP v0.1 |
|---|---|---|
| Loss of information | Source records, computation and correction history disappear on the way into a PDF | reaches it, narrowly
Signed at source, with provenance kept by the append-only chain of payload_hash and
prev_hash. What this detects is a broken link where a committed record was later
rewritten — not evidence that was never committed, nor a chain that simply stops |
| Numerical embellishment | Computing emissions, reduction rates or renewable share inappropriately | reaches it, narrowly
The Zero-Embellishment Constraint, plus returning no figure when the factor is ambiguous, closes
at least that one path. Putting a false value into a permitted field (say
electricity_kwh) and calling it measured is not prevented by this constraint alone |
| Methodological embellishment | Changing base year, emission factor or estimation method to look improved | reaches it, narrowly Rewriting past values tends to surface as a broken link. But adding the change as a legitimate new record contradicts nothing in the chain rules, and GAP has no provision for judging whether the change was methodologically sound |
| Boundary embellishment | Dropping inconvenient subsidiaries, regions or Scope 3 categories | partial
list_sources returns registered sources only, so an entity never registered does not
appear in that query either. Nothing enforces completeness of the reporting boundary; closing that
requires reconciliation against an external legal-entity identifier system such as LEI.
Scope 3 chaining is also undefined in v0.1 |
| Selective disclosure | Publishing only the indicators that improved | partial
An engagement source is visible as existing even without a grant. But nothing enforces
continuity — which indicators must be published, and how often |
| Falsehood | Describing measures that were never taken | structurally out of reach Arises when the holder of the signing key signs false activity data, or when the instrument itself is rigged. GAP places this explicitly outside its design scope |
| Temporal embellishment | Presenting future targets as if they were current performance | structurally out of reach A matter of how the accompanying prose is written. GAP governs the path data travels |
| Narrative embellishment | Covering reality with an unevidenced value-creation story | structurally out of reach As above. No wire protocol can govern honesty |
A signature does not attest that a value reflects reality. It attests only that a value signed with a registered key was not altered afterwards. Whether the instrument is correctly installed and calibrated lies outside GAP and remains the work of assurance and audit. Not having been tampered with, and having been measured correctly, are different questions.
Which leads to the most uncomfortable possibility for this protocol itself. Where the institutions for correcting a wrong figure remain thin, a figure that is wrong but demonstrably untampered can end up being trusted more, precisely because it is untampered [B3]. Above the layer that detects tampering, a separate layer is still needed: what happens when the value itself turns out to be wrong. (A concern drawn from the evidence, not a measured result.)
The eight kinds come from the framework of a book in progress [B1]. The mapping onto GAP follows the analysis derived in that book's chapter 8 from the specification text alone [B2]. The book is unpublished.
Deliberately out of scope in v0.1
These remain open as v0.2 questions. They are listed so that no one reads a capability into the spec that is not there.
public_key_hash is recorded on each reading so past data can be re-verified, but the rotation protocol itself is undefinedinput_hash lets identical inputs be matched to identical results, but there is no inter-node audit protocolStatus
| Specification | Draft (wire version gap/0.1) |
|---|---|
| License | Undetermined |
| Reference implementation | Private repository kokubee/gxceed (not publicly accessible at this time) |
| Provenance of this distribution | gxceed@19f2540 / tree a77935e |
This is a worked example, not a solution [B4]. It was written to show what closes and what remains when parts that already work in adjacent domains are assembled in a non-financial reporting context. It has no track record as an industry standard and has not been independently verified. Financial disclosure already has a working precedent — XBRL US operates an official MCP server over SEC filing data — and that gap is itself the measure of what non-financial reporting has yet to catch up on.
Because the license is undetermined, this specification does not currently grant or guarantee the right to implement it. Please confirm before adopting it.
In the reference implementation, GAP_MCP_ENABLED and GAP_INGEST_ENABLED both
default to false (fail-closed), and any distributed emission factor whose factor_id
begins with demo- is a proof-of-concept placeholder that must be replaced with a
published value before production use.
Why standardise
If every company implements its own MCP server, the investor side's integration cost grows in proportion to the number of companies. GAP fixes only the following, and leaves everything else to each implementer.
prev_hash + seq)With those four in place, a single agent implementation on the investor side can connect to, verify and compare across any company's GAP node using the same code.