PlatformAuthoring & Portability
Kernel
Verified Core Boundary
Which kernel symbols are inside the proof boundary, which shell methods may claim to reach them, and which concrete effects are audited assumptions instead.
Chio’s proof boundary is a list of Rust names. A name on it is a claim that some tool reasons about that code; a name off it is a claim about nothing at all. The list lives in one machine-readable file, and the two prose documents that describe it both defer to that file. What follows is which names are on the list, which shell methods may say they reach them, and what being listed does and does not entitle you to say.
The proof methods that work inside the boundary live one section over: Lean 4 Proofs, Kani Harnesses, Creusot Contracts, Aeneas Pipeline, and TLA+ Specs each explain what one tool checks. The Kernel-to-Node split, which is a different line through the same crates, is Core & Shell.
The boundary is one TOML file
Chio’s verified-core contract is machine-readable and lives at formal/proof-manifest.toml. It is not a prose claim with a code appendix; the prose in docs/architecture/CHIO_RUNTIME_BOUNDARIES.md and in spec/PROTOCOL.md section 5.5 both point back at the manifest and say the manifest is the definition. The manifest names its own status, its own anchors, and its own registries in its first eleven lines.
schema = "chio.proof-manifest.v1"
manifest_version = 1
proof_boundary_status = "implementation_linked_protocol_core"
verification_target = "security_critical_protocol_semantics"
assumption_registry = "formal/assumptions.toml"
claim_registry = "docs/reference/CLAIM_REGISTRY.md"
lean_root_module = "Chio"
boundary_doc = "docs/architecture/CHIO_RUNTIME_BOUNDARIES.md"
spec_anchor = "spec/PROTOCOL.md#55-verified-core-boundary"
primary_toolchain = ["lean4", "creusot", "kani", "aeneas"]
pilot_toolchain = ["aeneas"]Read implementation_linked_protocol_core literally. The manifest’s own note spells out what it means: the covered region is the pure security decision logic plus, in its words, "bounded models for adjacent state transitions", and "concrete platform effects are tracked as audited assumptions". Two claims are being made at once. The decision logic is linked to shipped Rust. The transitions around it are linked to bounded models that stand in for shipped Rust, and the models are not the Rust.
The file is larger than the boundary it draws. It carries 37 Lean root modules, a property_matrix of 10 entries covering P1 through P10, 5 refinement lanes, a list of named exclusions, a set of gate commands, and a run of [[mirror]] blocks pinning per-symbol SHA-256 hashes between Rust sources and Lean or TLA+ models. Exactly one axiom is allowed anywhere in the Lean development, Chio.Json.hash_collision_resistant, and formal/theorem-inventory.json registers it one to one as ASSUME-SHA256, with concrete SHA-256 left outside Lean. The parts a Kernel reader needs are the two lists below.
The covered pure core
The boundary document and the protocol spec agree on the same four entries, naming six functions between them, and both present those as the pure Rust the current claim rests on. Every one lives in chio-kernel-core, the crate that declares #![no_std] and #![deny(unsafe_code)] in its lib.rs.
| Symbol | What it decides | What it explicitly leaves to the caller |
|---|---|---|
capability_verify::verify_capability, verify_capability_with_trusted | Signature, issuer trust, and time bounds over one in-memory token. Returns VerifiedCapability or a CapabilityError. | Its own doc comment: delegation-chain validation, revocation lookup, and subject binding. It also fixes the crypto floor at CapabilityCryptoFloor::AllowClassical and runs a NoopBudgetRegistry, so neither the configured PQ floor nor sibling-sum admission applies. Kernels that load policy.crypto_floor are told to call verify_capability_with_floor instead. |
scope::resolve_matching_grants, resolve_capability_grants | Which grants cover a (server, tool, Invoke) request, sorted by a (server-exact, tool-exact, constraint-count) specificity tuple. | Anything needing regex or IO. Constraints the portable matcher cannot evaluate return ScopeMatchError::ConstraintError rather than a match. |
evaluate::evaluate | Capability verification, subject binding on agent_id, portable scope match, then the guard pipeline, fail-closed at each step. | Budget mutation, revocation lookup, dispatch, and persistence. It is also the AllowClassical wrapper: it denies outright when attenuation_proof is present, because chain binding needs a trust-root resolver it does not have. |
receipts::sign_receipt | Recomputes sha256_hex(canonical_content) inside the trust boundary, refuses on disagreement with body.content_hash, then checks the kernel key and signs. | Receipt body assembly. The body arrives fully formed; sign_receipt never decides what a field should contain. |
The manifest itself is broader than that table. Its covered_rust_symbols array holds 47 entries and its covered_rust_modules array holds 13 module paths. The two lists do not line up. The symbols sit in 17 distinct modules, 4 of which the manifest never declares: chio_core_types::capability, chio_core_types::delegation_receipt, chio_kernel_core::revocation_view, and chio_revocation_oracle::api. chio_kernel_core::revocation_view compiles only under the revocation-view feature, which is on by default for hosted builds and off for the portable no_std one, so the covered symbol RevocationView::install_if_newer is absent from the very build the boundary is drawn around.
Fewer of them sit in chio-kernel-core than a reader of the boundary document would guess: 33 of the 47. The rest reach into chio-core-types Merkle walks and capability delegation, chio-federation revocation gossip, chio-credit scalar conversion, chio-web3 settlement state ids, chio-open-market slash allocation, and the chio-revocation-oracle inclusion proof.
| Crate | Covered symbols |
|---|---|
chio_kernel_core | 33 |
chio_federation | 5 |
chio_core_types | 4 |
chio_credit | 2 |
chio_open_market | 1 |
chio_revocation_oracle | 1 |
chio_web3 | 1 |
The six named in the table above are the ones the spec elevates as the pure decision core; the remaining 41 are supporting predicates and bounded models, and the difference between the two groups is the whole subject of the theorem inventory.
The predicate module underneath
One file holds 18 of the covered symbols, crates/kernel/chio-kernel-core/src/formal_core.rs, whose module header states the constraint that earns it the listing: it "deliberately avoids heap allocation, IO, crypto, async, and external crates so Kani, Creusot wrappers, and Aeneas can reason about the same branch logic used by the portable kernel core". Every function in it is total, over scalars, booleans, small enums, and fixed-size arrays. budget_precheck takes four u64s. dpop_admits takes four bools. receipt_fields_coupled takes five. composite_quota_authorize takes three fixed-size arrays and returns a CompositeQuotaResult that returns the input unchanged on any refusal.
That shape is what makes them provable and what limits what a proof about them says. A theorem about guard_pipeline_allows(core_authorized, guards) establishes that deny and error are both absorbing over a slice of GuardStep values. It establishes nothing about whether the runtime built the right slice.
Three covered symbols are not reachable from outside the crate
formal_aeneas is declared pub(crate) mod in chio-kernel-core/src/lib.rs and nothing re-exports its items, so the manifest paths chio_kernel_core::formal_aeneas::ledger_apply and chio_kernel_core::formal_aeneas::ledger_is_terminal do not resolve for an external caller; ledger_is_terminal is itself a pub(crate) fn. evaluate::finish_verified_evaluation is listed too and is a private fn. Being inside the boundary is not the same as being callable.Its sibling
formal_core is pub mod, so every manifest path through it does resolve. What the crate root offers is narrower: the pub use formal_core list omits classify_time_window and delivery_contract_admits, both of which are in covered_rust_symbols, along with optional_u32_cap_is_subset and monetary_cap_is_subset_by_parts, which are covered_symbols one registry over in kani-public-harnesses.toml. The short path chio_kernel_core::classify_time_window does not exist; the module path does.Two shell entry points, and what "direct" means
The pure core is a library. Something in chio-kernel has to call it, and the moment it does, the caller is running with a tokio runtime, a SQLite handle, and mutable state. The boundary document handles that by naming exactly two methods that may claim to reach the pure core directly. The spec puts it as "the two shell entrypoints that may claim direct use of that pure core today".
| Method | Defined in | Visibility | Covered part |
|---|---|---|---|
ChioKernel::evaluate_portable_verdict | chio-kernel/src/kernel/validation.rs | pub | The delegation into core evaluation with trusted issuers and portable guard wiring supplied. |
ChioKernel::build_and_sign_receipt | chio-kernel/src/kernel/responses/receipt_persistence.rs | pub(crate) | The delegation into core signing, after the shell has assembled the receipt body. |
Both methods carry the claim in their own doc comments, which is the part that makes the boundary auditable from the code rather than only from the manifest. evaluate_portable_verdict is annotated as "the one chio-kernel entrypoint inside the current bounded verified core". build_and_sign_receipt is annotated as included "only for the direct call" into core signing, with body assembly, metadata shaping, and persistence named as shell behavior outside the claim.
The manifest carries a wider list than the pair. Its shell_entrypoints array also names ChioKernel::check_revocation, ChioKernel::run_guards, DpopNonceStore::check_and_insert_through, both budget-store charge paths in chio-kernel and chio-store-sqlite, and receipt_body_fields_coupled. Those are the shell surfaces a model projects. The two in the table are the only ones the boundary document lets a shell method claim as a direct call into the pure core.
Read the actual call, though, because neither method calls the symbol the manifest lists. evaluate_portable_verdict does three fail-closed checks of its own and then calls a wider entry point in the same core module.
pub fn evaluate_portable_verdict<'a>(
&self,
capability: &'a CapabilityToken,
request: &chio_kernel_core::PortableToolCallRequest,
guards: &'a [&'a dyn chio_kernel_core::Guard],
clock: &'a dyn chio_kernel_core::Clock,
session_filesystem_roots: Option<&'a [String]>,
) -> chio_kernel_core::EvaluationVerdict {
let trusted = self.trusted_issuer_keys();
let peer_profile = match self.capability_negotiation_for_remote(None, clock.now_unix_secs())
{
Ok(profile) => profile,
// Fail closed: a negotiation error denies rather than falling back
// to the permissive default profile.
Err(reason) => {
return chio_kernel_core::EvaluationVerdict {
verdict: chio_kernel_core::Verdict::Deny,
reason: Some(format!(
"capability negotiation failed; denying fail-closed: {reason}"
)),
matched_grant_index: None,
verified: None,
};
}
};
let trust_resolver = self.capability_trust_root_resolver_snapshot();
let direct_root = match self.negotiated_capability_root(capability, &peer_profile) {
Ok(root) => root,
Err(reason) => {
return chio_kernel_core::EvaluationVerdict {
verdict: chio_kernel_core::Verdict::Deny,
reason: Some(reason),
matched_grant_index: None,
verified: None,
};
}
};
let mut budgets = match self.budget_registry.lock() {
Ok(guard) => guard,
Err(_poisoned) => {
// The monetary lock is poisoned: a panic left the registry in an
// unknown state. Deny fail-closed and trip the degraded flag so
// later evaluations are denied at the pre-dispatch gate too.
self.record_tcb_lock_poison("budget_registry");
return chio_kernel_core::EvaluationVerdict {
verdict: chio_kernel_core::Verdict::Deny,
reason: Some("budget registry lock poisoned; denying fail-closed".to_string()),
matched_grant_index: None,
verified: None,
};
}
};
chio_kernel_core::evaluate_with_full_floor_and_root(
chio_kernel_core::EvaluateInput {
request,
capability,
trusted_issuers: &trusted,
clock,
guards,
session_filesystem_roots,
},
capability_crypto_floor(self.capability_crypto_floor),
&peer_profile,
direct_root.as_ref(),
&trust_resolver,
&mut *budgets,
)
}evaluate_with_full_floor_and_root verifies through verify_capability_full_with_root, rejects aggregate_invocation_budget and cumulative approval as unsupported features, and then hands off to finish_verified_evaluation, which is where subject binding, scope match, and the guard pipeline actually run. evaluate reaches the same private tail through a narrower door. So the shared covered region is the tail, not the entry; the manifest lists both evaluate and finish_verified_evaluation for that reason, and the three fail-closed checks evaluate_portable_verdict runs before the call, plus the trust-root snapshot it takes between them, are shell behavior on both sides of the boundary.
The signing path is cleaner, because the wider function is a thin forwarder. build_and_sign_receipt calls chio_kernel_core::sign_receipt_with_handle, which consumes a one-time ReceiptSigningHandle by value and forwards its canonical bytes straight into sign_receipt, the listed symbol. The crate marks one thing about that handle as unfinished. Producers still build it from canonical content they supply rather than receiving it from evaluate, and the seam note on sign_receipt_with_handle names the intended end state: have evaluate() return the handle so the only way to obtain one is to have run an evaluation. On the kernel path the preimage is the exact bytes receipt_content_for_output hashed, which is what makes the gate non-tautological there.
require_receipt_body_fields_coupled(&body, &expected)?;
// WYSIWYS: bind the signature to the exact content this receipt's
// `content_hash` was derived from. The handle recomputes
// `sha256_hex(canonical_content)` and the signing primitive refuses to
// sign if it disagrees with `body.content_hash`, closing the
// render-A / sign-B hole on the production path. The
// canonical_content is the same preimage `receipt_content_for_output`
// hashed to produce `content_hash`.
let handle = ReceiptSigningHandle::from_content_preimage(params.canonical_content);
// ...
// Verified-core boundary note:
// `formal/proof-manifest.toml` includes this shell method only for the
// direct call into `chio_kernel_core::sign_receipt_with_handle`. Receipt
// body assembly, metadata shaping, and persistence remain
// operational-shell behavior outside the current bounded proof claim.
let backend = chio_core::crypto::Ed25519Backend::new(authority.clone());
chio_kernel_core::sign_receipt_with_handle(body, &backend, handle).map_err(|error| {
// ...
})The elided closure maps KernelKeyMismatch, ContentHashMismatch, and SigningFailed onto KernelError::ReceiptSigningFailed (receipt_persistence.rs:176-190).
build_and_sign_receipt is pub(crate) and forwards in one line to build_and_sign_receipt_with_authority, which is where the coupling check, the handle, and the boundary note above actually sit. The manifest lists the outer name.
One documented exception to WYSIWYS, and it is not in the boundary
sign_receipt_relaying_trusted_body signs without recomputing anything. It exists for the thin FFI and WASM transport adapters that receive an already-minted body and do not hold the content preimage, so they cannot re-derive the hash. It still enforces the kernel-key match, and the spec states the rule around it: every path that does hold the evaluated content must call sign_receipt or sign_receipt_with_handle instead. That function is not in covered_rust_symbols and no gate enforces the rule; it is a reviewed convention, and the reason it exists as a named function rather than a flag is so the trust is auditable in one place. Receipts & Audit covers the signing contract in full.The audited assumptions
Everything the pure core touches but does not implement is registered in formal/assumptions.toml under schema chio.formal-assumptions.v1. Each entry is one pipe-delimited line carrying an id, a classification, a contract sentence, and the property ids it supports. There are 15 of them, in 8 classes. The registry is enforced: scripts/check-formal-proofs.sh fails when the set of ids in required_assumption_ids differs from the set actually defined, in either direction, and it refuses any entry whose line does not carry all four fields.
| Assumption | Class | What is being assumed | Properties |
|---|---|---|---|
ASSUME-ED25519 | audited_crypto | Ed25519 verification and signing are assumed to satisfy standard unforgeability for trusted public keys. | P2, P3, P4 |
ASSUME-SHA256 | audited_crypto | SHA-256 and Merkle hash collision resistance are assumed for concrete receipt and checkpoint evidence. | P4, P7 |
ASSUME-CANONICAL-JSON | audited_serialization | The production canonicalizer is assumed to agree byte-for-byte with the mechanized UTF-8 renderer on Unicode scalar strings, normalized bounded integers, arbitrary finite arrays, and UTF-16-ordered objects. Float rendering and float-bearing compound receipt fields outside that domain remain assumed deterministic and byte-stable. | P4, P7, P10 |
ASSUME-OS-CLOCK | audited_platform | The injected clock is assumed to report operator-accepted Unix time within the deployment tolerance. | P2, P3, P8 |
ASSUME-SQLITE-ATOMICITY | audited_storage | SQLite transactions are assumed to provide atomic committed updates for revocation, budget, receipt, and registry state per single-row write. Cross-row crash recovery, ordering, and conservation are not assumed or discharged and remain outside the current formal claim boundary. | P2, P4, P6, P7 |
ASSUME-TLS | audited_transport | TLS endpoint authentication and channel confidentiality are assumed for configured remote control and hosted HTTP surfaces. | P8, P9 |
ASSUME-NETWORK-TRANSPORT | audited_transport | Network delivery is not assumed reliable, but authenticated messages received by Chio are assumed not to be silently rewritten below TLS or signature checks. | P2, P8, P9 |
ASSUME-GOSSIP-FAIRNESS-PARTITION-BOUND | audited_transport | For a configured bilateral revocation peer set, correct connected peers are assumed to have recurring push or catch-up opportunities governed by weak fairness, local clocks remain within the declared skew bound, and declared partitions heal within the operator-declared partition bound. Loss, duplication, reordering, and invalid frames are modeled rather than assumed away. No finite delivery-step or raw-evaluation bound is assumed. | P2 |
ASSUME-EXTERNAL-REGISTRIES | audited_service | Hosted package, certification, DID, and registry services are assumed to return state they have durably accepted or else fail closed. | P9, P10 |
ASSUME-SUBPROCESS-ISOLATION | audited_platform | Tool-server subprocess isolation and OS process boundaries are assumed for effects outside the pure kernel decision core. | P3, P6 |
ASSUME-CHAIN-FINALITY | audited_external | External chain and oracle finality are assumed only after the configured confirmation/finality policy accepts the evidence. | P4, P7 |
ASSUME-TRACE-OBSERVER | audited_observability | An installed synchronous runtime trace observer is assumed to receive exactly once before finalization every successful revocation commit, completed tool-call revocation admission, and receipt append. Kernel-assigned source sequences, checked revocation subject identities, exact revocation-source identities, and other callback fields are not rewritten before recording, and the recorder runs without calibration mutations. Delivery reordering is reconciled from source sequences; detectable omissions, duplicates, inconsistent kernel depth limits, ambiguous admission-to-receipt joins, unmatched revocation sources, and relevant revocations between admission and receipt append fail closed. This assumption does not assert that any observed kernel decision is safe. | P2, P4, P10 |
ASSUME-WASM-ENGINE | audited_platform | Wasmtime is assumed to enforce its documented i32 return, trap, fuel-metering, memory-limiter, and in-process sandbox semantics for untrusted guest code. | P3 |
ASSUME-FINDING-STATUS-OPERATOR-COMPLETENESS | audited_service | The qualified cognition-market profile verifies authentic fresh status-feed state but assumes the external status operator inserts every required retraction into that feed. | P4, P7, P10 |
ASSUME-FINDING-SELLER-TOOL-SERVER | audited_service | The qualified cognition-market profile binds kernel-observed seller output bytes and receipts but assumes the seller tool server performs any claimed effect outside Chio's observation boundary. | P3, P6, P10 |
Two entries in that table are worth reading twice. ASSUME-SHA256 is the one the Lean development is allowed to axiomatize, and ASSUME-TRACE-OBSERVER closes with the line that bounds the whole registry: it "does not assert that any observed kernel decision is safe".
None of them has been retired. retired_assumption_ids and retired_assumptions are both empty arrays, and so is the manifest’s discharged_assumptions. The registry states the bar for changing that: retiring an assumption "requires named model evidence and a concrete implementation-refinement gate over the affected production boundary", and "abstract invariants and manual mirror hashes are not sufficient by themselves".
One near-miss is recorded in full, and it is the honest kind. The distributed-revocation model in formal/tla/DistributedRevocation.tla supplies the distributed-time story the transport assumption was standing in for, and its StaleEvaluationDenied invariant mirrors the production wall-clock freshness gate. That could plausibly have retired the broad transport assumption. It did not, and the manifest carries the decision as a field: m04_p5_t5_assumptions_decision = "ASSUME-NETWORK-TRANSPORT-scoped-not-retired", beside a m04_p5_t5_rationale_anchor pointing at formal/tla/DistributedRevocation.tla::StaleEvaluationDenied. The reason is in the comment above the field, and it is about what the model does not reach: the broad transport assumption "remains required alongside the narrower named fairness and partition assumption", and "weak fairness does not prove a finite evaluation-count bound". The model’s own header says the same thing from the other end, naming RejectedRawEvaluationCountBound as deliberately outside SafetyInv with a registered witness that arbitrary loss permits more evaluations than any finite count. What stands in for that is operational, not formal: the signer-id pin and signature check in crates/trust/chio-federation/src/revocation_gossip.rs run before RevocationView::install_if_newer, and the registry closes on the same note, that the distributed model "scopes authentication, weak fairness, skew, and partition-heal obligations, but does not retire either transport assumption". Scoping an assumption is not discharging it. Assumptions & TCB works through each entry.
What is named as outside
The manifest also lists its exclusions by name in excluded_surfaces, which is a different register from the assumptions: an assumption is something Chio relies on and audits, an exclusion is something Chio makes no claim about at all. The boundary document condenses them to four bullets. The machine-readable list is finer, and three entries are worth reading closely.
- Reservation-ledger refinement from the pure
ledger_applymodel to concreteBudgetStoremutations. Runtime debug replay and stateful tests are listed as evidence and explicitly not as a proof of refinement. - Cross-row receipt and budget crash recovery, ordering, and conservation, held out until implementation trace validation and crash-reopen gates establish refinement. That is the same line
ASSUME-SQLITE-ATOMICITYdraws from the other side. - Float-valued JSON leaves in
action,metadata, or other compound receipt-id fields. Their production rendering is covered by frozen vectors andASSUME-CANONICAL-JSON, not by the mechanized canonical-JSON domain.
The remaining nine cover concrete cryptography and platform implementations, effects after the decision core allows a call, cluster consensus and settlement rails, Aeneas extraction from async and IO-heavy modules, symlink resolution and OS filesystem root enforcement, treaty predicate parsing and hashing outside its decision domain, the canonical-JSON refinement to chio-core-types/src/canonical.rs beyond checked fixtures, the trace-observer callback boundary, and wasmtime engine behavior beyond typed verdict dispatch.
Under Kani, three functions inside covered modules take a different branch
receipts.rs, the tail sign_receipt forwards into, sign_receipt_relaying_trusted_body, carries a #[cfg(kani)] block that returns a ChioReceipt whose signature came from backend.sign_bytes(b"kani-receipt-signing-model"), and the real ChioReceipt::sign_with_backend call sits behind #[cfg(not(kani))]. In scope.rs, resolve_matching_grants has a #[cfg(kani)] single-grant fast path, and the private helper matches_pattern has a byte-level prelude under the same flag. Only resolve_matching_grants of the three is itself in covered_rust_symbols; the other two sit in covered modules. The harnesses are real proofs about the branch they execute. The recompute-and-refuse gate and the kernel-key check are on the shared path and are genuinely covered; the canonical-JSON encode and the Ed25519 signature underneath them are not, which is exactly what ASSUME-CANONICAL-JSON and ASSUME-ED25519 are registered for.What holds the boundary in place
Three of the manifest’s gate_commands entries are what keep the claims above from drifting, and each checks something narrower than its name suggests. A fourth check, the boundary document’s own regression test, is not a manifest gate at all; it is a cargo test in a platform crate, and it belongs here because it is the only automated thing standing between the boundary document and silent edits.
| Check | What it verifies | What it does not |
|---|---|---|
./scripts/check-formal-proofs.sh | Manifest schema is chio.proof-manifest.v1; every root_modules and covered_rust_modules path exists; the assumption registry parses, matches its schema, and its declared and defined id sets are equal; every refinement lane is well-formed. | Anything about the contents of the Rust modules it names. Lean structure it reads closely, down to per-file namespace, open, export, and abbrev controls and the declared theorem set. For covered_rust_modules it checks only that the path exists, so a covered module that no longer defines the function the manifest names still passes. |
cargo xtask gen proof-coverage --check | Every covered symbol resolves to a workspace crate, and where possible to a module file. | The function name. surface_from_symbol splits on ::, resolves the crate, tries src/<module>.rs then src/<module>/mod.rs, and otherwise returns the crate. A renamed or deleted function does not fail this gate. |
cargo xtask check formal-mirrors | The [[mirror]] blocks: per-symbol and rollup SHA-256 over normalized token streams, so any edit to a mirrored Rust symbol forces a model review. | Semantic equivalence. The manifest states it directly: matching hashes "do not prove semantic equivalence or modeled properties". |
crates/platform/chio-control-plane/tests/runtime_boundaries.rs | That the boundary document exists and still names the ownership files, plus line-count ceilings on four runtime shells. | The verified-core section of that document. runtime_boundary_map_is_present asserts eight ownership paths and no string from the covered-core or covered-shell tables. |
Underneath those, the Kani lane is the one that touches these exact symbols. formal/rust-verification/kani-public-harnesses.toml puts every harness it registers in lanes.pr, leaves lanes.nightly_only an empty array, and drives them from scripts/check-kani-public-core.sh. The comment above the lane assignment gives the reason and the cost: the full sweep is "~2.2 min locally, within the 10-minute warm PR target". Both Kani registries and the Creusot registry are marked required in rust_refinement_lanes; both Aeneas lanes are marked pilot and production. The manifest summarizes the division: Creusot and Kani are "required strict-CI lanes for production implementation linkage", and the public Kani harnesses "target verify_capability, NormalizedScope::is_subset_of, resolve_matching_grants, evaluate, sign_receipt, and the shared budget and revocation admission projections". That is a nearby split, not an identical one. The Kani list adds NormalizedScope::is_subset_of, which the boundary document’s four entries do not name, and drops the two companion functions verify_capability_with_trusted and resolve_capability_grants. Its own covered_symbols array is wider than either, reaching PublicKey::verify_canonical in chio-core-types and compute_slash_allocation in chio-open-market.
Guarantees and limits
| Status | Claim | Evidence |
|---|---|---|
| Shipped | The boundary is machine-readable and single-sourced. The architecture document and protocol spec both defer to formal/proof-manifest.toml, and the manifest names both of them back as boundary_doc and spec_anchor. | formal/proof-manifest.toml lines 1 through 11; docs/architecture/CHIO_RUNTIME_BOUNDARIES.md, Implementation-Linked Verified Core Boundary; spec/PROTOCOL.md 5.5 |
| Shipped | Four entries naming six pure functions are the covered decision core, and the architecture document and the spec list the same four. | verify_capability and verify_capability_with_trusted, resolve_matching_grants and resolve_capability_grants, evaluate, sign_receipt |
| Shipped | Exactly two chio-kernel methods may claim direct use of that core, and both carry the claim as a doc comment at the call site. | kernel/validation.rs ("the one chio-kernel entrypoint inside the current bounded verified core"); kernel/responses/receipt_persistence.rs ("only for the direct call") |
| Shipped | Receipt signing recomputes content_hash from the canonical preimage before the kernel-key check and before any signing work, and refuses on mismatch. The preimage is still supplied by the producer rather than returned by evaluate, which the crate records as a follow-up; on the kernel path it is the same bytes receipt_content_for_output hashed, which is what makes the gate non-tautological there. | sign_receipt in chio-kernel-core/src/receipts.rs; ReceiptSigningError::ContentHashMismatch; the WYSIWYS section of spec/PROTOCOL.md |
| Proved by harness | Both WYSIWYS arms are under Kani: a body claiming a hash that is not the SHA-256 of the supplied preimage is refused, and a body whose claimed hash matches signs. | public_sign_receipt_refuses_content_hash_mismatch and public_sign_receipt_accepts_matching_content_hash in kani_public_harnesses.rs, both in lanes.pr |
| Limit | Neither shell entry point calls the symbol the manifest lists. evaluate_portable_verdict calls evaluate_with_full_floor_and_root; build_and_sign_receipt calls sign_receipt_with_handle. The signing forwarder is a thin pass-through into sign_receipt; the evaluation one is not, and the shared region is the private finish_verified_evaluation tail. | The final expression of each method; sign_receipt_with_handle ends in sign_receipt(body, backend, &canonical_content) |
| Limit | Three fail-closed checks in evaluate_portable_verdict run before the boundary is crossed and are outside it: capability negotiation, the negotiated capability root, and the budget-registry lock. A poisoned lock denies and calls record_tcb_lock_poison. A fourth step between them, taking the trust-root resolver snapshot, returns a value directly and has no failure arm. | crates/kernel/chio-kernel/src/kernel/validation.rs |
| Limit | Under --cfg kani, the signing path returns a modeled receipt with a stub signature and never calls ChioReceipt::sign_with_backend; the scope matcher takes a single-grant fast path and a byte-level pattern comparison. | #[cfg(kani)] and #[cfg(not(kani))] blocks in chio-kernel-core/src/receipts.rs and src/scope.rs |
| Limit | Three covered names are unreachable from outside chio-kernel-core. formal_aeneas is pub(crate) mod with no re-export, which takes ledger_apply and ledger_is_terminal with it, and finish_verified_evaluation is private. formal_core is pub mod, so its manifest paths do resolve; classify_time_window and delivery_contract_admits are simply absent from the crate-root re-export list. | chio-kernel-core/src/lib.rs module declarations and the pub use formal_core::{...} list |
| Limit | The coverage gate is a module-file check. It asserts that every path in covered_rust_modules exists; nothing reads covered_rust_symbols. A renamed or deleted covered function passes as long as its module file still exists. | The covered_rust_modules loop in scripts/check-formal-proofs.sh:144-146 |
| Limit | No gate_commands entry reads the boundary document. The only automated check over it is a cargo test, and that test checks the ownership map only, so the verified-core section can be edited without failing anything. | runtime_boundary_map_is_present in crates/platform/chio-control-plane/tests/runtime_boundaries.rs |
| Assumption-bound | Concrete Ed25519, SHA-256, canonical JSON, TLS, the OS clock, SQLite, chain finality, wasmtime, subprocess isolation, hosted registries, network transport, gossip fairness, the trace observer, and the two cognition-market operator boundaries are audited, not proved. 15 entries, none retired. | formal/assumptions.toml; retired_assumption_ids = []; discharged_assumptions = [] in the manifest |
| Not claimed | Refinement from the pure reservation-ledger model to concrete BudgetStore mutations, and cross-row receipt and budget crash recovery, ordering, and conservation. Both are named exclusions rather than gaps discovered by a reader. | excluded_surfaces in formal/proof-manifest.toml |
| Not claimed | That any of this establishes a running kernel is safe. ASSUME-TRACE-OBSERVER ends by saying it "does not assert that any observed kernel decision is safe", and P10 is titled report truthfulness, which is a claim about reports and not about outcomes. | formal/assumptions.toml; the P10 row of property_matrix |
Next steps
- Formal Assurance Overview · the same boundary from the proof side, with the reading paths by role
- Assumptions & TCB · each registry entry worked through, and what retiring one would take
- Kani Harnesses · the lane that touches these exact symbols, its bounds, and what a bounded proof settles
- Core & Shell · the other line through the same crates: what pure evaluation excludes, and why
- Portable Kernel Core · the four build shapes the covered crate compiles into, and the two platform inputs it takes