Chio/Docs
LOGIN · JOIN

Signing authority

See which key signs each kind of record, and keep the kernel's receipt signer fixed across queues, fallback, recovery, and rotation.

Concept chio@5f63e6e

On this page

A verifier can name the authority behind any Chio signature. Each kind of record has its own signer, and the kernel keeps the receipt signer it selected at boot on every path a receipt takes, from the first signature to a recovered envelope.

Each signer makes a different statement. An issuer says a subject holds a grant; a receipt signer records a decision or an observation; a response authority permits one containment action. Moving signing work to a queue or a recovery path changes where the work happens, and the identity a verifier expects stays the one installed before serving.

Receipt signer pathsChoose an outcome.
submitsqueuessignsinlinesignsverifiesreturns as isrKqσρρ0receipt bodykernel_keykernelsigning queuecount · size · bytesreceipt signerselected at bootsigned envelopeChioReceiptretained envelopeoriginal bytes✓kernel_key✓signature, bindingsone primitive, one selected signerrKqσρρ0✓✓one primitive, one selected signer

rreceipt bodyKkernelqsigning queueσreceipt signerρsigned envelopeρ0retained envelope

The selected signer signs the body as submitted

The body names the current receipt identity and commits to its exact content preimage. The kernel signs it with the backend selected at boot, the key receipt_signing_public_key() reports. Receipts.

One receipt body on each path to a signature: inline, queued, inline fallback, and recovery of a retained envelope. Capability issuance, child receipts, session anchors, and checkpoints have their own signers and are left out.

Each kind of record has its own signer

Sharing an algorithm makes no two of these authorities interchangeable. A verifier decides which role it is trusting before a successful signature check supports any conclusion.

AuthoritySignsStays separate from
Capability issuerGrants and delegated authorityAny receipt or response signer
Receipt signerThe kernel's decision and observation recordsThe capability issuer set
Key-log operatorCheckpoints and activation recordsThe active capability key and the witness quorum
Response authorityExact bounded response plansGeneral administrative authority
Artifact-time authorityTrusted temporal evidenceThe host's unverified wall clock

The kernel exposes the receipt identity through receipt_signing_public_key() without exporting private material, and keeps the classical capability public key behind the separate public_key() accessor. Installing a receipt backend configures proposal and ordinary receipt signing only. The capability issuer set and separately pinned response authorities keep their own configuration.

Queued and fallback signing use the selected signer

Queue count, per-request size, and an aggregate byte budget bound queued signing. A request those limits turn away is signed inline, with the same signing primitive and the same selected backend.

Every path signs the body as submitted. The body names the current receipt identity and commits to its exact content preimage, and a stale key or a mismatched preimage fails with ReceiptSigningFailed. The signer keeps the submitted identity as written, because rewriting it would hide a caller error and change the content being attested.

Four inputs exercise the contract: a normal result, an oversized request, a burst that fills the queue's byte budget, and a body with a stale receipt identity. The first three keep the configured signer and floor while the path changes; the fourth fails, because its body names an identity the kernel no longer signs with.

Recovery returns the original envelope byte for byte

When recovery finds a retained receipt, it returns those exact bytes instead of signing the record again. A fresh signature over the same body would also verify, and it would still break the receipt hash, any inclusion proof that names it, and every downstream reference to the original.

Before returning the envelope, recovery verifies its signature and its retained operation bindings under the applicable authority contract. A durable return requires the original receipt signer to still be the selected signer. Recover an operation walks a recovery from the operator's side.

A boot-time install changes the receipt signer and keeps the capability key

An embedder of chio-kernel built with the pq feature can have the kernel sign its ordinary receipts and threshold proposals with a hybrid Ed25519 plus ML-DSA-65 signature; the same call sets the floor the kernel validates capabilities and threshold approvals against. Installation belongs before serving, outside any admitted operation.

Signer installation and receipt inspection, install_receipt_signer.rs
security-host/install_receipt_signer.rsrust
use chio_core_types::receipt::crypto_floor::{ReceiptCryptoFloor, ReceiptFloorVerifyError};
use chio_core_types::{PublicKey, SigningBackend};
use chio_kernel::boot::{KernelBootError, KernelSelfQuoteVerifier};
use chio_kernel::{
    ChioKernel, HybridSigningConfig, KernelCryptoFloor, KernelError, SecurityInvocationContext,
    ToolCallRequest, ToolCallResponse,
};

pub struct InstalledReceiptSigner {
    pub classical_capability_key: PublicKey,
    pub receipt_key: PublicKey,
    pub handle: Box<dyn SigningBackend>,
}

/// Compose before the host begins serving. The config seed and quote verifier
/// come from independent boot custody, not an application-supplied claim.
pub fn install_receipt_signer(
    kernel: &mut ChioKernel,
    config: &HybridSigningConfig,
    quote: &[u8],
    verifier: &dyn KernelSelfQuoteVerifier,
) -> Result<InstalledReceiptSigner, KernelBootError> {
    let classical_capability_key = kernel.public_key();
    let previous_receipt_key = kernel.receipt_signing_public_key();
    let handle = match kernel.with_hybrid_signing_backend(config, quote, verifier) {
        Ok(handle) => handle,
        Err(error) => {
            assert_eq!(kernel.receipt_signing_public_key(), previous_receipt_key);
            return Err(error);
        }
    };
    let receipt_key = kernel.receipt_signing_public_key();
    assert_eq!(handle.public_key(), receipt_key);
    assert_eq!(kernel.public_key(), classical_capability_key);
    Ok(InstalledReceiptSigner {
        classical_capability_key,
        receipt_key,
        handle,
    })
}

pub struct SignedReview {
    pub signer: InstalledReceiptSigner,
    pub invocation: Result<ToolCallResponse, KernelError>,
    pub receipt_key_matches: Option<bool>,
    pub receipt_signature: Option<Result<bool, ReceiptFloorVerifyError>>,
}

/// Boot, then invoke the already configured repository reader ONCE. Its
/// authenticated context and original request must carry compatible grants.
/// Preserve the response/error even when separate receipt inspection refuses.
pub fn install_and_review(
    kernel: &mut ChioKernel,
    config: &HybridSigningConfig,
    quote: &[u8],
    verifier: &dyn KernelSelfQuoteVerifier,
    original_read: &ToolCallRequest,
    authenticated_context: &SecurityInvocationContext,
) -> Result<SignedReview, KernelBootError> {
    let signer = install_receipt_signer(kernel, config, quote, verifier)?;
    let floor = match config.crypto_floor {
        KernelCryptoFloor::AllowClassical => ReceiptCryptoFloor::AllowClassical,
        KernelCryptoFloor::AllowHybrid => ReceiptCryptoFloor::AllowHybrid,
        KernelCryptoFloor::PqRequired => ReceiptCryptoFloor::PqRequired,
    };
    let invocation = kernel
        .evaluate_tool_call_blocking_with_security_context(original_read, authenticated_context);
    let (receipt_key_matches, receipt_signature) = match &invocation {
        Ok(response) => {
            let matches = response.receipt.kernel_key == signer.receipt_key;
            let signature = matches.then(|| response.receipt.verify_signature_with_floor(floor));
            (Some(matches), signature)
        }
        Err(_) => (None, None),
    };
    Ok(SignedReview {
        signer,
        invocation,
        receipt_key_matches,
        receipt_signature,
    })
}

The integration takes the configured kernel, a boot-owned HybridSigningConfig with its provisioned ML-DSA-65 seed, the quote bytes, and an independent KernelSelfQuoteVerifier that binds the quote to the classical kernel identity and the genesis receipt root; an always-accept callback verifies nothing. Download install_receipt_signer.rs. Its four outcomes:

  • Valid seed and verified quote. The installed handle and the receipt accessor select the same hybrid key, and the classical accessor keeps its capability role.
  • Rejected quote or missing post-quantum material. Installation returns the boot error, the previous signer and floor stay selected, and no new issuer appears.
  • Validation floor changed alone. Admission acceptance can change while ordinary receipts stay classical; a floor setter installs no signer.
  • Old handle retained after replacement. The old handle still owns its backend until its holders are drained or fenced through their lifecycle contract.

install_and_review then invokes the configured repository reader once, with the host-authenticated context and an original read request whose grants fit the selected floor. It keeps the invocation, the receipt-key comparison, and the signature-and-floor result separate. Accept the receipt signature only when its key matches the boot-selected identity and verification returns true; a valid signed Deny still refuses the read, and an outer error leaves the operation to its recovery owner. Post-quantum signatures verifies both hybrid components apart from kernel boot.

A failed installation leaves the previous signer selected

Installation loads the backend through the self-quote gate, constructs the replacement signing task, and only then updates the selected authority and floor. A failed load changes neither, so the public identity, the signing implementation, and the accepted floor never disagree.

To test it, retain the previous receipt public key and floor, submit a configuration whose required verification fails, and compare the selected state afterward; the resulting behavior matters as much as the returned error. After a successful installation, verify a new envelope under the new receipt identity and each retained envelope under the key that signed it.

Source reference: selected-backend installation

This excerpt shows where the receipt path and the validation floor meet. The returned handle shares the selected backend, so queue fallback has no separate key to choose and no algorithm to downgrade to.

crates/kernel/chio-kernel/src/kernel/signing_authority.rs108–132rust
    pub fn with_hybrid_signing_backend(
        &mut self,
        hybrid: &HybridSigningConfig,
        self_quote_bytes: &[u8],
        verifier: &dyn crate::boot::KernelSelfQuoteVerifier,
    ) -> Result<Box<dyn chio_core::crypto::SigningBackend>, crate::boot::KernelBootError> {
        let backend = crate::boot::load_kernel_signing_backend_after_self_quote(
            hybrid.crypto_floor,
            self.config.keypair.clone(),
            hybrid.pq_signing_seed.as_ref(),
            self_quote_bytes,
            verifier,
        )?;
        let backend: Arc<dyn SigningBackend> = Arc::from(backend);
        let signing_task = self
            .signing_task
            .reconfigured_with_backend(Arc::clone(&backend));
        self.signing_authority = KernelSigningAuthority {
            backend: Arc::clone(&backend),
            floor: hybrid.crypto_floor,
        };
        self.signing_task = Arc::new(signing_task);
        self.capability_crypto_floor = hybrid.crypto_floor;
        Ok(Box::new(SharedSigningBackend(backend)))
    }

The method's scope is specific: capability issuance, child receipts, session anchors, checkpoints, and other authorities each take their own reviewed configuration.

Rotation goes through key history, and old receipts keep their signer

Swapping a process's signing backend is a boot composition step. Rotating the authority is a witnessed event in the key log, and only that event changes which key verifiers accept.

Key lifecycleChoose a stage.
key historykey logwitnessesreceiptscandidateσ₁σ₂4f1c…a2e9epoch 1epoch 29b07…c41dauthorizesproves possessionerotation eventccheckpointw₁w₂w₃witnesses · 2 of 3 signv₁v₂auditors · both confirmactivationevent + checkpoint + epoch 2verifiesverifiesρ₁signed in epoch 1ρ₂signed in epoch 2keyskey logsignersreceiptscandidateσ₁σ₂4f1c…a2e9epoch 1epoch 29b07…c41dauthorizeseeventccheckpointw₁w₂w₃witnesses2 of 3 signv₁v₂auditorsboth confirmactivationverifiesverifiesρ₁epoch 1ρ₂epoch 2

Activation binds the exact event and starts epoch 2

The activation commit binds the event, its checkpoint, the witness set, and signing epoch 2. Serving switches to σ₂ through the generation-fenced signing backend, and the door keeps serving through the change. Key transparency.

One authority's key history on a time axis: a rotation from σ₁ to σ₂ through the key log, three witnesses, and two auditors. Times and fingerprints are illustrative; artifact-time roots, recovery roots, and equivocation evidence are left out.Witnessed rotation

The active key authorizes the rotation and the new key proves possession. The event enters the key log as a candidate while serving still signs with the old key; two of three witnesses sign the same checkpoint, both auditors confirm it, and activation binds the event, the checkpoint, the witness set, and the signing epoch. Serving then switches through the generation-fenced backend while the door keeps serving, and the authority key history verifies each old receipt against the key that signed it. A new signer can never make an old receipt valid by signing it again.

A process can still hold handles to a backend it obtained before reconfiguration, and replacing the selected backend leaves those handles live. Before a rotation, inventory long-lived workers, queues, authority selectors, and replay paths that use the old identity, drain or fence them under each component's contract, and keep the old verification material. During an incident, check the artifact class, the signer identity, the selected floor, and the retained envelope before treating a signature mismatch as data corruption; Verify security evidence gives the full verification order.

Read more in Witnessed rotation and Key transparency.

Next: choose the algorithms the kernel accepts