PlatformInventory & Assumptions
Formal Assurance
Assumptions & TCB
Every formal claim holds only where its assumptions hold. This is the registry of them and where the trusted computing base ends.
Assumptions bound each claim
The trusted computing base
Chio's TCB is the set of components whose failure can invalidate a formal claim.
formal/assumptions.toml:23-39at fe56570The registry and the list below are not the same set. The registry records what the proofs assume and names an id for each, which is what lets a property cite one. The list is wider: rustc and the hardware under it are trusted without a registry entry, because no proof in the tree is stated in terms of them.
- Cryptographic primitives · The implementations behind
ed25519-dalek,sha2, theecdsastack for P-256 and P-384, and the canonical-JSON serializer. The proofs treat these symbolically; Chio assumes they satisfy standard unforgeability and collision resistance. - Rust compiler · The rustc toolchain is trusted to lower the Rust source faithfully. Aeneas extracts a Lean model from the source-level Rust; the binary that actually runs is what rustc produces.
- Operating-system primitives · The OS clock is trusted to deliver operator-accepted Unix time within tolerance. Subprocess isolation and OS process boundaries are trusted for effects outside the kernel. SQLite is trusted for per-row write atomicity.
- Hardware · CPU, memory, and storage are assumed to behave correctly. Chio does not attempt to reason about Rowhammer, voltage glitching, or microarchitectural side channels. The TEE-mode lane (when enabled) reduces the hardware trust set to a specific enclave.
- TLS stack · Endpoint authentication and channel confidentiality on configured remote control and hosted HTTP surfaces.
The assumptions table
required_assumption_ids in formal/assumptions.toml lists 15 ids, and every one of them has a matching row in the assumptions array. scripts/check-formal-proofs.sh compares the two sets and rejects the tree on any difference in either direction, so an id without a statement stops the gate and so does a statement without an id.
required_assumption_ids = [
"ASSUME-ED25519",
"ASSUME-SHA256",
"ASSUME-CANONICAL-JSON",
"ASSUME-OS-CLOCK",
"ASSUME-SQLITE-ATOMICITY",
"ASSUME-TLS",
"ASSUME-NETWORK-TRANSPORT",
"ASSUME-GOSSIP-FAIRNESS-PARTITION-BOUND",
"ASSUME-EXTERNAL-REGISTRIES",
"ASSUME-SUBPROCESS-ISOLATION",
"ASSUME-CHAIN-FINALITY",
"ASSUME-TRACE-OBSERVER",
"ASSUME-WASM-ENGINE",
"ASSUME-FINDING-STATUS-OPERATOR-COMPLETENESS",
"ASSUME-FINDING-SELLER-TOOL-SERVER",
]Each row below carries the id, the statement verbatim from the registry, the property identifiers the row maps to, and a note on what would mitigate the assumption breaking in production. The property column is the row's fourth field: the properties whose evidence rests on the assumption, not properties the assumption discharges. Only the mitigation column is editorial; the other three are rendered from the registry.
| Assumption | Statement | Properties | Mitigation if violated |
|---|---|---|---|
ASSUME-ED25519 | Ed25519 verification and signing are assumed to satisfy standard unforgeability for trusted public keys. | P2, P3, P4 | Rotate the trusted-key set immediately, revoke affected lineages, and move the lane to ECDSA P-384 through SigningAlgorithm. |
ASSUME-SHA256 | SHA-256 and Merkle hash collision resistance are assumed for concrete receipt and checkpoint evidence. | P4, P7 | Re-anchor the affected receipt log under a different hash and treat every post-compromise receipt as unverified. |
ASSUME-CANONICAL-JSON | 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 | The differential tests in formal/diff-tests/tests/canonical_json_diff.rs compare the production canonicalizer against a separately derived implementation, so a divergence inside the mechanized domain shows up there. Outside that domain, pin the canonicalizer version and re-sign. |
ASSUME-OS-CLOCK | The injected clock is assumed to report operator-accepted Unix time within the deployment tolerance. | P2, P3, P8 | Sidecar to a trusted time source, lower the deployment tolerance, or fail closed when the injected clock disagrees with the configured oracle. |
ASSUME-SQLITE-ATOMICITY | 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 | Move to a stronger storage backend. Cross-row behavior is already outside the claim: the manifest states that ReceiptBeforeAllow does not discharge concrete cross-row crash recovery, which remains excluded until implementation trace validation and crash-reopen conservation gates establish refinement. |
ASSUME-TLS | TLS endpoint authentication and channel confidentiality are assumed for configured remote control and hosted HTTP surfaces. | P8, P9 | Pin trust roots, require mTLS, or run the surface on a network you control. |
ASSUME-NETWORK-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 | Every Chio message is signed, so a rewrite below TLS is caught by the signature check rather than by the transport. The assumption covers what TLS and the signature check together already reject. |
ASSUME-GOSSIP-FAIRNESS-PARTITION-BOUND | 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 | Tighten the declared skew and partition-heal bounds so a peer that misses its catch-up window falls outside the configured set. Observed-epoch freshness fails closed at the RevocationFreshness surface, so a stale peer denies rather than allows. |
ASSUME-EXTERNAL-REGISTRIES | Hosted package, certification, DID, and registry services are assumed to return state they have durably accepted or else fail closed. | P9, P10 | Mirror the registries locally and treat any hosted-registry response as advisory until it is gated on a Chio-signed receipt. |
ASSUME-SUBPROCESS-ISOLATION | Tool-server subprocess isolation and OS process boundaries are assumed for effects outside the pure kernel decision core. | P3, P6 | Run tool servers in a stronger sandbox. The kernel verdict bounds what is authorized, not what a tool server does once a call is allowed. |
ASSUME-CHAIN-FINALITY | External chain and oracle finality are assumed only after the configured confirmation/finality policy accepts the evidence. | P4, P7 | Raise the confirmation depth in the finality policy, or anchor to a chain whose finality semantics match the value at risk. |
ASSUME-TRACE-OBSERVER | 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 | Detectable omissions, duplicates, ambiguous admission-to-receipt joins and unmatched revocation sources already fail closed, so the residual exposure is a rewrite before delivery that the recorder cannot see. Treat trace-derived evidence as unqualified whenever the recorder is not the installed synchronous observer. |
ASSUME-WASM-ENGINE | Wasmtime is assumed to enforce its documented i32 return, trap, fuel-metering, memory-limiter, and in-process sandbox semantics for untrusted guest code. | P3 | Run the guard in advisory mode or move it out of process. The Lean wasm-boundary theorems cover Chio's verdict and error dispatch, including advisory mode; they do not verify wasmtime and do not establish engine information-flow non-interference. |
ASSUME-FINDING-STATUS-OPERATOR-COMPLETENESS | 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 | A retraction the operator never publishes is invisible to the profile. The freshness check establishes that the feed is authentic and current, not that it is complete, so treat a qualified finding as advisory when the status operator sits outside your trust boundary. |
ASSUME-FINDING-SELLER-TOOL-SERVER | 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 | Chio binds the seller output bytes and receipts it observed, not the effect those bytes describe. Where the effect is what matters, require evidence that itself lands inside the observation boundary. |
Registry metadata
The header of formal/assumptions.toml carries the schema, version, and owner, and the gate scripts/check-formal-proofs.sh rejects any deviation from this shape.
schema = "chio.formal-assumptions.v1"
registry_version = 1
owner = "formal-verification"| Field | Value | Purpose |
|---|---|---|
schema | chio.formal-assumptions.v1 | Pin so a future v2 schema cannot land without an explicit migration. |
registry_version | 1 | Bumped every time the required assumption set changes; release-claim diffs cite this number. |
owner | formal-verification | CODEOWNERS-resolved team that approves a change to the required set. |
required_assumption_ids | 15 IDs | The ids a formal claim may cite. Each must have a matching row in assumptions, and the gate compares the two sets in both directions. |
retired_assumption_ids | Empty | Where a discharged assumption would be kept for audit-trail resolvability. Nothing has been discharged. |
Each row in assumptions is a four-field pipe-delimited tuple, and the gate parses the fields as the id, the class, the statement, and the properties the row maps to. It rejects a row with any other arity and a row with an empty field. ASSUME-SQLITE-ATOMICITY carries class audited_storage, the statement shown verbatim in the table above, and P2,P4,P6,P7. The last field says which properties lean on the assumption. Every class name begins audited_: the class says what kind of thing is assumed, not how much it is trusted, and no class means the assumption is discharged.
How the manifest uses the registry
The proof manifest at formal/proof-manifest.toml names the registry in assumption_registry, spends the assumptions as lanes inside property_matrix, and keeps a discharged_assumptions list for retirements. The gate is scripts/check-formal-proofs.sh, which loads both files and rejects a mismatch between them.
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"]Each row of property_matrix names the lanes that contribute evidence, and an assumption appears there as a lane of the form audited_*_assumption. Six of them occur across the matrix:
| Manifest lane | Properties naming it |
|---|---|
audited_storage_assumption | P2, P6 |
audited_transport_assumption | P2, P8 |
audited_subprocess_assumption | P3 |
audited_wasm_engine_assumption | P3 |
audited_crypto_assumption | P4, P7 |
audited_registry_assumption | P9 |
Read the lane names as a second vocabulary rather than as ids. The registry sorts its 15 entries into 8 classes (audited_crypto, audited_external, audited_observability, audited_platform, audited_serialization, audited_service, audited_storage, audited_transport), and the manifest's six lane names are not those classes: three of the lanes name a subject the registry has no class for, and five classes name no lane. Nothing in formal/proof-manifest.toml, formal/assumptions.toml or the check script joins one to the other, so a lane on a property row does not resolve to a specific assumption id. To find which assumptions a property rests on, read the P identifiers in the fourth field of each registry row rather than the lane names in the matrix.
Discharged assumptions, of which there are none
A discharged assumption is a prior audited assumption that no longer load-bears, because a proof, an invariant or a contract took over the job. The registry keeps a place for them: retired_assumption_ids and retired_assumptions in formal/assumptions.toml, mirrored by discharged_assumptions in formal/proof-manifest.toml.
All three lists are empty. Every one of the 15 assumptions above is live, and the trusted computing base has not narrowed since the registry was written.
What a proof has to do before an assumption can go
The clearest case is storage. An Apalache invariant, ReceiptBeforeAllow, proves that a receipt is persisted before an allow is published, and a production replay exercises that ordering on the native path. It is a real proof of a real property, and it is the obvious candidate for retiring ASSUME-SQLITE-ATOMICITY.
The manifest declines, in its own words:
ReceiptBeforeAllow proves the abstract persist-before-publish ordering
and its production replay exercises the native happy path. It does not
discharge concrete cross-row crash recovery, which remains excluded until
implementation trace validation and crash-reopen conservation gates
establish refinement.That is the bar. An abstract model proving an ordering is not the same as the implementation preserving it across a crash, and the gap between them is exactly what an audited assumption is holding. Closing it needs an implementation-refinement gate over the affected production boundary, which the registry says in its own comment above the empty list: abstract invariants and manual mirror hashes are not sufficient by themselves.
The path out is written down and unused. The manifest fixes the shape a retirement takes in the comment above its empty list: a row of id|discharging_artifacts|rust_call_sites|prose citing the retired id in retired_assumptions, the artifacts that discharge it, and the Rust call sites the discharge constrains. Until such a row exists, an assumption that a proof appears to cover is still an assumption.
Components outside the TCB
The following inputs and services are outside the TCB, subject to the stated boundaries.
- Agent input · The agent authoring a tool call is treated as fully untrusted. Every field of every request is parsed and validated before it reaches a verified entrypoint. The fuzz lane covers the parsers; see Fuzz Infrastructure.
- Tool server output · The response from a tool server is treated as untrusted bytes. The kernel decides whether a call is allowed; what the server does with the call, and what it returns, is not part of the proof boundary.
- The network · Beyond the TLS and message-integrity assumptions noted above, the network is treated as actively adversarial. Replay protection, session continuity, and DPoP nonce binding are all proven in the bounded model; the protocol is designed to deny unsafe sequences on the wire. The TLA+
RevocationFreshnessinvariant constrains only local single-authority revocation-epoch freshness against the model clock; it does not model cross-peer gossip ordering, soASSUME-NETWORK-TRANSPORTremains audited rather than discharged. - Other tenants in a cluster · Multi-tenant clusters share data through verified interfaces. A misbehaving tenant cannot widen another tenant's capability by virtue of the structural
AttenuationPreservinginvariant. - Hosted registries · Their response is gated on a chio-signed receipt. If a hosted registry lies about state, the receipt log is the source of truth; the registry is advisory.
Side-channel scope
Chio addresses one side channel and explicitly does not address the others.
| Channel | In scope? | How |
|---|---|---|
| Timing | Yes (limited) | Three dudect harnesses across two crates: the byte-equality compare and NormalizedScope::is_subset_of in chio-kernel-core/tests/dudect/, and JWT verification in chio-credentials/tests/dudect/. See Constant-Time Tests. |
| Power analysis | No | Out of scope. Mitigation belongs to the underlying crypto library and the hardware platform. |
| Electromagnetic emanations | No | Out of scope. Same answer as power. |
| Microarchitectural (Spectre, etc.) | No | Out of scope. Operators should follow OS and hypervisor mitigations. |
| Cache timing | No (covered indirectly) | The dudect lane catches gross data-dependent variation in the proof-facing surfaces. Cache-level analysis is out of scope. |
| Speculative execution | No | Out of scope. Operators relying on this mitigation should run chio on hardware with the relevant patches. |
dudect detects, does not prove
Why the assumption list stays narrow
A proof that says "capability attenuation holds, assuming ASSUME-ED25519" identifies a smaller dependency set than a proof that assumes the signature stack, storage, canonical-JSON serializer, OS clock, and network together. That is what makes a narrow assumption list worth the bookkeeping.
Two things follow from this principle.
- New assumptions need a written justification. The gate
scripts/check-formal-proofs.shrejects any new entry inrequired_assumption_idsthat lacks a rationale string inassumptions. - Discharged assumptions are tracked, not deleted. A retired entry stays in the file, in
retired_assumption_idsandretired_assumptions, so that historical claims, MAPPING rows, and audit trails stay resolvable. The manifest'sdischarged_assumptionslist mirrors it, and its comment names the format each row must carry: the retired id, the discharging artifacts, the Rust call sites the discharge constrains, and prose.
What a release-facing claim must cite
The manifest's claim_gate_inputs list names the documents whose formal claims are checked against the proof state on every release:
claim_gate_inputs = [
"README.md",
"docs/start-here/VISION.md",
"docs/reference/CLAIM_REGISTRY.md",
"docs/release/RISK_REGISTER.md",
"spec/PROTOCOL.md",
]Each release-facing formal claim in those files must identify its property, model or code, assumptions, boundary, and remaining gap.
formal/proof-manifest.toml(the manifest the claim is making against)formal/theorem-inventory.json(the theorem IDs the claim relies on)formal/assumptions.toml(the assumptions the claim is conditioned on)
The script scripts/check-proof-report.sh is the gate that enforces this. A claim that names a theorem which does not appear in the inventory or omits an assumption the theorem requires fails the build.
Next
- Formal Assurance Overview · assurance methods, boundaries, and gaps.
- Constant-Time Tests · how the timing-channel scope is enforced.
- Trust Model · the broader product framing for what Chio trusts and does not.