Chio/Docs
LOGIN · JOIN

PlatformStatutory Mappings

Federation & Compliance

EU AI Act

A technical mapping from Chio receipts, checkpoints, and DPoP binding to selected EU AI Act logging, oversight, and risk-management provisions.

Technical mapping for self-assessment

This page maps Chio's tool-call controls and signed records to selected EU AI Act provisions. It covers the tool-invocation boundary, not model training or inference. Operators must determine their deployment's classification, applicable obligations, and conformity process. The clause-by-clause source this page follows lives in the Chio repository at docs/compliance/eu-ai-act-article-19.md, which names the test function behind each row.

Article 19: Automatically Generated Logs

Article 19(1) requires high-risk AI systems to automatically generate logs of their operation for a period appropriate to the intended purpose, with sufficient detail to enable traceability of the system's actions during its lifetime. Chio can provide technical logging evidence at the tool-invocation layer.

The kernel records each governed call with a signed receipt:

  • Automatic: the kernel emits a receipt for allow and deny decisions in the governed-call path, which all_calls_produce_verified_receipts asserts (crates/kernel/chio-kernel/src/kernel/tests/receipts.rs).
  • Tamper evidence: the receipt batches are Merkle-committed into signed kernel checkpoints. A changed receipt no longer matches its recorded Merkle root.
  • Attributable: every receipt carries capability_id, tool_server, tool_name, policy_hash, content_hash, a decision, and the kernel_key that signed it. The hash of the canonical invocation parameters sits one level down, as action.parameter_hash.
  • Cost-tracked: monetary invocations carry a FinancialReceiptMetadata block under the reserved metadata.financial key, recording cost_charged, currency, attempted_cost, and settlement_status (not_applicable, pending, settled, or failed).

The field list above is the wire form of ChioReceipt (crates/core/chio-core-types/src/receipt/body.rs). A real denial read back out of a receipt store looks like this. It is a guard refusal against the canonical policy shipped in the repository, captured by running the commands rather than typed:

eu-ai-act · receipttranscript
$ chio --receipt-db ./receipts.db receipt list --outcome deny --admin-all | jq .
{
  "id": "2e4541210abe95054175567578dda22730020c91bc0f036321582cae2d978bbc",
  "timestamp": 1788581158,
  "capability_id": "cap-01a06fbe-9f9a-7902-a2ef-07175cc81ed9",
  "tool_server": "*",
  "tool_name": "read_file",
  "action": {
    "parameters": {
      "path": "/workspace/.env"
    },
    "parameter_hash": "1708328df9dddb4147395f5741ee7df3cf5dc4922554c08feb6a73bc0d82f245"
  },
  "decision": {
    "verdict": "deny",
    "reason": "guard denied the request: guard \"guard-pipeline\" denied the request",
    "guard": "kernel"
  },
  "receipt_kind": "mediated_decision",
  "boundary_class": "prevent",
  "tool_origin": "caller_executed",
  "redaction_mode": "none",
  "content_hash": "74234e98afe7498fb5daf1f36ac2d78acc339464f950703b8c019892f982b90b",
  "policy_hash": "89e8cd101e3fb0c311cceb9bd7b988a6a65d371310b4fccceb08a015333885da",
  "evidence": [
    {
      "guard_name": "forbidden-path",
      "verdict": false,
      "details": "action=deny; reason=guard denied request"
    }
  ],
  "metadata": {
    "attribution": {
      "delegation_depth": 0,
      "issuer_key": "12438121ff668472c6bc58dd9ba9dc702913cbb57f560fe7961d5a88c0d92073",
      "subject_key": "f697768309183dd63ce40927fb163e9d95409e2101a40c3ff2ba66c3531f2915"
    },
    "budget_authority": {
      "authority_profile": "authoritative_hold_event",
      "guarantee_level": "single_node_atomic",
      "metering_profile": "max_cost_preauthorize_then_reconcile_actual"
    },
    "chio_receipt_signing_nonce": "rcpt-01a06fbe-a089-79f3-8a2c-823863da5d74",
    "receipt_context": {
      "request_id": "check-001"
    }
  },
  "trust_level": "mediated",
  "kernel_key": "12438121ff668472c6bc58dd9ba9dc702913cbb57f560fe7961d5a88c0d92073",
  "signature": "cd1752cdf468e72dc61530d142a8f670a9c4c9ea1ab3b06e3c013b6bfe933c937e277c3c68210f097499be4c5f01b069fe0d9ab699cf9cb48967d66e83a4e302"
}
exit 0deny

Two shapes in that record matter to an auditor. decision is an internally tagged enum, so a verdict is self-describing and a denial carries the reason and the guard alongside it rather than in a separate record:

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,
    },
}

And evidence is the per-guard array: one entry per guard that evaluated the call, each naming the guard and whether it passed. Note that verdict here is a boolean, not the string the receipt-level decision uses:

crates/core/chio-core-types/src/receipt/metadata.rs186-194rust
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>,
}

kernel_key and signature render as bare lowercase hex for Ed25519, with no algorithm prefix. Only the non-Ed25519 curves prefix themselves (p256:, p384:), which is how a verifier dispatches on the encoding alone (crates/core/chio-core-types/src/crypto.rs).


Denials Carry Equal Weight

Article 19 expects logs to record denied operations with sufficient detail for audit. Chio's kernel fails closed: every error path, every guard denial, and every revocation hit produces a signed deny receipt with the same field set as an allow. There is no code path that emits a decision and no receipt.

A local receipt read needs an explicit boundary, so --admin-all reads across every tenant as an audit operation and --tenant narrows to one. --outcome takes allow, deny, cancelled, or incomplete:

eu-ai-act · list-denytranscript
$ chio --receipt-db ./receipts.db receipt list --outcome deny --admin-all \
    | jq -c '{id, tool_name, verdict: .decision.verdict, reason: .decision.reason}'
{"id":"2e4541210abe95054175567578dda22730020c91bc0f036321582cae2d978bbc","tool_name":"read_file","verdict":"deny","reason":"guard denied the request: guard \"guard-pipeline\" denied the request"}
exit 0deny

A single receipt renders as a triage summary, naming the deciding guard and the policy hash in force at the time:

eu-ai-act · explaintranscript
$ DENY=$(chio --receipt-db ./receipts.db receipt list --outcome deny --admin-all | jq -r .id)
$ chio --receipt-db ./receipts.db receipt explain "$DENY" --admin-all
receipt: 2e4541210abe95054175567578dda22730020c91bc0f036321582cae2d978bbc
schema: chio.receipt.v1
identity: 2e4541210abe95054175567578dda22730020c91bc0f036321582cae2d978bbc
decision: deny
reason: guard denied the request: guard "guard-pipeline" denied the request
guard: kernel
policy_hash: 89e8cd101e3fb0c311cceb9bd7b988a6a65d371310b4fccceb08a015333885da
scope_diff: requested scope vs granted scope is not embedded in this receipt
parents: 0
repair_hint: inspect the guard and policy_hash, then mint or narrow a matching capability
exit 0

scope_diff reports that the requested-against-granted scope comparison is not embedded in the receipt, so the summary points the reader at the guard and the policy hash instead of inventing a diff it cannot derive.


Article 19(2): Describing the Logging Capability

Article 19(2) expects the technical documentation to describe the system's logging capabilities. The tool surface a governed agent can reach is itself a signed document: a ToolManifest carries one ToolDefinition per tool, each with a human-readable description and a parameter schema, and the manifest is verified by Ed25519 signature before a tool server is registered. The round trip is asserted by sign_and_verify_manifest in crates/platform/chio-manifest/src/lib.rs.


Annex IV: Retention and Archival

Annex IV Section 2(g) expects records to be retained for a period appropriate to the intended purpose and to remain accessible for post-hoc review. Retention is configured by RetentionConfig (crates/kernel/chio-kernel/src/receipt_store.rs), an optional field on KernelConfig. When it is absent, retention is off and receipts accumulate.

crates/kernel/chio-kernel/src/receipt_store.rs39-50rust
impl Default for RetentionConfig {
    fn default() -> Self {
        Self {
            retention_days: 90,
            max_size_bytes: 10_737_418_240,
            archive_path: "receipts-archive.sqlite3".to_string(),
            tenant_id: None,
            check_interval_secs: 3_600,
            explicit_cutoff_unix_secs: None,
        }
    }
}
FieldDefaultBehavior
retention_days90Time-based rotation ceiling. Operators set this to match their regulatory obligation, and retention_rotates_at_time_boundary covers the boundary.
max_size_bytes10737418240Size-based archival trigger. Rotation archives older receipts rather than deleting them, covered by retention_rotates_at_size_boundary.
archive_pathreceipts-archive.sqlite3Destination file for rotated receipts. It must be writable at the first rotation.
check_interval_secs3600How often the kernel maintenance task evaluates whether a rotation is due.

Archival keeps archived receipts verifiable rather than merely stored: the rotation copies checkpoint rows whose batch end is fully covered by the archived receipts, so a partially archived batch leaves its checkpoint in the live database. archive_preserves_checkpoint_rows in crates/kernel/chio-kernel/tests/retention.rs asserts that split.

Certain high-risk systems require ten-year retention

The Act sets the default minimum at ten years for some high-risk categories. Chio's retention config accepts an arbitrary ceiling; operators must set retention_days to match their obligation. Do not ship a high-risk deployment with the default 90-day setting.

Annex IV Section 7: Tamper-Evident Monitoring

Annex IV Section 7 expects technical documentation to describe measures that ensure log integrity. Chio's Merkle checkpoint pipeline is the implementation answer.

  • A batch of receipts produces a signed KernelCheckpoint carrying the Merkle root over the batch. checkpoint_batch_size on KernelConfig sets the cadence, defaulting to 100 receipts. It is a deployment setting, not an invariant: checkpoint_batch_size: 0 turns automatic checkpointing off (crates/kernel/chio-kernel/src/kernel/kernel_struct.rs).
  • Individual receipts can be verified to belong to the log via Merkle inclusion proofs, without re-reading the full batch. build_inclusion_proof (crates/kernel/chio-kernel/src/checkpoint.rs) produces them, and an evidence export writes them to inclusion-proofs.ndjson.
  • Archived receipts remain verifiable against checkpoint rows copied into the archive database.
  • The checkpoint signature is canonical JSON (RFC 8785) signed by the kernel's Ed25519 keypair, so an auditor can verify the signature with public information alone. build_checkpoint_signature_verifies in crates/kernel/chio-kernel/src/checkpoint/tests.rs asserts it.

The chain reports its own state. Reading it is the first thing an auditor does with a store they did not produce:

eu-ai-act · checkpoint-statustranscript
$ chio receipt checkpoint status --receipt-db ./receipts.db
status: healthy
committed_entry_seq: 3
checkpoint_seq: none
checkpointed_entry_seq: 0
next_range: 1..=3
retention_watermark_entry_seq: none
exit 0

The store above holds three committed receipts and no checkpoint yet, so checkpoint_seq reads none and next_range names the window the next checkpoint would cover. An auditor reads committed_entry_seq minus checkpointed_entry_seq as the tail that is durable but not yet tamper-evident.

receipt checkpoint verify walks the whole chain rather than reporting its head, and prints the same report when the walk finds nothing wrong:

eu-ai-act · checkpoint-verifytranscript
$ chio receipt checkpoint verify --receipt-db ./receipts.db
status: healthy
committed_entry_seq: 3
checkpoint_seq: none
checkpointed_entry_seq: 0
next_range: 1..=1
retention_watermark_entry_seq: none
exit 0

Article 14: Human Oversight

Article 14 requires that high-risk AI systems enable human oversight. Actions must be attributable to the invoking entity, replay of oversight evidence must be prevented, and stale oversight material must be rejected. The rows below map to the DPoP module at crates/kernel/chio-kernel/src/dpop.rs and its test file crates/kernel/chio-kernel/tests/dpop.rs.

RequirementChio MappingTest
Attributable decisionsEvery receipt pairs decision with capability_id and policy_hash; the receipt is signed by the kernel keypair.all_calls_produce_verified_receipts
Proof of possessionA DPoP proof body binds capability_id, tool_server, tool_name, action_hash and nonce to the agent's registered keypair. proof.body.agent_key must equal capability.subject.dpop_valid_proof_accepted
Replay preventionaction_hash is the SHA-256 of canonical invocation arguments. A nonce store blocks same-invocation replay within the TTL window.dpop_nonce_replay_within_ttl_rejected
FreshnessDPoP issued_at is checked against the kernel clock. Proofs older than proof_ttl_secs (default 300) are rejected.dpop_expired_proof_rejected
Agent identity bindingA proof signed by any key other than capability.subject is rejected before any action runs.dpop_wrong_agent_key_rejected
Step-up for sensitive tiersGovernedApprovalToken supplies a signed human-review anchor when GovernedAutonomyTier requires escalation.governed_approval_token_binds_every_authorization_field_and_time_window in crates/kernel/chio-kernel/src/kernel/tests/approval_flow.rs

Oversight evidence is portable

Because DPoP proofs and approval tokens are signed independently of the receipt they authorize, an auditor can inspect oversight material without access to live session state. Approval tokens are the structured anchor for "a human approved this step".

Article 9: Risk Management

Article 9 requires risk management that addresses monetary and operational consumption of AI systems. Chio's budget and velocity primitives are the tool-layer implementation.

  • max_total_cost and max_cost_per_invocation are enforced atomically inside the kernel. Exhaustion produces a signed deny receipt, which monetary_full_pipeline_three_invocations_third_denied walks end to end.
  • Velocity guards cap invocation rate and spend rate across time buckets, preventing runaway consumption.
  • Allow receipts for monetary actions record the actual charged cost reported by the tool server, so downstream reconciliation can compare intended and actual cost (monetary_allow_receipt_contains_financial_metadata).
  • Deny receipts for monetary denials record the attempted cost and the reason, so non-execution is evidenced (monetary_denial_receipt_contains_financial_metadata).
tool-grant.jsonjson
{
  "server_id": "billing-api",
  "tool_name": "charge.create",
  "operations": ["invoke"],
  "constraints": [
    { "type": "minimum_autonomy_tier", "value": "delegated" },
    { "type": "require_approval_above", "value": { "threshold_units": 200000 } }
  ],
  "max_invocations": 2000,
  "max_cost_per_invocation": { "units": 2000, "currency": "USD" },
  "max_total_cost": { "units": 500000, "currency": "USD" },
  "dpop_required": true
}

A ToolGrant carries the budget caps directly (max_cost_per_invocation and max_total_cost, each a minor-unit amount plus currency). Rate limiting is a separate concern: the velocity guard caps invocation and spend rate from the guard configuration, not from the grant. Autonomy is expressed as a minimum_autonomy_tier constraint, whose value is a GovernedAutonomyTier (direct, delegated, or autonomous); grant lifetime is bounded by the enclosing capability token's expires_at.


Annex III High-Risk Systems

Annex III lists categories of systems that are treated as high-risk: biometrics, critical infrastructure, education, employment, essential services, law enforcement, migration, and administration of justice. Agents that call tools in these domains benefit from Chio's fail-closed posture:

  • Fail-closed default: every unknown tool, every expired capability, every revoked capability, every evaluation error produces a deny receipt rather than a silent allow.
  • Signed evidence as default: a mediated decision emits a signed receipt as a side effect of normal operation. How durable that record is remains a deployment setting: allow_ephemeral_receipt_log permits a process-local log when no durable store is installed, and a deployment that wants durable persistence before any tool side effect leaves it false (crates/kernel/chio-kernel/src/kernel/kernel_struct.rs).
  • Explicit autonomy tiers: GovernedAutonomyTier names the level of human involvement expected for each capability. Sensitive categories stay at tiers that require step-up approval.
  • Revocation re-checked per call: evaluation checks the revocation status of the capability and its whole delegation chain on every governed call, before the guard pipeline runs (crates/kernel/chio-kernel/src/kernel/evaluation/evaluation_entry.rs).

Classification is operator-owned

Whether a specific agent system falls under Annex III, and which obligations attach, is a legal question. Chio provides the technical controls the Act's articles describe; it cannot decide whether your deployment is high-risk. Obtain legal review against the final Official Journal text before filing.

Kernel Stages and the Clauses They Serve

A governed call runs through the kernel in order. Each stage is where a specific clause's evidence is produced, which is the mapping an assessor walks:

StageWhat it doesClause
Validate capabilityChecks issuer, expiry, scope, and revocation on the token and its delegation chain.Article 19(1), through capability_id
Check DPoP proofRuns when dpop_required is set on the matched ToolGrant.Article 14
Run guard pipelineVelocity, policy, and budget guards evaluate the call. A guard that records a finding contributes a GuardEvidence entry to the receipt's evidence array; a receipt with an empty array serializes without the field.Article 9
Dispatch or denyThe one branch in the chain: the call reaches the tool server, or the kernel produces a deny receipt instead.Article 19(1)
Sign the receiptAllow or deny, signed by the kernel keypair over canonical JSON.Article 19(1)
Append to the receipt storeDurable and sequential.Annex IV Section 2(g)
Merkle checkpointTriggered every checkpoint_batch_size receipts.Annex IV Section 7
RotateArchives per RetentionConfig, carrying the covering checkpoint rows across.Annex IV Section 2(g)

Verification Commands

The full receipt-log audit runs the claim-log projection validation and a complete checkpoint-chain verification in one pass. Its next_range differs from the status command's above because the audit pins the checkpoint status to one receipt per batch, while receipt checkpoint status takes it from --max-batch, which defaults to 1024:

eu-ai-act · audittranscript
$ chio receipt audit --receipt-db ./receipts.db
status: healthy
committed_entry_seq: 3
checkpoint_seq: none
checkpointed_entry_seq: 0
next_range: 1..=1
retention_watermark_entry_seq: none
exit 0

The evidence export assembles the Article 19 package. It reads the local receipt database through the global --receipt-db flag, needs an explicit read boundary, and refuses to write into a directory that already holds files. Narrow the window with --since and --until, both Unix seconds:

eu-ai-act · exporttranscript
$ chio --receipt-db ./receipts.db evidence export \
    --policy-file ./policy.yaml --admin-all --output ./article-19-package
$ ls ./article-19-package
README.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
policy
query.json
receipts.ndjson
retention.json
exit 0

Without a boundary the export refuses rather than defaulting to every tenant:

eu-ai-act · export-no-boundarytranscript
$ chio --receipt-db ./receipts.db evidence export \
    --policy-file ./policy.yaml --output ./article-19-unbounded
error [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.
exit 1

The package is self-describing: every file the export wrote is listed in manifest.json with its SHA-256 and byte count, alongside the policy source hash and the runtime hash it compiled to. Projecting the counts out of that manifest:

eu-ai-act · manifesttranscript
$ jq '{schema, counts, proofCoverage, receiptSemantics, policy}' ./article-19-package/manifest.json
{
  "schema": "chio.evidence_export_manifest.v1",
  "counts": {
    "toolReceipts": 3,
    "childReceipts": 0,
    "checkpoints": 0,
    "capabilityLineage": 3,
    "inclusionProofs": 0,
    "uncheckpointedReceipts": 3
  },
  "proofCoverage": {
    "checkpointedReceipts": 0,
    "uncheckpointedReceipts": 3
  },
  "receiptSemantics": {
    "mediatedDecisions": 3,
    "traceObservations": 0,
    "advisoryEvaluations": 0,
    "prevent": 3,
    "detectOnly": 0,
    "advisoryOnly": 0,
    "cannotSee": 0,
    "authorized": 2
  },
  "policy": {
    "format": "hushspec",
    "sourceHash": "11e1aa04a3696c42034ae77afb4f9aee4e5c16c4e38a58f2a21fc2fa137779d8",
    "runtimeHash": "89e8cd101e3fb0c311cceb9bd7b988a6a65d371310b4fccceb08a015333885da",
    "sourcePath": "policy/source.yaml",
    "sourceBytes": 1739
  }
}
exit 0

authorized: 2 against mediatedDecisions: 3 is the denial showing through the counts: three decisions were mediated, two were authorized, one was refused. A recipient re-checks the package without the original store:

eu-ai-act · verifytranscript
$ chio evidence verify --input ./article-19-package
evidence package verified
tool_receipts:          3
child_receipts:         0
checkpoints:            0
checkpoint_publications: 0
checkpoint_witnesses:   0
checkpoint_consistency_proofs: 0
checkpoint_equivocations: 0
capability_lineage:     3
inclusion_proofs:       0
uncheckpointed_receipts: 3
authorized_receipts:     2
trace_observations:      0
advisory_evaluations:    0
verified_files:         14
child_receipt_scope:    FullQueryWindow
transparency_preview_logs: 0
publication_state:      transparency_preview
exit 0

verified_files: 14 counts the files the manifest hashes, which is every file the export wrote except manifest.json itself. Verification re-derives each hash, so editing one byte of any of them fails the check and names the file that changed.


Non-Goals

  • Chio does not perform conformity assessment. It supplies the logs an assessor inspects.
  • Chio does not register AI systems in the EU database (Annex VIII). Registration remains the provider's responsibility.
  • Chio does not evaluate model-layer properties such as bias, robustness under distribution shift, or hallucination rate.
  • Chio's evidence posture does not replace the technical file (Annex IV). It supplies inputs that populate sections about logging, oversight, and monitoring.
  • The clauses on this page are the ones the repository's own mapping covers: Article 9, Article 14, Article 19, Annex IV Section 2(g), and Annex IV Section 7. Transparency and post-market monitoring obligations are not mapped here, because no Chio mechanism is documented against them.

For the process-oriented management-system framing of these controls, continue with ISO/IEC 42001. For the operational log surface itself, see Receipts and Receipt Query API.