EconomyProof for Auditors
Audit APIs
A signed, read-only receipt export for regulators, auditors, and compliance officers.
A regulator asking what an agent did needs two things: the receipts, and a way to tell that the operator did not edit them on the way out. The export answers both. It is one handler in crates/platform/chio-http-core/src/regulatory_api.rs that reads a filtered slice of the receipt log, wraps it in a kernel-signed envelope, and returns it. It never writes.
What compliance means here
The handler and its query
chio-http-core embeds no HTTP server. A framework adapter wires handle_regulatory_receipts_signed into its own router and forwards query-string fields through RegulatoryReceiptsQuery. The route constant is REGULATORY_RECEIPTS_PATH, /regulatory/receipts, registered as a GET.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct RegulatoryReceiptsQuery {
/// Filter by agent subject (hex-encoded Ed25519 public key).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent: Option<String>,
/// Include only receipts with `timestamp >= after`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub after: Option<u64>,
/// Include only receipts with `timestamp <= before`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub before: Option<u64>,
/// Maximum rows to return (capped at
/// [`MAX_REGULATORY_EXPORT_LIMIT`]).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
}The handler runs four steps in order: it refuses a caller with no RegulatorIdentity, refuses a window whose after is greater than its before with the message "after must be <= before", queries the store under ReceiptReadContext::admin_service(), and signs the body. The body is what the signature covers:
pub const REGULATORY_RECEIPT_EXPORT_SCHEMA: &str = "chio.regulatory.receipt-export.v1";
pub const MAX_REGULATORY_EXPORT_LIMIT: usize = 200;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RegulatoryReceiptExport {
pub schema: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub agent_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub after: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub before: Option<u64>,
pub matching_receipts: u64,
pub generated_at: u64,
pub receipts: Vec<ChioReceipt>,
}
pub type SignedRegulatoryReceiptExport = SignedExportEnvelope<RegulatoryReceiptExport>;matching_receipts is the full count before the limit; receipts holds up to limit_or_default() rows, which is the caller's limit clamped to the range 1 through 200 and defaulted to 200. A regulator verifies the envelope against ChioKernel::public_key() using verify_regulatory_export, which additionally checks the schema id and that the body canonicalizes before it asks the library to check the signature.
Read-only by construction
RegulatoryReceiptSource) exposes exactly one method, query_receipts. There is no write path through this API.Co-registered: POST /compliance/score
POST /compliance/score (COMPLIANCE_SCORE_PATH, handler in crates/platform/chio-http-core/src/compliance.rs). It takes an agent subject and a time window and returns a ComplianceScore on a 0..=1000 scale, computed by compliance_score in chio-kernel. It is a compute-only rollup, regulatory-adjacent but distinct from the export: the receipt export is the evidence; the score is a summary over it, and as_underwriting_evidence projects it into underwriting.Errors
RegulatoryApiError has four variants. Each carries a status, a stable code, and a message, and body() renders them as {"error": <code>, "message": <message>}.
| Variant | Status | Code | Cause |
|---|---|---|---|
BadRequest(String) | 400 | bad_request | Malformed query, including after > before. |
Unauthorized | 401 | unauthorized | No authorized RegulatorIdentity reached the handler. Its message is "regulatory API access denied". |
StoreUnavailable(String) | 503 | store_unavailable | The backing receipt store could not respond. |
Signing(String) | 500 | signing_error | Canonical-JSON encoding or signing failed. |
Authorization
The handler requires the framework adapter to validate the caller and pass in an authorized RegulatorIdentity:
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RegulatorIdentity {
/// Stable identifier for audit logging (e.g. regulator name,
/// agency id).
pub id: String,
}The header the adapter reads has a constant of its own, REGULATORY_TOKEN_HEADER, X-Regulatory-Token. Its doc comment states the rule the adapter has to keep: an adapter must not expose /regulatory/* without requiring that header, and the handler fails closed when the caller identity is missing. Operators typically map each token to one RegulatorIdentity and log every successful query. The kernel imposes no token format, so the adapter can use whatever mechanism a security review demands.
Tax and billing hooks
Receipt-driven tax reporting is a downstream consumer of the same billing pipeline that powers ordinary reconciliation. The regulatory export gives auditors the canonical receipts; the billing export (chio.billing-export.v1 from chio-metering) gives finance the per-receipt cost rollup. Each is signed; each is independently verifiable.
For tax reporting specifically, the typical pattern is:
- Run the billing export at jurisdiction-relevant cadence (monthly for VAT, annually for income, quarterly for sales tax).
- Filter records by agent, tool, or jurisdiction tag carried in the receipt's rail metadata.
- Cross-reference against the regulatory export for any periods under audit; the receipt ids appear in both, so the tax filing is backed by signed receipts and matching billing records.
See Reconciliation for the cycle that produces these records and Export Billing Records for the operator workflow.
Compliance framework mappings
Chio receipts and policy elements map onto external compliance frameworks at well-defined points. The mappings are documented per framework on dedicated pages:
- NIST AI RMF: control-id mapping for the GOVERN, MAP, MEASURE, MANAGE functions.
- EU AI Act: mapping for automatic logging and traceability (Article 19), technical documentation and retention (Annex IV), human oversight (Article 14), and monetary risk-management accountability (Article 9).
- ISO 42001: clause-level mapping for AI Management System requirements.
- Compliance Frameworks, the synthesis page with SOC 2 trust-service-criteria mappings and cross-framework references.
Code-level claim labels
Each Chio component that participates in a control has a stable identifier the framework page can reference. Examples:
| Chio mechanism | Maps to |
|---|---|
| Receipt signature + checkpoint inclusion | SOC 2 CC7.2 (monitoring), NIST AI RMF MS-2.7 (security and resilience) |
| Capability scope and authority chain | SOC 2 CC6.1 (logical access), NIST AI RMF GV-2.1 (roles and responsibilities) |
| Settlement watchdog automation outcomes | SOC 2 CC8.1 (change management), ISO 42001 8.1 (operational planning and control) |
| Regulatory export envelope | EU AI Act Article 19(1) / Annex IV Section 2(g) (record-keeping), ISO 42001 9.1 (monitoring) |
| Compliance certificates and bilateral peer attestations | NIST AI RMF GV-6.1 (third-party AI risk) |
The framework pages carry the full mappings.
HIPAA, PCI, and GDPR
HIPAA
Protected Health Information (PHI) must never appear in an unredacted receipt. Operators handling PHI use Chio's data guards (see Data Layer) to redact prompts and responses before the receipt is sealed. The regulatory export contains the redacted form. Operators should keep unredacted PHI out of signed envelopes; the export does not itself provide HIPAA compliance.
PCI DSS
When a payment rail settles a receipt, the receipt carries only the rail's opaque payment_reference, which is the transaction_id the adapter returned, never the PAN. Three adapters ship: X402PaymentAdapter (an x402 payment reference), AcpPaymentAdapter (an ACP-Commerce shared-payment-token id), and SimPaymentAdapter (a deterministic id that performs no HTTP and moves no funds). A card-network bridge is an adapter an operator writes against the PaymentAdapter trait, and the trait shape holds it to the same rule: every method returns a rail identifier and a status, and none of them return cardholder data. The regulator cross-references the rail's own logs through the operator's PCI-scoped systems.
GDPR
Three GDPR-relevant facts about the regulatory API:
- Data minimization. The export filters by agent, time window, and limit. There is no call that returns the whole log; even an authorized regulator can only pull the slice their query targets, and the 200-row cap bounds that slice.
- Audit trail. Every export carries
generated_atand the regulator id is logged by the framework adapter. The export is a discoverable record for the data subject's right of access. - Right to erasure. Receipts are append-only and signed; there is no per-record deletion or in-place erasure path. The log is governed by time- and size-based retention (
RetentionConfigonKernelConfig:retention_daysdefault 90,max_size_bytesdefault 10 GB). Aged receipts rotate into a separate archive database instead of being deleted, and remain verifiable against the Merkle checkpoint roots preserved in that archive. Chio ships no Article 17 erasure mechanism; operators keep a data subject's identifying data out of the sealed receipt at write time through the data guards, so the signed log never has to be rewritten.
For breach notification (Article 33 GDPR), the operator joins the receipt log against access logs to establish whose data was affected; the regulatory API is the read-side that auditors use to verify the operator's claim post-incident.
Auditor workflow
- Receive credentials. The operator provisions a regulator token bound to a
RegulatorIdentity. - Issue a query.
GET /regulatory/receipts?after=…&before=…&agent=…&limit=…with the token in the configured header. - Verify the envelope. Run
verify_regulatory_export(or its framework-adapter equivalent) against the operator's published kernel public key. Verification covers the schema id, the signer key, and canonical-JSON integrity. - Request inclusion evidence. The receipts this API returns carry no Merkle path, so proving one was committed to a checkpoint takes a second artifact. See below.
- Cross-reference billing. Pull the billing export over the same window. Receipt ids appear in both; the rolled-up cost in the billing export must reconcile to the per-receipt amounts in the regulatory export.
Inclusion evidence
A ChioReceipt carries its signature, content hash, and policy hash, and no Merkle path. To prove a receipt was committed to a checkpoint, the auditor asks for an evidence export: a directory that pairs the receipts with the signed KernelCheckpoint batch and an inclusion-proof list of ReceiptInclusionProof records (checkpoint_seq, leaf_index, merkle_root, proof). Each proof verifies the receipt's canonical bytes against the checkpoint's Merkle root.
Run it
$ chio evidence export \
--output ./pkg \
--receipt-db ./receipts.db \
--admin-all \
&& ls ./pkgREADME.txt capability-lineage.ndjson checkpoint-consistency-proofs.ndjson checkpoint-equivocations.ndjson checkpoint-publications.ndjson checkpoint-witnesses.ndjson checkpoints.ndjson child-receipts.ndjson inclusion-proofs.ndjson manifest.json query.json receipts.ndjson retention.json
The package carries its own manifest.json under the schema chio.evidence_export_manifest.v1, which lists every file with its SHA-256 and byte count alongside the receipt, checkpoint, and inclusion-proof counts, so the directory is self-describing and tamper-evident before an auditor opens a single receipt.
When it refuses
The export fails closed on the read boundary. Without --admin-all or an explicit --tenant, it refuses rather than guessing which tenant's evidence the caller may read, and exits 1:
$ chio evidence export \
--output ./pkg-unscoped \
--receipt-db ./receipts.dberror [urn:chio:error:attest:provenance-missing]: receipt read boundary error: evidence export requires an explicit receipt read boundary
context: {"domain":"attest","severity":"error","stability":"unstable","string_code":"CHIO-ATTEST-PROVENANCE-MISSING"}
suggested fix: Regenerate the evidence bundle and include provenance before submitting the operation.For a receipt anchored on a Web3 rail the on-chain form is an AnchorInclusionProof (chio.anchor-inclusion-proof.v1). It bundles the receipt, a Web3ReceiptInclusion, the signed Web3CheckpointStatement, and an optional chain-anchor record tying the checkpoint root to the root published in the on-chain ChioRootRegistry contract. verify_anchor_inclusion_proof checks the receipt signature, the checkpoint statement signature, and the Merkle inclusion path in one pass.
Worked example: a ninety-day window
A regulator asks for every decision one deployed agent made over the last ninety days. The auditor has the agent's subject key and a window in Unix seconds.
Step 1: query
curl -s 'https://kernel.example.com/regulatory/receipts?\
agent=<agent-subject-hex>&after=1740000000&before=1747776000&limit=200' \
-H 'X-Regulatory-Token: <regulator-token>' \
> export.jsonThe response is a SignedExportEnvelope, which is rename_all = "camelCase" and deny_unknown_fields over exactly three keys: body, signerKey, and signature. The signature covers the canonical JSON of body alone, and body is camelCase too. Values below are illustrative, in the real field names:
{
"body": {
"schema": "chio.regulatory.receipt-export.v1",
"agentId": "[64 hex, the agent subject key]",
"after": 1740000000,
"before": 1747776000,
"matchingReceipts": 142,
"generatedAt": 1747776123,
"receipts": [ /* up to 200 ChioReceipt records, seq ascending */ ]
},
"signerKey": "[64 hex, the kernel receipt-signing public key]",
"signature": "[128 hex]"
}A field whose value is absent is omitted rather than sent as null: agentId, after, and before all carry skip_serializing_if. An unfiltered export therefore has three keys in its body, not six.
Step 2: verify
use chio_http_core::regulatory_api::{
verify_regulatory_export, SignedRegulatoryReceiptExport,
};
let envelope: SignedRegulatoryReceiptExport =
serde_json::from_reader(File::open("export.json")?)?;
let kernel_public_key = load_kernel_public_key();
let verified = verify_regulatory_export(&envelope, &kernel_public_key)?;
assert!(verified, "envelope signature did not match kernel key");The function separates three outcomes. A body whose schema is not chio.regulatory.receipt-export.v1 is a BadRequest naming the schema it found. A body that will not canonicalize is a Signing error, so a malformed body is distinguishable from a bad signature. A signer key that is not the expected one returns Ok(false) rather than an error.
Step 3: page if needed
matchingReceipts says whether the response is complete. At MAX_REGULATORY_EXPORT_LIMIT of 200, a matching count above 200 means the auditor narrows the time window or filters by agent and repeats.
Step 4: filter by decision class
The query has no decision-class filter; that is a client-side pass over the receipt stream. For each returned receipt the auditor reads the decision field on the receipt body and keeps the variants in scope.
Step 5: archive
The signed envelope is the primary audit record. The auditor retains it alongside the kernel public key fingerprint used to verify it, so re-verification later works from the same envelope without the live kernel.
Limits
- Read-only. The handler cannot mutate, supersede, or amend a receipt. The backing store is append-only; retention rotates aged receipts into a separate archive instead of deleting them. This API only reads: it never creates, archives, or erases receipt state.
- Jurisdiction-scoped. The operator's authorization rules decide which regulators see which slices of the receipt log. Chio does not enforce jurisdiction at the kernel level; that policy belongs in the framework adapter, the same way capability scoping handles cross-tenant isolation elsewhere.
- Pagination is window narrowing. There is no cursor. The 200-row cap makes a regulator with a large query either narrow the time window or filter by agent, which keeps the handler stateless and the export deterministic.
See also
- Compliance Frameworks for the framework-by-framework control mappings this page only summarizes.
- Query Audit Receipts for the operator-side query patterns that mirror the regulator API.
- Compliance Certificates for the per-deployment certification record that participates in regulatory exports.
- Bilateral Receipts for cross-operator receipt sharing under federation peering.
- Reconciliation for how receipts get to a state worth exporting.