Security receipts
Find the receipt family for a signed security record, the fields that bind it, and the wire schema that fixes its shape.
On this page
A security receipt is a signed statement by the component that observed a security event. Active defense, the keyring, the secret broker, and the cage each sign their own receipts, and each receipt body has a wire schema that fixes its shape.
A receipt records a decision, an authority change, a broker exchange, or an observed execution condition, and its meaning comes from what that signer was in a position to observe. Signature verification authenticates the statement and its bindings; the claim you need decides which history or resource evidence to add, as verify security evidence lays out.
Receipt families
| Receipt you have | Family | Verification input |
|---|---|---|
Detection or response | Trusted receipt signer; the cited finding, plan, or effect | |
Key rotation or recovery | Trusted operator key; the transparency- | |
Broker execution or administration | Trusted broker signer; the original request and capability | |
Confined launch | Configured cage trust root; the launch described |
A schema match establishes an artifact's shape; its authority comes from the signature under a pin you chose. Combining records from several families can support a larger claim, and each record still stands only for its own stage.
Active defense receipts
pub enum ActiveDefenseReceiptKind {
FlowDenial,
DeclassificationConsumption,
DeclassificationOutcome,
TripwireObservation,
CorrelatedFinding,
ResponsePlan,
ResponseStateTransition,
EffectTransition,
ResponseCompletion,
LiftRollbackCompletion,
DetectorHealth,
SchedulerHealth,
}ActiveDefenseReceiptKind declares one variant per point in the detection and response lifecycle, from a denied flow through a completed or rolled-back response: FlowDenial, DeclassificationConsumption, DeclassificationOutcome, TripwireObservation, CorrelatedFinding, ResponsePlan, ResponseStateTransition, EffectTransition, ResponseCompletion, LiftRollbackCompletion, DetectorHealth, and SchedulerHealth.
Every kind has its own body struct, from FlowDenialReceiptBody to SchedulerHealthReceiptBody. The closed union ActiveDefenseReceiptBody tags each body with its kind, so a verifier matches on the kind and decodes the corresponding body without guessing its shape.
Signing wrapper
The control plane's security receipt sink, NativeSecurityReceiptSink, wraps the body in a ChioReceiptBody under the fixed system capability id chio.active-defense.system and tool server chio.kernel, with a decision, a content hash of the body, and the whole body in the receipt metadata. It signs that into a ChioReceipt with the signing backend it was constructed with, so the signature covers the full body.
{
"capability_id": "chio.active-defense.system",
"tool_server": "chio.kernel",
"tool_name": "flow_denial",
"content_hash": "26dd75039c660a721882345773ff1c0a36dcc8300596174ea5a52df638a39b03",
"active_defense_kind": "flow_denial",
"transition_id": "receipt-comparison-1",
"denial_code": "flow.clearance_denied"
}The excerpt selects fields from the retained flow-denial fixture used in the producer example below. It omits the signature: it shows where the body and its commitment sit, and verification always takes the complete record.
capability_idandtool_server. The system receipt context,chio.active-defense.systemandchio.kernel.metadata.active_defense_body. The complete kind-tagged statement, with its tenant, event, and request bindings.- Canonical body commitment and evidence ID. Bind the record to the exact statement; repeating the same append recovers that original evidence.
- Wrapper signature and signer pin. Authenticate the statement under the receipt owner's key, pinned independently.
Key receipts
The keyring records an enterprise key operation, such as rotation or recovery, as a KeyEnterpriseReceiptBody: a schema tag, receipt id, transaction id, issue time, and the transparency log id the operation was recorded under. The operator's key transparency service wraps that body in a SignedKeyEnterpriseReceipt, adding the operator's key id, signing algorithm, and signature. Key transparency verifies the history the log id points into.
Broker receipts
The secret broker issues a BrokerReceiptBody under schema chio.broker-execution-receipt.v2 for a completed brokered execution, recording the schema, receipt id, issue time, the execution evidence, the operation id, and the authorizing event id. A denied or failed execution produces a BrokerFailureReceiptBody under chio.broker-execution-failure-receipt.v1, recording the stage at which it stopped.
The broker daemon also signs each applied administrative operation. A SignedAdminMutationReceipt (chio.broker-admin-mutation-receipt.v1) follows a provision, rotation, disable, or delete of a credential; a SignedAdminControlReceipt (chio.broker-admin-control-receipt.v1) follows an issue, revoke, or status control call to its authority.
Cage receipts
The cage records a confined launch attempt as a CageReceiptBody, tagging the schema, the attempt id, and a CageReceiptStage such as rejection, bootstrap, or enforcement. The trusted-key verifier verify_signed_cage_receipt_with_trusted_key, which the chio CLI and the MCP adapter use, refuses a receipt whose signer is not a configured trust root. Process confinement covers how a launch record reaches a call's receipt.
Security wire schemas
Every receipt kind above has a schema file under spec/schemas/chio-wire/v1/security. The inventory chio.security-required-schema-inventory.v1 is a closed list of every schema file in that directory. The security-wire-vectors gate fails if the inventory and the directory differ and validates recorded test vectors against their declared schemas, and the schema-registry gate requires every schema to be registered and hashed in the schema manifest. Every Chio release passes both gates before it is tagged. chio release verify --evidence qualification shows their results for the release you run.
The catalogue lists each schema id without the common prefix https://chio.world/schemas/chio-wire/v1/security/, beside the file that defines it.
87 schemas. Search by ID or file.
| Schema id | File |
|---|---|
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
broker- | spec/ |
cage- | spec/ |
cage- | spec/ |
cage- | spec/ |
cage- | spec/ |
cage- | spec/ |
cage- | spec/ |
cage- | spec/ |
cage- | spec/ |
cage- | spec/ |
correlated- | spec/ |
correlated- | spec/ |
declassification- | spec/ |
declassification- | spec/ |
declassification- | spec/ |
detector- | spec/ |
effect- | spec/ |
flow- | spec/ |
information- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
key- | spec/ |
keyring- | spec/ |
lift- | spec/ |
mcp- | spec/ |
response- | spec/ |
response- | spec/ |
response- | spec/ |
response- | spec/ |
response- | spec/ |
scheduler- | spec/ |
security- | spec/ |
signed- | spec/ |
signed- | spec/ |
tool- | spec/ |
tool- | spec/ |
tripwire- | spec/ |
Signature and current authority
A valid signature authenticates this owner's statement. It gives a detector key no broker role, shows nothing about whether a response adapter changed a resource, and leaves open whether a key that signed in the past is still active. The comparisons below ran on separate copies, and the original stayed unchanged.
- One signed
tool_namebyte changed. Original-wrapper verification refuses. Recover the unmodified original; never re-sign the altered record. - Another trusted-role key file. Signer-pin verification refuses. Select the configured receipt owner's independent pin.
- A missing predecessor or stale key history. The three generic wrapper checks leave this claim open. Verify the exact predecessor, inclusion and consistency, and artifact-time authority.
- A schema-valid projected body. It omits the original signature and wrapper. Retain the full signed artifact.
Native receipt retention
A host passes the producer's original ActiveDefenseReceiptBody, the configured indexed store, the configured signing backend, and the owner's independently selected signer pin to retain_security_body. It validates the body, submits its exact canonical commitment to NativeSecurityReceiptSink, and reads back the authoritative signed wrapper. Repeating the exact body resumes its original evidence ID after a lost response, with no new transition.
The complete retention and export functions
use std::sync::Arc;
use std::io::Write;
use std::path::Path;
use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt};
use chio_control_plane::security::NativeSecurityReceiptSink;
use chio_core::canonical::canonical_json_bytes;
use chio_core::crypto::{PublicKey, SigningBackend};
use chio_core::receipt::body::ChioReceipt;
use chio_core::receipt::security::ActiveDefenseReceiptBody;
use chio_kernel::IndexedSecurityEvidenceStore;
use chio_security_types::ports::{
CanonicalBody, ExactSecurityReceiptSink, ReceiptAppendRequest, RecordId,
SecurityReceiptSink,
};
/// The producer supplies the original validated body. This function does not
/// infer an observed denial, effect or predecessor from an untrusted summary.
pub fn retain_security_body(
store: Arc<dyn IndexedSecurityEvidenceStore>,
signer: Arc<dyn SigningBackend>,
independently_selected_signer: &PublicKey,
body: &ActiveDefenseReceiptBody,
) -> Result<ChioReceipt, Box<dyn std::error::Error>> {
if signer.public_key() != *independently_selected_signer {
return Err("configured receipt signer differs from the owner's pin".into());
}
body.validate()?;
let request = ReceiptAppendRequest {
tenant_id: body.header().tenant_id.clone(),
evidence_type: RecordId::new(body.kind().as_str())?,
evidence_id: body.evidence_id()?,
canonical_body: CanonicalBody::new(canonical_json_bytes(body)?)?,
body_hash: body.body_digest()?,
transition_id: body.header().transition_id.clone(),
occurred_at_unix_ms: body.header().occurred_at_unix_ms,
};
let sink = NativeSecurityReceiptSink::new(Arc::clone(&store), signer);
sink.ensure_receipts_ready()?;
let retained_id = sink.sign_and_append(&request)?;
let exact = sink.load_exact(&retained_id)?.ok_or("retained body missing")?;
if exact.receipt != request {
return Err("retained body differs from the original append request".into());
}
// Read the authoritative signed wrapper, not a locally reconstructed body.
store.load_indexed_security_evidence(&retained_id)?
.ok_or_else(|| "original signed wrapper missing".into())
}
/// First export of the authoritative wrapper and separately configured owner pin.
/// Existing evidence directories/files refuse; an error preserves partial evidence.
pub fn write_security_evidence(
fresh: &Path,
original: &ChioReceipt,
independently_selected_signer: &PublicKey,
) -> Result<(), Box<dyn std::error::Error>> {
if original.kernel_key != *independently_selected_signer {
return Err("original wrapper differs from the owner's signer pin".into());
}
std::fs::DirBuilder::new().mode(0o700).create(fresh)?;
let mut receipt = std::fs::OpenOptions::new().write(true).create_new(true)
.mode(0o600).open(fresh.join("original.jsonl"))?;
serde_json::to_writer(&mut receipt, original)?;
receipt.write_all(b"\n")?;
receipt.sync_all()?;
let mut pin = std::fs::OpenOptions::new().write(true).create_new(true)
.mode(0o600).open(fresh.join("kernel.pub"))?;
writeln!(pin, "{}", independently_selected_signer.to_hex())?;
pin.sync_all()?;
std::fs::File::open(fresh)?.sync_all()?;
Ok(())
}RETAIN native signed flow_denial fixture; exact append replay returns the original wrapperThe comparison retained a flow_denial body twice through the native sink and SQLite store and returned the identical signed wrapper. The fixture's nonzero request, label, and guard digests are structural values, so the result exercises signing, persistence, and exact replay, and makes no claim about what a deployed flow guard observed.
Call write_security_evidence(Path::new("./evidence"), &original, &owner_pin) with the returned wrapper and the same pin. The first export creates original.jsonl and kernel.pub in a new private directory, syncs both, and refuses existing evidence paths; keep exported signed text byte for byte.
Wrapper verification
chio receipt verify \
--input ./evidence/original.jsonl \
--trusted-kernel-pubkey ./evidence/kernel.pub \
--json{
"checks": [
"signature",
"signer_pin",
"action_parameter_hash"
],
"receipts_verified": 1,
"schema": "chio.receipt.signatures.v1",
"trusted_kernel_key": "a12d82bdf394fc79fe4aa23c0202e193aa3d999f5c2002029b128baaba7e52a4"
}The original body travels in metadata.active_defense_body under capability chio.active-defense.system and server chio.kernel, and its evidence ID commits to the kind and the canonical body. Submit the complete wrapper; a selected-field display or a re-created summary has no signature to check.
Refusals
Each producer refuses to build or accept a receipt that does not hold together. A direct caller of each validation or verification function gets the typed error below.
| Error | Condition | Raised by |
|---|---|---|
Active | Plan omits its trigger finding receipt | Active defense validation |
Active | Effect targets another tenant | Active defense validation |
Cage | Signer is not the configured trust root | Cage verifier |
Broker | Wrong schema, signer, or algorithm; invalid body or signature | verify_, verify_ |
Broker | Signed receipt misbound to the request and capability | Broker |
MissingTriggerFindingReceipt: "response plan receipt does not cite its trigger finding receipt", when a response plan omits its trigger finding receipt from its prior receipts.CrossTenantTarget: "response effect target crosses the receipt tenant".UntrustedSigner: "signed cage receipt signer is not a configured trust root".AuthorizationDenied: "broker authorization denied: broker receipt schema, signer, or algorithm is invalid" or "broker authorization denied: broker receipt signature is invalid"; failure receipts say "broker failure receipt".ResponseRejected: "broker response was rejected: broker execute response evidence is malformed or misbound", for a correctly signed receipt misbound to the request and capability the client sent.
Hosts report these in their own error: the control plane's security receipt sink as a PortError of kind InvalidData, the MCP adapter as AdapterError::ConnectionFailed (for example "cage release receipt persistence failed: Invalid(UntrustedSigner)"), and the chio CLI's cage evidence check as its error message. The security specification holds the full wire contract each schema belongs to.