OpenChainGraph v0.4 · Compute Binding

Integration Guide

How to implement a conformant OpenChainGraph tool, emit a verifiable artifact, self-certify using the conformance suite, and display the Powered by OpenChainGraph badge.

Contents
  1. What is OpenChainGraph v0.4?
  2. Artifact envelope schema
  3. Computing the execution hash
  4. Self-certification with the conformance suite
  5. Badge
  6. Worked example — my-kyc-checker
  7. Further reading
§ 1

What is OpenChainGraph v0.4?

OpenChainGraph is an open standard for verifiable, composable decision artifacts produced by fintech tools. Every conformant tool takes a policy_parameters object as input, runs a deterministic pure-function kernel, and emits a JSON artifact that includes a cryptographic execution_hash committing to both the inputs and the output.

Version 0.4 adds Compute Binding: tools can run server-side (via the MCP Apps server at mcp.ainumbers.co/mcp) and return their full artifact — including the hash — directly to an AI agent. The agent, a regulator, or any third party can then verify the hash client-side using the public canonicalization algorithm without trusting any intermediary.

The standard is maintained by Post Oak Labs and published at ainumbers.co/chaingraph/openchain-graph-spec.html. All reference kernels are open-source under CC BY 4.0.

§ 2

Artifact envelope schema

Every OpenChainGraph artifact must include these top-level fields:

FieldTypeReq?Description
@contextstringrequiredJSON-LD context URI — use https://ainumbers.co/chaingraph/context/v0.3/context.jsonld
chaingraph_versionstringrequiredSpec version — "0.4.0"
tool_idstringrequiredStable slug identifying the tool (e.g. "art-09-dora-incident-classifier")
mandate_typestringrequiredSemantic type tag (e.g. "infrastructure_mandate", "payment_mandate", "compliance_mandate")
policy_parametersobjectrequiredThe exact inputs passed to compute(). Part of the hash preimage.
output_payloadobjectrequiredThe exact outputs returned by compute(). Part of the hash preimage.
execution_hashstringrequiredSHA-256 of the canonical preimage. Format: "sha256:<64-hex-chars>"
chainobjectrequired{ parent_hashes: string[], parent_tool_ids: string[], chain_depth: number }
generated_atstringoptionalISO 8601 timestamp of artifact generation (not part of hash preimage)
compute_modestringoptional"server" or "client"
compliance_flagsstring[]optionalMachine-readable compliance outcome codes
tool_versionstringoptionalSemver of the tool kernel at time of emission
audit_signatureobjectoptionalJWS / placeholder for audit attestation (payloadType, payload, signatures)
Hash preimage rule

Only policy_parameters and output_payload are hashed. All other envelope fields (generated_at, chain, audit_signature, etc.) are framing and excluded from the hash preimage — they may be added by the caller without invalidating the hash.

§ 3

Computing the execution hash

The canonicalization algorithm is RFC 8785 (JSON Canonicalization Scheme) over the I-JSON subset. The exact steps:

1
Build preimage object
Wrap inputs and outputs: { policy_parameters, output_payload }
2
Recursively sort object keys
Sort all object keys (at every nesting level) in ascending Unicode code-point order. Preserve array element order.
3
Emit minimal-whitespace JSON
Call JSON.stringify(canonicalized) with no spacing arguments. This produces the canonical preimage string.
4
SHA-256 → lowercase hex
UTF-8 encode the preimage string, compute SHA-256, and hex-encode to 64 lowercase characters. Prefix with "sha256:" in the artifact.
JavaScript implementation (copy-paste ready — identical to kernels/_hash.mjs)
// Recursively sort object keys (RFC 8785 / JCS — I-JSON subset)
function cgCanon(v) {
  if (Array.isArray(v)) return v.map(cgCanon);
  if (v && typeof v === 'object')
    return Object.keys(v).sort().reduce((o, k) => { o[k] = cgCanon(v[k]); return o; }, {});
  return v;
}

// The canonical string that gets hashed
function canonicalPreimage(policy_parameters, output_payload) {
  return JSON.stringify(cgCanon({ policy_parameters, output_payload }));
}

// Returns lowercase hex (64 chars) — no prefix
async function executionHash(policy_parameters, output_payload) {
  const bytes = new TextEncoder().encode(canonicalPreimage(policy_parameters, output_payload));
  const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes);
  return Array.from(new Uint8Array(digest))
    .map(b => b.toString(16).padStart(2, '0')).join('');
}

// Usage
const hash = await executionHash(policy_parameters, output_payload);
artifact.execution_hash = `sha256:${hash}`;

This function is available in all environments that expose globalThis.crypto.subtle (browsers, Cloudflare Workers, Node 18+). The reference implementation lives at chaingraph/kernels/_hash.mjs in the AINumbers repo (CC BY 4.0).

§ 4

Self-certification with the conformance suite

The AINumbers conformance suite lives at chaingraph/conformance/ and provides three reference test vectors (art-01, art-09, art-34) plus a Node.js runner. To self-certify your own tool:

1
Verify parity on reference vectors
Run node chaingraph/conformance/run.mjs. All three reference fixtures must pass — this proves your hash implementation is compatible with the standard.
2
Write a fixture for your tool
Create conformance/vectors/your-tool-id.fixture.json following the fixture schema (see conformance/README.md). Set expected_execution_hash to "sha256:COMPUTE_ON_FIRST_RUN".
3
Verify output, then pin the hash
Run node run.mjs to check your expected_output_payload matches kernel output. Then run node run.mjs --update to pin the real hash into the fixture file.
4
Verify artifacts in the browser
Paste any artifact your tool emits into verify.html. The page recomputes the hash client-side and reports PASS or MISMATCH.
§ 5

Badge

Once your tool emits conformant artifacts, display the badge in your README or documentation:

Powered by OpenChainGraph
<img src="https://ainumbers.co/chaingraph/badge-ocg.svg" alt="Powered by OpenChainGraph" width="180" height="20">

Or link the badge to the integration guide: <a href="https://ainumbers.co/chaingraph/ocg-integration-guide.html"><img src="https://ainumbers.co/chaingraph/badge-ocg.svg" …></a>

§ 6

Worked example — my-kyc-checker

Suppose you have a KYC screening tool with tool_id my-kyc-checker. Its compute() function takes a customer risk profile and returns a risk tier.

Example kernel (simplified)
// my-kyc-checker.kernel.mjs
export function compute(pp) {
  const score = (pp.pep ? 30 : 0) + (pp.sanctions_hit ? 50 : 0)
              + (pp.high_risk_jurisdiction ? 20 : 0);
  const tier = score >= 50 ? 'high' : score >= 20 ? 'medium' : 'low';
  return {
    output_payload: { risk_score: score, risk_tier: tier },
    compliance_flags: [`KYC_TIER_${tier.toUpperCase()}`],
  };
}
Emitting a conformant artifact
import { compute } from './my-kyc-checker.kernel.mjs';
import { executionHash } from './chaingraph/kernels/_hash.mjs';

const pp = {
  entity_name: 'ACME Corp',
  pep: false,
  sanctions_hit: false,
  high_risk_jurisdiction: true,
};

const { output_payload, compliance_flags } = compute(pp);
const hash = await executionHash(pp, output_payload);

const artifact = {
  '@context':         'https://ainumbers.co/chaingraph/context/v0.3/context.jsonld',
  chaingraph_version: '0.4.0',
  tool_id:           'my-kyc-checker',
  mandate_type:      'compliance_mandate',
  generated_at:      new Date().toISOString(),
  execution_hash:    `sha256:${hash}`,
  chain:             { parent_hashes: [], parent_tool_ids: [], chain_depth: 0 },
  policy_parameters: pp,
  output_payload,
  compliance_flags,
};
Resulting artifact (for the inputs above)
{
  "@context":         "https://ainumbers.co/chaingraph/context/v0.3/context.jsonld",
  "chaingraph_version": "0.4.0",
  "tool_id":           "my-kyc-checker",
  "mandate_type":      "compliance_mandate",
  "generated_at":      "2026-06-19T12:00:00.000Z",
  "execution_hash":    "sha256:<64-hex-chars>",
  "chain":             { "parent_hashes": [], "parent_tool_ids": [], "chain_depth": 0 },
  "policy_parameters": {
    "entity_name": "ACME Corp", "pep": false,
    "sanctions_hit": false, "high_risk_jurisdiction": true
  },
  "output_payload":   { "risk_score": 20, "risk_tier": "medium" },
  "compliance_flags": ["KYC_TIER_MEDIUM"]
}

Paste this artifact into verify.html to confirm the hash verifies. Replace "sha256:<64-hex-chars>" with the actual computed value before verifying.