OpenChainGraph v0.4 adds Export Profiles — generated, non-canonical renderings of an already-verified artifact, produced under one umbrella term: chaingraph_export. The lead profile is xlsx: a three-sheet analyst/board workbook built straight from output_payload, with csv, pdf, and xbrl as siblings under the same exporter discipline. Every export is generated after execution_hash is computed and is excluded from the hash preimage — the cryptographic anchor travels to the last mile instead of breaking the moment someone hand-copies a number into a spreadsheet.
v0.3 introduced a two-layer framing — OKF (context-in) and OCG (provenance-out). v0.4 extends it to three. Export Profiles are the third layer: a rendered-for-review surface that takes an already-verified artifact and produces the file format a human, pipeline, or regulator actually opens. Understanding what this layer does — and does not do — is the foundation of a correct integration.
execution_hash (SHA-256 over the sorted-key JSON of policy_parameters + output_payload), a chain block, compliance_flags[], and an audit_signature (Ed25519 / in-toto envelope). This is the only canonical, hash-anchored layer. It answers: what decision was made, on what inputs, by which tool?xlsx (analyst/board spreadsheet), csv (pipeline-tabular sibling), pdf (signed one-pager), xbrl (regulator-submission profile, taxonomy per regime). None of the four carries its own execution_hash; none is independently verifiable. It answers: what does this decision look like to the person or system that has to act on it?The three layers interlock cleanly and derive from the same source artifact: an agent consults OKF to plan a chain, executes it to receive a signed OCG artifact, and — only when a human, pipeline, or regulator needs to consume that artifact outside a JSON parser — calls export_artifact to render it. The planning surface, the proof surface, and the review surface never overlap.
An export file describes a decision. It is not a decision artifact in its own right. An exported .xlsx, .csv, .pdf, or .xbrl file must never be treated as independently verifiable — it carries no new execution_hash, and verification always routes back to the canonical JSON artifact it was generated from. Putting verification weight on the rendered file would conflate the proof layer with the review layer, making it possible for a tampered spreadsheet to masquerade as ground truth. Keep them strictly, permanently separate: the JSON artifact is the fact; the export is a picture of the fact.
This separation is also why export profiles are additive, not a dependency. Nothing in OCG's verification path reads or depends on the rendered files. The export layer is a convenience for the last mile of consumption; removing it would not affect the cryptographic integrity of any artifact.
In OpenChainGraph v0.4, every export format is a documented sub-profile under one umbrella term: chaingraph_export:<format>. This mirrors the OKF companion bundle's precedent (§10 of the spec) — a generated view, never hand-edited, never independently verifiable, fully swappable, and additive. No existing tool, kernel, or verifier changes to remain v0.4-valid.
chaingraph_export:xlsx // analyst / board spreadsheet — this guide's lead profile chaingraph_export:csv // pipeline-tabular sibling of xlsx — near-free once xlsx exists chaingraph_export:pdf // signed board / audit one-pager chaingraph_export:xbrl // regulator-submission profile (taxonomy per regime)
Every sub-profile, regardless of format, must satisfy the same five conformance rules. These are the rules that make "generated, swappable, additive" actually true rather than aspirational:
| # | Conformance rule | Why it matters |
|---|---|---|
| 1 | Generated solely from the verified artifact's output_payload (+ optionally policy_parameters) — never hand-authored. |
If a human can edit the rendered file independently of the artifact, the file stops being a faithful view of the decision. |
| 2 | Embed a metadata block: tool_id, execution_hash, chaingraph_version, compute_mode, and (if present) audit_signature.signatures[0].keyid, plus a link or QR to the canonical JSON artifact / verification endpoint. |
Keeps the cryptographic anchor attached at the last mile — anyone holding the file can trace it back to the signed source. |
| 3 | Not carry a new execution_hash and not be treated as independently verifiable. |
It is a view, not a fact. Verification always routes back to the canonical JSON — never to the rendered copy. |
| 4 | Optional and additive — absence of an exporter never invalidates a tool. | A node with zero export formats is still a fully valid v0.4 node; JSON-only is always a conforming state. |
| 5 | Deterministic: same artifact + same exporter version → byte-stable output (static per-mandate_type templates, sorted rows, fixed number formats). |
Two parties holding the same artifact and exporter version can confirm they hold the identical rendering — no drift, no ambiguity. |
Export profiles are generated downstream of execution_hash = SHA-256(JCS({policy_parameters, output_payload})) and never alter it. A v0.4 artifact with exports attached remains verifiable by any v0.3 verifier that simply ignores the unknown export-related fields.
Export generation runs in the Cloudflare Worker — the same runtime as the Compute-Binding kernels (§12 of the spec) — as one canonical generator rather than N drifting client implementations. The rationale mirrors Compute Binding: agent-native, single round-trip, reproducible. The zero-egress privacy boundary applies unchanged here too: exporters are pure functions over an artifact the caller already holds. No payload is logged, stored, or transmitted beyond the JSON-RPC response.
Exporters live at repo/chaingraph/exporters/<format>/ — parallel to kernels/ — each a pure function with the same shape:
// buildExport(artifact) → Uint8Array // Pure: no DOM, no window, no network, no Date.now(). // Receives the full verified v0.4 artifact object; returns the rendered bytes. // Vendored into the Worker by generate.mjs on the same push (same // two-repo discipline as kernels — see Compute Binding §12.3). export async function buildExport(artifact) { … } // Static metadata for the Worker's export registry. export const meta = { format: "xlsx", media_type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" };
Strict client-side generation remains permissible for an air-gapped deployment using the published exporter spec — nothing about the conformance rules in §2 requires a server. The reference implementation is server-side because it gives one canonical generator instead of drifting reimplementations across browser, CLI, and pipeline consumers. If you build a client-side exporter, it must still satisfy all five conformance rules, especially determinism (rule 5) — your output must be byte-identical to the reference Worker's output for the same artifact and exporter version.
Export Profiles surface through a single new read-only MCP tool — not a format parameter bolted onto every existing tool (which would inflate every schema in the suite), and not an out-of-band HTTP endpoint (which would split the agent-callable story in two). This follows MCP best practice: a focused, domain-aware tool that an agent can reason about on its own, with one tool owning the render concern end-to-end.
// Input { "artifact": // full v0.4 artifact object (REQUIRED — stateless Worker, no cache) "format": "xlsx" | "pdf" | "csv" | "xbrl", "xbrl_taxonomy": "<regime id>" // required only when format = xbrl } // Output { "format": "xlsx", "filename": "<tool_id>-<short_hash>.xlsx", "media_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "bytes_base64": "…", // standard base64 (NOT url-safe) — avoids client decode bugs "metadata": { "tool_id", "execution_hash", "chaingraph_version", "compute_mode" } }
The agent flow stays end-to-end and entirely MCP-native: call a compute tool → receive a JSON artifact with execution_hash → call export_artifact with that artifact → receive the rendered file. Both calls are readOnlyHint:true, so an agent (or a policy layer governing one) can treat the whole sequence as side-effect-free.
| Detail | Behavior |
|---|---|
| Statelessness | The Worker holds no cache between calls — artifact is required on every call, even for the same tool_id called twice. There is no implicit "last artifact" state. |
| Large outputs | For big GPU-batch outputs (e.g. run_liquidity_stress_test at 1,000×250), the Worker returns an MCP resource — URI-addressable, read-only — instead of an oversized inline base64 blob. |
| Capability mismatch | The tool rejects any (tool, format) pair not present in the node's export_capability array (§6 of this guide) — a deterministic, discoverable failure rather than a silent best-effort render. |
xlsx is the lead export profile and the one with the most direct mapping to existing tool output: most OCG tools already produce tabular output_payload shapes (percentile tables, batch rows, distributions), so the exporter's job is largely extraction and layout rather than transformation. Every xlsx export follows a fixed three-sheet convention:
tool_id, mandate_type, timestamp. A metadata block carrying execution_hash, chaingraph_version, compute_mode, and signing keyid. A QR code encoding the verification URL for the canonical JSON artifact.output_payload — percentile tables, batch rows, distributions. One or more sheets depending on how many logical tables the tool's output contains; sheet names follow the payload's own field names where sensible.chain.parent_hashes, chain.parent_tool_ids, compliance_flags[] — included when the artifact carries chain-of-custody data worth surfacing to a reviewer who won't open the raw JSON.The workbook is generated with a Workers-compatible OOXML writer — pure JS/WASM, no Node fs dependency. Given the simple, deterministic structure (three fixed sheet roles, no formulas, no macros, no embedded objects beyond the QR image), a minimal hand-rolled OOXML writer is an acceptable implementation choice; nothing about the format requires a full-featured spreadsheet library.
The xlsx profile launches against tools that are already tabular in shape: compute_rwa_scenarios, validate_einvoice_batch, simulate_vop_matching, and run_liquidity_stress_test. These were chosen because their output_payload structures map onto the Data sheet with no reshaping — a percentile table or batch-row array drops in directly. Tools with deeply nested or non-tabular payloads are not excluded by the format, but will need a payload-specific flattening step in the exporter before they're added to the pilot list.
Determinism (conformance rule 5, §2) applies in full here: sorted rows, fixed number formats, and a static per-mandate_type sheet layout mean the same artifact run through the same exporter version always produces a byte-identical .xlsx file. Two analysts holding the same artifact can confirm — without opening the JSON — that they're looking at the same workbook.
Mirroring compute_capability from Compute Binding, every live node in chaingraph.json may declare which export profiles it supports — so MCP clients and agents can discover available formats before calling export_artifact, rather than finding out by trial and error.
{
"tool_id": "compute_rwa_scenarios",
"compute_capability": "server",
"export_capability": ["xlsx", "csv", "xbrl:eba-corep-own-funds"]
}
| Field state | Meaning |
|---|---|
Absent or [] |
JSON-only. Still a fully v0.4-valid node — export support is purely additive (conformance rule 4, §2). |
"xlsx" / "csv" / "pdf" |
Format name with no further qualification. The tool supports a direct, single-variant render in that format. |
"xbrl:<taxonomy-id>" |
Names a specific taxonomy from the XBRL regime table (§7 of this guide). XBRL is the one format where the umbrella alone is insufficient — a regime selector is required. |
This gives the Graph Index / DCAT surface a discovery signal symmetric with compute_capability: an agent or static-site generator walking chaingraph.json can build a "what can I do with this artifact" menu without a single failed export_artifact call.
xlsx is the lead profile for this guide, but the umbrella has three siblings, each tuned to a different consumer. All four share the same export_artifact call shape and the same five conformance rules from §2 — only the rendering differs.
| Profile | Consumer | Notes |
|---|---|---|
| csv | Data-engineering pipelines; large GPU-batch outputs | The pipeline-tabular sibling of xlsx — same extraction logic, simpler serialization, near-free once the xlsx exporter exists. One CSV per logical table; a manifest row carries the metadata block (§2 rule 2). Built for volume: run_liquidity_stress_test at 1,000×250 rows, simulate_stablecoin_reserve at 1,000×90. |
| Board members, auditors, human reviewers | A signed one-pager: verdict headline, key output_payload fields as prose or table, execution_hash and signing keyid in the footer, and a QR encoding the verification URL. Uses a static template per mandate_type — one layout for attestation_mandate, a different one for compliance_mandate — so output is reproducible artifact-to-artifact with no freeform layout. Best fit: precheck_reserve_attestation, assess_ai_act_conformity, classify_dora_incident. |
|
| xbrl | Regulators; submission pipelines | The strategic profile: renders artifacts as XBRL/iXBRL so they are submission-ready, not merely analyst-readable. Requires a taxonomy per regulatory regime — there is no single universal mapping, which is why xbrl_taxonomy is a required selector on the MCP call (§4). See the regime table below. |
The xbrl taxonomy landscape at v0.4:
| xbrl_taxonomy | Regime / taxonomy | Maps from | v0.4 status |
|---|---|---|---|
| eba-corep-own-funds | EBA COREP (capital / RWA) | compute_basel31_delta, compute_rwa_scenarios, optimize_settlement_capital | Pilot |
| eba-corep-lcr-nsfr | EBA COREP LCR/NSFR (liquidity) | run_liquidity_stress_test | Pilot |
| eba-dora-ict | EBA DORA ICT register / incident ITS | classify_dora_incident | Mapped, lower priority |
| ocg-ext:attestation | OCG extension (no regulator standard yet) | precheck_reserve_attestation (GENIUS); US Treasury clearing tools | Custom extension |
Start with regimes that already have a published taxonomy — EBA COREP for capital and liquidity is the v0.4 pilot. Where no regulator taxonomy exists yet, publish a minimal namespaced OCG extension taxonomy (ocg-ext:*) rather than forcing a half-mapped standard one onto data it wasn't designed for. Every XBRL export records its source taxonomy and version in-context, and no concept may be fabricated — each one must trace to a published regulator element or an explicitly-declared OCG extension element.
Parquet is a plausible future columnar option once a concrete pipeline consumer asks for it, but it is deliberately not part of v0.4 — adding it now would pull in Arrow/Parquet Workers-runtime dependency weight for a format with no confirmed v0.4 consumer. csv covers the pipeline-tabular case today at a fraction of the implementation cost.
Six steps to wire up Export Profiles end-to-end, from artifact to rendered file. Each step is independently verifiable.
| Step | Action | Tool / verification |
|---|---|---|
| 1 | Confirm the target node declares export_capability for the format you want. If the array is absent or [], the node is JSON-only — stop here, or request the exporter be added upstream. |
Read chaingraph.json for the node's export_capability array (§6); confirm the desired format string is present |
| 2 | Call the live MCP tool to obtain a fully verified v0.4 artifact — note its execution_hash and chaingraph_version before proceeding. |
Standard tool call via https://mcp.ainumbers.co/mcp; confirm the response includes execution_hash and chaingraph_version: "0.4.0" |
| 3 | Call export_artifact with the full artifact object and your chosen format (plus xbrl_taxonomy if format is xbrl). Confirm the returned metadata.execution_hash matches the hash from step 2. |
export_artifact response → metadata.execution_hash === the hash captured in step 2 |
| 4 | Decode bytes_base64 as standard (non-url-safe) base64 and write to disk using the returned filename and media_type. For oversized responses, follow the returned MCP resource URI instead of expecting an inline blob. |
Decoded file opens correctly in the target application (Excel/LibreOffice for xlsx, a PDF reader for pdf, etc.) |
| 5 | Open the rendered file and confirm the embedded metadata block (tool_id, execution_hash, chaingraph_version, compute_mode, keyid) is present and matches the source artifact. Follow the embedded QR / verification link back to the canonical JSON. | Visual inspection of the Decision sheet (xlsx) or footer (pdf); QR / link resolves to the verification endpoint for the matching execution_hash |
| 6 | If verifying provenance, never trust the rendered file's numbers in isolation — re-fetch or recompute the canonical JSON artifact and confirm execution_hash independently. Treat the export strictly as a view. |
Recompute execution_hash from the canonical artifact's policy_parameters + output_payload per the §2 hash preimage algorithm; confirm it matches the value embedded in the exported file |
Export Profiles require no spreadsheet SDK on the consumer side beyond a standard reader, no special runtime, and no changes to the underlying OCG tool implementations. The Worker-side generator is a pure function with no external npm dependencies beyond a minimal Workers-compatible OOXML writer. Consumers need only the ability to decode base64 and open a standard file format — any HTTP client, any MCP-aware agent, any spreadsheet or PDF application will work. The export layer is an additive last-mile convenience: the entire OCG suite functions identically whether any export profile exists or not.