Chio/Docs
LOGIN · JOIN

EU AI Act

Map signed receipts, checkpoints, approval tokens, and retention settings to the EU AI Act articles an assessor checks.

Reference chio@5f63e6e

On this page

The EU AI Act asks a provider of a high-risk AI system to record its events automatically, keep those logs, and let people oversee and halt it. Chio supplies signed records of every kernel decision on a governed tool call, with checkpoints and approval tokens, that an assessor verifies offline.

Mapped against Regulation (EU) 2024/1689, reviewed 10 September 2026. The rows cover Chio's technical contributions to risk management, oversight, event recording, and retention. The operator determines its provider or deployer role and whether the regulation applies to the system.

Each row reads requirement, technical contribution, operator action, evidence. A signature proves who signed and binds the recorded content; the truth of a business claim and the completion of an organizational process stay with the operator. Build and verify an evidence package.

Clause coverageSelect a clause to see the record that answers it.
EU AI ActChio controlsKept by the operatorArt. 9risk managementArt. 12automatic recordingArt. 14human oversightArt. 18documentation keepingArt. 19log retentionAnnex IIIhigh-risk classificationAnnex IVtechnical documentationAnnex VIIIEU database registrationκcapabilityxrevocationτapproval⊥stopgguardsBbudgetμmanifestρreceiptχcheckpointαretentionEexporthoperator
EU AI ActChio controlsKept by the operatorArt. 9Art. 12Art. 14Art. 18Art. 19Annex IIIAnnex IVAnnex VIIIκcapabilityxrevocationτapproval⊥stopgguardsBbudgetμmanifestρreceiptχcheckpointαretentionEexporthoperator

Article 9: risk management

Evidence Deny receipt with FinancialReceiptMetadata · owned by the operator

A lifecycle risk-management system covering risks to health, safety, and fundamental rights. Budget and rate controls address selected operational risks inside it. Answered by the guard pipeline and budget caps.

Operator. Run the Article 9 process; the kernel's caps and guards are inputs to it.

Read the mapping in EU AI Act.

Selected articles and annexes of Regulation (EU) 2024/1689 joined to the Chio controls whose records answer them, with the obligations the operator keeps. Clause titles are paraphrased. Model training, inference, and transparency obligations are left out.

The map covers the tool-invocation boundary: what an agent asked a tool to do, what the kernel decided, and who approved it. Articles drawn against the neutral node are obligations the operator keeps, with Chio records as inputs.

Clause mapping

Clause ids follow Regulation (EU) 2024/1689, and the titles beside them are paraphrased.

ClauseChio controlEvidence recordOwner
Art. 9 risk management
Guard pipeline, budget caps
Deny receipt with FinancialReceiptMetadata
Operator
Art. 12 automatic recording
Capability token, signed receipt
ChioReceipt
Chio kernel
Art. 14 human oversight
Approval token, revocation, emergency stop
GovernedApprovalToken, chio emergency-stop
Operator
Art. 18 documentation keeping
Operator process
Provider documentation archive
Operator
Art. 19 log retention
Retention and archive, Merkle checkpoint
RetentionConfig archive, KernelCheckpoint
Chio kernel
Annex III high-risk classification
Operator process
Classification record
Operator
Annex IV technical documentation
Tool manifest, evidence export
ToolManifest, evidence package
Operator
Annex VIII registration
Operator process
Registration record
Operator

Articles 12 and 19 are where the kernel does the work: it signs a receipt for each decision and archives receipts with the checkpoints that keep them verifiable, while the operator chooses the retention schedule. Everywhere else a Chio record is an input to a process the operator runs, such as an approval token inside an Article 14 oversight procedure.

A governed call produces each record in a fixed order

An assessor can walk one call through the kernel and name the article each stage serves.

  1. Validate the capability. The kernel checks issuer, expiry, scope, and revocation on the token and its delegation chain. The capability_id it records serves Article 12.
  2. Check the DPoP proof. This runs when dpop_required is set on the matched ToolGrant. It supports authentication; human oversight is a separate application flow.
  3. Run the guard pipeline. Velocity, policy, and budget guards evaluate the call for Article 9. A guard that records a finding adds a GuardEvidence entry to the receipt’s evidence array; an empty array serializes without the field.
  4. Dispatch or refuse. The one branch: the call reaches the tool server, or the kernel produces a deny receipt instead (Article 12).
  5. Sign the receipt. Allow or deny, signed by the kernel keypair over canonical JSON (Article 12, retained under Article 19).
  6. Append to the receipt store. Durable and sequential, for Article 19 retention.
  7. Checkpoint. Every checkpoint_batch_size receipts the kernel signs a Merkle checkpoint, an integrity control the Act leaves to the implementation.
  8. Rotate. RetentionConfig archives aged receipts and carries the covering checkpoint rows across (Article 19).

Read more in Receipts.

Review evidence comes from one verified package

The complete evidence workflow produces three signed receipts, samples allow and deny decisions from the same recorded window, exports them with checkpoint proofs, and verifies the package. It carries the kernel host, every command, the captured results, and two deliberate refusals.

For a deployment review, substitute its receipt database and the approved tenant and time boundary. Preserve the original package, record the trusted kernel public key through an independent channel, and attach the framework owner, assessment scope, findings, and open work. A passing cryptographic check is one input to that review.

Produce, sample, and export the example receipts

Produce the receipts

Complete the project setup, then run these steps from federation-guide/ in a fresh evidence-run directory. The host and the program ship in the project download.

Terminalbash
cargo run --locked --bin evidence
Output · exit 0
two allowed calls; one out-of-scope denial; handler calls 2
three receipt signatures verified; checkpoint batch size 1
wrote evidence-run/receipts.sqlite3, window.json, and kernel-public-key.txt

Collect samples from the same window

Terminalbash
EVIDENCE_SINCE=$(jq -r .since evidence-run/window.json)
EVIDENCE_UNTIL=$(jq -r .until evidence-run/window.json)
mkdir -p samples
chio receipt list --receipt-db evidence-run/receipts.sqlite3 --admin-all \
  --since "$EVIDENCE_SINCE" --until "$EVIDENCE_UNTIL" \
  --outcome allow --limit 100 > samples/allow.jsonl
chio receipt list --receipt-db evidence-run/receipts.sqlite3 --admin-all \
  --since "$EVIDENCE_SINCE" --until "$EVIDENCE_UNTIL" \
  --outcome deny --limit 100 > samples/deny.jsonl
jq -s length samples/allow.jsonl
jq -s length samples/deny.jsonl
Output · exit 0
2
1

Export with complete checkpoint coverage

Terminalbash
EVIDENCE_SINCE=$(jq -r .since evidence-run/window.json)
EVIDENCE_UNTIL=$(jq -r .until evidence-run/window.json)
chio evidence export --receipt-db evidence-run/receipts.sqlite3 --admin-all \
  --since "$EVIDENCE_SINCE" --until "$EVIDENCE_UNTIL" \
  --require-proofs --output evidence-package
chio evidence verify --input evidence-package
Output · exit 0
evidence package verified
tool_receipts:          3
child_receipts:         0
checkpoints:            3
checkpoint_publications: 3
checkpoint_witnesses:   2
checkpoint_consistency_proofs: 2
checkpoint_equivocations: 0
capability_lineage:     1
inclusion_proofs:       3
uncheckpointed_receipts: 0
authorized_receipts:     2
trace_observations:      0
advisory_evaluations:    0
verified_files:         12
child_receipt_scope:    FullQueryWindow
transparency_preview_logs: 1
publication_state:      transparency_preview

Verify security evidence covers how to pin the trusted kernel key and what each verifier check establishes.

An Article 19 package verifies without the original store

The evidence export reads the receipt database through the global --receipt-db flag and needs an explicit read boundary. It 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 instead of 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[CHIO-KERNEL-RECEIPT-READ-CONTEXT-MISSING]: evidence export needs a read boundary
  help: pass --tenant <id> to export one tenant's receipts, or --admin-all to export every tenant as the operator
  code: urn:chio:error:kernel:receipt-read-context-missing
  docs: https://chio.computer/docs/reference/errors#error-kernel-receipt-read-context-missing
exit 1

manifest.json lists every file the export wrote with its SHA-256 and byte count, beside the policy source hash and the runtime hash it compiled to. Projecting the counts:

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

You should see authorized: 2 against mediatedDecisions: 3: three decisions were mediated, two were authorized, and 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 every file the manifest hashes, which is every file the export wrote except manifest.json. Verification re-derives each hash, so a changed byte in any of them fails the check and names the file that changed.

The operator owns classification, the technical file, and registration

Chio supplies the logs an assessor inspects. These obligations stay with the operator, who decides its provider or deployer role first:

  1. Classification. Decide whether the system falls under Annex III and which obligations attach, with legal review against the Official Journal text before filing.
  2. Risk management. Run the Article 9 process. The kernel’s caps and guards are inputs to it.
  3. Oversight. Assign the human roles, supply the interface and procedures, and evaluate whether oversight works in context.
  4. Technical file. Write and maintain the Annex IV file. Chio records populate its sections on logging, oversight, and monitoring.
  5. Documentation and logs. Keep the Article 18 documentation for ten years, separately from the receipt log, and set rotation and archive retention to the Article 19 schedule.
  6. Conformity and registration. Complete the conformity assessment, which reads these logs, and register the system in the EU database under Annex VIII.

Model-layer properties such as bias, robustness under distribution shift, and hallucination rate are evaluated above the tool-invocation boundary. Transparency and post-market monitoring obligations stay with the operator, outside this mapping.

Evidence by article

The clause-by-clause source for these entries is docs/compliance/eu-ai-act-article-19.md in the Chio repository, which names the test function behind each row. The official text is Regulation (EU) 2024/1689.

Articles 12 and 19: automatically generated logs

Article 12(1): “High-risk AI systems shall technically allow for the automatic recording of events (logs) over the lifetime of the system.” Article 19 covers provider retention of the logs it controls, for at least six months unless other applicable law provides otherwise. 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 evident. 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: cost_charged, currency, attempted_cost, and settlement_status (not_applicable, pending, settled, or failed).

These fields are the wire form of ChioReceipt (crates/core/chio-core-types/src/receipt/body.rs). This refusal was read back out of a receipt store after a guard refused a call under the canonical policy shipped in the repository:

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

decision is an internally tagged enum, so a verdict is self-describing and a refusal carries its reason and guard in the same 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,
    },
}

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

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. Only the non-Ed25519 curves carry a prefix (p256:, p384:), so a verifier dispatches on the encoding alone (crates/core/chio-core-types/src/crypto.rs).

Refusal records

The kernel records refusals with the same field set as allowed decisions, so a refusal names the control that stopped the request. Each executed call ends in exactly one terminal receipt.

A local receipt read takes an explicit boundary: --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 that names the deciding guard and the policy hash in force:

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 states that the comparison of requested and granted scope is outside the receipt, so the summary points to the guard and the policy hash. The receipt query API serves the same records to an operator’s tooling.

Annex IV: admitted tool interface

Tool manifests support the operator’s technical documentation. A ToolManifest carries one ToolDefinition per tool, each with a human-readable description and a parameter schema, and the kernel verifies the manifest’s Ed25519 signature before it registers the tool server. sign_and_verify_manifest in crates/platform/chio-manifest/src/lib.rs asserts the round trip.

Articles 18 and 19: retention and archival

Configure live rotation and archive retention together to meet the required period. Chio uses RetentionConfig (crates/kernel/chio-kernel/src/receipt_store.rs), an optional field on KernelConfig. With the field 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_days
90
Time-based rotation ceiling, set to the obligation; tested by retention_rotates_at_time_boundary
max_size_bytes
10737418240
Size trigger that archives aged receipts; tested by retention_rotates_at_size_boundary
archive_path
receipts-archive.sqlite3
Archive file, writable by the first rotation
check_interval_secs
3600
How often the maintenance task checks whether rotation is due

Archived receipts stay verifiable. Rotation copies the checkpoint rows whose batch end the archived receipts fully cover, 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.

Article 18 requires the provider to keep specified documentation for ten years after a high-risk system is placed on the market or put into service. Article 19 requires provider-controlled automatic logs for an appropriate period of at least six months, subject to other applicable Union or national law. Set live rotation and archive retention to the deployment’s schedule; see Articles 18 and 19 in the official regulation.

Checkpoint integrity

A signed Merkle checkpoint supports log-integrity checks. The Act prescribes no mechanism here; the operator describes this one as part of the implemented logging design.

  • A batch of receipts produces a signed KernelCheckpoint carrying the Merkle root over the batch. checkpoint_batch_size on KernelConfig sets the cadence, 100 receipts by default, and checkpoint_batch_size: 0 turns automatic checkpointing off (crates/kernel/chio-kernel/src/kernel/kernel_struct.rs).
  • A Merkle inclusion proof shows one receipt belongs to the log without re-reading the 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 verify against the 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 checks it 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, which is the first thing an auditor reads in a store someone else produced:

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

This store holds three committed receipts and no checkpoint, so checkpoint_seq reads none and next_range names the window the next checkpoint covers. committed_entry_seq minus checkpointed_entry_seq is the durable tail that no checkpoint covers.

receipt checkpoint verify walks the whole chain instead of 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

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 because the audit pins 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

Article 14: human oversight

Article 14 concerns effective oversight by natural persons: understanding the system’s limits, monitoring and interpreting its output, deciding when to override it, and intervening or bringing it to a safe halt. Chio supplies the technical parts of an application’s approval and intervention path; the operator assigns the roles, supplies the interface and procedures, and evaluates whether oversight works in context. See Article 14 in the official regulation.

Review, approve, deny, and stop an action

Create a pending approval, bind a reviewer’s signed decision to the exact request, dispatch only after approval, and revoke future use of the capability.

Complete the project setup, then run the program from federation-guide/. The download includes every constructor and input.

Terminalbash
cargo run --locked --bin oversight -- approve
Output on darwin/arm64 · exit 0
approval requested; handler calls 0
changed action binding: refused; request remains pending
approved action: ALLOW; handler calls 1
decision replay: refused
operator revoked capability: next call denied; handler count unchanged

The example uses a local reviewer key and process-local approval store. Captures pass approve or deny explicitly; omit the argument for a terminal prompt. An application must authenticate its reviewer, protect their key, provide the review interface, and retain decisions durably. Revocation does not reverse a completed effect or cancel arbitrary remote work.

Read the complete program 125 lines · complete file
src/bin/oversight.rsrust
use chio_core_types::{
    capability::governance::{
        GovernedApprovalDecision, GovernedApprovalToken, GovernedApprovalTokenBody,
    },
    Keypair,
};
use chio_kernel::{approval::*, Verdict};
use federation_guide::*;
use std::{
    io::{self, Write},
    path::PathBuf,
    sync::{atomic::Ordering, Arc},
};

#[tokio::main]
async fn main() -> Result<(), Error> {
    let choice = match std::env::args().nth(1) {
        Some(v) => v,
        None => {
            println!("Review proposal-7: record the release decision. This changes the local review record.");
            print!("Type approve or deny: ");
            io::stdout().flush()?;
            let mut v = String::new();
            io::stdin().read_line(&mut v)?;
            v.trim().into()
        }
    };
    if choice != "approve" && choice != "deny" {
        return Err("expected approve or deny; nothing dispatched".into());
    }
    let directory = PathBuf::from(format!("oversight-{choice}"));
    let host = host(&directory)?;
    let request = request(&host.capability, "review-proposal-7");
    let approver = Keypair::generate(); // Local reviewer identity for this example.
    let store = Arc::new(InMemoryApprovalStore::new());
    let guard = ApprovalGuard::new(store.clone());
    let trusted = [approver.public_key()];
    let result = guard.evaluate(
        ApprovalContext {
            request: &request,
            constraints: &[],
            policy_id: "review-policy",
            trusted_approvers: &trusted,
            presented_token: None,
            force_approval: true,
            approval_id_override: Some("review-7".into()),
        },
        now(),
    )?;
    let pending = match result {
        HitlVerdict::Pending { request, .. } => request,
        other => return Err(format!("expected pending: {other:?}").into()),
    };
    assert_eq!(host.calls.load(Ordering::SeqCst), 0);
    std::fs::write(
        directory.join("approval-request.json"),
        serde_json::to_vec_pretty(&pending)?,
    )?;
    println!("approval requested; handler calls 0");
    let token = GovernedApprovalToken::sign(
        GovernedApprovalTokenBody {
            id: "review-token-7".into(),
            approver: approver.public_key(),
            subject: host.capability.subject.clone(),
            governed_intent_hash: pending.parameter_hash.clone(),
            request_id: pending.approval_id.clone(),
            threshold_proposal_hash: None,
            issued_at: now(),
            expires_at: now() + 120,
            decision: if choice == "approve" {
                GovernedApprovalDecision::Approved
            } else {
                GovernedApprovalDecision::Denied
            },
        },
        &approver,
    )?;
    let decision = ApprovalDecision {
        approval_id: pending.approval_id.clone(),
        outcome: if choice == "approve" {
            ApprovalOutcome::Approved
        } else {
            ApprovalOutcome::Denied
        },
        reason: Some(format!("local reviewer chose {choice}")),
        approver: approver.public_key(),
        token,
        received_at: now(),
    };
    let mut wrong = decision.clone();
    let mut wrong_body = decision.token.body();
    wrong_body.governed_intent_hash = "0".repeat(64);
    wrong.token = GovernedApprovalToken::sign(wrong_body, &approver)?;
    assert!(resume_with_decision(store.as_ref(), &wrong, now()).is_err());
    assert!(store.get_pending(&pending.approval_id)?.is_some());
    println!("changed action binding: refused; request remains pending");
    let outcome = resume_with_decision(store.as_ref(), &decision, now())?;
    std::fs::write(
        directory.join("reviewer-decision.json"),
        serde_json::to_vec_pretty(&decision)?,
    )?;
    // The embedder owns this branch. Only a verified Approved resolution dispatches.
    if outcome == ApprovalOutcome::Approved {
        let response = host.kernel.evaluate_tool_call(&request).await?;
        assert_eq!(response.verdict, Verdict::Allow);
        record(&directory, "approved", &response)?;
        assert_eq!(host.calls.load(Ordering::SeqCst), 1);
        println!("approved action: ALLOW; handler calls 1");
    } else {
        assert_eq!(host.calls.load(Ordering::SeqCst), 0);
        println!("reviewer denied: no dispatch; handler calls 0");
    }
    assert!(resume_with_decision(store.as_ref(), &decision, now()).is_err());
    println!("decision replay: refused");
    // Stop future governed use of this capability. This does not undo an effect
    // already completed, cancel a remote task, or stop another capability.
    host.kernel.revoke_capability(&host.capability.id)?;
    let count = host.calls.load(Ordering::SeqCst);
    let stopped = host.kernel.evaluate_tool_call(&request).await?;
    assert_ne!(stopped.verdict, Verdict::Allow);
    assert_eq!(host.calls.load(Ordering::SeqCst), count);
    record(&directory, "stopped", &stopped)?;
    println!("operator revoked capability: next call denied; handler count unchanged");
    Ok(())
}

Open the source file. The shared kernel host registers the tool, issues the capability, and persists receipts. Each program asserts its expected refusals; keys, timestamps, and digests change between runs.

The same program with the reviewer’s denial dispatches nothing:

Terminalbash
cargo run --locked --bin oversight -- deny
Output on darwin/arm64 · exit 0
approval requested; handler calls 0
changed action binding: refused; request remains pending
reviewer denied: no dispatch; handler calls 0
decision replay: refused
operator revoked capability: next call denied; handler count unchanged

For a safe halt, an operator runs chio emergency-stop from the CLI, the process-host socket, or the HTTP route. The stop is durable: a stopped kernel restarts stopped and refuses every governed call until chio emergency-resume.

These checks protect attribution and the handling of evidence. DPoP establishes possession of an agent key; human review comes from the approval token.

RequirementChio mappingTest
Attributable decisions
decision with capability_id and policy_hash, signed by the kernel keypair
all_calls_produce_verified_receipts
Proof of possession
Proof binds capability_id, tool_server, tool_name, action_hash, nonce
dpop_valid_proof_accepted
Replay prevention
Nonce store refuses the same invocation within the TTL
dpop_nonce_replay_within_ttl_rejected
Freshness
Proof refused once issued_at is past proof_ttl_secs (default 300)
dpop_expired_proof_rejected
Agent identity binding
proof.body.agent_key must equal capability.subject
dpop_wrong_agent_key_rejected
Step-up for sensitive tiers
GovernedApprovalToken when GovernedAutonomyTier requires escalation
governed_approval_token_binds_every_authorization_field_and_time_window

action_hash is the SHA-256 of the canonical invocation arguments, and the kernel checks issued_at against its own clock. A proof signed by any key other than capability.subject is refused before any action runs. The approval-token test lives in crates/kernel/chio-kernel/src/kernel/tests/approval_flow.rs.

DPoP proofs and approval tokens are signed independently of the receipt they authorize, so an auditor inspects oversight material without access to live session state. The approval token is the structured anchor for “a human approved this step”.

Article 9: risk management

Article 9 addresses a lifecycle risk-management system, including risks to health, safety, and fundamental rights. Budget and rate controls address selected operational risks inside that process:

  • 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 from issue to refusal.
  • Velocity guards cap invocation rate and spend rate across time buckets.
  • Allow receipts for monetary actions record the charged cost the tool server reported, so reconciliation compares intended and charged cost (monetary_allow_receipt_contains_financial_metadata).
  • Deny receipts for monetary refusals 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, each a minor-unit amount plus currency. The velocity guard takes its invocation and spend rates from the guard configuration. Autonomy is a minimum_autonomy_tier constraint whose value is a GovernedAutonomyTier (direct, delegated, or autonomous), and the enclosing capability token’s expires_at bounds the grant’s lifetime.

Annex III: high-risk categories

Annex III lists categories of systems 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 run under these kernel defaults:

  • Fail closed. An unknown tool, an expired or revoked capability, and an evaluation error each produce a deny receipt.
  • Signed evidence by default. A mediated decision emits a signed receipt in normal operation. allow_ephemeral_receipt_log permits a process-local log when no durable store is installed; a deployment that requires 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 human involvement expected for each capability. Sensitive categories stay at tiers that require step-up approval.
  • Revocation 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).

Whether a specific agent system falls under Annex III, and which obligations attach, is a legal decision the operator records in its classification.

Next: build the package an assessor verifies