Chio/Docs

LearnAnatomy of a Governed Call

Receipts

The kernel signs a receipt for each mediated decision, recording the request, the decision, the guard evidence, and the charge.

Overview

A receipt is a signed JSON document for an agent's tool request. It records the request, the kernel's decision, and the guard evidence. For priced calls, it also records attribution and cost. See Autonomous Commerce for how receipts participate in billing. The kernel produces one receipt per decision, including denials. Receipts answer four questions:

  • Who: which agent, using which capability token, on which tool server
  • What: which tool, with what parameters
  • When: Unix timestamp of the decision
  • Result: the kernel's decision and whatever evidence the guards reported

Receipt coverage

The kernel signs a receipt for every mediated decision: allow, deny, cancelled, or incomplete. If the kernel mediated a tool call, the log contains a receipt. The same signed format also records trace observations and advisory evaluations without a kernel decision.

Model

ChioReceipt fields

The ChioReceipt struct in crates/core/chio-core-types/src/receipt/body.rs is the whole record. 20 of its 24 fields feed the content-addressed id; the signature covers those plus the id itself, 21 in all; and 9 carry a serde rule that drops them from the wire when they are empty, absent, or at their default.

FieldTypeOn the wirePurpose
idStringalwaysContent-addressed receipt ID derived from the canonical receipt body.
timestampu64alwaysUnix timestamp (seconds) when the receipt was created.
capability_idStringalwaysID of the capability token that was exercised (or presented).
tool_serverStringalwaysTool server that handled the invocation.
tool_nameStringalwaysTool that was invoked (or attempted).
actionToolCallActionalwaysThe action that was evaluated.
decisionOption<Decision>omitted when NoneThe Kernel's decision. Present only for mediated decisions.
receipt_kindReceiptKindalwaysSigned receipt semantic kind.
boundary_classBoundaryClassalwaysSigned runtime boundary class.
observation_outcomeOption<ObservationOutcome>omitted when NoneSigned observation outcome for trace and advisory records.
tool_originToolOriginalwaysSigned tool-origin classification.
redaction_modeRedactionModealwaysSigned redaction mode.
actor_chainVec<ActorRef>omitted when emptySigned actor attribution chain.
content_hashStringalwaysSHA-256 hash of the evaluated content for this receipt.
policy_hashStringalwaysSHA-256 hash of the policy that was applied.
evidenceVec<GuardEvidence>omitted when emptyPer-guard evidence collected during evaluation.
metadataOption<serde_json::Value>omitted when NoneOptional receipt metadata for stream/accounting details.
trust_levelTrustLevelalwaysStrength of kernel mediation that produced this receipt.
tenant_idOption<String>omitted when NoneMulti-tenant receipt isolation: tenant identifier for multi-tenant deployments. `None` in single-tenant mode; derived from the authenticated session's enterprise identity context and MUST NOT be taken from caller-provided request fields (caller choice would defeat the isolation intent). Serialized only when set.
bbs_projection_versionOption<String>omitted when NoneBBS projection version bound into the receipt id when BBS material is present.
kernel_keyPublicKeyalwaysThe Kernel's public key (for verification without out-of-band lookup).
bbs_signatureOption<BbsReceiptSignature>omitted when NoneOptional BBS material for selective disclosure over this receipt.
algorithmOption<SigningAlgorithm>omitted when is_default_optional_algorithmSigning algorithm used for [`ChioReceipt::signature`]. Informational only: verification dispatches off the self-describing encoding of the signature itself.
signatureSignaturealwaysSignature over canonical JSON of [`ChioReceiptSigningBody`].

The id is content-addressed: it is a hash of the canonical id input, not a random UUID, so the same input always yields the same id and any edit changes it. The decision is optional: it is present for mediated decisions and absent on the trace and advisory receipt kinds described next. See the Receipt Format reference for the complete field-by-field shape and wire semantics.

a allow receiptcaptured by chio --receipt-db .chio/receipts.db receipt list --admin-all \
{      "name": "Chio"    },  },  },      "compensation_status": "not_compensated",      "coordinator_lease_epoch": 1,      "coordinator_lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",      "operation_id": "a78bde4b5404092f9eecca27e3d5109dcda0a870befee69f32e9dcbd49e12343",      "projected_dispatch_state": "terminal",      "projected_operation_version": 8,      "projected_state": "completed",      "request_binding_hash": "91dc5c706136b424cd8c4bb77df99232e8f40028b2217d5d2a1c84adfd246e70",      "request_id": "check-001",      "request_namespace_digest": "b6fe626fa0256f31deefc202a2efaecb2defd6a4ba93a5237dbc5f063948a651",      "retained_dispatch_commit": {        "committed_version": 6,        "coordinator_lease_epoch": 1,        "coordinator_lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",        "provider_attempt": {          "attempt_id": "attempt:a78bde4b5404092f9eecca27e3d5109dcda0a870befee69f32e9dcbd49e12343",          "operation_id": "a78bde4b5404092f9eecca27e3d5109dcda0a870befee69f32e9dcbd49e12343",          "transport_id": "kernel-tool-server:hello",          "transport_key_epoch": 1        },        "store_fence": {          "lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",          "owner_epoch": 1,          "store_uuid": "01a06cb0-a62a-7ef0-a2bc-fee0de0a9828"        }      },      "schema": "chio.admission-receipt.v1",      "store_fence": {        "lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",        "owner_epoch": 1,        "store_uuid": "01a06cb0-a62a-7ef0-a2bc-fee0de0a9828"      },      "tool_outcome_id": "74a24dc524ae52adfe5594445a9fabb7f54d5c8d4580be5124ceab8ca7a87144",      "tool_outcome_version": 2,      "trusted_time_unix_ms": 1788529911968    },      "delegation_depth": 0,      "grant_index": 0,      "issuer_key": "e08d69a6a3bf74cf4971a7e0a1ff3f566129fad32ec5580c3342316859ecf436",      "subject_key": "bb82465acb67f9413d39ef26ba3ceda6b5cd6413739e41a58b33edeb2909d8d8"    },    "receipt_context": {      "request_id": "check-001"    }  },}
The quickstart's allow receipt with every field annotated from the structs that define it. The panel recomputes the content-addressed id and the parameter hash from the receipt alone.
sourcecrates/core/chio-core-types/src/receipt/body.rs:42-109at fe56570

Receipt kinds and boundary class

A group of signed fields identifies the record type and the portion of the call Chio observed or enforced. A verifier can assess the record's boundary from the signed fields alone.

FieldVariantsMeaning
receipt_kindMediatedDecision, TraceObservation, AdvisoryEvaluationWhether the record is an authorization decision or an observation the kernel logged without mediating
boundary_classPrevent, DetectOnly, AdvisoryOnly, CannotSeeWhat Chio could enforce on this call: full prevention down to no visibility at all
observation_outcomeObserved, Evaluated, DroppedFor non-mediated records: whether the observation was seen, evaluated, or dropped
tool_originCallerExecuted, HostExecutedProviderReported, HostExecutedUnmediatedWhere the tool effect ran relative to Chio
redaction_modeNone, Summary, RedactedHow much of the signed detail was redacted before storage or export
trust_levelMediated, Verified, AdvisoryHow the kernel participated: inline mediation, in-process authorization, or advisory evaluation the caller could ignore
actor_chainVec<ActorRef>The signed attribution chain of actors behind the request

The two BBS fields, bbs_projection_version and bbs_signature, carry optional BBS+ material for selective disclosure. A holder can present selected receipt fields without revealing the rest of the receipt.

Decision variants

The Decision enum captures the outcomes of a mediated call. On the wire the variants serialize under a "verdict" tag with lowercase names: allow, deny, cancelled, and incomplete. The deny variant carries both reason and guard, the name of the blocking step.

crates/core/chio-core-types/src/receipt/decision.rs8-31rust
/// The Kernel's verdict on a tool call.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "verdict", rename_all = "snake_case")]
pub enum Decision {
    /// The tool call was allowed and executed.
    Allow,
    /// The tool call was denied.
    Deny {
        /// Human-readable reason for the denial.
        reason: String,
        /// The guard or validation step that triggered the denial.
        guard: String,
    },
    /// The tool call was interrupted by explicit cancellation.
    Cancelled {
        /// Human-readable reason for the cancellation.
        reason: String,
    },
    /// The tool call did not reach a complete terminal result.
    Incomplete {
        /// Human-readable reason for the incomplete terminal state.
        reason: String,
    },
}

Each Deny records the step that rejected the call, so a verifier can identify the rule. When the capability check refuses a request before any guard runs, guard reads kernel.

ToolCallAction

The action field captures the parameters passed to the tool alongside a hash a reader can recompute.

crates/core/chio-core-types/src/receipt/decision.rsrust
/// Describes the tool call that was evaluated.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallAction {
    /// The parameters that were passed to the tool (or attempted).
    pub parameters: serde_json::Value,
    /// SHA-256 hash of the canonical JSON of `parameters`.
    pub parameter_hash: String,
}

parameter_hash is the bare lowercase hex SHA-256 of the canonical JSON (RFC 8785) of the parameters, with no prefix. A reader can check it without checking the receipt signature.

Guard evidence

The evidence array records what each guard reported during evaluation. Reporting is the guard's choice: a guard returns evidence alongside its verdict, and GuardPipeline::evaluate concatenates whatever it gets, in registration order.

crates/core/chio-core-types/src/receipt/metadata.rs184-194rust
/// Evidence from a single guard's evaluation.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GuardEvidence {
    /// Name of the guard (e.g. "ForbiddenPathGuard").
    pub guard_name: String,
    /// Whether the guard passed (true) or denied (false).
    pub verdict: bool,
    /// Optional details about the guard's decision.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub details: Option<String>,
}

guard_name holds the value Guard::name() returns, which for the default pipeline is the hyphenated runtime name rather than the struct name the doc comment shows. In registration order those names are forbidden-path, shell-command, egress-allowlist, path-allowlist, mcp-tool, secret-leak, and patch-integrity.

None of them reports anything when it allows: each returns GuardDecision::allow(), whose evidence vector is empty. A call that clears the default pipeline therefore ends with an empty array, and serde drops the key, which is why the quickstart receipts carry no evidence at all. A guard that does report on allow, such as AdvisoryPipeline, contributes one entry per signal it raised.

The pipeline adds an entry of its own only when a guard stops the call, and then it stops evaluating, so nothing after that guard appears.

crates/guards/chio-guards/src/pipeline.rsrust
                        Verdict::Deny => {
                            evidence.push(GuardEvidence {
                                guard_name: guard.name().to_string(),
                                verdict: false,
                                details: Some(
                                    "action=deny; reason=guard denied request".to_string(),
                                ),
                            });
                            return Ok(GuardDecision::deny(evidence));
                        }
                    }
                }
                Err(e) => {
                    // Fail closed: guard errors are treated as denials.
                    evidence.push(GuardEvidence {
                        guard_name: guard.name().to_string(),
                        verdict: false,
                        details: Some(format!("action=error; reason=fail-closed; error={e}")),
                    });
                    return Ok(GuardDecision::deny(evidence));
                }

details is absent where the guard reported none. See Guards for the pipeline itself.

Child request records

Nested operations within a parent tool call produce ChildRequestReceipt records. These link back to the parent through parent_request_id, forming a DAG of parent and child receipt chains across nested operations. Each child records its operation kind and how the operation ended: Completed, or Cancelled or Incomplete with a reason. The terminal state describes the lifecycle. The authorization verdict is the parent receipt's decision.

crates/core/chio-core-types/src/receipt/lineage.rs24-43rust
/// Signed audit record for a nested child request handled under a parent tool call.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChildRequestReceipt {
    pub id: String,
    pub timestamp: u64,
    pub session_id: SessionId,
    pub parent_request_id: RequestId,
    pub request_id: RequestId,
    pub operation_kind: OperationKind,
    pub terminal_state: OperationTerminalState,
    pub outcome_hash: String,
    pub policy_hash: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub metadata: Option<serde_json::Value>,
    pub kernel_key: PublicKey,
    /// Signing algorithm. Absent means Ed25519 (the default).
    #[serde(default, skip_serializing_if = "is_default_optional_algorithm")]
    pub algorithm: Option<SigningAlgorithm>,
    pub signature: Signature,
}

Child request receipts are signed and verified the same way standard receipts are, so the same non-repudiation argument covers nested operations.


How it works

Content hashing

content_hash is the bare lowercase hex SHA-256 of the evaluated content, with no sha256: prefix. crates/kernel/chio-kernel/src/receipt_support/receipt_content.rs computes it three ways:

  • a value output hashes the canonical JSON bytes of that value;
  • a streamed output hashes the concatenated per-chunk digests, not the raw payload, so the signing-boundary recompute reads the same preimage;
  • no output at all hashes the four bytes null.

The third case is the one a deny lands in: the call never reaches a tool, so its content_hash is the SHA-256 of null. Hash those four bytes and compare them with the quickstart deny receipt.

Policy hashing

policy_hash identifies the policy that decided the call. It is not a hash of the file: runtime_hash_for_hushspec in crates/platform/chio-control-plane/src/policy/util.rs builds a JSON fingerprint of the loaded policy, its format, kernel block, default capabilities, rule blocks, reputation extensions, and the digests of any auxiliary assets, then hashes that. Two policy files that differ only in comments or whitespace load to the same value, and a rule change moves it.

chio check prints that value as policy: alongside source:, the hash of the policy bytes and their assets. The receipt carries the runtime hash and not the source hash.

Policy pinning for compliance

Each receipt carries the runtime hash of the policy evaluated for that call. An auditor reloads the policy believed to be in force and compares the hash it produces.

Signing

The kernel signs receipts with its Ed25519 key. The signed object is not the receipt and not the receipt body: ChioReceiptSigningBody in crates/core/chio-core-types/src/receipt/signing.rs holds the id, the ChioReceiptIdInput the id was derived from, and the BBS signature when one is present. Signing runs two validations first and computes the id last.

crates/core/chio-core-types/src/receipt/body.rsrust
    /// Sign a receipt body with the Kernel's Ed25519 keypair.
    pub fn sign(body: ChioReceiptBody, keypair: &Keypair) -> Result<Self> {
        validate_bbs_receipt_binding(&body, None)?;
        ensure_keypair_matches_embedded_key(&body.kernel_key, keypair, "receipt", "kernel_key")?;
        let body = prepare_receipt_body_for_signing(body)?;
        let signing_body = ChioReceiptSigningBody::from(&body);
        let (signature, _bytes) = keypair.sign_canonical(&signing_body)?;
        Ok(Self::from_signed_body(body, None, None, signature))
    }
  1. validate_bbs_receipt_binding rejects a body whose BBS material and projection version disagree.
  2. ensure_keypair_matches_embedded_key rejects a signature by a key other than the kernel_key the receipt embeds.
  3. prepare_receipt_body_for_signing validates the body's semantics, folds the caller-supplied nonce into metadata under chio_receipt_signing_nonce, and sets id to chio_receipt_id(&body), the hex SHA-256 of the canonical JSON of the id input.
  4. The keypair signs the canonical JSON of the signing body.

Canonical JSON defines signed bytes

RFC 8785 maps the same signing body to the same byte sequence across implementations and field orderings. Signature verification uses those canonical bytes.

Verifying a receipt

A third party verifies a receipt without contacting the kernel: the record embeds the kernel's public key. verify_signature checks four things, and re-deriving the id is one of them, so a receipt whose fields were edited fails even if its signature bytes were left alone.

crates/core/chio-core-types/src/receipt/body.rsrust
    /// Verify the receipt signature against the embedded kernel key.
    pub fn verify_signature(&self) -> Result<bool> {
        let body = self.body();
        if body.validate_signable_semantics().is_err() {
            return Ok(false);
        }
        if validate_bbs_receipt_binding(&body, self.bbs_signature.as_ref()).is_err() {
            return Ok(false);
        }
        if chio_receipt_id(&body)? != self.id {
            return Ok(false);
        }
        let signing_body =
            ChioReceiptSigningBody::from_body_and_bbs(&body, self.bbs_signature.as_ref());
        self.kernel_key
            .verify_canonical(&signing_body, &self.signature)
    }

Checking the signature says the embedded key signed this record. It does not say the key belongs to a kernel you trust. Compare kernel_key against a trusted key registry for that.

The parameter hash inside action is independently checkable by the same method the kernel used to compute it.

crates/core/chio-core-types/src/receipt/decision.rsrust
    /// Verify that `parameter_hash` matches the canonical hash of `parameters`.
    pub fn verify_hash(&self) -> Result<bool> {
        let canonical = canonical_json_bytes(&self.parameters)?;
        let expected = sha256_hex(&canonical);
        Ok(self.parameter_hash == expected)
    }

A receipt from the quickstart

The receipt below is the one the quickstart's allow produced. The starter policy grants hello_world on server hello, chio check allowed the call, and chio receipt list piped through jq selects it from the log. Running the quickstart yourself produces a receipt of the same shape with your own ids, keys, and timestamps.

quickstart · receipt-json-allowtranscript
$ chio --receipt-db .chio/receipts.db receipt list --admin-all \
    | jq 'select(.decision.verdict == "allow")'
{
  "id": "448440b65fa15559a364d24512cbe4f08631befe2c3d2ab471dad7204b8b69c8",
  "timestamp": 1788529911,
  "capability_id": "cap-01a06cb0-a7d2-7e10-b32d-b985627e17e7",
  "tool_server": "hello",
  "tool_name": "hello_world",
  "action": {
    "parameters": {
      "name": "Chio"
    },
    "parameter_hash": "319910ed4307f4c6486b4d282a01d4276a7aec7626c66be0f7eb2173d4a973a3"
  },
  "decision": {
    "verdict": "allow"
  },
  "receipt_kind": "mediated_decision",
  "boundary_class": "prevent",
  "tool_origin": "caller_executed",
  "redaction_mode": "none",
  "content_hash": "8813cfed2cad28d238168ce79dde8ff3b39d38f2a5bea3661c7726d63f69f5ad",
  "policy_hash": "69e943b96e9ce64d0264bb56bf8930e77bd4e68adf68fcf1395790dae03e6b55",
  "metadata": {
    "admission_operation": {
      "compensation_status": "not_compensated",
      "coordinator_lease_epoch": 1,
      "coordinator_lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",
      "operation_id": "a78bde4b5404092f9eecca27e3d5109dcda0a870befee69f32e9dcbd49e12343",
      "projected_dispatch_state": "terminal",
      "projected_operation_version": 8,
      "projected_state": "completed",
      "request_binding_hash": "91dc5c706136b424cd8c4bb77df99232e8f40028b2217d5d2a1c84adfd246e70",
      "request_id": "check-001",
      "request_namespace_digest": "b6fe626fa0256f31deefc202a2efaecb2defd6a4ba93a5237dbc5f063948a651",
      "retained_dispatch_commit": {
        "committed_version": 6,
        "coordinator_lease_epoch": 1,
        "coordinator_lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",
        "provider_attempt": {
          "attempt_id": "attempt:a78bde4b5404092f9eecca27e3d5109dcda0a870befee69f32e9dcbd49e12343",
          "operation_id": "a78bde4b5404092f9eecca27e3d5109dcda0a870befee69f32e9dcbd49e12343",
          "transport_id": "kernel-tool-server:hello",
          "transport_key_epoch": 1
        },
        "store_fence": {
          "lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",
          "owner_epoch": 1,
          "store_uuid": "01a06cb0-a62a-7ef0-a2bc-fee0de0a9828"
        }
      },
      "schema": "chio.admission-receipt.v1",
      "store_fence": {
        "lease_id": "01a06cb0-a712-7440-b724-c4fe54913090",
        "owner_epoch": 1,
        "store_uuid": "01a06cb0-a62a-7ef0-a2bc-fee0de0a9828"
      },
      "tool_outcome_id": "74a24dc524ae52adfe5594445a9fabb7f54d5c8d4580be5124ceab8ca7a87144",
      "tool_outcome_version": 2,
      "trusted_time_unix_ms": 1788529911968
    },
    "attribution": {
      "delegation_depth": 0,
      "grant_index": 0,
      "issuer_key": "e08d69a6a3bf74cf4971a7e0a1ff3f566129fad32ec5580c3342316859ecf436",
      "subject_key": "bb82465acb67f9413d39ef26ba3ceda6b5cd6413739e41a58b33edeb2909d8d8"
    },
    "chio_receipt_signing_nonce": "rcpt-01a06cb0-a8c2-7ad2-a78a-562ac4366063",
    "receipt_context": {
      "request_id": "check-001"
    }
  },
  "trust_level": "mediated",
  "kernel_key": "e08d69a6a3bf74cf4971a7e0a1ff3f566129fad32ec5580c3342316859ecf436",
  "signature": "676bef1ac96b8519f5b316a98c05de7b46300e06983e0da58bb70db6d6c02f619222d07e44da61f2ca689015ab731d9c9e7727aeb826ad4690a2428f9fa82a01"
}
exit 0allowin my-agent

The same log holds the quickstart's deny. The request named drop_tables, a tool no grant covers, so the capability check refused it before any guard ran: decision.guard is kernel and the reason names the scope. Neither receipt carries an evidence key; the field serializes only when the array holds at least one entry, per its skip_serializing_if attribute in crates/core/chio-core-types/src/receipt/body.rs.

quickstart · receipt-jsontranscript
$ chio --receipt-db .chio/receipts.db receipt list --admin-all \
    | jq 'select(.decision.verdict == "deny")'
{
  "id": "28b6e2576ca2ccbd031e769d8d2bd504317161115a600f27771a7f932307a284",
  "timestamp": 1788529912,
  "capability_id": "cap-01a06cb0-aae3-7ad3-8ba7-01350a3e01a0",
  "tool_server": "hello",
  "tool_name": "drop_tables",
  "action": {
    "parameters": {},
    "parameter_hash": "44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
  },
  "decision": {
    "verdict": "deny",
    "reason": "requested tool drop_tables on server hello is not in capability scope",
    "guard": "kernel"
  },
  "receipt_kind": "mediated_decision",
  "boundary_class": "prevent",
  "tool_origin": "caller_executed",
  "redaction_mode": "none",
  "content_hash": "74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b",
  "policy_hash": "69e943b96e9ce64d0264bb56bf8930e77bd4e68adf68fcf1395790dae03e6b55",
  "metadata": {
    "attribution": {
      "delegation_depth": 0,
      "issuer_key": "3dbf7b1230475796d5c78cdcf441c84721d9e3c24362fbbfb4225e307d1b9fb4",
      "subject_key": "af2da8097f2c133affd89145302f029c7a5854e4e3a692a92b0cd20f84d07b37"
    },
    "chio_receipt_signing_nonce": "rcpt-01a06cb0-ab51-70d0-bcb3-72a8b78fe057",
    "receipt_context": {
      "request_id": "check-001"
    }
  },
  "trust_level": "mediated",
  "kernel_key": "3dbf7b1230475796d5c78cdcf441c84721d9e3c24362fbbfb4225e307d1b9fb4",
  "signature": "e89f7a9cbe6c6b207cf5ac0c3e197a3957d62d2994217a7dcaeeeb9869abef1233680328432ee7a502a76238eb6ee9e72b19715e2458a3ef85aebfbed0df9202"
}
exit 0denyin my-agent

Financial metadata

When a tool call exercises a monetary grant, the receipt's metadata field includes a FinancialReceiptMetadata record under the "financial" key. The full field set is:

FieldTypePurpose
grant_indexu32Index of the matching grant in the capability token's scope
cost_chargedu64Cost charged for this invocation in currency minor units
currencyStringISO 4217 currency code
budget_remainingu64Remaining budget after this charge, in minor units
budget_totalu64Total budget for this grant, in minor units
delegation_depthu32Depth of the delegation chain at the time of invocation
root_budget_holderStringIdentifier of the root budget holder in the delegation chain
payment_referenceOption<String>External payment reference for settlement
settlement_statusSettlementStatusSettlement status for this charge
cost_breakdownOption<Value>Itemized cost breakdown for audit purposes
oracle_evidenceOption<OracleConversionEvidence>Oracle price evidence used for cross-currency conversion
attempted_costOption<u64>Cost attempted but denied, populated only on denial receipts

SettlementStatus reads NotApplicable when no external settlement applies, such as a pre-execution denial; Pending when settlement has started but is not final; Settled when the recorded charge is final for the current execution path; and Failed when execution completed but settlement failed or became invalid.

On denial receipts caused by budget exhaustion, attempted_cost records the cost that would have been charged while cost_charged is zero. For cross-currency invocations, the oracle_evidence field holds an OracleConversionEvidence record: the base and quote currencies, the integer rate_numerator and rate_denominator pair, the oracle source and feed_address, an updated_at timestamp with its max_age_seconds and cache_age_seconds freshness bounds, and the converted and original cost in units. The record is itself a signed oracle attestation: when the quote is signed it carries its own oracle_public_key and signature, so a verifier can check the rate against the oracle's key without trusting the kernel. See Economics for the full struct.

FinancialReceiptMetadata is the settled summary. Alongside it the same module defines the budget-hold lineage records that back it ( FinancialBudgetHoldAuthorityMetadata, FinancialBudgetAuthorizeReceiptMetadata, and FinancialBudgetTerminalReceiptMetadata ) which capture the authorize, lease, and terminal steps of the budget hold that check_and_increment_budget opens and later reconciles. Economics walks that lifecycle.

financial is one of the 11 top-level metadata keys the kernel reserves. Three others record admission and delivery: admission_operation (schema chio.admission-receipt.v1), the durable admission projection the allow receipt above carries; delivery_contract (schema chio.delivery-contract.v1), the expected digest, observed digest, and match result of a digest-constrained call; and finding_delivery (schema chio.finding.delivery.v1), the overlay for a purchased finding reveal. The kernel writes these blocks, merges them last, and rejects caller or hook metadata that already holds a reserved key, so a verifier can treat a block under a reserved key as kernel-authored and covered by the receipt signature. See The Delivery Contract for the digest comparison and the Receipt Format reference for the wire shape of the receipt around them.

Reading the log

chio receipt list queries the receipt log with filters. Every local read requires an explicit tenant boundary: pass --tenant <id> to scope the listing to one tenant, or --admin-all to read across all tenants as an administrative operation. Output is JSON Lines: one receipt per line.

bash
# List all receipts for a specific tool server
$ chio receipt list --admin-all --tool-server srv-files

# Filter by tool name and outcome
$ chio receipt list --admin-all --tool-name file_read --outcome allow

# Time range: --since / --until take Unix seconds
$ chio receipt list --admin-all --since 1735689600 --until 1735776000

# Filter by cost (minor currency units)
$ chio receipt list --admin-all --min-cost 100 --max-cost 500 --cost-currency USD

# Page through results
$ chio receipt list --admin-all --limit 20 --cursor 1042

Against the quickstart store, the unfiltered listing prints one line per receipt. Piping through jq narrows each line to the fields worth scanning.

quickstart · receipt-listtranscript
$ chio --receipt-db .chio/receipts.db receipt list --admin-all \
    | jq -r '[.decision.verdict, .tool_server, .tool_name, .id] | @tsv'
allow	hello	hello_world	448440b65fa15559a364d24512cbe4f08631befe2c3d2ab471dad7204b8b69c8
deny	hello	drop_tables	28b6e2576ca2ccbd031e769d8d2bd504317161115a600f27771a7f932307a284
exit 0in my-agent

Dropping the read boundary is the failure worth meeting once. With neither flag, chio receipt list fails before touching the store with --tenant <id> or --admin-all is required for local receipt reads; with both, it fails with --tenant <id> and --admin-all are mutually exclusive. Both come from local_receipt_read_context in crates/products/chio-cli/src/cli/trust/receipt/list.rs. A remote read through --control-url takes neither flag and derives its scope from the control token instead.

FilterFlagDescription
Read boundary--tenant / --admin-allRequired: scope to one tenant, or read across all tenants (mutually exclusive)
Capability--capabilityFilter by capability ID
Tool server--tool-serverFilter by tool server ID
Tool name--tool-nameFilter by tool name
Outcome--outcomeFilter by decision outcome: allow, deny, cancelled, or incomplete
Start time--sinceReceipts with timestamp ≥ this Unix-seconds value
End time--untilReceipts with timestamp ≤ this Unix-seconds value
Min cost--min-costMinimum cost in minor currency units (financial receipts only)
Max cost--max-costMaximum cost in minor currency units (financial receipts only)
Cost currency--cost-currencyCurrency for the cost filters, as a three-letter uppercase code
Page size--limitMaximum receipts per page, default 50
Cursor--cursorPagination cursor: sequence value to start after

SIEM export

Receipts export to external security information and event management (SIEM) systems for centralized monitoring and alerting. chio-siem ships exporters for Splunk HEC, Elasticsearch, Datadog, Sumo Logic, OCSF, and a generic webhook, plus a CEF formatter that renders one CEF v0 event per receipt and leaves transport to one of the others. Each exporter implements a common Exporter trait and takes a Rust config struct; none of them is configured through policy YAML.

The five exporters that take an endpoint URL, Splunk, Elasticsearch, OCSF, Sumo Logic, and the webhook, call require_https_endpoint at construction and refuse a plain http:// URL. Datadog takes a site rather than a URL and composes https://http-intake.logs.<site>/api/v2/logs itself, then enforces an HttpEgressContract over it.

The Splunk HTTP Event Collector exporter streams receipt batches as newline-separated JSON event envelopes, each wrapping the full ChioReceipt under the event key.

rust
pub struct SplunkConfig {
    /// Splunk HEC endpoint URL (e.g. "https://splunk.example.com:8088").
    pub endpoint: String,
    /// HEC authentication token.
    pub hec_token: String,
    /// Splunk sourcetype for all exported events. Default: "chio:receipt".
    pub sourcetype: String,
    /// Optional index. Omit to use the HEC token's default index.
    pub index: Option<String>,
    /// Optional host field sent with each event envelope.
    pub host: Option<String>,
    /// HTTP request timeout. Default: 30 seconds.
    pub timeout: Duration,
    /// Typed HTTP egress contract enforced on every dispatch and redirect.
    pub egress_contract: Option<HttpEgressContract>,
}

The Elasticsearch exporter posts receipt batches through the /_bulk API, using receipt.id as the document _id so retries are idempotent.

rust
pub struct ElasticConfig {
    /// Elasticsearch endpoint URL (e.g. "https://es.example.com:9200").
    pub endpoint: String,
    /// Target index for all exported receipts. Default: "chio-receipts".
    pub index_name: String,
    /// Authentication method and credentials.
    pub auth: ElasticAuthConfig,
    /// HTTP request timeout. Default: 30 seconds.
    pub timeout: Duration,
    /// Typed HTTP egress contract enforced on every dispatch and redirect.
    pub egress_contract: Option<HttpEgressContract>,
}

pub enum ElasticAuthConfig {
    /// Sends `Authorization: ApiKey <key>`.
    ApiKey(String),
    /// HTTP Basic auth; the password is zeroized on drop.
    Basic { username: String, password: Zeroizing<String> },
}

Guarantees and limits

The store rejects rewrites

Receipts land in a SQLite log the schema keeps append-only. Triggers named chio_tool_receipts_reject_update and chio_tool_receipts_reject_delete abort any UPDATE or DELETE on the receipt table with tool receipts are immutable, and a matching pair covers child receipts. The log is Merkle-committed in batched checkpoints, so an inclusion proof verifies one receipt without replaying the log.

Protect the receipt log

The SQLite store rejects receipt updates and deletes. Deployments that need stronger retention guarantees should add infrastructure controls, such as write-once storage or replication.

What a receipt establishes

A verifying signature establishes that the embedded kernel key signed this id and this id input, and that the id still matches the fields the record carries. It does not establish that the key belongs to a kernel you trust, that the tool did what its output claims, or that the policy behind policy_hash was the right policy. boundary_class and trust_level record how much of the call Chio saw, and a CannotSee or Advisory record is evidence of an observation, not of enforcement. See Assurance Boundaries & Limits.

Who reads the log

Security teams inspect guard evidence and request history. Finance teams reconcile charges against budgets. Compliance teams compare the policy hash with the policy in force for a decision. Reputation systems derive denial, spending, and cancellation rates for agents and tool servers. These uses share the one signed format.

Next steps