Cryptographic provenance for AI-agent financial decisions. Where v0.2 aligned the provenance envelope with general-purpose standards (W3C PROV-DM, in-toto, Ed25519, OpenTelemetry, DCAT 3.0), v0.3 aligns the financial payload and the knowledge layer: an optional ISO 20022-aligned semantic profile, optional LEI / did:key party identity, and an Open Knowledge Format (OKF) companion bundle generated from the Graph Index. v0.3.1 formalizes profile conformance: profiles are now published as dereferenceable URIs declared via dct:conformsTo (W3C PROF / Content Negotiation by Profile), with semantic_profile retained as a registered token alias, and a new iso20022:party-identification profile (REDA/ACMT-aligned) for party-only payloads. Every v0.3.1 addition is optional and additive: all v0.1/v0.2/v0.3 artifacts remain valid, the hash preimage is unchanged, and internal protocol identifiers are preserved. v0.4 adds Compute Binding (§12) (server-side kernels make every gpu:false node agent-native over MCP in a single round-trip) and Export Profiles (§13): the chaingraph_export umbrella (xlsx / pdf / csv / xbrl) renders a verified artifact for human, pipeline, and regulator consumption, generated downstream of and excluded from the hash preimage. v0.4.1 adds the Verifiable Credentials profile (§13.11): chaingraph_export:vc renders any verified artifact as a W3C Verifiable Credentials 2.0 credential that re-states the canonical execution_hash via ocg:hashAnchor; it is a base profile (every node), mints no new hash or proof, and leaves the artifact envelope unchanged.
"MUST", "MUST NOT", "SHOULD", and "MAY" are used per RFC 2119. An implementation is v0.4-conformant if and only if it (a) emits artifacts that validate against openchain-graph-v0.4.schema.json, and (b) passes every gate in §15. Conformance is defined by the schema plus the gates, not by prose alone.
The complete artifact envelope at v0.4. Fields marked new are additions in v0.3 (§8–§9); the v0.2 envelope fields (@context, buildType, the in-toto audit_signature) are retained, and all v0.1 and v0.2 artifacts remain valid; implementations may omit any optional field.
{
"@context": "https://ainumbers.co/chaingraph/context/v0.3/context.jsonld", // JSON-LD context (§3); ISO 20022 tools use an array adding iso20022-context.jsonld (§8)
"chaingraph_version": "0.4.0",
"compute_mode": "server", // NEW in v0.4 — "server" | "browser" (§12); excluded from hash preimage
"buildType": "https://ainumbers.co/chaingraph/context/v0.2#WebCryptoSHA256", // version-independent algorithm URI — unchanged in v0.4 (§4)
"mandate_id": "uuid-v4-string",
"tool_id": "ain:art-01",
"mandate_type": "ap2_mandate_chain_record",
"semantic_profile": "iso20022:pacs.008-subset", // NEW in v0.3 — optional ISO 20022 alignment (§8)
"execution_hash": "sha256:3f9a1b2c8d7e4f0a5c6b2e1d9f3a7c4b8f1e5d2a9c0b6f3e7d4a1b8c5e2f9d0",
"supersedes": ["sha256:…"], // NEW in v0.7 — optional: execution_hashes this artifact corrects or replaces; declared at creation, no reverse link, no status registry
"anchor_bindings": […], // NEW in v0.7 — optional §20 anchor evidence; attached AFTER hashing, excluded from execution_hash scope
"chain": {
"parent_hashes": ["sha256:abc123..."],
"parent_tool_ids": ["ain:art-00"],
"chain_depth": 1
},
"policy_parameters": { /* tool-specific inputs */ },
"output_payload": { /* tool-specific outputs */ },
"compliance_flags": [],
"audit_signature": { // UPDATED — now in-toto envelope (§4)
"payloadType": "application/vnd.openchain.graph+json;version=0.2",
"payload": "<base64url(canonical_json_of_artifact_without_this_field)>",
"signatures": [
{
"keyid": "sha256:<fingerprint_of_ed25519_public_key>",
"sig": "<base64url(ed25519_signature_over_payload)>"
}
]
}
}
A v0.1 artifact (no @context, no buildType, audit_signature as a bare string or empty) is still valid. Verifiers MUST accept both. The buildType URI defaults to https://ainumbers.co/chaingraph/context/v0.2#WebCryptoSHA256 if absent.
An artifact carries several version numbers and, by design, none of them is the headline release number. OCG uses layered, decoupled version identifiers, following the in-toto / SLSA convention where additive changes do not move the wire identifier (a SLSA predicateType URI resolves to the latest minor version and only changes on a breaking revision). Read each one for what it answers:
| Identifier | Answers | Current | Bumps when |
|---|---|---|---|
spec_version (catalog) | which OCG release | 0.8.0 | every release |
chaingraph_version (artifact) | which schema parses it | 0.4.0 (frozen) | only a breaking envelope change |
@context URL | which JSON-LD vocabulary | v0.3 | only on a vocabulary change |
payloadType version | which signing-envelope shell | 0.2 | only on a DSSE shell change |
The unifying rule: every additive layer (the vc export of §13.11, and the §16 proof, §17 build_identity, and §18 compute_proof attestations) is hash-excluded and rides under audit_signature, so it never moves chaingraph_version: a v0.6 artifact still validates under the frozen v0.4 schema. chaingraph_version is the schema version ("parse me with this"), not the standard release (which is spec_version). Renaming chaingraph_version to a clearer format_version or a self-describing type URI would itself be a breaking envelope change (the envelope root is additionalProperties:false), so it is reserved for a future major revision.
The canonical preimage for execution_hash is deterministic and reproducible by any party. No timestamps, nonces, or process-specific state may enter the preimage.
preimage = {
"output_payload": artifact.output_payload,
"policy_parameters": artifact.policy_parameters
}
// 1. Serialize with keys sorted lexicographically (deep sort — all nested objects)
canonical_json = JSON.stringify(preimage, sorted_replacer)
// 2. Encode as UTF-8 bytes
bytes = TextEncoder().encode(canonical_json)
// 3. SHA-256 via WebCrypto (or any compliant SHA-256 implementation)
hash_buffer = await crypto.subtle.digest("SHA-256", bytes)
// 4. Hex-encode
execution_hash = "sha256:" + hex(hash_buffer)
Only policy_parameters and output_payload enter the preimage. Fields like mandate_id, tool_id, chaingraph_version, and chain are excluded: they describe the artifact but are not part of the decision being fingerprinted. Any party can re-run this with the recorded inputs and confirm the hash.
The recommended internal taxonomy for mandate_type: prompt_template, payment_mandate, payment_policy, compliance_mandate, liquidity_mandate, capital_assessment, risk_control, settlement_mandate, infrastructure_mandate, credit_assessment, treasury_mandate, account_mandate, model_governance, attestation_mandate, cryptographic_mandate, aml_rule, risk_parameter. This is an internal AINumbers taxonomy, not AP2 v0.2 vocabulary.
The schema treats mandate_type as a non-empty string, not a hard enum: shipped nodes also use agent_guardrail_mandate (fit diagnostics) and scheme_rule, and the enum is not CI-enforced. New tools SHOULD prefer a type from the recommended taxonomy where one fits.
Publishing a JSON-LD context makes OpenChainGraph artifacts interpretable by any system that speaks W3C PROV (regulatory tooling, DORA/Basel/EU AI Act compliance platforms, semantic web processors). The mapping is additive; existing tools need not change their output format.
https://ainumbers.co/chaingraph/context/v0.2/context.jsonld{
"@context": {
"@vocab": "https://ainumbers.co/chaingraph/context/v0.2#",
"prov": "http://www.w3.org/ns/prov#",
"dcat": "http://www.w3.org/ns/dcat#",
"schema": "https://schema.org/",
// Tool = prov:Agent that performed the computation
"tool_id": { "@id": "prov:wasAttributedTo" },
// policy_parameters = prov:Entity (the input data consumed)
"policy_parameters": { "@id": "prov:used" },
// output_payload = prov:Entity generated by the activity
"output_payload": { "@id": "prov:wasGeneratedBy" },
// chain.parent_hashes = set of prov:wasDerivedFrom relations
"parent_hashes": { "@id": "prov:wasDerivedFrom", "@container": "@list" },
// execution_hash = prov:hadPrimarySource (the content-addressed identifier)
"execution_hash": { "@id": "prov:hadPrimarySource" },
// mandate_type = schema:additionalType (semantic classification)
"mandate_type": { "@id": "schema:additionalType" },
// chain_depth = schema:position (ordinal distance from root)
"chain_depth": { "@id": "schema:position", "@type": "xsd:integer" }
}
}
| OpenChainGraph Field | W3C PROV-DM Concept | Notes |
|---|---|---|
| artifact (whole) | prov:Entity | The decision artifact is a provenance entity identified by its execution_hash |
tool_id | prov:Agent | The software tool that performed the computation |
| tool computation | prov:Activity | The deterministic computation that produced the output_payload from policy_parameters |
policy_parameters | prov:used (Entity) | Input data consumed by the activity |
output_payload | prov:wasGeneratedBy | Output entity produced by the activity |
chain.parent_hashes[n] | prov:wasDerivedFrom | Each parent hash identifies a predecessor entity in the provenance graph |
execution_hash | prov:hadPrimarySource | Content-addressed identifier: functions as the PROV entity IRI |
DORA ICT risk assessments, Basel RWA decision chains, and EU AI Act Art. 12/14 audit trails all increasingly reference W3C PROV vocabulary. An OpenChainGraph artifact with a @context is machine-readable by every existing PROV processor without modification.
The audit_signature field is upgraded from an opaque string to a structured in-toto Attestation Framework envelope (ITE-6 compatible). This enables independent signature verification using any in-toto-compliant verifier, and future integration with Sigstore and Cosign.
The buildType URI identifies the specific hash construction algorithm used for the execution_hash. The currently defined values:
| buildType URI | Algorithm |
|---|---|
https://ainumbers.co/chaingraph/context/v0.2#WebCryptoSHA256 | WebCrypto crypto.subtle.digest("SHA-256") over sorted-key JSON: the default |
https://ainumbers.co/chaingraph/context/v0.2#NodeCryptoSHA256 | Node.js crypto.createHash('sha256') over the same canonical preimage: server-side variant |
// 1. Build a copy of the artifact without the audit_signature field artifact_for_signing = omit(artifact, "audit_signature") // 2. Serialize canonically (sorted keys, no extra whitespace) canonical_json = JSON.stringify(artifact_for_signing, sorted_replacer) // 3. Base64url-encode the UTF-8 bytes — this is the "payload" payload = base64url(TextEncoder().encode(canonical_json)) // 4. Sign (see §5 for Ed25519 details) sig = base64url(ed25519.sign(private_key, TextEncoder().encode(payload))) // 5. Embed the envelope artifact.audit_signature = { payloadType: "application/vnd.openchain.graph+json;version=0.2", payload: payload, signatures: [{ keyid: key_fingerprint, sig: sig }] }
// 1. Extract payload and signature from the artifact { payload, signatures } = artifact.audit_signature { keyid, sig } = signatures[0] // 2. Look up the vendor's public key from the Graph Index (§7) public_key = graph_index.tools .find(t => t.tool_id === artifact.tool_id) .signing_keys .find(k => k.keyid === keyid) .public_key // 3. Verify Ed25519 signature is_valid = ed25519.verify( public_key, TextEncoder().encode(payload), // message = the payload bytes base64url_decode(sig) ) // 4. Separately, verify the execution_hash (as in v0.1) artifact_body = base64url_decode(payload) recomputed_hash = sha256(canonical_preimage(artifact_body)) hash_matches = ("sha256:" + recomputed_hash) === artifact.execution_hash
The execution_hash provides integrity: it proves the artifact hasn't been modified since it was created. The Ed25519 signature provides authenticity: it proves the specific tool vendor produced this artifact. Both checks should be run. An artifact that passes hash verification but fails signature verification could be a replay of a legitimate artifact with a different mandate_id.
OpenChainGraph specifies Ed25519 (RFC 8032) as the signing algorithm. Ed25519 is compact (32-byte keys, 64-byte signatures), fast, and the algorithm mandated by SLSA L2+ provenance attestations.
// Generate key pair (store private key in a secure enclave or HSM) const { publicKey, privateKey } = await crypto.subtle.generateKey( { name: "Ed25519" }, true, // extractable (for export) ["sign", "verify"] ) // Export public key as raw bytes → hex → publish in Graph Index const raw = await crypto.subtle.exportKey("raw", publicKey) const pub_hex = Array.from(new Uint8Array(raw)) .map(b => b.toString(16).padStart(2, '0')).join('') // Key fingerprint = sha256(raw_public_key_bytes), hex-encoded const fp_buf = await crypto.subtle.digest("SHA-256", raw) const keyid = "sha256:" + Array.from(new Uint8Array(fp_buf)) .map(b => b.toString(16).padStart(2, '0')).join('')
// payload = base64url string (from §4 step 3)
const message = TextEncoder().encode(payload)
const sig_bytes = await crypto.subtle.sign("Ed25519", privateKey, message)
const sig = base64url(sig_bytes)
// Import vendor's public key from Graph Index (raw format)
const pub_raw = hex_to_bytes(graph_index_public_key_hex)
const publicKey = await crypto.subtle.importKey(
"raw", pub_raw, { name: "Ed25519" }, false, ["verify"]
)
const is_valid = await crypto.subtle.verify(
"Ed25519",
publicKey,
base64url_decode(audit_signature.signatures[0].sig),
TextEncoder().encode(audit_signature.payload)
)
Vendors SHOULD publish key rotation events in their Graph Index with a valid_until timestamp on old keys and a valid_from on the new key. Verifiers MUST use the key that was active at the time the artifact was produced (recorded in the artifact's creation context if needed). Historic artifacts signed under a rotated key remain valid if the old key was not revoked.
An OpenChainGraph artifact can be exported as an OpenTelemetry span, making decision chains visible in any OTel-compatible APM stack (Datadog, Grafana Tempo, Jaeger, Honeycomb) without new infrastructure. The mapping is read-only; OTel data flows from OpenChainGraph, never the reverse.
| OTel Span Field | ← | OpenChainGraph Field | Notes |
|---|---|---|---|
traceId | ← | Chain root execution_hash | First 16 bytes (32 hex chars) of the depth-0 ancestor's hash |
spanId | ← | execution_hash | First 8 bytes (16 hex chars) of this artifact's hash |
parentSpanId | ← | chain.parent_hashes[0] | First 8 bytes. For multi-parent nodes, emit one span per parent or use the primary parent |
name | ← | tool_id | e.g. ain:art-01 |
kind | ← | – | Always INTERNAL |
attributes["ocg.mandate_type"] | ← | mandate_type | String attribute |
attributes["ocg.mandate_id"] | ← | mandate_id | String attribute |
attributes["ocg.chain_depth"] | ← | chain.chain_depth | Integer attribute |
attributes["ocg.chaingraph_version"] | ← | chaingraph_version | String attribute |
attributes["ocg.buildType"] | ← | buildType | String attribute (URI) |
startTimeUnixNano | ← | – | Set at emission time: not stored in artifact (would break hash reproducibility) |
endTimeUnixNano | ← | – | Same as startTimeUnixNano for instantaneous decisions |
status.code | ← | compliance_flags | OK if empty; ERROR if any flag has severity ERROR |
function artifactToOtelSpan(artifact, rootHash, emitTimeNs) {
const hexSlice = (h, bytes) => h.replace('sha256:','').slice(0, bytes*2)
return {
traceId: hexSlice(rootHash, 16),
spanId: hexSlice(artifact.execution_hash, 8),
parentSpanId: artifact.chain.parent_hashes[0]
? hexSlice(artifact.chain.parent_hashes[0], 8)
: undefined,
name: artifact.tool_id,
kind: 1, // INTERNAL
startTimeUnixNano: emitTimeNs,
endTimeUnixNano: emitTimeNs,
attributes: [
{ key: "ocg.mandate_type", value: { stringValue: artifact.mandate_type } },
{ key: "ocg.mandate_id", value: { stringValue: artifact.mandate_id } },
{ key: "ocg.chain_depth", value: { intValue: artifact.chain.chain_depth } },
{ key: "ocg.chaingraph_version", value: { stringValue: artifact.chaingraph_version } },
{ key: "ocg.buildType", value: { stringValue: artifact.buildType } },
],
status: {
code: artifact.compliance_flags?.some(f => f.severity === 'ERROR') ? 2 : 1
}
}
}
The Graph Index (chaingraph.json) is the machine-discoverable catalog of a vendor's OpenChainGraph-conformant tools. v0.2 expresses it as a W3C DCAT 3.0 catalog, making it crawlable by data portals, regulatory reporting platforms, and semantic web processors, without removing any existing fields.
{
"@context": {
"dcat": "http://www.w3.org/ns/dcat#",
"ocg": "https://ainumbers.co/chaingraph/context/v0.2#",
"prov": "http://www.w3.org/ns/prov#",
"dct": "http://purl.org/dc/terms/"
},
"@type": "dcat:Catalog",
"@id": "https://ainumbers.co/chaingraph/chaingraph.json",
"dct:title": "AINumbers OpenChainGraph Suite",
"dct:description": "48 Policy-Mandate-emitting, hash-anchored financial decision tools.",
"dct:publisher": { "@id": "https://postoaklabs.com", "dct:title": "Post Oak Labs" },
"ocg:conformance_level": "L4",
"ocg:mcp_endpoint": "https://mcp.ainumbers.co/mcp",
"dcat:dataset": [
{
"@type": ["dcat:Dataset", "ocg:DecisionTool"],
"@id": "ain:art-01",
"dct:title": "AP2 Mandate Chain Validator",
"dct:description": "Validates AP2 mandate chain integrity with execution_hash.",
"ocg:mandate_type": "ap2_mandate_chain_record",
"ocg:consumes": [],
"ocg:feeds": ["ain:art-02", "ain:art-17"],
"ocg:mcp_tool_name": "validate_ap2_mandate_chain",
// Ed25519 signing key published here for artifact verification
"ocg:signing_keys": [
{
"keyid": "sha256:<key_fingerprint>",
"public_key": "<hex-encoded Ed25519 public key>",
"valid_from": "2026-01-01"
}
],
// DCAT access service — MCP endpoint for this specific tool
"dcat:accessService": {
"@type": "dcat:DataService",
"dcat:endpointURL": "https://mcp.ainumbers.co/mcp",
"dct:conformsTo": "https://modelcontextprotocol.io/spec/2025-11-05"
}
}
// ... one entry per tool ...
]
}
The ocg:consumes and ocg:feeds edges are the machine-readable map agents use to plan a full chain before calling anything. No existing standard provides this. DCAT gives the catalog a standard container and makes it crawlable by data portals; the ocg: namespace terms are what make it actionable by agents.
v0.2 gave the provenance envelope a semantic standard (W3C PROV-DM). v0.3 gives the financial payload one. The ISO 20022 semantic profile is an optional, additive overlay that maps a curated subset of payload fields to ISO 20022 business elements and adopts the ISO external code lists by reference. It is minimal by design (amount, party, and agent fields only) and it never enters the execution_hash preimage (§2 is unchanged).
OpenChainGraph is ISO 20022-aligned, not a registered ISO 20022 message. ISO 20022 is governed by the ISO Registration Authority / SWIFT. The iso20022: terms below are a crosswalk named after ISO 20022 business elements (ISO 20022 publishes no official JSON-LD vocabulary). Tools do not generate or transmit live ISO 20022 messages: they emit decision artifacts whose financial fields are interpretable by ISO 20022-native systems.
https://ainumbers.co/chaingraph/context/v0.3/iso20022-context.jsonld{
"@context": [
"https://ainumbers.co/chaingraph/context/v0.3/context.jsonld",
"https://ainumbers.co/chaingraph/context/v0.3/iso20022-context.jsonld"
],
"chaingraph_version": "0.3.0",
"tool_id": "ain:rca-03",
"mandate_type": "compliance_mandate",
"output_payload": {
"instructed_amount": { "amount": "15000.00", "currency": "EUR" }, // ActiveCurrencyAndAmount · Ccy=ISO 4217
"debtor": { "party_name": "ACME SA", "lei": "529900T8BM49AURSDO55" }, // ISO 17442
"creditor": { "party_name": "Beta GmbH", "lei": "391200X3K8N4WLB9AB12" },
"debtor_agent": { "bicfi": "DEUTDEFFXXX" }, // BICFI · ISO 9362
"creditor_agent": { "bicfi": "BNPAFRPPXXX" },
"settlement_date": "2026-11-23", // IntrBkSttlmDt · ISO 8601
"remittance_information": "INV-2026-0042" // RmtInf
}
}
| OCG payload field | ISO 20022 business element | External code set |
|---|---|---|
amount | ActiveCurrencyAndAmount | – |
currency | Ccy / CurrencyCode | ISO 4217 |
country | CountryCode | ISO 3166-1 alpha-2 |
debtor / creditor | Debtor / Creditor (PartyIdentification) | – |
lei | OrganisationIdentification / LEI | ISO 17442 |
debtor_agent / creditor_agent | DebtorAgent / CreditorAgent (FinInstnId) | – |
bicfi | BICFI | ISO 9362 |
iban | IBAN (CashAccount) | ISO 13616 |
settlement_date | IntrBkSttlmDt | ISO 8601 |
remittance_information | RmtInf | – |
RCA-03 (verify_address_migration_batch) already validates ISO 20022 pacs.008 PostalAddress24 structured addresses for the SWIFT CBPR+ structured-address mandate (Nov 2026). The profile formalizes that usage into a declared crosswalk. A full practitioner walkthrough, code-list validators (ISO 4217/3166/9362/13616/17442) and a pacs.008 crosswalk, is in the ISO 20022 integration guide.
v0.3 formalizes two optional identity anchors that already appeared informally in the v0.2 guides. Both are optional; neither changes the hash preimage.
The in-toto signature envelope's keyid (§4) SHOULD be a did:key identifier: a multicodec-prefixed, base58btc encoding of the raw Ed25519 public key (the z6Mk… form). This makes the signer self-describing and resolvable without a separate key registry, while remaining compatible with the Graph Index signing_keys lookup (§7).
A publisher or tool MAY carry an ISO 17442 Legal Entity Identifier. This binds an OpenChainGraph vendor (and, where relevant, the signing key) to a regulator-grade legal-entity anchor that banks, PSPs, and supervisors already consume. The LEI is the natural identity bridge between the §8 ISO 20022 profile (where debtor/creditor parties carry LEIs) and the §4/§5 signing identity.
{
"publisher": {
"name": "Post Oak Labs",
"lei": "<ISO 17442 LEI, if registered>" // optional
},
"audit_signature": {
"signatures": [
{ "keyid": "did:key:z6MkrJVnaZkeFzdQyMZu1cgjaeW7MoFaNKg4tLCf4g3kEhWh", "sig": "…" }
]
}
}
v0.3 publishes an Open Knowledge Format (OKF) companion bundle (a directory of markdown concept files, one per Graph Index node) auto-generated from chaingraph.json. It is the narrative knowledge layer that complements the machine-readable DCAT catalog (§7): DCAT is the catalog an agent plans a chain from; OKF is the curated context an agent or human reads to learn what a tool means and when to use it.
OKF = context-in (knowledge read before acting). OpenChainGraph = provenance-out (the decision artifact produced and proven after acting). An OKF concept document is knowledge, never a decision artifact: it MUST NOT carry an execution_hash or audit_signature. The two are kept strictly separate.
okf/ ├── index.md # root · progressive disclosure ├── log.md # generation history ├── mandate-types/ │ ├── index.md │ └── <mandate_type>.md └── tools/ ├── index.md └── <tool_id>.md # one concept per live node; consumes/feeds → markdown links
Each concept carries OKF YAML frontmatter (type, title, description, resource, tags, timestamp); the node's consumes/feeds edges become markdown links between concepts, reproducing the graph. The bundle is regenerated on every Graph Index change and is fully reversible: nothing in OpenChainGraph's verification path depends on it. The generator and consumer walkthrough are in the OKF integration guide.
OKF is a v0.1 specification (published June 2026). It is adopted here as a companion artifact only. Tie nothing in artifact verification to it; treat the bundle as a generated, swappable discovery surface.
v0.3 introduced ISO 20022 alignment via an inline semantic_profile string token. v0.3.1 makes that conformance a first-class, machine-resolvable assertion using the W3C Profiles Vocabulary (PROF) and Content Negotiation by Profile: a profile is a dct:Standard published at a dereferenceable URI and declared with dct:conformsTo. This is fully additive: the execution_hash preimage is unchanged and semantic_profile tokens stay valid as registered aliases.
Every node declares conformance the same way (dct:conformsTo), but only declares what it truthfully conforms to. ISO 20022 payment/party profiles are applied solely to nodes with the matching payload; a VaR engine or DORA classifier carries no ISO 20022 profile. Forcing a payment profile onto a non-payment node would be a false conformance claim, which this design avoids.
| Token (alias) | Profile URI (dereferenceable) | Applies to |
|---|---|---|
iso20022:pacs.008-subset | https://ainumbers.co/chaingraph/profiles/iso20022/pacs008-subset.jsonld | Payment/settlement nodes (amount + parties + agents): e.g. x402 settlement, VoP, ISO 20022 address. |
iso20022:party-identification new | https://ainumbers.co/chaingraph/profiles/iso20022/party-identification.jsonld | Party/issuer/legal-entity identity without a transaction (party_name + lei). Named after the ISO 20022 PartyIdentification element used in the REDA/ACMT families. |
The authoritative token↔URI map is published at chaingraph-standard/profiles/registry.json (Content-Negotiation-by-Profile permits a short token alongside the mandatory URI). Each profile URI dereferences to a PROF prof:Profile document listing its schema (the v0.3 ISO 20022 context) and guidance resources.
{
"@context": [
"https://ainumbers.co/chaingraph/context/v0.3/context.jsonld",
"https://ainumbers.co/chaingraph/context/v0.3/iso20022-context.jsonld"
],
"chaingraph_version": "0.3.1",
"ocg:semantic_profile": "iso20022:pacs.008-subset", // token alias (retained)
"dct:conformsTo": ["https://ainumbers.co/chaingraph/profiles/iso20022/pacs008-subset.jsonld"] // authoritative, dereferenceable
}
@context, ocg:semantic_profile, and dct:conformsTo are all outside the execution_hash preimage (sorted-key SHA-256 over {policy_parameters, output_payload}). A verifier correct for v0.1/v0.2/v0.3 computes identical hashes for v0.3.1 artifacts. See the profile-conformance guide.
OpenChainGraph v0.4 introduces Compute Binding: the mechanism by which an MCP tool computes a node's decision function server-side and returns a fully verifiable v0.4 artifact in a single round-trip, instead of delegating to the browser. This makes every gpu:false node with a registered kernel agent-native: an AI agent can call the tool, receive the structured output_payload and execution_hash, and immediately chain to downstream tools without requiring a browser session.
Every live node in chaingraph.json carries a compute_capability field that declares how the Worker will dispatch it:
{
"tool_id": "art-05-eu-ai-act-credit-scoring-conformity",
"gpu": false,
"compute_capability": "server" // "server" | "browser" | "auto"
}
| Value | Condition | Worker behaviour |
|---|---|---|
"server" |
gpu:false + kernel registered in kernels/index.mjs |
Runs the kernel, returns artifact. Falls back to browser delegation if compute:"browser" is passed. |
"browser" |
gpu:true |
Always returns the browser delegation URL + structured metadata. Compute runs client-side. |
"auto" |
gpu:false + no kernel yet |
Behaves like "browser" today; transitions to "server" when a kernel is added: no schema change needed. |
Callers may override dispatch per-call using the compute input parameter on any ChainGraph MCP tool:
{
"compute": "auto" // default — server for gpu:false nodes with kernels
"compute": "server" // force server-side (errors if no kernel)
"compute": "browser" // always return browser delegation URL
}
Nodes with gpu:true ignore compute and always delegate to the browser: the GPU dependency cannot be satisfied server-side. Future nodes may carry a per-node gpu_fallback flag for seeded LCG reduced-path approximation, but this is not part of v0.4.
A kernel is a pure ESM module at repo/chaingraph/kernels/<tool-id>.kernel.mjs exporting three symbols:
// compute(pp) → { output_payload, compliance_flags } // Pure: no DOM, no window, no network, no Date.now(). // policy_parameters is passed verbatim so the execution_hash anchors the full input. export function compute(policy_parameters) { … } // buildArtifact(pp, opts) → Promise<v0.4 artifact object> // Calls compute(), then hashes with executionHash() from _hash.mjs. export async function buildArtifact(pp, { now, parent_hashes, parent_tool_ids, chain_depth } = {}) { … } // Static metadata for the Worker registry. export const meta = { tool_id, tool_version, gpu: false, mandate_type };
Kernels are vendored from repo/chaingraph/kernels/ into both mcp-apps-poc/kernels/ (bundled into the Worker by wrangler/esbuild) and mcp-apps-poc/data/kernels/ (served as static assets) by running node generate.mjs before every push.
The zero-egress contract is preserved for all compute modes. Server-side kernels are pure functions: they receive policy_parameters, run deterministic logic, and return output_payload. No policy parameters, outputs, or user data are logged, stored, or transmitted beyond the JSON-RPC response. The only telemetry written is structural metadata (tool name, latency, success/error flag, and a salted one-way hash of the caller IP: never the payload). gpu:true nodes continue to run entirely in the user's browser with zero network egress.
The v0.4 artifact adds two fields to the v0.3.1 envelope:
{
"chaingraph_version": "0.4.0", // bumped from 0.3.x
"compute_mode": "server", // "server" | "browser" — new in v0.4
… // all v0.3 fields retained
}
The hash preimage definition is unchanged: execution_hash is still SHA-256(JCS({ policy_parameters, output_payload })). A v0.4 artifact is verifiable by any v0.3 verifier that ignores unknown fields.
OpenChainGraph v0.4 adds export profiles: generated, non-canonical renderings of an already-verified artifact, produced after execution_hash is computed and excluded from the hash preimage. They exist so the cryptographic anchor stays attached at the last mile (where a human reviewer, a downstream pipeline, or a regulator actually consumes the decision) instead of breaking the provenance chain the moment someone hand-copies output_payload into a spreadsheet, board memo, or filing.
Export profiles follow the exact precedent of the OKF companion bundle (§10): 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 umbrellaEvery export format is a documented sub-profile under one umbrella term, chaingraph_export:<format>:
chaingraph_export:xlsx // analyst / board spreadsheet chaingraph_export:pdf // signed board / audit one-pager chaingraph_export:csv // pipeline-tabular sibling of xlsx chaingraph_export:xbrl // regulator-submission profile (taxonomy per regime) chaingraph_export:vc // W3C Verifiable Credentials 2.0 (base profile, all nodes; new in v0.4.1)
1. Be generated solely from the verified artifact's output_payload (+ optionally policy_parameters): never hand-authored.
2. Embed a metadata block carrying tool_id, execution_hash, chaingraph_version, compute_mode, and (if present) audit_signature.signatures[0].keyid, plus a link/QR to the canonical JSON artifact or its verification endpoint.
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.
4. Be optional and additive: absence of an exporter never invalidates a tool.
5. Be deterministic: same artifact + same exporter version → byte-stable output (static per-mandate_type templates, sorted rows, fixed number formats), so two parties can confirm they hold the same rendering.
Export generation runs in the Worker (the same runtime as the Compute-Binding kernels) as one canonical generator rather than N drifting client implementations. The rationale mirrors §12: agent-native, single round-trip, reproducible. The zero-egress/privacy boundary of §12.4 applies unchanged: exporters are pure functions over an artifact the caller already holds; no payload is logged, stored, or transmitted beyond the JSON-RPC response. (Strict client-side generation remains permissible for an air-gapped deployment using the published exporter spec, but the reference generator is server-side.) Exporters live at repo/chaingraph/exporters/<format>/ (parallel to kernels/), each a pure buildExport(artifact) → Uint8Array, vendored into the Worker by generate.mjs on the same push (same two-repo discipline as kernels).
export_artifact toolA single new read-only MCP tool: not a format parameter on every tool (which would inflate all schemas), nor an out-of-band endpoint (which would split the agent-callable story). This follows MCP best practice: focused, domain-aware tools an agent can reason about, with one tool owning the render concern.
// Tool: export_artifact (readOnlyHint: true) { "artifact": // full v0.4 artifact object (REQUIRED — stateless Worker, no cache) "format": "xlsx" | "pdf" | "csv" | "xbrl" | "vc", "xbrl_taxonomy": "<regime id>" // required only when format = xbrl (§13.8) } // Returns: { "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: call a compute tool → receive JSON artifact + execution_hash → call export_artifact with that artifact → receive the rendered file. Both calls are readOnlyHint:true. For large outputs (big GPU-batch CSVs), the Worker returns an MCP resource (URI-addressable, read-only) instead of an oversized inline blob. The tool rejects any (tool, format) pair not present in the node's export_capability (§13.10): a deterministic, discoverable failure.
chaingraph_export:xlsxWorkbook convention:
Sheet "Decision" // verdict, tool_id, mandate_type, timestamp; // metadata block (execution_hash, chaingraph_version, compute_mode, keyid); QR Sheet "Data"/… // tabular output_payload — percentile tables, batch rows, distributions Sheet "Provenance" // optional: chain.parent_hashes, parent_tool_ids, compliance_flags[]
Generated with a Workers-compatible OOXML writer (pure JS/WASM, no Node fs); a minimal hand-rolled OOXML writer is acceptable given the simple, deterministic structure. Pilot tools: compute_rwa_scenarios, validate_einvoice_batch, simulate_vop_matching, run_liquidity_stress_test: all already tabular in shape.
chaingraph_export:pdfBoard/audit one-pager: verdict headline, key output_payload fields as prose/table, execution_hash + signing keyid in the footer, and a QR encoding the verification URL. Uses a static template per mandate_type (one layout for attestation_mandate, one for compliance_mandate, etc.) so output is reproducible artifact-to-artifact: no freeform layout. Best fit: precheck_reserve_attestation, assess_ai_act_conformity, classify_dora_incident. Server-side deterministic renderer.
chaingraph_export:csvThe pipeline-tabular sibling of xlsx: same extraction, simpler serialization (near-free once xlsx exists). For data-engineering consumers and large GPU-batch outputs (run_liquidity_stress_test 1,000×250, simulate_stablecoin_reserve 1,000×90). One CSV per logical table; a manifest row carries the metadata block. (Parquet is a future columnar option once a concrete pipeline consumer asks, not in v0.4, to avoid Arrow/Parquet Workers-runtime dependency weight.)
chaingraph_export:xbrl (regulator-submission profile)The strategic profile: render artifacts as XBRL/iXBRL so they are submission-ready, not merely analyst-readable. XBRL requires a taxonomy per regulatory regime: there is no single mapping. v0.4 defines the umbrella, the export_artifact interface (xbrl_taxonomy selector), and the initial mapping; each regime's concept mapping is a tracked work item.
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 |
Rule: No fabricated taxonomy concepts: every concept must trace to a published regulator element or an explicitly-OCG extension element. The lightweight default is the OCG extension taxonomy ocg-ext, published at chaingraph/xbrl/ocg-ext/ocg-ext-2026.xsd: a small, self-contained, regime-neutral XBRL 2.1 schema that produces a fully validatable instance (verify with any XBRL processor, e.g. Arelle) with no external taxonomy dependency. Mapping into a regulator taxonomy (e.g. EBA COREP for EU capital & liquidity) is optional and pull-driven: undertaken only when a real submission to that regulator is required, since it is version-coupled and dimensional. Every XBRL export records its source taxonomy + version in-context.
v0.4 extends §10's "two layers" framing to three: provenance-out (JSON) → knowledge-in (OKF) → rendered-for-review (xlsx / pdf / csv / xbrl / vc). All three derive from the same verified artifact; only the JSON layer is canonical and hash-anchored.
export_capability discovery fieldMirroring compute_capability, every live node in chaingraph.json MAY declare which export profiles it supports, so MCP clients/agents discover formats before calling export_artifact:
{
"tool_id": "compute_rwa_scenarios…",
"compute_capability": "server",
"export_capability": ["xlsx", "csv", "xbrl:eba-corep-own-funds"]
}
Absent or [] → JSON-only (still v0.4-valid). xbrl:<taxonomy-id> entries name a specific taxonomy from §13.8. The Graph Index / DCAT surface (§7) gains a discovery signal symmetric with compute_capability. Base profiles (see §13.11 vc) are exempt: available on every node without a declaration.
chaingraph_export:vc (W3C Verifiable Credentials profile) new in v0.4.1Renders a verified artifact as a W3C Verifiable Credentials 2.0 credential (application/vc+json): the standards-conformant interchange shell for agents and counterparties that already speak VC. It is a base profile: available on every node regardless of export_capability, because a lossless structural re-expression of the canonical artifact always exists. Envelope → VC mapping:
{
"@context": ["https://www.w3.org/ns/credentials/v2", "<ocg vc context>"],
"id": "urn:ocg:artifact:<bare-execution-hash>", // deterministic — no UUID/clock
"type": ["VerifiableCredential", "OpenChainGraphCredential"],
"issuer": "<issued_by>",
"validFrom", "validUntil": // from valid_from / valid_until
"credentialSubject": { "mandate_type", "policy_parameters", "output_payload" },
"ocg:hashAnchor": { // re-states the canonical hash — NOT a VC proof suite
"type": "OpenChainGraphHashAnchor2026", "digestMethod": "sha-256",
"executionHash", "verify_url"
}
}
Like every §13 profile, vc is a view, not a fact: it mints no new execution_hash and adds no securing proof: verification routes back to the canonical JSON. A deployer needing a secured VC adds an enveloping JOSE/COSE or Data Integrity proof downstream (out of profile scope; §16 Proof Binding provides exactly this over the canonical artifact). Deterministic: the same artifact renders byte-identical bytes. Because export profiles are not part of the artifact envelope, the envelope and hash preimage are unchanged at v0.4.1: artifacts still emit chaingraph_version:"0.4.0"; only spec_version bumps to 0.4.1.
An implementation MAY export an artifact as an RFC 9901 SD-JWT whose claims map deterministically from the envelope, EXCEPT the disclosure salts, which MUST be freshly CSPRNG-generated per export (the one permitted nondeterminism; it is confined to the export and never touches the envelope or execution_hash). ALWAYS-DISCLOSED (non-selectively-disclosable) claims: execution_hash, chaingraph_version, spec_version, compute_capability, the §17 kernel/build identity fields, all outputs (output_payload), and timestamps. SELECTIVELY DISCLOSABLE: top-level input values only. Signature: JWS (EdDSA) under the §16 signing key.
Normative limitation (MUST be stated by presenting UIs): a redacted export is NOT re-executable and does NOT permit execution_hash recomputation; its verification yields (a) issuer-signature integrity and (b) hash-binding of each disclosed claim. The full envelope remains the artifact of record. Gate: sd-export-roundtrip.test.mjs (§15).
ocg-xbrl-json@1 new in v0.8.12A regulatory-reporting export profile rendering a verified artifact as xBRL-JSON (OIM 1.0, REC 2021-10-13), the JSON serialization of the XBRL Open Information Model. Like every §13 profile it is a view, not a fact: generated after execution_hash, excluded from the hash preimage, mints no new hash, and MUST NOT bump chaingraph_version.
Canonical form: the document MUST set xbrl:canonicalValues: true (OIM's own canonical-lexical-value mode) AND apply JCS (RFC 8785) key ordering on top, since xBRL-JSON itself leaves member order undefined, together making the document byte-identical for byte-identical inputs. execution_hash is embedded in the document's metadata block, never inside a fact.
Taxonomy metadata is INPUT, not derived: concept QNames, dimensions, units, and decimals are supplied as taxonomy metadata (the §13.8 never-fabricate discipline: a concept is emitted only when it maps to a published taxonomy element, or an explicit ocg-ext:* element where none exists). DTS discovery, XDT validation, and formula-linkbase execution are explicitly out of scope.
Normative caveat (MUST be surfaced by presenting UIs): this is NOT a submittable wire format. FFIEC Call Report submission is XBRL 2.1 XML; EBA submission is xBRL-CSV (mandatory from 2026-03); SEC/ESEF submission is iXBRL. This profile's output is a canonical interchange form, lossless via a certified OIM processor (e.g. Arelle, Apache-2.0); an implementation MUST NOT label it "submittable" or "regulator-accepted."
Validation is external, by pointer: this spec vendors no OIM/XBRL validator (mirrors §18.1's stark-verifier delegation). Annex 1, the FFIEC Call Report mapping: concept namespace is the published FFIEC 031/041 taxonomy, item alignment keys off public-domain MDRM item codes (e.g. RCON2170 Total assets, RCON3210 Total equity capital), never a fabricated concept. Annex slots are reserved for EBA DPM 2.0 (xBRL-CSV) and future regimes; an unpopulated slot returns a "pending" error exactly as §13.8's COREP maps do.
Gate: xbrl-json-fixtures.test.mjs (§15): round-trip determinism, canonicalValues conformance, and Annex 1 structural load against the committed fixtures. Fence: this profile ships as spec text + schema + the two fixtures; the exporter implementation (exporters/xbrl-json.mjs, worker wiring) is a separate, later work unit.
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 remains verifiable by any v0.3 verifier that ignores unknown fields.
A node MAY bind authenticity to a verified artifact by attaching an OPTIONAL W3C Data Integrity proof (cryptosuite eddsa-jcs-2022, W3C Recommendation 2025-05) at audit_signature.proof. This turns the §4 execution_hash from tamper-evidence (recompute → matches inputs) into authenticated attestation (a named key vouches for the artifact): filling the §13.11 gap, where the vc view mints no securing proof. Proof Binding is OPTIONAL and holder-chosen; an artifact with no audit_signature.proof is fully v0.5-conformant.
audit_signature.proofThe proof lives at audit_signature.proof: not at artifact root and not inside the DSSE-style audit_signature.signatures[] array. The artifact root is additionalProperties:false in the frozen v0.4 schema, so a root proof would make a signed artifact fail a v0.4 verifier; audit_signature tolerates added properties, so a signed v0.5 artifact still validates under the frozen v0.4 schema. signatures[] keeps its separate DSSE/in-toto meaning (§9).
The secured document is the full artifact with audit_signature.proof removed. Generation follows eddsa-jcs-2022 verbatim:
document = artifact \ { audit_signature.proof }
hashData = SHA-256( JCS(proofOptions) ) ‖ SHA-256( JCS(document) ) // RFC 8785 / JCS
proofValue = "z" + base58btc( Ed25519.sign(privateKey, hashData) ) // multibase
Because execution_hash sits inside the secured document, a whole-artifact signature transitively secures the §4 anchor. The JCS canonicalizer is the same kernels/_hash.mjs cgCanon used by §4: no second canonicalization path (the array-replacer forms FORBIDDEN by §4 stay forbidden). Produced by the shared kernels/_proof.mjs (browser inlines it; Worker imports it; byte-identical). Verification is reproducible offline: recompute execution_hash per §4, then verify proofValue against the verificationMethod key over the same input. The signing key SHOULD be published in the Graph Index ocg:signing_keys (§7).
The hash preimage is unchanged and chaingraph_version stays "0.4.0": only spec_version bumps to 0.5.0. An unsigned artifact is byte-identical to v0.4.1; a signed one still validates under the frozen v0.4 schema (the proof is tolerated under audit_signature).
A §16 proof makes a run linkable to a key, eroding the default zero-PII anonymous posture (a feature for some users). Proof Binding therefore MUST default OFF and MUST NOT be auto-applied; a tool offering it MUST surface that signing de-anonymizes the run. zero_pii_verified semantics are unchanged: inputs stay PII-free; the signer becomes known.
A §16 proof secures the canonical OCG artifact, not the vc re-expression (§13.11). When that artifact is exported as a VC, its ocg:hashAnchor routes a verifier back to the canonical JSON where the proof lives, so the secured artifact stays verifiable through the VC. A deployer needing a proof on the VC document itself re-runs the same eddsa-jcs-2022 pipeline over the VC (a distinct proof, outside §16 scope).
For ephemeral / fully client-side signing a did:key suffices and is the only CONTRACT-compatible option (a private key MUST NOT ship in client HTML or storage). A stable institutional issuer SHOULD anchor to did:web over the publisher's domain with the private key held in an HSM/KMS and signing performed server-side (e.g. the §12 compute path). Both are interoperable verificationMethod values.
audit_signature.proof MAY be an array. A parallel proof set (multiple independent signers over the same artifact) follows VC Data Integrity 1.0 proof-set semantics. An ENDORSEMENT (countersignature approving a prior signature) MUST use proof-chain semantics: the endorsing proof's previousProof references the id(s) of the proof(s) it endorses, and verifiers MUST verify chained proofs in dependency order. No new cryptosuite: eddsa-jcs-2022 throughout. Gate: countersignature fixture in proof-binding.test.mjs (§15).
A node MAY publish, and an artifact MAY record, the content digest of the exact kernel that produced it: closing the §4 gap that execution_hash proves "this output follows from these inputs by some logic" but does not pin which logic ran.
audit_signature.build_identityThe binding lives at audit_signature.build_identity (hash-excluded, like §16: keeps the frozen v0.4 root schema; an artifact without it is byte-identical to v0.5). It MUST carry a kernel_digest (a sha256:-prefixed digest produced by the shared kernels/_buildid.mjs over the kernel's canonical source bytes, UTF-8, LF-normalized, via WebCrypto SHA-256) and a buildType URI; source_ref is optional.
build_identity = {
"kernel_digest": "sha256:…", // SHA-256 over LF-normalized kernel source
"buildType": "…#WebCryptoSHA256",
"source_ref": "kernels/<tool_id>.kernel.mjs" // optional
}
A node SHOULD publish its kernel digest in the Graph Index node field compute_images[] ({ system:"sha256-source", image_id:"sha256:…", valid_from }). A verifier cross-checks three values: artifact.audit_signature.build_identity.kernel_digest == the node's compute_images[].image_id == _buildid.digest(recomputed from source). Any mismatch FAILS the binding.
§17 is an advisory published claim of which kernel source ran: not a cryptographic proof of execution. A dishonest server could record a digest different from the code it actually executed. §17 strengthens tamper-evidence (the digest is in the secured set when §16 also applies) but does not, by itself, prove the named kernel produced the output: that is §18. It mints no new execution_hash and does not bump chaingraph_version (stays "0.4.0").
A node MAY attach an OPTIONAL zkVM compute-integrity proof at audit_signature.compute_proof, turning the §4 hash from re-execute-to-verify (a verifier must re-run the kernel with cleartext inputs) into a succinct proof of correct execution a verifier checks without re-execution and, optionally, without seeing the inputs. This is the §17 claim made cryptographic. It is OCG's analogue of the chained-verifiable-computation goal in Trusted Compute Units (arXiv:2504.15717), but deliberately software/cryptographic only: no TEE, no hardware enclave, no blockchain anchor. An artifact with no compute_proof is fully v0.6-conformant.
audit_signature.compute_proofcompute_proof = {
"type": "ZkVmReceipt",
"system": "risc0", // | "sp1" | "jolt" | … — system-agnostic
"receiptFormat": "groth16-bn254", // RECOMMENDED ~200-byte SNARK | "stark"
"imageId": "sha256:…", // guest-program identity (ImageID / vkey)
"seal": "<base64>", // proof bytes — produced OFFLINE (§18.2)
"journal": { "output": … } // MUST equal artifact output_payload
}
(a) §4 recompute: still valid when inputs are public; or (b) receipt verification: verify seal against imageId, valid even when inputs are withheld. Cryptographic seal-verification is DELEGATED to the named system's vetted verifier (risc0/sp1 verify), exactly as §4 delegates SHA-256 and §16 delegates Ed25519 to WebCrypto: OCG specifies the binding, it does not re-implement a proof system. A self-contained BN254 Groth16 pairing-check verifier for receiptFormat:"groth16-bn254" is a RECOMMENDED reference. OCG's gate (compute-proof.test.mjs) checks the binding: object shape, imageId ↔ Graph Index compute_images, journal ↔ output_payload, no new execution_hash, chaingraph_version stays "0.4.0".
zkVM proving needs a Rust toolchain and heavy compute (multiple orders of magnitude over native: arXiv:2504.15717); it MUST NOT be claimed to run in the browser tool, the Worker (§12 compute path), or CI. A compute_proof is produced offline and attached; the live surfaces only verify.
When the receipt is used to withhold inputs, policy_parameters MAY carry commitments/hashes in place of cleartext; then §4 recompute is unavailable to third parties (only the input-holder can re-run) and the receipt becomes the sole verification path: a tool in this mode MUST surface that. Like §16, attaching a compute_proof links the run to a published imageId, so it MUST default OFF.
§17 = advisory claim of which kernel; §18 = cryptographic proof that that program produced this output; §16 = a named key vouching for the whole artifact. They compose: a §18 receipt MAY itself be covered by a §16 proof. L1 §4 hash (tamper-evidence) → L2 §16 proof (authenticated attestation) → L3 §18 receipt (succinct compute-integrity, optionally confidential).
A §18 proof runs the kernel in a fixed zkVM guest (a pinned JS runtime), not the browser's V8. Where a rule would otherwise depend on a V8 platform API that is locale- or environment-sensitive, or whose faithful in-guest port is infeasible to prove, the kernel MUST use a deterministic, fully specified replacement applied identically on every surface (guest, browser tool, Worker) so the verdict is byte-identical everywhere and the proof binds the rule users actually see. Shipped instances: a pinned en-US number formatter in place of toLocaleString; a deterministic https-scheme plus non-empty-authority check in place of WHATWG URL parsing (faithful parsing measured near 5.4 billion guest cycles, infeasible to prove); and a shared pure-JS fdlibm port for transcendental math (Math.exp/log/sin/cos/pow), since only the four arithmetic operations and square root are IEEE bit-portable across engines. Each replacement documents its scope: it defines the rule, not an approximation of richer V8 behavior. A node whose deterministic proving cost is still prohibitive (for example tens of thousands of transcendental calls in a Monte-Carlo loop) MAY carry the replacement for cross-surface determinism yet stay compute_proof_ready:"deferred" until a larger prover exists.
ocg-p18-deterministic)The base standard keeps §18 OPTIONAL. An implementation MAY additionally declare conformance to the deterministic-node proof profile. Under it, every status:"live" node with gpu:false MUST either carry a compute_proof that verifies against its published imageId (§18.1) or declare compute_proof_ready:"deferred" with a stated deferral_reason; a node with neither is non-conformant to the profile. A gpu:true node is out of scope (heavy or parallel compute whose faithful in-guest proving cost is prohibitive, §18.2; it MAY still attach a proof voluntarily). A blanket §18 MUST is impossible because legitimate heavy or nondeterministic-cost nodes cannot be proven at acceptable cost, while an all-optional posture makes a proof an unreliable, and therefore unusable, trust signal. The profile is the middle path: a MUST exactly over the class where it is achievable, leaving the base standard unchanged for external implementers. Conformance is machine-checked by check-compute-proof-coverage.mjs. Its endgame, zero deferrals on gpu:false, is the state where every deterministic live node carries a real compute-integrity proof; the AINumbers reference deployment conforms with zero deferrals.
An artifact MAY carry portable, offline-verifiable evidence that its execution_hash was included in a transparency log or timestamp service by a point in time. Anchor evidence attaches at the OPTIONAL top-level array anchor_bindings, which, like §16 audit_signature, is attached AFTER hashing and is EXCLUDED from execution_hash scope. Each entry:
{
"type": "rfc3161-tst" | "opentimestamps" | "c2sp-tlog-proof-v1" | "scitt-receipt-rfc9942",
"anchored_hash": "sha256:…", // MUST equal the artifact's execution_hash
"log_origin": "<TSA URL / log origin string / calendar or service identifier>",
"proof": "<base64 verbatim TST DER | base64 OTS proof | tlog-proof@v1 text | base64 COSE receipt>",
// rfc3161-tst additional members (all REQUIRED for that type):
"policy_oid", "serial", "gen_time", "signer_cert_chain_b64"
}
Verification (per type): rfc3161-tst: RFC 3161 TimeStampToken verification: messageImprint matches anchored_hash, CMS signature over TSTInfo valid, signer chains to a verifier-pinned TSA root, signing cert carries critical EKU id-kp-timeStamping, genTime sane; the DER is stored VERBATIM (never re-encoded) so openssl ts -verify remains possible independently, forever. opentimestamps: standard OTS verification (complete proofs verify against Bitcoin block headers alone). c2sp-tlog-proof-v1: the C2SP tlog-proof verification procedure (checkpoint signature against the log's public key, cosignature policy per the verifier's trust policy, Merkle inclusion proof for the leaf committing anchored_hash). scitt-receipt-rfc9942: COSE receipt verification per RFC 9942 (accepted as an evidence type for interop; OCG implementations are NOT SCITT Transparency Services and SCRAPI is out of scope). Multi-TSA redundancy across independent authorities and algorithms is RECOMMENDED per RFC 4998 (informative). A verifier MUST reject a binding whose anchored_hash differs from the artifact's recomputed execution_hash. Multiple bindings MAY coexist (several logs, plus OTS). Gate: anchor-binding.test.mjs (§15), whose rfc3161 fixture carries a REAL TimeStampToken from a public TSA verified against a pinned root.
A rfc3161-tst or opentimestamps binding MAY carry an OPTIONAL merkle_inclusion member so ONE timestamp covers MANY artifacts: the anchored value is the ROOT of an RFC 6962 Merkle tree and each covered artifact's execution_hash is a LEAF. (c2sp-tlog-proof-v1 and scitt-receipt-rfc9942 already carry their own inclusion proof inside proof, so they do not use this member.)
{
"merkle_inclusion": {
"leaf": "<the artifact execution_hash>",
"index": 0, "path": ["sha256:…"], "tree_size": 6, "algorithm": "rfc6962"
}
}
When present, a verifier MUST: (1) confirm leaf equals the artifact's recomputed execution_hash; (2) reconstruct the root from leafHash(leaf) + path via the RFC 6962 inclusion procedure (the SAME leafHash/nodeHash/rootFromInclusion the c2sp/scitt verifiers use, no second Merkle implementation); and (3) require the reconstructed root to equal anchored_hash. Any mismatch FAILS the binding. Absent this member, anchored_hash == execution_hash directly (unchanged v0.7 behavior). merkle_inclusion rides OUTSIDE the hash preimage like the rest of anchor_bindings. Gate: anchor-binding.test.mjs (§15), whose offline fixture synthesizes a small RFC 6962 tree over test hashes.
An anchor binding proves EXISTENCE of the artifact bytes by a time and INCLUSION in the named log. It does not prove computational correctness (§18), authorship (§16), or kernel identity (§17); the four are independent, composable claims. Because inclusion evidence is SHA-256 Merkle data, anchor bindings retain their timestamp value even against a future signature break (informative).
A §20 binding carrying a §20.1 merkle_inclusion root (or a c2sp-tlog-proof-v1 anchor) MAY carry an OPTIONAL witness_cosignatures[] array: independent k-of-n cosignatures over the anchored Merkle root, in the C2SP tlog-checkpoint + witness-cosignature note format (a signed note of origin line, tree size, root, then one — <keyname> <base64 sig> line per witness). Suites follow the §PQC-1 reserved-extension discipline: Ed25519 now, an ML-DSA entry RESERVED, no hardcoded suite id in the envelope. A batch anchor proves a root existed at a time (§20.1); cosignatures close anchor equivocation (one root shown to verifier A, a different root to verifier B) WITHOUT the reference deployment operating a log, because the cosigners are independent third parties (existing verifiers such as sigsum-verify and Armored Witness). Verification is offline: the note origin and root must match anchored_hash and at least k valid cosignatures must verify against the pinned witness key set; fewer than k, or a cosignature over a different root, FAILS the binding. witness_cosignatures rides OUTSIDE the hash preimage like the rest of anchor_bindings, so an anchor with no cosignatures is byte-identical and fully conformant. Gate (existing, extended, no new gate): anchor-binding.test.mjs. Attribution: C2SP tlog-checkpoint + witness cosignature note format; Sigsum and Armored Witness named as conformant independent-witness mechanisms.
Post-quantum resilience of the anchor path (informative, new in v0.8.9). The anchor types do not share a post-quantum exposure profile, and the difference is worth stating rather than leaving a reader to infer it. An opentimestamps binding derives its integrity from SHA-256 Merkle aggregation committed via Bitcoin proof-of-work: there is no signature primitive anywhere in the OTS attestation path, so there is no classically-or-quantum-forgeable signature to break. Forging one requires a SHA-256 second preimage, which is not a target of Shor's algorithm and is only marginally weakened by Grover at a 256-bit output, so a store-now-forge-later adversary gains nothing against it. OTS is therefore the PQ-resilient timestamp anchor in the OCG anchor set today. An rfc3161-tst anchor (the FreeTSA / JAdES path) does carry a classical signature and inherits that scheme's exposure; the mitigation available now is the §PQC-1 hybrid proof over the artifact itself, and the eventual fix is a PQC TSA. No public PQC RFC 3161 TSA exists as of 2026-07-18 — the surveyed offerings are niche or private-cert only — so migrating the TSA path is a WATCH item, not a build. NIST IR 8547 (classical signatures deprecated 2030, disallowed 2035) is the timeline this is measured against. No field, no MUST, no gate row: this records rationale for an existing anchor set.
Until v0.8 chain execution (run_chain / composite_execution_hash) was implementation-defined. §21 makes the existing linear contract normative (§21.1–§21.3, descriptive of shipped behavior) and adds decision gates on top (§21.4). A chain is a chaingraph.json chains[] entry: { name, title?, steps: [{ tool_id, handoff?, id?, gate? }] }. Execution is deterministic, zero-PII, zero payload logging. The reference surfaces are the Worker run_chain and the byte-identical embedded runChain; both MUST produce the identical composite_execution_hash for the same chain and inputs.
Steps run in array order. For each step by tool_id: an unknown node gets status unknown_node; a gpu:true node gpu_browser_only; a node with no kernel no_kernel_browser_only; otherwise the kernel's buildArtifact runs. policy_parameters are caller inputs[tool_id], else the vendored fixture, else {}; a kernel that throws for missing inputs yields input_required (never a silent failure). A step producing an artifact has status ok and is a RAN step. Parent threading: parent_hashes = the previous RAN step's execution_hash (or []), and chain_depth = the step's zero-based array index. Only a RAN step advances the "previous" pointer. §17/§18 attestations attach exactly as in the single-node compute path (hash-excluded).
The chain emits ONE composite artifact whose execution_hash is the §4 canonical hash over exactly { policy_parameters: composite_policy, output_payload: composite_output }, computed over the RAN steps only, via the same kernels/_hash.mjs canonicalizer (no new hash path):
composite_policy = { compute_mode: "server", chain, chain_title, step_count: <#ran>, step_tool_ids: [<ran tool_ids>] } composite_output = { chain, steps: [ { tool_id, mandate_type, execution_hash, output_payload } … per RAN step ] }
Per-step timestamps and mandate_ids are EXCLUDED from the preimage, so the composite hash is reproducible. If no step ran, composite_execution_hash is null.
The composite artifact carries chaingraph_version:"0.4.0", compute_mode:"server", tool_id:"chaingraph/chains/<name>", chain.parent_hashes = each RAN step's execution_hash, chain.chain_depth = #ran, and the composite policy/output as its preimage halves. It re-verifies under §4 like any artifact.
A step MAY carry an OPTIONAL string id (defaults to tool_id; MUST be unique within a GATED chain) and an OPTIONAL gate routing control after the step runs:
gate = { input: <RFC 6901 pointer into THIS step's output_payload>,
rules: [ { op, value?, next } … ],
default: <step id | "end"> }
op is the closed enum { eq, neq, gt, gte, lt, lte, in, present, absent }. Comparison is strict, no coercion; gt/gte/lt/lte require FINITE numbers on both sides; in tests membership in a literal array; present/absent test existence only. Rules evaluate first-match; the REQUIRED default is a total function, so an agent runs a gated chain end-to-end with no human in the loop. All targets are FORWARD-ONLY (a later array index) or the literal "end", making the chain acyclic and guaranteed-terminating. A jumped-over step gets status skipped_by_gate; a step with no gate falls through linearly.
Hash binding, CONDITIONAL-PRESENCE (only when the chain defines ≥1 gate). A chain with no gate is pure linear and its composite_execution_hash is UNCHANGED (a golden-fixture freeze gate enforces this). When the chain has ≥1 gate, and only then, three members enter the composite preimage: composite_policy.route_plan_digest (bare-hex SHA-256 over the JCS-canonical full steps[] definition, the ONE canonicalizer), composite_output.decisions[] (one record per evaluated gate: { step_id, input_pointer, observed_value, matched_rule_index, op, value, next }, recomputable by a verifier and tamper-evident), and composite_output.path_taken[] (ordered executed step ids). The evaluator is ONE pure ECMA-262 module (kernels/_gateval.mjs): no expression language, no second canonicalizer, byte-identical on every executing surface. Gates: validate-chains.mjs + gate-static.test.mjs (static), gate-semantics.test.mjs (evaluator), gate-branch-coverage.test.mjs (both-branch), gate-parity.test.mjs (surface byte-parity), linear-hash-freeze.mjs (no linear hash moves).
A §21 chain receipt MAY carry a hash-excluded claim_strength equal to the min of its per-step claim strengths, where each step's strength reflects its evidence class: a zk-proven step (§18) is stronger than a hash-replayable step (§4), which is stronger than an asserted step. A chain mixing a §18 zk-proven step with a hash-replayable step reports the WEAKER composite faithfully, so the chain cannot claim to be stronger than its weakest link. claim_strength is DERIVED and hash-excluded, so it moves no composite_execution_hash (a receipt with and without it is byte-identical); it pairs with §24.6 per-kernel determinism classes as the per-step inputs. Gate (existing, extended, no new gate): linear-hash-freeze.mjs proves the field is hash-excluded, gate-parity.test.mjs proves Worker run_chain and embedded runChain derive the same min. Attribution: FrankenSim weakest-link design, IDEAS ONLY (unvetted "AI rider" license, no text or code copied).
A Work Mandate is a signed OpenChainGraph artifact that delegates bounded authority: a principal authorizes an agent to run a defined set of nodes and chains, under stated conditions, within a validity window, with named escalation triggers. It is the "Authorize" step of the estate loop, authored once by a human and then enforced deterministically on every run. §22 makes the mandate format and the compile_work_mandate I/O contract normative (additive; spec_version 0.8.1). The escalation-record lifecycle and the escalate evaluator semantics are RESERVED here and specified in a forthcoming revision, so no reader mistakes escalation execution for shipping in this section.
This section changes NO frozen structure: chaingraph_version stays 0.4.0, the §4 hash preimage is unchanged, the v0.4 artifact envelope is untouched. mandate_type:"work_mandate" is an accepted value of the EXISTING open mandate_type string (§5, not a hard enum), so there is no envelope edit. The mandate DOCUMENT is a separate schema $def (workMandateDocument), never the artifact envelope.
A Work Mandate's authority lives in its output_payload: mandate_type (the literal "work_mandate"); scope { tool_ids, chains } (REQUIRED, at least one array non-empty) naming the authorized nodes and chains; conditions (REQUIRED, MAY be empty), an array of { pointer, op, value } threshold conditions where pointer is an RFC 6901 pointer into a step's output_payload and op is from the §21.4 closed op enum; escalation_triggers (REQUIRED, MAY be empty), the same shape, whose satisfaction routes to the reserved escalation target; validity { not_before, not_after } (REQUIRED, ISO 8601); principal { id } (REQUIRED), a did:key or LEI the §16 signature verifies against; and an OPTIONAL §20 anchor over the mandate hash.
A Work Mandate MUST carry a §16 eddsa-jcs-2022 Data Integrity proof at audit_signature.proof over the whole artifact. An unsigned mandate is a DRAFT and is not enforceable; the runtime (§22.5) MUST reject a mandate with no valid signature. The mandate's own §4 execution_hash IS its identifier, the mandate hash; a run that references "the mandate in force" references this hash, and folding it into the receipt (§22.5) proves which policy governed the run.
§22 RESERVES the literal string "escalate" as a decision-gate routing target, beside the existing §21.4 "end". A compiled mandate's escalation triggers become gate rules whose next is "escalate". This section reserves the NAME and its meaning ("route the run out of the automated path into the exception path") only. The evaluator semantics of "escalate", that is halting remaining steps, the skipped_by_escalation status, and the OPEN escalation record, are NOT normative here; they are specified in a forthcoming revision and implemented in its kernels/_gateval.mjs update. Until then a compiler MAY emit "escalate" targets and a static validator MUST accept "escalate" as a reserved forward target (not an unresolved step id), but no shipped evaluator executes escalation halting. "end" remains the terminal target for the fully-automated path.
compile_work_mandate I/O contractcompile_work_mandate is a deterministic gpu:false node that turns a mandate document into a §21.4 gated-chain configuration, the write side of the loop. INPUT: the mandate document supplied as the node's policy_parameters (input_parameters.mandate = the mandate output_payload, plus a mandate_hash string); the compiler treats the mandate as data and does NOT verify the signature (that is the runtime's job). OUTPUT: output_payload.chain_config:
chain_config = { steps: [ {
tool_id, // REQUIRED
id?, // OPTIONAL step id (defaults to tool_id; unique in a gated chain)
handoff?,
gate?: { input, // present iff this step carries ≥1 condition/trigger
rules: [ { op, value?, next } … ],
default } // REQUIRED (total function, §21.4)
} … ] }
Compilation rules (v0.8.2): (1) scope.chains[0] supplies the ordered steps[] skeleton, else scope.tool_ids in array order, each becoming one step with id defaulting to tool_id. (2) One gate per checkpoint step, one input pointer per gate: since §21.4 allows one gate per step and one pointer per gate, all conditions and triggers on a step MUST share one pointer; a step with two different pointers is a multi-pointer policy that is rejected with { error: "multi_pointer_gate" }, deferred to the forthcoming decision-table artifact (spec_version 0.9, reserved). (3) Rules in fixed order: the step's escalation_triggers first, each { op, value, next: "escalate" }, then its conditions, each routing to the next scope step's id or "end". (4) default: "escalate" (management-by-exception). (5) Determinism: same mandate gives a byte-identical chain_config and hash-stable execution_hash; the compiler reads only mandate fields (no clock, RNG, or environment) and hashes over { policy_parameters, output_payload } via the ONE canonicalizer kernels/_hash.mjs (RFC 8785 / JCS).
Worked example (authoritative target for the compiler fixture). A mandate over chain loan-preflight (steps [assess_loan, record_outcome], where assess_loan emits decision.approved) with condition { "/decision/approved", eq, true } and trigger { "/decision/approved", eq, false } compiles to:
{ "steps": [
{ "tool_id": "assess_loan", "id": "assess_loan",
"gate": { "input": "/decision/approved",
"rules": [ { "op": "eq", "value": false, "next": "escalate" },
{ "op": "eq", "value": true, "next": "record_outcome" } ],
"default": "escalate" } },
{ "tool_id": "record_outcome", "id": "record_outcome" }
] }
The trigger rule precedes the condition rule (first-match); the condition continues to record_outcome; the default escalates.
run_chain MAY accept an OPTIONAL mandate argument. When absent, run_chain behavior and every composite and step execution_hash are BYTE-IDENTICAL to pre-mandate behavior (the linear-hash-freeze invariant; mandate keys are conditional-presence, exactly the §21.4 discipline). When present, the runtime MUST, before executing any step: verify the mandate's §16 signature against principal.id (missing or invalid gives { error: "mandate_unsigned" | "mandate_bad_signature" }, no steps run); verify the execution instant is within validity (mandate_not_yet_valid / mandate_expired); and verify the run is within scope (mandate_out_of_scope). On acceptance it folds mandate_hash into EVERY step's policy_parameters and into composite_policy as a CONDITIONAL-PRESENCE key: absent for a no-mandate run (so that hash is unchanged from §21.2/§21.4), present for a governed run (so the receipt proves which policy was in force). This is enforced by the linear-hash-freeze gate plus the runtime mandate-binding gate (with and without a mandate give different composite hashes, each stable). §22.5 is the contract the runtime layer (forthcoming) MUST satisfy.
Field names align with adjacent agent-authority vocabularies, aligned with but with no runtime dependency on any (Rider R2). Google AP2 mandates (donated to the FIDO Alliance): an AP2 Intent/Cart mandate is a bounded, signed grant of authority; scope + conditions + principal + signature play the same role for arbitrary compute rather than payment authority (principal maps to the AP2 issuer, the mandate hash to the AP2 mandate id). OpenID AuthZEN AARP: its subject / resource / action request maps to principal / scope / the authorized invocation, with conditions and escalation_triggers as the obligation layer AARP leaves to the policy decision point. SCITT / RFC 9942: a run's composite receipt (mandate hash folded in) is the transparency statement and a §20 anchor over it is the SCITT-style receipt, so a future registration is a re-labelling rather than a re-model.
NORMATIVE in v0.8.2: the mandate document shape (§22.1), the signature requirement and mandate-hash identity (§22.2), the reservation of the "escalate" target name (§22.3), the compile_work_mandate I/O contract with its compilation rules, determinism, and worked example (§22.4), and the run_chain mandate-binding contract with its conditional-presence hash rule (§22.5). NORMATIVE in v0.8.2 (§22.8): the "escalate" evaluator semantics, the skipped_by_escalation runtime-halt contract, the open escalation record with its deterministic (wall-clock-excluded) record hash, and the countersigned closure contract. FORTHCOMING: the run_chain emit/halt implementation and the verify_escalation_closure utility, the exception-queue surface, and the SEP-2322 / MCP-Tasks transport binding.
§22.8.1 Evaluator semantics. "escalate" is a TERMINAL routing target beside "end": a gate rule (or default) whose next is "escalate" routes control OUT of the chain, preserving every §21.4 invariant, differing from "end" in MEANING only (normal completion vs. leaving the automated path for the exception path). The evaluator (kernels/_gateval.mjs) returns the SAME decision record: { step_id, input_pointer, observed_value, matched_rule_index, op, value, next } with next === "escalate"; no escalation field is added, so the hashed composite_output.decisions[] is byte-identical to a non-escalating gate and no composite hash moves. Escalation is recovered by the single-source classifiers the evaluator exports, isTerminalTarget / isEscalationTarget, consulted byte-parity across the four executing surfaces (run_chain, embed, the QuickJS guest, the composer pages).
§22.8.2 Runtime semantics (contract; implemented in the runtime layer). On an escalate decision, run_chain MUST record the triggering decision and escalating step (§21.4), HALT, give every not-yet-run step the status skipped_by_escalation (DISTINCT from skipped_by_gate), and attach an OPEN escalation record. The composite hash is still over RAN steps only (§21.2).
§22.8.3 Open escalation record + DETERMINISM. The record is { mandate_hash?, decision, halted_steps, opened_at }. opened_at is wall-clock and MUST NOT enter any hash preimage. The escalation-record hash is the §4 canonical hash (kernels/_hash.mjs) over the DETERMINISTIC subset { mandate_hash?, decision, halted_steps } only (mandate_hash conditional-presence per §22.5); opened_at is hash-EXCLUDED adjacent metadata, exactly as §20 anchor bindings and §17/§18 identity are excluded. The record hash is therefore REPRODUCIBLE from the recorded deterministic fields, and the closure binds THAT hash.
§22.8.4 Closure contract. An open record CLOSES only via a countersigned closure: the escalation-record hash signed through Anchorproof create_signature_envelope (passkey / JAdES). The closure is { record_hash, decision ∈ {approve,reject}, anchor, envelope } whose signed payload IS record_hash. Verification (verify_escalation_closure, forthcoming) passes IFF Anchorproof verify_signature_envelope accepts the envelope AND the record hash recomputed from the deterministic subset equals closure.record_hash AND closure.decision echoes an approver decision.
§22.8.5 Transport-agnostic (D3). Escalation records and closures are artifacts with no transport dependency; a SEP-2322 / MCP-Tasks binding is a forthcoming worker rider. §22.8.6 Vocabulary alignment (informative). The approver decision maps to an OpenID AuthZEN AARP access decision; a countersigned closure over a record hash is a SCITT / RFC 9942 receipt-shaped statement, aligned with and with no dependency on.
A failed verify (a §16/§18/§20/§21.4 gate failure, or a §22 mandate rejection) MAY be emitted as a signed FAILURE receipt whose body carries an AR4SI trustworthiness-vector tier (affirming / warning / contraindicated, with the numeric AR4SI code points), shaped per IETF RATS EAR (EAT Attestation Result). It is signed with the same §16 eddsa-jcs-2022 whole-artifact proof, so the failure is portable and tamper-evident; it references the subject execution_hash (or mandate hash) and names the failing rule id. This is the failure side, complementing but never duplicating the queued OCG-VSA success side; §22 gates and ML-2 escalations MAY consume a §22.9 receipt as input evidence. Gate (existing, extended, no new gate): proof-binding.test.mjs for the signed round-trip and tamper-detection, reusing the §21.4 gate-semantics.test.mjs tier mapping where a gate failure is the source. Attribution: IETF RATS EAR / AR4SI (draft-ietf-rats-ar4si), PIN draft -10. RFC 9457 problem-details is REJECTED (an unsigned HTTP-error shape at the wrong layer).
A multi-hop work mandate MAY be delegated by Biscuit-style offline attenuation-block chaining: each hop is a signed block bound to the previous by a single-use keypair; a holder narrows scope OFFLINE by appending a caveat block, never widening; a verifier checks the whole block chain under ONE root public key. Widening re-delegation is structurally impossible (a hop can only add restrictions), which makes the §SIDECAR.2 resource-narrowing invariant concrete for multi-hop mandates. This is a CONSTRUCTION and FORMAT only: OCG defines the block-chaining and single-use-key binding and NEVER the Biscuit Datalog authorization runtime, which stays external and out of scope. Gate (existing, extended, no new gate): mandate-binding.test.mjs gains an attenuation-chain fixture (a valid narrowing chain verifies under one root key; a widening block MUST fail; a broken link MUST fail). Attribution: Eclipse Biscuit (Apache-2.0), the block/attenuation construction ONLY, never the Datalog runtime. UCAN is REJECTED (DID baggage + quadratic nested-JWT bloat); draft-prakash-aip-00 is REJECTED as a dependency.
§22.8 defines what an escalation record is; §22.11 refines how a failed item is classified and how a human resolution is evidenced. A record MAY carry an OPTIONAL exception_class of exactly two values: business (the data is wrong or a business rule rejected the item — no automatic retry, the item routes to a human queue in a terminal-until-resolved state) or application (the environment failed — retry up to N, then MUST escalate). The two classes have opposite correct responses, which is why the distinction is load-bearing: retrying a business exception cannot succeed and only hides the item, while routing an application exception to a human wastes the one resource that cannot scale. A machine-readable exception_detail {type, code, message} MAY accompany it, and an application record MAY carry retry {attempt, max}; reaching attempt == max MUST become a §22.8 escalation rather than silently drop. State is tracked per item, never per run: item_state is done/failed/pending_human, and one failed item MUST NOT abort its siblings in the same batch. An escalation MAY declare a counted-resume gate resume_approval {required_events, approver_group, resume_form, timeout} — the gate stays suspended until N distinct approvals (or one cancel) arrive, the approve payload MUST validate against the resume_form JSON Schema, and on timeout with fewer than N events it MUST resolve to a §22.8 escalation, never a silent auto-approve. Every resume message and exception record emitted as an OCG artifact MUST carry a §16 eddsa-jcs-2022 proof bound to a named human, referencing the subject execution_hash and the approver identity; an unsigned resume is NOT conformant §22.11 evidence. That is what makes the approval trail portable and offline-verifiable rather than a claim held inside one vendor's queue. A §22.9 failure receipt MAY be the evidence that opens a business-class exception. Every field is hash-EXCLUDED and the frozen §22.8 envelope is untouched, so a record carrying none of them is byte-identical and fully conformant. Scope limit: OCG defines these formats and their invariants and does NOT operate a queue, runtime, approver directory, or timer. Gate (existing, extended, no new gate): mandate-binding.test.mjs for the classification, retry-to-escalation, sibling-isolation and suspend/timeout fixtures; proof-binding.test.mjs for the signed round-trip, tamper-detect, and hash-identity with and without the fields. Attribution: Robocorp work-items (Apache-2.0, patterns only), Windmill suspend/approval (AGPLv3, semantics only, never code or text), UiPath's Business/System split as convergent prior art.
The §4 execution_hash proves the computation over the inputs the artifact was given; it says nothing about whether those inputs were themselves authentic. §23 lets an artifact carry portable, per-input evidence that a named input was vouched for by an external source, without changing what execution_hash means. Evidence attaches at the OPTIONAL top-level array input_attestations, which, like §16 audit_signature and §20 anchor_bindings, is attached AFTER hashing and is EXCLUDED from execution_hash scope. An artifact with zero attestations remains fully conformant; attestations never enter the preimage, so adding, removing, or re-ordering them leaves every existing execution_hash byte-identical.
Each entry is { type, pointer, proof, source_ref }. pointer is an RFC 6901 JSON Pointer evaluated against policy_parameters (naming WHICH input); an entry whose pointer does not resolve inside policy_parameters is rejected. Where a type binds a digest, that digest MUST equal the SHA-256 of the §4-canonical encoding of the resolved input value. source_ref names who vouches and is informative, never a substitute for verifying proof.
§23.1 Type phasing (D2). Three types verify NOW with shipped machinery; zktls is defined with EXTERNAL verification (no vendored verifier: vendoring a TLSNotary-class verifier would break the zero-dependency posture). vc-2.0: a W3C VC 2.0 credential binding the input by value or digest; verified via the shipped §16 / §13.11 Data Integrity (eddsa-jcs-2022) or enveloping JOSE/COSE proof, plus subject-digest equality. rfc3161-snapshot: an RFC 3161 TimeStampToken (verbatim base64 DER) whose messageImprint is the input's SHA-256, verified by the SAME §20 rfc3161-tst verifier (no second implementation). c2pa-manifest: a C2PA manifest whose hard-binding digest matches the input; verified STRUCTURALLY now (parse + claim-signature well-formedness + digest match), full signer trust-chain a link-out. zktls: reported as PRESENT with verifiable:"external": structural fields checked, the cryptographic claim neither confirmed nor refuted by OCG machinery.
§23.2 Verifier report. A verifier reports per-input status ALONGSIDE (never folded into) the execution_hash result: for each entry { pointer, type, structural: "pass"|"fail", verifiable: "verified"|"failed"|"external"|"n/a" }. The execution_hash verdict is computed INDEPENDENTLY of attestation status: a failed or absent attestation never changes it, a passing attestation never substitutes for it. §23 attests input provenance where sources support it; it does not prove computational correctness (§18), authorship (§16), kernel identity (§17), or existence-in-time (§20); these are independent, composable claims. A UI presenting attestations MUST keep the zero-attestation caveat visible.
§23.3 Frozen-envelope invariance. input_attestations is an OPTIONAL top-level property (exactly as §20 anchor_bindings), so $defs/artifact.required, the §4 preimage members, and chaingraph_version 0.4.0 are UNTOUCHED. A verifier correct for v0.7/v0.8 computes an identical execution_hash for a v0.8.3 artifact and MAY ignore input_attestations entirely. Any type whose binding would require folding attestation data INTO the preimage is out of profile.
§23.4 Freshness and consent new in v0.8.9. An input_attestations entry MAY carry an OPTIONAL freshness object: { observed_at, expires_at?, freshness_class, consent_ref?, retention_class? }, where freshness_class is realtime|intraday|daily|point-in-time|static and retention_class is declarative only (transient|case-file|regulatory-N-years). Like the rest of §23 it is hash-excluded — a freshness block never enters the execution_hash preimage. A verifier's §23.2 report gains one field per entry, freshness_status: "fresh"|"stale"|"undeclared" (stale when expires_at is past at verification time, undeclared when no freshness object is present); staleness is reporting-only and NEVER changes structural, verifiable, or the execution_hash verdict. A §21.4 decision gate MAY target a copied-forward freshness_status so a mandate can require fresh evidence.
OCG's integrity model already assumes a kernel is a pure, deterministic function of its inputs: §4 makes the output tamper-evident, §17 pins which kernel source ran, §18 proves that it ran, and §18.5 fixes the specific cross-engine hazards a real deployment hit. This section adds no new machinery. It names the determinism those layers already enforce as one citable profile, ocg-deterministic-compute, modeled on the W3C WebAssembly 3.0 Deterministic Profile (W3C Candidate Recommendation, April 2026), which conforms an execution environment by enumerating every source of nondeterminism and fixing or banning each one. An implementation that already passes the §15 gate suite already conforms; the profile's value is a name a verifier or a second implementer can point at.
§24.0 Scope. The profile governs a kernel's compute(policy_parameters) under §12, over the same class §18.6 delimits: every gpu:false, status:"live" node (gpu:true is out of scope for the same cost and parallelism reason as §18.6). It binds the compute surfaces to byte-identical output for identical input across the browser tool, the Cloudflare Worker (§12 server path), and the fixed zkVM guest (§18). As the in-browser QuickJS VM lands it runs the same C interpreter family as the §18 guest, so its output is diff-checked against the Worker rather than adding a fourth engine. Determinism here means reproducible-bit, not "same up to rounding": one differing bit is a different execution_hash.
§24.1 Enumerated nondeterminism sources. A conforming kernel MUST NOT let any of the following affect output_payload. Each binds to an existing §15 gate; the profile introduces no gate of its own. D1 Non-finite floats (NaN, ±Infinity): output MUST canonicalize under RFC 8785 / I-JSON, which forbids non-finite numbers, so a kernel returns finite output or rejects input cleanly, never a silent NaN (enforced at hash time by the §4 canonicalizer and the empty-input finite sweep). D2 Iteration order: serialization MUST NOT depend on property order; the preimage is built by the one canonical RFC 8785 (JCS) sorter, never by hand (§4 hash gates; ad-hoc canonicalization is linted out). D3 Transcendental math: only + − × ÷ √ are IEEE-754 bit-portable, so every transcendental routes through the shared pure-JS fdlibm port (§18.5c), never engine libm. D4 Wall-clock time: no clock or timer may enter the payload or preimage; time-bearing evidence (anchor genTime, escalation opened_at) is defined hash-excluded (§20, §22.8), and the guest disables Date. D5 Randomness: none may reach the payload; Math.random is stubbed, and the sole CSPRNG (§13.12 SD-JWT salts) is confined to hash-excluded disclosure material. D6 Locale / Intl: locale-sensitive formatting routes through a pinned en-US formatter verified value-for-value against V8 (§18.5a); the guest and the QuickJS VM ship no Intl at all. D7 Environment / platform APIs: where a rule would depend on an environment-sensitive V8 API, the kernel substitutes a fully specified deterministic replacement used identically on every surface (§18.5), and network, filesystem, and ambient I/O are already forbidden by the zero-fetch contract. Rows D1 through D7 are exhaustive over the escape hatches the Wasm 3.0 profile enumerates; any future source is a normative profile change (§24.2), not a silent addition.
§24.2 Freeze clause. Modeled on the RISC-V base-ISA freeze policy: a ratified kernel-semantics version is never revised. Once a profile version fixes the meaning of D1 through D7 that meaning is immutable for that version; a change that could alter any conforming kernel's output_payload (for example re-baselining the shared libm) MUST ship as a new profile version ocg-deterministic-compute@<n>, never an in-place edit. This is what lets execution_hash be a durable identifier: a receipt minted under @1 verifies bit-for-bit forever, because @1's semantics can never move under it. A version bump is additive (a new named profile beside the old, as §11 profiles compose); it never mutates the frozen v0.4 envelope and never bumps chaingraph_version (stays "0.4.0").
§24.3 Conformance. An implementation MAY declare ocg-deterministic-compute@1 for its gpu:false live kernel set. The declaration asserts exactly that D1 through D7 hold, which, because each binds to a gate already in §15, is decided by the existing gate suite: an implementation green on §4, §12, §18.5/§18.6, and §21.4 parity is conforming. The profile adds a name and an enumeration, not a new obligation, and deliberately introduces no §15 row of its own (the §15 meta-rule is satisfied because every requirement cites an existing wired gate). This mirrors §18.6. The AINumbers reference deployment conforms: its gpu:false live set is green on every cited gate.
§24.4 Prior art (informative). The enumerate-and-close method is the W3C WebAssembly 3.0 Deterministic Profile. The never-revise-a-ratified-version rule is the RISC-V frozen-base-ISA policy. The precedent for proving a deterministic JS/Wasm-class guest inside a zkVM is Cartesi (a RISC-V zkVM running a full deterministic guest); OCG differs by proving a single pinned kernel per receipt rather than a whole machine image, and by carrying the proof as an OPTIONAL §18 attachment rather than a consensus artifact. A deeper lineage of a frozen, total, deterministic instruction semantics as the base of a permanent record (Nock, and Kelvin versioning counting down to a frozen zero) is noted only as motivation; OCG cites the ratified engineering standards above as its normative models.
§24.5 Profile version ocg-deterministic-compute@2: WebCrypto subset split (new in v0.8.6). @1 stated D7 at a granularity a literal reader can over-read as banning all of WebCrypto, conflating two subsets D5 and D7 already treat differently. @2 is a new profile version (per the §24.2 freeze clause, a new named profile alongside @1, never an in-place edit) that keeps D1 through D6 unchanged and enumerates the split inside D7. Allowed as fully-specified deterministic replacements: crypto.subtle.digest (SHA-256/384), importKey, and verify, all pure functions of their inputs, so they MUST be byte-identical across the browser tool, the Cloudflare Worker, and the in-browser QuickJS VM (a surface that ships no WebCrypto, like the §18 guest, simply does not host the crypto-using kernels). Banned as D5 randomness: crypto.getRandomValues, generateKey, and sign (fresh key) MUST throw. A kernel that reaches for them fails, never silently degrades an output_payload. @2 is additive: it moves no execution_hash (all six previously-VM-unrunnable kernels emit the same bytes the Worker already produces), the frozen v0.4 envelope and §4 preimage are UNTOUCHED, chaingraph_version stays "0.4.0", and only spec_version bumps to 0.8.6. The reference deployment re-declares its gpu:false live set as @2-conforming; @1 receipts verify under @1 forever.
§24.6 Per-kernel determinism-class declaration (new in v0.8.8). A kernel entry in chaingraph.json metadata MAY declare a determinism class: bit-exact (byte-identical across the browser tool, the Cloudflare Worker, and the QuickJS VM, the §24 default for gpu:false live kernels), replayable (same output for the same inputs but not cross-surface byte-identical), seeded-stochastic (§24.6.2, draws from a declared pseudo-random generator, bit-identical on replay from the declared seed), or estimated (stochastic / model-output whose replay bounds are not bit-exact). The declaration is TESTED, not merely asserted: the existing §4 parity and finite gates are RE-BOUND per class, so a bit-exact kernel MUST pass the cross-surface byte-identity check while an estimated kernel is exempt from byte-identity but MUST still pass finite-output and idempotency-within-tolerance. A kernel whose declared class does not match its measured behavior MUST fail. It refines §24's global profile to per-kernel granularity, complementing rather than replacing it, and the declared class is hash-excluded metadata that moves no execution_hash. Gate (existing, extended, no new gate and no new §15 row): determinism-replay.test.mjs and kernel-contract.test.mjs read the declared class and assert the class-appropriate property; golden-parity.test.mjs enforces cross-surface identity for bit-exact. Attribution: FrankenSim determinism-class idea, IDEAS ONLY (same license caveat as §21.5), modeled alongside the W3C WebAssembly Deterministic Profile already cited in §24.
§24.6.1 quantization_parity sub-declaration (new in v0.8.8, ZKML-GUEST-1-BUILD-SPEC.md §ZG-3). A bit-exact kernel that performs pure-integer inference over a statically quantized model MAY attach a hash-excluded top-level quantization_parity object declaring the float-reference-vs-quantized agreement rate the quantization achieved over a committed, frozen held-out test-vector set: quantization method, bit width, scale/zero_point, granularity, reference-model and test-vector digests, vector count, and a top1-match agreement figure. Field names align ONNX QuantizeLinear and the HuggingFace quantization_config convention. The declaration is metadata about the offline quantization step, never a claim compute() makes: the kernel's integer arithmetic stays 100% deterministic and the float figures are recorded, never evaluated, inside compute(). Same doctrine as §24.6: TESTED, not merely asserted. Gate (existing infrastructure plus one new script, extends §24.6, no new §15 row): quantization-parity.test.mjs re-runs the kernel over the committed test-vector set, reproduces every row's own recorded quantized prediction (kernel-fidelity check), independently recomputes the agreement rate, and fails on drift between the declared and recomputed value. No new hash, no chaingraph_version bump, no envelope change; a kernel with no quantization_parity block is unaffected and fully conformant.
§24.6.2 seeded-stochastic determinism class (new in v0.8.8). A kernel whose output depends on pseudo-random draws MAY declare the class seeded-stochastic instead of estimated. The class is a STRONGER claim than estimated and carries a replay obligation: the kernel MUST record, in the receipt, the prng_algorithm (a named, integer-only generator; the reference deployment uses xoshiro256** seeded through splitmix64), the integer seed, and the draw_count actually consumed; and a replay of the kernel at the SAME policy_parameters and the SAME declared seed MUST reproduce a bit-identical output_payload, hence a byte-identical execution_hash. Floating-point draws are permitted only where the generator's integer state is the seeded quantity and the float derivation is itself deterministic. A kernel that cannot meet bit-identical replay MUST declare estimated, not seeded-stochastic: under-claiming is always conformant (§11), and the weaker class is the conservative default while a kernel's replay behavior is unproven. Same §24.6 doctrine, TESTED, not merely asserted, and this class earns a gate of its own because its claim, replay determinism, is not what the §4 parity and finite gates already measure. Gate (new script, new §15 row): seed-replay.test.mjs re-runs every kernel declaring the class at its own declared seed and requires a byte-identical execution_hash; re-runs it at a tampered seed and requires the hash to CHANGE, which is what proves the seed is load-bearing rather than a decorative field; and exercises both paths unconditionally against a committed reference vector, so replay and tamper-detect stay proven in an estate with zero adopters. No envelope change, no new hash, no chaingraph_version bump. At v0.8.8 the class is specified and not yet adopted: no live kernel declares it, and the Monte Carlo VaR node art-371 carries the prng_algorithm, seed and draw_count fields already but continues to declare estimated, a sound under-claim (§11), because migrating it would move that node's execution_hash and is therefore a kernel-versioning event rather than a spec change.
Normative reference — the profile substrate (normative, new in v0.8.9). §24 opens by naming the W3C WebAssembly 3.0 Deterministic Profile (pinned at Candidate Recommendation, April 2026) as the method this profile is modeled on. This paragraph makes it a normative reference for the substrate as well, which strengthens an existing claim rather than adding one: where a conforming compute binding's engine is a WebAssembly module, that module MUST be executable under the Deterministic Profile — it MUST NOT use relaxed-SIMD instructions, MUST NOT declare or import shared memory or threads, and MUST NOT observe non-deterministic NaN bit patterns. A module meeting those constraints is bit-reproducible across conformant engines by the referenced profile's construction, not merely measured-equal across the surfaces we happen to run; the D1–D7 enumeration in §24.1 remains the OCG-side statement of the same closure and is unchanged. The reference deployment's shipped quickjs-ng guest wasm is the reference module and already meets these constraints; it is asserted profile-clean by a build-time CI check over the shipped binary — a tooling assertion on our own engine, deliberately not a §15 conformance-gate row, because §15 gates run over OCG artifacts that third-party implementers can reproduce and a check on our vendored binary is not reproducible by them. The RISC-V zkVM guest freeze clause (§24.2) is the parallel determinism substrate for the proving tier; the two are cited together and neither is required of the other. No field, no envelope change, no new §15 row.
§18.3 already permits a receipt to be used in an input-hiding mode: policy_parameters may carry a commitment in place of cleartext, §4 recompute becomes unavailable to third parties, and the receipt is the sole verification path. That paragraph states the mode; it does not pin the declaration (which input is private, under which commitment) or the commitment construction. §25 closes that gap: it names a profile, ocg-private-input@1, that makes input-hiding machine-declared and machine-checkable, so a high-value node (sanctions screen, RBC action-level, capital-ratio) can carry it as a first-class, gated claim. It adds no new integrity machinery: the commitment binds through the §4 preimage and the §18 journal that already exist. It adds only a declaration, a named hiding commitment scheme, and a verifier contract.
§25.0 Declaration object. An artifact MAY carry a top-level private_inputs[] array, a map telling a verifier which policy_parameters locations hold commitments rather than cleartext, and under which scheme. Like §16 audit_signature, §20 anchor_bindings, and §23 input_attestations, it is attached and EXCLUDED from execution_hash scope: an artifact with zero entries is byte-identical to a plain v0.8.4 artifact and fully conformant. The binding force does not come from the array; it comes from the commitment sitting inside policy_parameters (§25.2). Each entry carries a pointer (RFC 6901, into policy_parameters, naming WHICH input is private), a commitment (sha256:-prefixed), and a commitment_scheme.
§25.1 Commitment scheme sha256-salted@1 (the crux). A bare SHA-256(cgCanon(input)) is NOT admissible: for low-entropy inputs (booleans, small integers, a list-membership flag, a modest enumerated set) an attacker precomputes the digest of every possible value and recovers the plaintext by table lookup, defeating hiding. The commitment MUST be salted with a high-entropy secret held by the prover: commitment = "sha256:" + hex(SHA-256(salt ‖ cgCanon(input_value))), where salt is a fresh ≥256-bit CSPRNG value and cgCanon is the one §4 canonicalizer. This is simultaneously hiding (a 256-bit secret salt makes precomputation over even a two-element input space infeasible; the standard hash-based commitment, not a bare digest), deterministic given (input, salt) (the same pair yields the same commitment bit-for-bit, reproducible on every §24 surface because SHA-256 and cgCanon are already bit-portable there), and risc0-private-input-bindable (the guest reads both salt and input_value over the zkVM private-input channel, recomputes the commitment in-guest, and commits the commitment, never the salt and never the plaintext, to the journal). The salt MUST be fresh per (artifact, private input) and never reused, and MUST NEVER appear in the artifact (not in policy_parameters, output_payload, the §4 preimage, private_inputs[], or the §18 journal). It is disclosure material held only in the prover's private witness and an optional out-of-band package, exactly as §13.12 SD-JWT salts and §24 D5 CSPRNG material are excluded from the hash, and it lets an authorized verifier holding (salt, input_value) recompute and confirm the commitment, while a public verifier holds neither and relies solely on the proof.
§25.2 Plaintext-exclusion invariant (the profile's defining MUST). For every private_inputs[] entry, the value that pointer resolves to inside policy_parameters MUST BE the entry's commitment string (the sha256: digest) and MUST NOT be the plaintext. The plaintext NEVER appears anywhere in policy_parameters or any §4 preimage. So the §4 execution_hash binds the commitment not the value, the §18 journal (§25.3) binds that same commitment, and there is no location from which a third party can recover the plaintext. A verifier that finds a pointed value which is not the declared commitment MUST FAIL the artifact.
§25.3 Proving and journal binding. A conforming artifact MUST carry a §18 compute_proof (groth16-bn254 recommended). The guest named by imageId reads each private input_value and its salt over the private-input channel (never committed), recomputes each commitment = SHA-256(salt ‖ cgCanon(input_value)) in-guest and commits every declared commitment to the journal, and computes and commits output_payload. The journal thus exposes only the public commitments and the public result, never the salt or plaintext. Proving stays off-band (§18.2): the browser tool, the Worker, and CI only verify, never prove. This is the risc0 private-input pipeline the flagship kernels build against.
§25.4 Verification via validate_private_inputs. A verifier checks, without ever seeing the plaintext: (1) structural, meaning private_inputs[] well-formed, each pointer valid RFC 6901 and resolving into policy_parameters, each commitment a sha256: digest, each commitment_scheme known; (2) plaintext-exclusion (§25.2), the value at each pointer equals its commitment; (3) proof binds commitment, meaning a §18 compute_proof is present, its journal commits every declared commitment, seal verifies against imageId via the §18.1 BN254 Groth16 reference verifier, and imageId is in the node's Graph Index compute_images; (4) output binding, the journal's committed output equals output_payload (§18.0). A public verifier stops here, hiding intact. An authorized verifier additionally takes (salt, input_value) from the disclosure package, recomputes the commitment, asserts equality, and MAY re-run the kernel to reproduce output_payload.
§25.5 Composition with §23 Input Attestations. Hiding and attesting are orthogonal and stack. A §23 input_attestation MAY point at the same policy_parameters pointer to vouch for the committed input. Because §23 evaluates its digest binding against the resolved value, which under §25.2 IS the commitment, the existing §23 rule works unchanged: the attestation vouches for the commitment (a vc-2.0 subject binding the sha256: commitment, or an rfc3161-snapshot whose messageImprint is the commitment), so a public verifier confirms the committed input was vouched for without seeing the plaintext, and an authorized verifier ties the attestation to the cleartext through the disclosure package. Neither section changes to admit the other; the claims are independent and composable.
§25.6 Frozen-envelope invariance. private_inputs is an OPTIONAL top-level property (exactly as §20 anchor_bindings and §23 input_attestations), so $defs/artifact.required, the §4 preimage members, and chaingraph_version 0.4.0 are UNTOUCHED. A verifier correct for v0.8.4 computes an identical execution_hash for a v0.8.5 artifact and MAY ignore private_inputs entirely. The commitment lives in policy_parameters.input_parameters, an already-open object, so seating a sha256: string where a plaintext would otherwise sit is not a schema or envelope change. Any construction that would fold salt or plaintext INTO the preimage is out of profile.
§25.7 Conformance. An implementation MAY declare ocg-private-input@1 for a node whose sensitive input is committed under this profile. Conformance is machine-checked by validate-private-inputs.test.mjs (§15): structural shape, plaintext-exclusion, commitment-scheme validity, and the journal-commits-the-commitment binding, layered on the §18 pairing check compute-proof.test.mjs already runs. The §24.2 freeze discipline applies to the commitment construction: sha256-salted@1's meaning is immutable; a different construction ships as ocg-private-input@<n>, never an in-place edit, so a private-input artifact minted under @1 re-verifies bit-for-bit forever. Like §18, the profile defaults OFF: a node MUST surface that it withholds an input (§18.3) and MUST NOT auto-apply commitment mode.
ocg-control-plane@1 new in v0.9-draft§26.1 Purpose and scope. Defines the artifact vocabulary for connected, operated workflows: executions involving external data retrieval, durable local state, scheduling, human review, or agent delegation, run by the artifact producer on infrastructure it controls (the AINumbers Helm Local Hub). It is a superset layered beside a conformant OCG artifact, not a replacement: a control-plane deployment produces ordinary OCG artifacts for its deterministic compute steps and additionally emits the objects defined here. A verifier ignorant of this profile remains fully correct for the core artifact. Nothing here makes AINumbers an operator of any log, registry, or authority; every durable journal is producer-local, its integrity claims derived from internal hash-chaining plus anchoring to external §20 authorities.
§26.2 Envelope. Every signed object in this profile is an in-toto Statement v1 carried in a DSSE envelope, with subject[] digests of the bound artifacts, a predicateType naming the object kind (§26.4), Ed25519 signing (SHOULD additionally ML-DSA-44, RFC 9964 JOSE identifiers), and JCS-canonical payload JSON before signing. Verifiers accept an envelope as verified when at least one trusted signature family verifies.
§26.3 Workflow manifest binding. A control-plane execution MUST record workflow_manifest_digest, the SHA-256 of the JCS-canonical workflow manifest (trigger, connectors, compute nodes, gates, actions, per-node kernel digests, connector contract digests, environment overlays by reference only, never secret values). Every §26.4 object pertaining to a run carries run_id and workflow_manifest_digest.
§26.4 Object kinds. Ten kinds, schemas SSOT in the Helm repo and mirrored here by fixture: connector_attestation and execution_state are REQUIRED-producible; policy_decision, review_task, review_decision, override, agent_delegation (mirroring the RFC 8693 exchange-chain sub/act shape), external_action_intent, external_action_receipt, and disclosure_receipt are reserved in @1: schema normative, production OPTIONAL until Helm Phase 2+, but a @1 verifier MUST parse and verify any it encounters.
§26.5 Journal and checkpoints. The producer-local journal is append-only, each entry carrying a monotonic journal_seq, stream_id, entry_digest, a per-stream running hash, and the record-keeping fields period_start, period_end, reference_db_version, triggering_input_digest, and humans_involved[] (may be empty, must be present), named to satisfy EU AI Act Art. 12(2)/(3) logging minima directly. Signed checkpoints summarize per-stream state periodically and at run completion, anchorable per §20; unknown anchor types are reported as unrecognized, never as failures.
§26.6 Trust labels. Every claim on a verification surface or in an evidence bundle carries exactly one of hash_verified, kernel_verified, connector_asserted (no claim about payload truth), human_attested, or external_ack_captured. Surfaces MUST NOT collapse these into an undifferentiated "verified" indicator or present any label as establishing source-data truth or regulatory acceptance.
§26.7 Evidence bundles. A self-contained, offline-verifiable archive: a signed bundle manifest listing every included object by digest and trust label, the objects themselves, covering checkpoints, and anchor proofs, with a mandatory redaction profile (no secrets, no raw credential material, no payload above the export's disclosure level). A conformant verifier evaluates a bundle with zero network access; anchor freshness checks are optional and clearly separated.
§26.8 Conformance. A producer conforms iff every emitted object validates against its schema, every envelope verifies (§26.2), running hashes and checkpoints verify for every exported journal segment, and the gate suite's control-plane fixtures pass including tampered negative fixtures (an envelope, a journal segment, and a bundle each proven to fail when altered). A verifier conforms iff it accepts the golden fixtures, rejects every tampered fixture, and reports trust labels per §26.6. The profile is entirely additive: §4 preimage members and chaingraph_version "0.4.0" are UNTOUCHED, no existing execution_hash changes, and a verifier correct for v0.8.12 MAY ignore §26 entirely. §26 is the gap §27 (Human Accountability) deliberately left open.
The §4 hash proves what computed; §16 and §18 prove that it computed correctly; §22 proves who was authorized to run it. None of them records which named human took responsibility for the result, the preparer, reviewer and approver act that a bank examiner, an auditor, or a court actually asks for. §27 adds that layer as a first-class, signed, machine-checkable artifact type without touching any existing hash, envelope, or gate. It follows the standards-precedent verdict: the C2PA to CAWG identity split (assertions about content are separate from the content), in-toto's integer threshold for dual control, SCITT's statements about statements (a receipt about a receipt), and the ZCAP over-abstraction failure, so §27 defines banking-concrete record types rather than a generic workflow-authorization calculus. §26 is reserved for the control-plane profile; §27 deliberately leaves the gap.
§27.0 Additivity and scope (the defining constraint). Every construct here is attached after hashing and EXCLUDED from every existing execution_hash preimage, exactly as §20 anchor_bindings, §23 input_attestations, and §25 private_inputs are. An approval record is a NEW artifact ABOUT an existing sealed artifact (SCITT-style), never a mutation of it: minting, revising, or discarding approval records leaves the subject artifact's execution_hash byte-identical, $defs/artifact.required UNTOUCHED, and chaingraph_version at "0.4.0". A verifier correct for v0.8.11 validates every existing artifact unchanged and MAY ignore §27 entirely. Scope is banking-concrete record types only (preparer, reviewer, approver, submitter over a reporting artifact); §27 defines no generic workflow-engine, no delegation calculus, no policy DSL. It is a FORMAT for evidencing human acts, never a runtime, a queue, an approver directory, or a timer.
§27.1 Roles. §27 defines a closed set of accountability roles: preparer (assembles the reporting artifact), reviewer (checks it), approver or attestor (accepts responsibility, the legally-effective sign-off), submitter (transmits it), and the OPTIONAL model_owner, compliance_officer, and examiner (READ-ONLY: an examiner binding grants inspection, never approval authority). A role binding is a signed record that ties one §9 identity (did:key or LEI) to one role for one subject; its §16 eddsa-jcs-2022 proof MUST verify against that identity. An identity MAY hold more than one role, but §27.3 distinctness is by identity, so one human cannot satisfy a dual-control threshold alone by wearing two role hats.
§27.2 Approval records (SCITT-style statements about statements). An approval record is a conformant OCG artifact whose mandate_type is the accepted envelope value "human_accountability_record" and whose output_payload matches $defs/humanAccountabilityRecord. Its payload carries record_type (role_binding, approval, rejection, override, annotation), role, subject_hash (the sha256:-prefixed execution_hash of the artifact acted upon, the SCITT reference), the acting identity, an optional decision, a reason_code, and a timestamp. The record is itself a first-class artifact with its own §4 execution_hash and MUST carry a §16 whole-artifact proof bound to the named human (an unsigned approval record is NOT conformant §27 evidence, exactly the §22.11 rule). Because it only references subject_hash and never contains the subject's preimage, it has zero impact on the subject's hash: the accountability trail is portable, tamper-evident, and offline-verifiable rather than a row locked inside one vendor's queue.
§27.3 Dual control and thresholds (in-toto integer threshold). A gate MAY require N distinct role-bound identities to have signed approval records over the SAME subject_hash before it is satisfied. This is the in-toto threshold construction: an integer threshold: N on the required role, satisfied when at least N distinct identities (counted by identity.id, NEVER by key, so a single human rotating keys or signing twice counts once) have each emitted an approval record naming that role and subject. dual_control(2) is the N=2 case (the FDIC GENIUS CEO and CFO certification is its first production use). A threshold gate over fewer than N distinct approvers is UNSATISFIED and MUST NOT auto-pass.
§27.4 Gate-policy vocabulary (wired to §21.4). §27 defines a closed gate-policy enum, the $defs/haGatePolicy value applied to a §21.4 decision gate as an OPTIONAL precondition: auto_pass, review_required (at least one reviewer approval), dual_control (the §27.3 threshold), escalate (route to the §22.3 reserved target), hold, reject, and emergency_override (§27.5). A gate carrying a policy is satisfied ONLY when the named approval records exist at the stated threshold over the gate's subject; absent the required records, the gate does NOT fall through to its §21.4 default, because the human precondition is a hard predicate evaluated before the routing rules. This composes with §21.4 without changing its evaluator: the policy is a precondition annotation consumed by the accountability layer, the §21.4 _gateval.mjs routing math is untouched, and a chain with no HA policy evaluates byte-identically to today.
§27.5 Override and waiver (time-boxed §22.10 attenuation). An emergency_override is NOT an unconditional bypass. It is a time-boxed §22.10 attenuated mandate paired with a mandatory evidence bundle carrying at minimum a reason_code, an explicit scope (which gate or subject it covers, narrowing only, never widening, per §22.10 and §SIDECAR.2), an expiry, the authorizing identity, and an immutable link to the overridden artifact's subject_hash. The override is itself a signed record_type:"override" approval record. On expiry the override lapses and the underlying gate policy REVERTS: an expired override MUST be treated as absent, so the gate returns to review_required, dual_control, or hold as originally specified. A timeout MUST NEVER resolve to a silent permanent auto-pass.
§27.6 Evidence bundle (§13.12 SD-JWT export). The evidence bundle is a profile object, $defs/haEvidenceBundle, aggregating the trail for one subject: input-hash lineage, kernel version (§12 and §17 build identity), policy version, the subject_hash, the §16/§18/§20 verification result, exception rationale, annotations, the reviewer and approver identities with their signed approval-record hashes, timestamps (with optional §20 anchor bindings), and a submission-receipt slot populated only after a real transmission, never fabricated. The bundle is exportable via §13, and specifically via §13.12 SD-JWT selective disclosure, so an examiner receives exactly the fields their remit requires and no others: approver identities and the verdict are always-disclosed, while selectively-disclosable input values follow §13.12 (disclosure salts the sole permitted nondeterminism, never touching any execution_hash). A redacted bundle is NOT re-executable (§13.12's limitation); it evidences the human trail, not a recomputation.
§27.7 Separation of concerns (four distinct artifact types). §27 keeps four routinely-conflated things structurally separate, each its own artifact type: calculation or validation (a §4/§18 compute result, what the numbers are); recommendation (a machine or model suggestion, what could be decided); human judgment (a reviewer or annotation record, a person's assessment); and legally-effective sign-off (an approver or attestor approval record, a person accepting responsibility). An approval record is evidence OF a human act, NOT a claim of regulator acceptance: §27 produces the accountable-signature artifact a filing requires, and explicitly does not assert that the artifact is regulator-submittable or that the computation substitutes for the human's judgment (the §0 no-filing-claims and §18.3 evidence-not-unsupervised-automation doctrine, made concrete for the sign-off layer).
§27.8 Agent-parity clause. An agent MAY create review tasks and MAY submit approval records, but ONLY under a §22 mandate whose scope explicitly grants the corresponding §27.1 role to that agent's identity. An agent MUST NOT satisfy a review_required or dual_control gate absent an explicit human-role mandate: the default posture is that these gates require a named human, and an agent-signed approval record counts toward a threshold only when a human principal has, in a signed mandate, delegated that exact role to the agent within a validity window. Counted-resume (§22.11) is the mechanism for human approval events and is referenced here, not duplicated: a §27 dual_control threshold and a §22.11 resume_approval.required_events are the same integer-threshold discipline viewed from the accountability side. This closes the obvious failure mode: an autonomous agent MUST NOT quietly become both preparer and approver of its own output.
§27.9 Conformance. An implementation MAY declare human-accountability support for a node or chain; the declaration asserts §27.0 through §27.8 hold for that surface. Conformance is machine-checked by validate-ha-records.test.mjs (§15): the approval-record shape ($defs/humanAccountabilityRecord, closed record_type, role, and gate-policy enums, subject_hash a valid sha256ref); the additivity invariant (an approval record's presence leaves the subject's execution_hash byte-identical, and a subject with zero HA records is byte-identical to a plain v0.8.11 artifact); threshold distinctness (dual_control at N counts distinct identities, so a repeated identity satisfies only N=1 while two distinct identities satisfy N=2); override expiry (an expired override reverts the gate policy, never a silent permanent pass); and the signed-named-human requirement (an unsigned approval record is rejected, layered on the §16 proof-binding.test.mjs round-trip). Like §18 and §25, the layer defaults OFF: a node MUST NOT synthesize accountability records, and absence of §27 records is fully conformant and carries no meaning. Backward compatible and purely additive: no existing artifact, hash, gate, or golden vector moves. Attribution: the identity split follows C2PA and CAWG, the integer threshold follows in-toto, the statement-about-statement framing follows IETF SCITT, and ZCAP-LD is cited as the anti-pattern the §27.0 scope discipline avoids; no text or code is copied.
For a worked walkthrough of a preparer to reviewer to dual-control approval trail, see the Human Accountability explainer.
The standard is enforced by executable gates, not by prose. SPEC.md §15, in the normative single source of truth this page renders, carries a rule-to-gate matrix: every normative rule MUST name at least one on-disk gate script that checks it, and a meta-gate parses that matrix and fails if any rule lacks a wired gate. A rule with no live gate is, by definition, not part of the standard.
The practical consequence is that conformance is a decidable property, not a matter of interpretation. An implementation conforms if and only if it validates against the published JSON schema and passes the gate suite. "v0.4-compliant" means exactly one thing: passes the SPEC.md §15 gate suite.
The suite spans strict schema validation of the artifact envelope and Graph Index node object (schema-validate.mjs), a single version of record across every public surface (spec-version-consistency.mjs), the rule-to-gate meta check itself (spec-gate-coverage.mjs), surface and catalog parity (surface-parity.mjs, catalog-parity.mjs), canonicalization lints against the §4 forbidden constructions, kernel determinism checks, and sign-and-verify round trips for the §16, §17, and §18 attestation layers. After every deployment, a sweep re-verifies that each live node still recomputes a valid execution_hash and a smoke test performs a real MCP handshake against the live endpoint.
The full matrix and gate list live in SPEC.md §15 in the public standard repository. For the reasoning behind conformance-by-construction, see Section 9 of the white paper; for a gentler introduction to the whole standard, see the interactive explainer.
spec_version of record in chaingraph.json stays 0.8.8 until the next coordinated landing moves it, the same separation the v0.8.9 through v0.8.11 text passes used. §27 records which named human took responsibility for a result, the layer the §4 hash, §16/§18 proofs, and §22 mandates never captured. It defines a closed set of roles (§27.1: preparer, reviewer, approver or attestor, submitter, plus optional model_owner, compliance_officer, and a read-only examiner); SCITT-style approval records (§27.2) that are NEW artifacts ABOUT a sealed artifact, referencing its execution_hash through subject_hash and carrying their own §16 named-human proof (an unsigned record is not conformant evidence); in-toto integer dual_control(N) thresholds counted over DISTINCT identities, never keys (§27.3); a closed gate-policy vocabulary wired to §21.4 as a precondition that leaves the _gateval.mjs routing math untouched (§27.4); a time-boxed emergency_override that is a §22.10 attenuation plus a mandatory evidence bundle and reverts the gate policy on expiry, never a silent permanent pass (§27.5); an evidence bundle exportable via §13.12 SD-JWT selective disclosure (§27.6); a four-way separation of calculation, recommendation, human judgment, and legally-effective sign-off (§27.7); and an agent-parity clause requiring an explicit §22 human-role mandate before an agent may satisfy a review or dual-control gate (§27.8). Purely additive: every construct is attached after hashing and EXCLUDED from every execution_hash preimage, $defs/artifact.required and chaingraph_version "0.4.0" are UNCHANGED, and absence is conformant and carries no meaning. The schema adds $defs/humanAccountabilityRecord, haEvidenceBundle, haGatePolicy, haRole, and haApprovalThreshold, and human_accountability_record joins the recommended (non-enforced) mandate_type taxonomy. One new §15 gate row (validate-ha-records.test.mjs) proves shape, additivity, threshold distinctness, override expiry, and the signed-named-human requirement. §26 is reserved for the control-plane profile, so §27 leaves the gap deliberately. Attribution: C2PA and CAWG (identity split), in-toto (threshold), IETF SCITT (statement about statement), ZCAP-LD (cited anti-pattern), patterns only with no code or text copied.
policy_parameters_hash. This is a spec-text pass, not a record bump: the spec_version of record in chaingraph.json stays 0.8.8 until the next coordinated landing moves it, the same separation v0.8.7 through v0.8.9 used. §PPH-1 defines an OPTIONAL top-level member equal to the JCS-SHA-256 of policy_parameters alone, computed through the one canonical _hash.mjs path and typed #/$defs/sha256ref — bare hex, the sha256: prefix optional. Producers should emit bare, since the shared digest path returns bare hex; verifiers must accept either and must not fail a value for carrying or lacking the prefix. No general prefix rule is claimed: both candidate rules are falsified by the shipped schema — self-digest-vs-foreign-hash dies on chain.parent_hashes[], and producer-format dies on §20 anchored_hash — so the counterexamples are recorded and no third rule is proposed. It is excluded from the §4 preimage, so canonicalPreimage() is unchanged, chaingraph_version stays "0.4.0", and every pinned golden vector is byte-identical. Adoption is per-implementation and absence is conformant and carries no meaning — a verifier must read nothing into an omitted member. It is a content digest, not a signature: it adds no authority §16 does not already supply, and it is not a §HASHRES-1 resolution address. This closes a live incoherence rather than adding a capability — the v0.8.9 §XMAP-1 annex already named this member as the anchor for AGT covenantHash, agent-receipts action.parameters_hash and AGA arguments_hash, but nothing defined it and $defs.artifact rejected it outright, so an implementer following the annex would have produced artifacts that fail validation. The annex text is unchanged and is not restated. Placement is at artifact level, deliberately not nested under audit_signature: that subschema is permissive and would have needed no schema change, but a content digest is not signing metadata and all three mapped formats place their equivalent at artifact level. One new §15 gate row proves the exclusion claim directly and asserts mutation-sensitivity, which catches a real trap — cgCanon returns a sorted object, so digesting it without JSON.stringify yields a constant hash that passes a determinism check. No kernel emits the member yet; emission is a separate row.
spec_version of record in chaingraph.json stays 0.8.8 until the next coordinated landing moves it, the same separation v0.8.7 and v0.8.8 used. §23.4 (attestation freshness and consent) already carried the 0.8.9 label from an earlier pass and belongs to this line. §22.11 adds an OPTIONAL two-class exception taxonomy (business = no retry, routes to a human queue; application = retry-to-N then escalate), exception_detail {type, code, message}, per-item terminal states with sibling isolation, and a counted-resume approval gate resume_approval {required_events, approver_group, resume_form, timeout} where a timeout MUST escalate and never silently auto-approves, and every resume message and exception record MUST carry a §16 named-human proof. §20 gains an informative post-quantum note: OpenTimestamps carries no signature primitive and is the PQ-resilient anchor, while rfc3161-tst inherits classical-signature exposure (no public PQC TSA exists as of 2026-07-18 — a WATCH item). §24 gains a normative reference making the W3C WebAssembly 3.0 Deterministic Profile the substrate as well as the method, upgrading the byte-parity claim from measured to profile-guaranteed. §XMAP-1 adds an informative annex mapping AGT / agent-receipts / AGA onto OCG members via the OPTIONAL hash-excluded policy_parameters_hash alias, with partial coverage labeled as partial and the AGA column recorded as a dated (2026-07-16) INTEROP-ONLY observation with its patent posture noted — not an endorsement, and no AGA export profile. Fully additive: no envelope/hash/schema change, chaingraph_version stays "0.4.0", every existing execution_hash is byte-identical, and every new normative MUST binds to an existing §15 gate.
claim_strength weakest-link composite), §22.9 (signed AR4SI / RATS-EAR failure receipts, AR4SI pinned at draft -10), §22.10 (Biscuit offline attenuation-block chaining, construction only), §24.6 (per-kernel determinism-class declaration) and §24.6.1 (quantization_parity sub-declaration) all landed in an earlier additive pass; this release moves the spec_version of record from 0.8.7 to 0.8.8 across every declared surface. New in this release: §24.6.2 adds the seeded-stochastic determinism class, a stronger claim than estimated that requires a declared prng_algorithm, integer seed and draw_count in the receipt and bit-identical replay from that seed, gated by seed-replay.test.mjs (replay against a committed reference vector plus a tampered-seed negative fixture that must fail). The class is specified and not yet adopted: no live kernel declares it, and the Monte Carlo VaR node continues to declare the sound weaker class estimated. Fully additive: no envelope/hash/schema change, chaingraph_version stays "0.4.0", every existing execution_hash is byte-identical.
spec_version of record from 0.8.6 to 0.8.7 across every declared surface. §HASHRES-1 adds a Ledger hash-resolution addressing contract (RFC 6920 / ISO 18670 SWHID); §PQC-1 adds a hybrid dual §16 Data Integrity proof over the same JCS bytes (eddsa-jcs-2022 + a TBD ML-DSA cryptosuite); §REVOKE-1 adds an OPTIONAL W3C BitstringStatusList revocation reference; §SIDECAR adds small riders (Identity Sidecar, tiered OCG-Verify/OCG-Execute/OCG-Prove labels, a reserved resource-narrowing invariant, Vouch Protocol prior-art). Fully additive: no envelope/hash/schema change, chaingraph_version stays "0.4.0", every existing execution_hash is byte-identical.
@2: WebCrypto subset split. A new profile version ocg-deterministic-compute@2, a new named profile ALONGSIDE @1 per the §24.2 freeze clause (never an in-place edit), that keeps D1 through D6 unchanged and ENUMERATES the WebCrypto split inside D7. ALLOWED as fully-specified deterministic replacements: crypto.subtle.digest (SHA-256/384), importKey, verify, all pure functions of their inputs, MUST be byte-identical across browser / Worker / QuickJS VM. BANNED as D5 randomness: crypto.getRandomValues, generateKey, sign (fresh key) MUST throw. Additive: moves NO execution_hash (all six previously-VM-unrunnable kernels emit the same bytes the Worker already produces), the frozen v0.4 envelope and §4 preimage UNTOUCHED, chaingraph_version stays "0.4.0", only spec_version bumps to 0.8.6. The reference deployment re-declares its gpu:false live set as @2-conforming; @1 receipts verify under @1 forever.
ocg-deterministic-compute@1 that NAMES the determinism §4, §12, §17, §18.5/§18.6, and §21.4 already enforce, modeled on the W3C WebAssembly 3.0 Deterministic Profile with a RISC-V-style freeze clause (a ratified kernel-semantics version is never revised; changes ship as a new profile version). It enumerates seven kernel nondeterminism sources (non-finite floats, iteration order, transcendental math, wall-clock time, randomness, locale/Intl, environment APIs) and binds each to the EXISTING §15 gate that fixes or bans it. Adds NO new machinery, NO §15 row, and NO obligation beyond a §15-green implementation. No envelope or hash change: chaingraph_version stays "0.4.0" and every existing execution_hash is byte-identical; only spec_version bumps to 0.8.4.
input_attestations[] array carrying per-input evidence that a named input was vouched for by an external source, without changing what execution_hash means. Like §16 audit_signature and §20 anchor_bindings it is attached AFTER hashing and EXCLUDED from the preimage, so a zero-attestation artifact stays fully conformant and every existing execution_hash is byte-identical. Each entry is { type, pointer, proof, source_ref }, pointer an RFC 6901 JSON Pointer into policy_parameters. Type phasing (D2): vc-2.0 (§16/§13.11 Data Integrity + subject-digest), rfc3161-snapshot (the §20 rfc3161-tst verifier), and c2pa-manifest (structural + hard-binding digest) verify now; zktls is defined with EXTERNAL verification (no vendored verifier). A verifier reports per-input status INDEPENDENTLY of the hash verdict.
$defs/inputAttestation definition and an OPTIONAL input_attestations property on the artifact object (exactly as anchor_bindings was declared). $defs/artifact.required untouched; chaingraph_version stays "0.4.0"; only spec_version bumps to 0.8.3.
schema-validate.mjs (shape + optional property, envelope unchanged) and validate-input-attestations.test.mjs (pointer resolution, per-type digest binding + verification, zero-attestation hash identity, tamper-detect). The attestation-specific gate is verified in the worker CI.
mandate_type:"work_mandate") that delegates bounded authority: scope, conditions, escalation_triggers, validity, and principal in its output_payload, with a REQUIRED §16 signature (an unsigned mandate is a non-enforceable draft) and the mandate execution_hash as its identifier. §22.4 pins the deterministic compile_work_mandate I/O contract (mandate to §21.4 gated-chain config, conditions to continue rules, triggers to "escalate" rules, mandatory default, byte-identical output per mandate). §22.5 pins the run_chain mandate binding: an OPTIONAL mandate whose mandate_hash folds into every step and the composite as a conditional-presence key, so a no-mandate run stays hash-identical to pre-mandate behavior. The "escalate" target name is reserved; its evaluator semantics (halting, the open escalation record) are forthcoming.
workMandateDocument $def (plus workMandateCondition) for the mandate output_payload, not referenced by the top-level oneOf, so existing artifact and catalog validation is unchanged. $defs/artifact untouched; chaingraph_version stays "0.4.0"; only spec_version bumps to 0.8.1.
schema-validate.mjs (mandate $def present, envelope unchanged), compile-mandate-determinism.test.mjs (compiler byte-determinism), validate-chains.mjs ("escalate" accepted as a reserved forward target), and mandate-binding.test.mjs with linear-hash-freeze.mjs (runtime binding and no-mandate hash freeze). The two mandate-specific gates are verified in the worker CI.
run_chain contract is made normative (§21.1–§21.3: array-order steps, RAN-step composite preimage, parent/depth threading), and forward-only decision gates are added on top (§21.4). An optional step id + gate {input: RFC 6901 pointer, rules:[{op,value?,next}], default} routes control to a later step id or "end"; closed op enum, strict typing, first-match, mandatory default (total function), acyclic by construction; jumped steps get skipped_by_gate. Hash binding is CONDITIONAL-PRESENCE: route_plan_digest + decisions[] + path_taken[] enter the composite preimage ONLY for chains with ≥1 gate, so every existing linear chain's composite_execution_hash is frozen. ONE pure evaluator (kernels/_gateval.mjs) on every surface; no expression language, no second canonicalizer.
merkle_inclusion {leaf, index, path[], tree_size, algorithm:"rfc6962"} on rfc3161-tst / opentimestamps bindings: the artifact execution_hash is a LEAF of an RFC 6962 tree whose ROOT is the anchored value, so one timestamp covers many artifacts. Verifier reconstructs the root from leaf+path and requires root == anchored_hash and leaf == recomputed execution_hash. Reuses the shipped Merkle primitives; outside the hash preimage.
spec_version pattern to ^0\.[45678]\.[0-9]+$; documented optional id/gate on $defs/chain steps and optional merkle_inclusion on the two anchor types. Filename stays openchain-graph-v0.4.schema.json; $defs/artifact untouched; chaingraph_version stays "0.4.0"; only spec_version bumps to 0.8.0.
linear-hash-freeze.mjs (no linear composite hash moves; goldens captured pre-change), validate-chains.mjs Layer 4 + gate-static.test.mjs (gate static validity), gate-semantics.test.mjs (evaluator vectors), gate-branch-coverage.test.mjs (both-branch fixtures), gate-parity.test.mjs (Worker/embed byte-parity); anchor-binding.test.mjs extended for §20.1.
execution_hash was included in a transparency log or timestamp service by a point in time, at the OPTIONAL top-level anchor_bindings array (attached after hashing, excluded from hash scope). Evidence types: rfc3161-tst (verbatim TST DER), opentimestamps, c2sp-tlog-proof-v1, scitt-receipt-rfc9942. Proves existence-by-a-time and log inclusion, never correctness or authorship.
execution_hash, versions, compute_capability, §17 identity fields, all outputs, timestamps; selectively disclosable: top-level input values only. JWS (EdDSA) under the §16 key. A redacted export is not re-executable.
audit_signature.proof MAY be an array (VC Data Integrity proof-set semantics); an endorsement countersignature MUST chain via previousProof, verified in dependency order. eddsa-jcs-2022 throughout.
supersedes. Top-level array of sha256:-prefixed execution_hashes this artifact corrects or replaces, declared at creation. No reverse link, no status registry. No envelope, hash, or schema-filename change anywhere in v0.7: chaingraph_version stays "0.4.0"; only spec_version bumps to 0.7.0.
ocg-p18-deterministic). Additive, opt-in conformance profile: under it every status:"live" gpu:false node MUST carry a verifying compute_proof or declare compute_proof_ready:"deferred" with a reason; gpu:true is out of scope (prohibitive in-guest proving cost, §18.2). Base §18 stays OPTIONAL for external implementers. Machine-checked by check-compute-proof-coverage.mjs. No envelope, hash, or schema change: chaingraph_version stays "0.4.0"; only spec_version bumps to 0.6.1. The AINumbers reference deployment conforms with zero deferrals.
audit_signature.build_identity ({kernel_digest, buildType, source_ref?}), closing the §4 gap that execution_hash does not pin which logic ran. kernel_digest = WebCrypto SHA-256 over the LF-normalized kernel source via the shared kernels/_buildid.mjs; published per-node in Graph Index compute_images[] and cross-checked artifact ↔ Graph Index ↔ recomputed source. Caveat (§17.2): an advisory claim of which source ran, not a proof of execution.
audit_signature.compute_proof ({type:"ZkVmReceipt", system, receiptFormat, imageId, seal, journal}), turning the §4 hash into a succinct proof of correct execution: verifiable without re-execution and, optionally, without seeing the inputs (§18.3). OCG's analogue of Trusted Compute Units, but software/cryptographic only (no TEE, no enclave, no chain). System-agnostic; groth16-bn254 RECOMMENDED. Seal crypto-verify DELEGATED to the named zkVM verifier (§18.1); proving is off-band (§18.2). Default-off.
$defs.buildIdentity + $defs.computeProof; optional audit_signature.build_identity + audit_signature.compute_proof; optional node compute_images[]; widened the catalog spec_version pattern to ^0\.[456]\.[0-9]+$. Filename stays openchain-graph-v0.4.schema.json. All three fields are hash-excluded and ride under the tolerant audit_signature object: a v0.6 artifact still validates under the frozen v0.4 schema; hash preimage unchanged; chaingraph_version stays "0.4.0"; only spec_version bumps to 0.6.0.
kernel-identity.test.mjs (§17): digest determinism + LF-normalization, the three-way cross-check, tamper-detection, backward-compat. compute-proof.test.mjs (§18): binding (structure + journal↔output + imageId↔compute_images), tamper-detection, backward-compat, and that verifySeal() throws (seal verification is delegated, never silently skipped).
eddsa-jcs-2022) at audit_signature.proof, signing the whole artifact via the same §4 JCS canonicalizer (kernels/_hash.mjs): no second canonicalization path. Turns the execution_hash from tamper-evidence into authenticated attestation; fills the §13.11 "no securing proof" gap. Default-off and holder-chosen (signing de-anonymizes the run). did:key for ephemeral signing; did:web + HSM/KMS + server-side for institutional issuers (§16.4).
$defs.dataIntegrityProof + optional audit_signature.proof; widened the catalog spec_version pattern to ^0\.[45]\.[0-9]+$. Filename stays openchain-graph-v0.4.schema.json (envelope unchanged). Hash preimage unchanged; chaingraph_version stays "0.4.0"; unsigned artifacts byte-identical to v0.4.1: only spec_version bumps to 0.5.0.
proof-binding.test.mjs (§15): asserts sign→verify round-trip, tamper-detection (execution_hash / output_payload / proofValue), determinism (byte-identical proofValue), and backward-compat (unsigned artifact unchanged, signing mints no new hash and no chaingraph_version bump).
chaingraph_export umbrella with sub-profiles xlsx, pdf, csv, xbrl: generated, hash-excluded, server-side (Worker) renderings of a verified artifact. Added the read-only export_artifact MCP tool (full-artifact input; standard-base64 blob or MCP resource output) and the per-node export_capability field in chaingraph.json. XBRL defines a taxonomy-per-regime mapping (EBA COREP own-funds + LCR/NSFR pilots; ocg-ext:* for regimes without a standard taxonomy). Hash preimage unchanged.
compute_capability field to all live nodes ("server" | "browser" | "auto"); added compute override parameter on all ChainGraph MCP tools; compute_mode field in artifact envelope. Kernels extracted for art-05 (EU AI Act credit-scoring conformity), art-06 (GENIUS Act reserve attestation), art-09 (DORA incident classifier), art-29 (DORA readiness diagnostic): joining art-01, art-03, art-04, art-11, art-12. chaingraph_version advanced to "0.4.0" in artifacts. Hash preimage unchanged.
validate-chains.mjs. Errors pre-deploy if any two live nodes share an mcp_name, or if any node name collides with a PILOT widget or utility tool name: the class of registration crash that caused the 2026-06-19 outage. Complements the existing runtime dedup guard in worker.mjs.
scripts/smoke-mcp.mjs). Performs a real MCP initialize handshake against the live endpoint after every deploy, with 6 retries and 4s edge-propagation delay. Catches runtime registration throws that the bundle dry-run cannot catch. A failure turns the deploy job red immediately.
execution_hash as a WebCrypto SHA-256 over an RFC 8785 (JSON Canonicalization Scheme)–aligned serialization of {policy_parameters, output_payload} — recursive lexicographic key sort, minimal whitespace, constrained to the I-JSON subset — so any party can independently reproduce the hash from an exported artifact. Verification normalizes the optional sha256: prefix on either side. This canonicalization is enforced in CI by a parity self-test, a forbidden-pattern lint, and golden-snapshot checks. Hash preimage definition unchanged from §2; spec version stays 0.3.1.
dct:conformsTo (W3C PROF + Content Negotiation by Profile); semantic_profile retained as a registered token alias; token↔URI registry at profiles/registry.json. Hash preimage unchanged.
iso20022:party-identification profile (REDA/ACMT-aligned PartyIdentification subset: party_name + lei) for party-only payloads, so non-payment nodes can declare conformance truthfully instead of being force-tagged with a payment profile.
chaingraph.json (ocg namespace and ocg:spec_version advanced from 0.2 to 0.3.1); added dataset-level dct:conformsTo on profiled nodes.
chaingraph_version as the single version anchor (Standard §1). The legacy tool-export field ap2_version (value "1.0.0") is a deprecated alias, it is NOT the AP2 standard version (Agent Payments Protocol is at v0.2), retained for back-compat and slated for removal in v0.4. Genuine AP2 conformance is now declared explicitly via dct:conformsTo → the AP2 v0.2 spec on the tools that validate AP2 structures (ART-01, ART-15/16/17, ART-30, the AP2 payments checker), not via a blanket version field.
openchain.graph to the live ainumbers.co/chaingraph base (Option B). Profile URIs use the .jsonld extension so they dereference on static hosting.
guide-profiles.html (now eight integration guides).
ap2_version still accepted (deprecated). Internal chaingraph_version and folder paths unchanged.
iso20022-context.jsonld) mapping amount/party/agent payload fields to ISO 20022 business elements, with external code lists (ISO 4217/3166/9362/13616/17442) adopted by reference. Minimal, optional, additive. Hash preimage unchanged.
did:key as the recommended keyid form, and ISO 17442 LEI on publisher/tool identity, optionally bound to the Ed25519 signing key.
ocg:semantic_profile marker on Graph Index nodes so crawlers and agents can discover ISO 20022-aligned tools.
chaingraph.json: the narrative knowledge layer beside the DCAT catalog. OKF concepts are knowledge, never decision artifacts.
guide-iso20022.html and guide-okf.html to the integration-guide series (now seven: PROV-DM, buildType, in-toto, Ed25519, OTel, ISO 20022, OKF).
buildType is unchanged: the WebCryptoSHA256 URI is a version-independent identifier of the hash construction, which is byte-identical in v0.3. v0.3 artifacts retain https://ainumbers.co/chaingraph/context/v0.2#WebCryptoSHA256; only @context and chaingraph_version advance to v0.3 (minting a new buildType URI is reserved for an actual algorithm change).
chaingraph_version and folder paths are unchanged.
"@context" field pointing to the published JSON-LD context. Artifacts without this field remain valid (backwards compatible).
"buildType" URI identifying the hash construction algorithm. Default value defined for WebCrypto SHA-256.
audit_signature from an opaque field to an in-toto Attestation Framework (ITE-6) envelope with payloadType, payload, and signatures array. Bare-string values from v0.1 are still accepted by verifiers.
audit_signature.signatures.
ocg:signing_keys per tool for Ed25519 key lookup. Added dcat:accessService per tool pointing to the MCP endpoint.
chaingraph_version, x-chaingraph, folder paths) remain unchanged.
execution_hash (SHA-256 over inputs and outputs); each artifact may cite parent artifact hashes in a chain.parent_hashes array, forming a verifiable directed acyclic graph (DAG) of linked decisions. Any party can independently verify integrity by re-running the hash — no trusted authority required.