ReferenceSpec
Receipt Format Reference
The ChioReceipt fields, signing process, and verification procedure for mediated tool invocations.
ChioReceipt
The ChioReceipt is the top-level signed structure. Every field is present in the canonical JSON serialization.
| Field | Type | Description |
|---|---|---|
id | string | Content-addressed receipt ID: the SHA-256 hex digest of the canonical JSON (RFC 8785) of the receipt's identity-defining fields (ChioReceiptIdInput — every field except id, signature, bbs_signature, and algorithm). The digest identifies the receipt; it is neither caller-assigned nor time-ordered. |
timestamp | u64 | Unix timestamp (seconds) when the receipt was created |
capability_id | string | ID of the capability token that was exercised or presented |
tool_server | string | Tool server that handled the invocation |
tool_name | string | Tool that was invoked or attempted |
action | ToolCallAction | The tool call action that was evaluated (parameters + hash) |
decision | Decision (optional) | The kernel's verdict on this tool call. Present only on mediated_decision receipts (the prevent + mediated case). trace_observation and advisory_evaluation receipts omit it entirely |
receipt_kind | ReceiptKind | Signed semantic class of the receipt: mediated_decision, trace_observation, or advisory_evaluation. Determines whether the receipt is authorization or evidence |
boundary_class | BoundaryClass | Signed runtime boundary: prevent, detect_only, advisory_only, or cannot_see. A signed receipt never carries cannot_see |
observation_outcome | ObservationOutcome (optional) | observed, evaluated, or dropped. Carried only on trace_observation and advisory_evaluation receipts; omitted on mediated decisions |
tool_origin | ToolOrigin | Where the tool effect executed relative to the kernel: caller_executed, host_executed_provider_reported, or host_executed_unmediated |
redaction_mode | RedactionMode | Redaction applied to signed or exported receipt details: none, summary, or redacted |
actor_chain | ActorRef[] | Signed actor-attribution chain (each entry an actor_id with optional actor_kind). Omitted from the wire when empty |
content_hash | string | SHA-256 hash of the evaluated content |
policy_hash | string | SHA-256 hash of the policy that was applied |
evidence | GuardEvidence[] | Per-guard evidence collected during evaluation (omitted when empty) |
metadata | JSON (optional) | Optional receipt metadata for financial, governed-transaction, or runtime-assurance details |
trust_level | TrustLevel | Strength of kernel mediation: mediated, verified, or advisory. Bound to receipt_kind: mediated_decision receipts require mediated, trace_observation requires verified, and advisory_evaluation requires advisory. Older receipts omitting this field deserialize to mediated |
tenant_id | string (optional) | Tenant identifier for multi-tenant deployments, derived from the authenticated session (never from caller-provided fields). Omitted from the wire when unset. Part of the signed body, so verifiers computing canonical bytes without this field will fail on multi-tenant deployments |
bbs_projection_version | string (optional) | Selective-disclosure projection version, fixed at chio.bbs-projection.receipt.v1. Present together with bbs_signature |
kernel_key | PublicKey (hex) | The kernel's verifying public key, in the same algorithm-aware form used across v1: bare 64-hex for Ed25519, p256:<130-hex> for SEC1 P-256, or p384:<194-hex> for SEC1 P-384. Lets a verifier check the signature without an out-of-band key lookup |
bbs_signature | BbsReceiptSignature (optional) | BBS selective-disclosure material over this receipt. When present it is covered by the authoritative signature and travels with bbs_projection_version |
algorithm | SigningAlgorithm (optional) | Envelope hint for signature (ed25519, p256, or p384). Absent means Ed25519. Informational only: verification dispatches off the self-describing signature encoding, not this field |
signature | Signature (hex) | Algorithm-aware signature over the canonical JSON of ChioReceiptSigningBody ({ id, body, bbs_signature? }). The schema regex is ^([0-9a-f]{128}|p256:[0-9a-f]+|p384:[0-9a-f]+)$: bare 128-hex for Ed25519, p256:<DER hex> for P-256, or p384:<DER hex> for P-384 |
Receipt body vs. signed receipt
ChioReceiptSigningBody wrapper: the content-addressed id, the identity-defining ChioReceiptIdInput fields, and the optional bbs_signature. The default algorithm is Ed25519, which hashes internally as part of EdDSA (no separate SHA-256 pre-hash); P-256 and P-384 sign the same canonical bytes and are distinguished by the self-describing signature encoding.Only mediated decisions authorize
mediated_decision receipt with boundary_class = prevent and an Allow decision may ever be displayed or exported as authorization. trace_observation and advisory_evaluation receipts are evidence, never authorization.Decision
The Decision enum represents the kernel's verdict. It is serialized as a tagged union with a verdict discriminator field.
| Variant | Payload | Description |
|---|---|---|
allow | none | The tool call was allowed and executed |
deny | reason (string), guard (string) | The tool call was denied. Includes the human-readable reason and the guard that triggered the denial |
cancelled | reason (string) | The tool call was interrupted by explicit cancellation |
incomplete | reason (string) | The tool call did not reach a complete terminal result |
JSON Serialization
{
"verdict": "allow"
}{
"verdict": "deny",
"reason": "path matches forbidden pattern **/.env",
"guard": "forbidden-path"
}{
"verdict": "cancelled",
"reason": "user cancelled the operation"
}{
"verdict": "incomplete",
"reason": "tool server did not respond within timeout"
}ToolCallAction
Describes the tool call that was evaluated. Contains the raw parameters and their canonical hash for integrity verification.
| Field | Type | Description |
|---|---|---|
parameters | JSON value | The parameters that were passed to the tool (or attempted) |
parameter_hash | string | SHA-256 hash of the canonical JSON of parameters |
The parameter_hash is computed by serializing parameters to canonical JSON (RFC 8785), then computing the SHA-256 hex digest. This allows verifiers to confirm that parameters were not modified after receipt creation.
{
"parameters": {
"path": "./workspace/README.md"
},
"parameter_hash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}GuardEvidence
Evidence from a single guard's evaluation. The evidence array contains one entry per guard that was evaluated.
| Field | Type | Description |
|---|---|---|
guard_name | string | Name of the guard (e.g. forbidden-path) |
verdict | bool | true if the guard passed, false if it denied |
details | string (optional) | Optional details about the guard's decision |
[
{ "guard_name": "forbidden-path", "verdict": true },
{ "guard_name": "path-allowlist", "verdict": true },
{ "guard_name": "shell-command", "verdict": true },
{ "guard_name": "egress-allowlist", "verdict": true },
{ "guard_name": "mcp-tool", "verdict": true },
{ "guard_name": "secret-leak", "verdict": true },
{ "guard_name": "patch-integrity", "verdict": true },
{
"guard_name": "forbidden-path",
"verdict": false,
"details": "path matches forbidden pattern **/.env"
}
]Financial Metadata
For tool calls governed by monetary grants, the metadata field includes a financial key with detailed cost and budget information.
| Field | Type | Description |
|---|---|---|
grant_index | u32 | Index of the matching grant in the capability token's scope |
cost_charged | u64 | Cost charged in currency minor units (e.g. cents for USD) |
currency | string | ISO 4217 currency code (e.g. USD) |
budget_remaining | u64 | Remaining budget after this charge, in minor units |
budget_total | u64 | Total budget for this grant, in minor units |
delegation_depth | u32 | Depth of the delegation chain at invocation time |
root_budget_holder | string | Identifier of the root budget holder in the delegation chain |
payment_reference | string (optional) | Payment reference for external settlement systems |
settlement_status | SettlementStatus | Serialized snake_case: not_applicable, pending, settled, or failed |
cost_breakdown | JSON (optional) | Optional itemized cost decomposition reported by the tool (e.g. input tokens, output tokens, fixed fee) |
oracle_evidence | OracleConversionEvidence (optional) | Rate evidence for cross-currency conversions |
attempted_cost | u64 (optional) | Cost that was attempted but denied (populated on denials at authorization) |
OracleConversionEvidence
Evidence for cross-currency conversions when the cost was denominated in a currency different from the grant currency. The struct is deny_unknown_fields: a consumer parsing metadata.financial.oracle_evidence must expect exactly this field set.
| Field | Type | Description |
|---|---|---|
schema | string | Fixed schema identifier, chio.oracle-conversion-evidence.v1 |
base | string | Base currency of the quoted rate |
quote | string | Quote currency of the quoted rate |
authority | string | Rate authority that issued the evidence |
rate_numerator | u64 | Numerator of the conversion rate |
rate_denominator | u64 | Denominator of the conversion rate |
source | string | Identifier of the rate source |
feed_address | string | Address of the price feed the rate was read from |
updated_at | u64 | Unix seconds when the source last published the rate |
max_age_seconds | u64 | Maximum accepted age of the rate, in seconds |
cache_age_seconds | u64 | Age of the cached rate at conversion time, in seconds |
converted_cost_units | u64 | Cost after conversion, in minor units of grant_currency |
original_cost_units | u64 | Cost before conversion, in minor units of original_currency |
original_currency | string | Currency the cost was originally denominated in |
grant_currency | string | Currency of the grant the cost was converted into |
oracle_public_key | PublicKey (optional) | Verifying key of the oracle that signed the rate |
signature | Signature (optional) | Oracle signature over the evidence |
Governed Transaction Metadata
The flat financial block above is retained for backward compatibility. Governed receipts also carry a governed_transaction block under metadata, holding the intent identifiers plus optional approval, commerce, metering, runtime-assurance, and call-chain evidence bound into the request.
| Field | Type | Description |
|---|---|---|
intent_id | string | Governed transaction intent identifier |
intent_hash | string | Canonical intent hash used for approval-token binding |
purpose | string | Human- or policy-readable purpose of the intent |
server_id | string | Target tool server from the intent |
tool_name | string | Target tool from the intent |
max_amount | MonetaryAmount (optional) | Explicit spend bound carried on the intent |
commerce | object (optional) | Seller-scoped commerce approval: seller, shared_payment_token_id |
metered_billing | object (optional) | Settlement mode, the pre-execution metered quote, and optional post-execution usage evidence |
approval | object (optional) | Approval evidence: token_id, approver_key, approved |
runtime_assurance | object (optional) | Accepted runtime-assurance tier and the verifier that accepted the upstream attestation evidence, after any configured verifier trust-policy rebinding |
call_chain | object (optional) | Delegated call-chain provenance bound through the governed intent hash. Records the strongest projection the kernel will sign; an original caller assertion is preserved separately under assertedContext and must not be collapsed into verified truth |
autonomy | object (optional) | Requested autonomy tier and optional signed delegation-bond id |
economic_authorization | EconomicAuthorizationReceiptMetadata (optional) | Versioned economic envelope (see below) |
economic_authorization
A versioned typed envelope that consolidates budget, meter, rail, and settlement truth into separate sub-blocks. It is additive: the compatibility financial, commerce, metered_billing, approval, runtime_assurance, call_chain, and autonomy fields remain intact beside it.
| Field | Type | Description |
|---|---|---|
version | enum | Envelope version. Serialized v1 |
economic_mode | enum | budget_only, prepaid_fixed, hold_capture, metered_hold_capture, or external_dispatch |
payer | object | Payer binding: party_id, funding_source_ref, optional custody_provider, optional obligor_ref |
merchant | object | Merchant binding: merchant_id, optional merchant_of_record, optional order_ref |
payee | object | Payee binding: beneficiary_id, settlement_destination_ref |
rail | object | Settlement rail: kind, asset, optional network, facilitator, contract_or_account_ref |
amount_bounds | object | approved_max, optional hold_amount, and settlement_cap, each a MonetaryAmount |
pricing_basis | object (optional) | Quote and tariff binding: optional quote_hash, tariff_hash, quote_expiry |
metering | object (optional) | Meter binding: provider, meter_profile_hash, optional max_billable_units, billing_unit |
liability_refs | object (optional) | Optional bond_id, policy_id, indemnity_ref, dispute_policy_ref |
budget | object | Budget truth: grant_index, cost_charged, currency, budget_remaining, budget_total, delegation_depth, root_budget_holder, optional attempted_cost |
settlement | object | Settlement truth: settlement_status |
Signing Process
The receipt signing process is deterministic and reproducible. Any party with the receipt body can verify the signature.
- Validate receipt semantics. Reject any body whose
receipt_kind,boundary_class,decision, andtrust_levelcombination is invalid (for example amediated_decisionwithout adecision). - Bind the signing nonce. Before the id is computed, write the pre-binding id into
metadata["chio_receipt_signing_nonce"]. Becausemetadatais part ofChioReceiptIdInput, the nonce is covered by both the receipt id and the signature. This is a normative step of the receipt-id algorithm, not an incidental detail. - Compute the content-addressed id.
id = H(canonical_jcs(ChioReceiptIdInput))over the now-nonce-bound body. - Sign the signing body. Serialize
ChioReceiptSigningBody({ id, body, bbs_signature? }) to RFC 8785 canonical JSON — keys sorted lexicographically, no whitespace, deterministic number formatting — and sign those bytes with the kernel's signing key (Ed25519 by default; P-256 or P-384 when configured). - Attach signature. Embed the algorithm-aware signature and the kernel's public key in the receipt.
WYSIWYS: the signer recomputes content_hash
chio_kernel_core::receipts::sign_receipt recomputes content_hash from the canonical content preimage inside its own trust boundary and MUST NOT trust a caller-asserted content_hash. The recompute runs before the kernel-key check and before any signing work, so a mismatch fails closed with ContentHashMismatch and can never produce a signature. This closes the render-A / sign-B forgery. The one audited exception is sign_receipt_relaying_trusted_body, a trusted relay reserved for FFI and WASM transport adapters.Canonical JSON is required
Verification
Receipt verification can be performed by any party with no additional context. The receipt is self-contained.
- Re-validate semantics. Confirm the
receipt_kind/boundary_class/decision/trust_levelcombination is well-formed. A malformed combination fails verification. - Re-derive the id. Recompute
H(canonical_jcs(ChioReceiptIdInput))and confirm it equalsid. A mismatch means the identity-defining fields were altered. - Verify the signature. Re-serialize
ChioReceiptSigningBodyto RFC 8785 canonical JSON and verifysignatureagainst those bytes usingkernel_key. The verifier dispatches off the self-describing signature encoding (Ed25519 / P-256 / P-384), not thealgorithmhint. - Verify parameter hash. Recompute the SHA-256 over RFC 8785 canonical JSON of
action.parametersand compare withaction.parameter_hash.
import {
verifyReceipt,
parseReceiptJson,
} from "@chio-protocol/sdk/invariants";
// Parse the receipt from JSON
const receipt = parseReceiptJson(receiptJsonString);
// Verify signature and parameter hash
const result = await verifyReceipt(receipt);
if (result.signatureValid && result.parameterHashValid) {
console.log("Receipt is valid");
} else {
console.error("Receipt verification failed", result);
}# Verify using openssl (manual)
# 1. Extract body (all fields except "signature")
# 2. Serialize to canonical JSON
# 3. Verify Ed25519 signature against kernel_key
# Or use the SDK:
$ node -e "
const { verifyReceiptJson } = require('@chio-protocol/sdk/invariants');
const fs = require('fs');
const receipt = fs.readFileSync('receipt.json', 'utf8');
verifyReceiptJson(receipt).then(r => console.log(r));
"Complete JSON Example
{
"id": "7c1e5a2b9d34f60e8a1b4c7d0e3f6a9b2c5d8e1f4a7b0c3d6e9f2a5b8c1d4e70",
"timestamp": 1744537862,
"capability_id": "cap_7f3a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7de91d",
"tool_server": "srv-files",
"tool_name": "read_file",
"action": {
"parameters": {
"path": "./workspace/README.md"
},
"parameter_hash": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
},
"decision": {
"verdict": "allow"
},
"receipt_kind": "mediated_decision",
"boundary_class": "prevent",
"tool_origin": "caller_executed",
"redaction_mode": "none",
"content_hash": "d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8",
"policy_hash": "b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6",
"evidence": [
{ "guard_name": "forbidden-path", "verdict": true },
{ "guard_name": "path-allowlist", "verdict": true },
{ "guard_name": "shell-command", "verdict": true },
{ "guard_name": "egress-allowlist", "verdict": true },
{ "guard_name": "mcp-tool", "verdict": true },
{ "guard_name": "secret-leak", "verdict": true },
{ "guard_name": "patch-integrity", "verdict": true }
],
"metadata": {
"financial": {
"grant_index": 0,
"cost_charged": 1,
"currency": "USD",
"budget_remaining": 999,
"budget_total": 1000,
"delegation_depth": 0,
"root_budget_holder": "ca-prod-01",
"settlement_status": "settled",
"cost_breakdown": {
"base": 1
}
}
},
"trust_level": "mediated",
"kernel_key": "9c7b3f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e",
"signature": "e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"
}Deny Receipt Example
{
"id": "b8d2f47a06c19e35d7a0b3c6e9f2a5b8c1d4e70f3a6b9c2d5e8f1a4b7c0d3e69",
"timestamp": 1744537863,
"capability_id": "cap_7f3a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7de91d",
"tool_server": "srv-files",
"tool_name": "read_file",
"action": {
"parameters": {
"path": "./workspace/.env"
},
"parameter_hash": "f0e1d2c3b4a5968778695a4b3c2d1e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d"
},
"decision": {
"verdict": "deny",
"reason": "path matches forbidden pattern **/.env",
"guard": "forbidden-path"
},
"receipt_kind": "mediated_decision",
"boundary_class": "prevent",
"tool_origin": "caller_executed",
"redaction_mode": "none",
"content_hash": "c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5",
"policy_hash": "b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6",
"evidence": [
{
"guard_name": "forbidden-path",
"verdict": false,
"details": "path matches forbidden pattern **/.env"
}
],
"trust_level": "mediated",
"kernel_key": "9c7b3f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e",
"signature": "d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6"
}Receipt Querying
Receipts can be queried from the receipt store using filters. The CLI and SDK both support the same filter set:
| Filter | Type | Description |
|---|---|---|
capability | string | Filter by capability ID |
tool_server | string | Filter by tool server ID |
tool_name | string | Filter by tool name |
outcome | string | Filter by decision: allow, deny, cancelled, incomplete |
since | u64 (Unix seconds) | Receipts with timestamp >= value |
until | u64 (Unix seconds) | Receipts with timestamp <= value |
min_cost | u64 | Minimum cost in minor units (financial receipts) |
max_cost | u64 | Maximum cost in minor units (financial receipts) |
limit | integer | Maximum results per page (default 50) |
cursor | u64 | Pagination cursor (seq value to start after) |
# All denials in the last hour
$ chio receipt list --outcome deny --since $(date -v-1H +%s)
# Financial receipts over $1.00
$ chio receipt list --min-cost 100
# Receipts for a specific capability
$ chio receipt list --capability cap_7f3a...e91d --limit 100Storage and Merkle-Committed Log
The local receipt store durably persists every signed receipt along with its sequence number. Beyond durable storage, receipts are periodically batched into checkpoints: each checkpoint commits to a Merkle root over the receipts it contains, letting verifiers check inclusion of any single receipt with an O(log n) proof instead of replaying the entire log.
- Checkpoint. A signed commitment over a Merkle root of a contiguous window of receipts, plus the window's first and last sequence numbers and timestamp.
- Inclusion proof. For a given receipt, the set of sibling hashes needed to reconstruct the Merkle root. A verifier recomputes the root from the receipt hash and sibling hashes, then matches it against the checkpoint root.
- Offline verification. With a checkpoint plus an inclusion proof, a third party can confirm a receipt was part of the committed log without access to the full receipt store.
Receipt Lineage (Multi-Parent DAG)
Receipts can declare multi-parent lineage so a verifier can order and connect them across kernels. When present, a receipt carries the following signed lineage fields:
| Field | Type | Description |
|---|---|---|
chainId | string | Lineage chain this receipt belongs to. Every parent must share the same chainId |
parentReceiptIds | string[] | Parent receipt ids, sorted and deduplicated before signing |
parentSetHash | string | H(canonical(parentReceiptIds)) over the sorted, deduplicated set |
dagOrdinal | u64 | Monotonic ordinal within the chain. A child MUST have dagOrdinal > max(parent.dagOrdinal) |
{ wallSeconds, logical, kernelId } | ReceiptHybridLogicalClock | Hybrid logical clock triple used for cross-kernel ordering without a single global clock |
The verifier rejects a child unless its parent descriptors match the signed parent set, every parent shares the same chainId, and dagOrdinal strictly exceeds every parent's. This rejects cross-kernel cycles without relying on one global clock. This multi-parent lineage is distinct from the ChildRequestReceipt mechanism below; the two can coexist on the same operation.
Child Request Receipts
For nested operations (tool calls that spawn child requests), the kernel also produces ChildRequestReceipt records. These track the session, parent request, child request, operation kind, and terminal state, all signed with the same canonical-JSON process. Child receipts link to the originating tool call via parent_request_id. This is a separate mechanism from the multi-parent receipt lineage described above.
// Parent receipt (tool call that spawned a nested operation)
{
"id": "a4f0c9d2e5b81736a0c3d6e9f2b5a8c1d4e70f3a6b9c2d5e8f1a4b7c0d3e692f",
"tool_name": "run_task",
"action": { "parameters": { "task": "analyze" }, "parameter_hash": "..." },
"decision": { "verdict": "allow" },
"receipt_kind": "mediated_decision",
...
}
// Child request receipt linked via parent_request_id
{
"id": "child-4b8e-8f1d",
"session_id": "sess_4b8e",
"parent_request_id": "req_2b8e",
"request_id": "req_8f1d",
"operation_kind": "subtool_call",
"terminal_state": "completed",
"outcome_hash": "...",
"policy_hash": "...",
"kernel_key": "...",
"signature": "..."
}| Field | Type | Description |
|---|---|---|
id | string | Unique child request receipt ID |
timestamp | u64 | Unix timestamp (seconds) |
session_id | string | Parent session identifier |
parent_request_id | string | ID of the parent tool call request |
request_id | string | ID of this child request |
operation_kind | enum | Type of operation performed |
terminal_state | enum | Final state of the child operation |
outcome_hash | string | SHA-256 hash of the operation outcome |
policy_hash | string | SHA-256 hash of the policy applied |
metadata | JSON (optional) | Optional child-receipt metadata. Omitted from the wire when unset |
kernel_key | PublicKey | Kernel's verifying public key, in the same algorithm-aware form used across v1 (bare 64-hex Ed25519, p256:<130-hex>, or p384:<194-hex>) |
algorithm | SigningAlgorithm (optional) | Envelope hint for signature (ed25519, p256, or p384). Absent means Ed25519, matching ChioReceipt |
signature | Signature | Algorithm-aware signature over the canonical JSON of the body |