Chio/Docs

BuildOperationsnew

Secrets & Signing Keys

Reference for Chio's kernel-signing, trusted-issuer, admin-control, and federation-peer keys, including custody, rotation, and failure handling.

Source

Source files: crates/products/chio-cli/src/cli/runtime.rs, crates/platform/chio-control-plane/src/lib.rs, crates/core/chio-core-types/src/crypto.rs, and crates/kernel/chio-kernel/src/dpop.rs.

The Keys Chio Holds

KeyPurposeWhere it lives
Kernel signing keySigns every chio receipt and every kernel checkpoint statement.Authority seed file (default; root of the load_or_create_authority_keypair helper) or a delegated SigningBackend.
Trusted issuer keysVerify capability tokens issued by upstream authorities.CHIO_TRUSTED_ISSUER_KEY or CHIO_TRUSTED_ISSUER_KEYS env vars.
Sidecar control tokenAuthenticates admin calls to the trust-control HTTP API.CHIO_SIDECAR_CONTROL_TOKEN env var (with CHIO_API_PROTECT_CONTROL_TOKEN as alias).
Federation peer keysVerify checkpoints and revocation deltas from other kernels in a federation.Pinned in federation config; see bilateral receipts.

Environment Variables

The variables that Chio actually reads at runtime, taken directly from crates/products/chio-cli/src/cli/runtime.rs and crates/trust/chio-tee/src/config.rs.

VariableMeaning
CHIO_TRUSTED_ISSUER_KEYSingle trusted issuer public key, hex-encoded Ed25519.
CHIO_TRUSTED_ISSUER_KEYSComma-separated list of trusted issuer public keys. Both this and the singular form may be set; the parser deduplicates.
CHIO_SIDECAR_CONTROL_TOKENBearer token for admin calls to chio api protect.
CHIO_API_PROTECT_CONTROL_TOKENAlias for the same control token, used by some adapter helpers.
CHIO_TEE_MODEHighest-priority TEE mode override (verdict-only, shadow, enforce).
CHIO_TEE_CONFIGPath to the TEE sidecar TOML; default /etc/chio/tee.toml.
CHIO_HOMEPersistent state root, default /var/lib/chio in the shipped images.

No CHIO_SIGNING_KEY env var

The kernel signing key is not configured through an environment variable. It is loaded from a seed file via load_or_create_authority_keypair on a path the operator passes with the global --authority-seed-file flag (declared once on the top-level chio command, available to every subcommand). If the file does not exist, a fresh keypair is generated and persisted with 0600 permissions.

Key Format

Chio's primary signing algorithm is Ed25519. Keys round-trip through hex; the seed (private half) is 32 bytes encoded as 64 hex characters; the public key is 32 bytes encoded as 64 hex characters.

rust
// chio_core_types::crypto

impl Keypair {
    pub fn from_seed(seed: &[u8; 32]) -> Self { ... }
    pub fn from_seed_hex(hex_str: &str) -> Result<Self> {
        // Strips optional 0x prefix; rejects anything but 32 bytes.
    }
    pub fn seed_hex(&self) -> String { ... }
    pub fn public_key(&self) -> PublicKey { ... }
}

Public keys for non-Ed25519 algorithms carry a self-describing prefix: p256:<hex> and p384:<hex> for the P-256 and P-384 ECDSA backends (the fips feature), and hybrid:<hex> for a classical signature composed with ML-DSA-65. Plain hex with no prefix means Ed25519. This is a wire-format choice, not a config knob; you do not set it manually.

Hybrid post-quantum signing

Under the pq feature, chio signs receipts and capability tokens with a hybrid classical-plus-ML-DSA-65 backend. The kernel crypto floor determines the configuration, which mirrors the HushSpec policy field policy.crypto_floor and takes one of three values: allow_classical (the default), allow_hybrid, or pq_required. Both allow_hybrid and pq_required require a 32-byte ML-DSA-65 keygen seed, from which the kernel constructs the hybrid backend at boot (HybridSigningConfig). Verifiers key off the hybrid: wire prefix, so a post-quantum deployment stays legible to relying parties that understand the hybrid form.


Custody Patterns

Where the secret half lives is the most consequential decision in any Chio deployment. Four patterns, ordered from least to most assurance.

Plain seed file (development)

The default. The CLI helper load_or_create_authority_keypair reads a file at the path you pass; if the file is missing, generates a keypair and writes it back with 0600 permissions through a same-directory atomic rename. Good enough for a developer workstation. Not enough for production.

bash
# Generate or reuse a kernel signing seed at this path.
$ chio --authority-seed-file /var/lib/chio/authority.seed mcp serve-http
$ ls -la /var/lib/chio/authority.seed
-rw------- 1 chio chio 65 ... /var/lib/chio/authority.seed

Plain seed files are for dev only

A seed file on a regular volume is readable by any process that can become the chio user. In production, use one of the custody patterns below.

Mounted file via secret store

Project the seed in from a Kubernetes Secret, the AWS Secrets Manager CSI driver, or the Azure Key Vault CSI provider. The Chio process sees a normal file path; the orchestrator handles rotation and access auditing.

yaml
# Kubernetes Secret + CSI projection sketch
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: chio-edge
      image: chio-sidecar:latest
      args:
        - --authority-seed-file=/secrets/authority.seed
        - mcp
        - serve-http
      volumeMounts:
        - name: chio-keys
          mountPath: /secrets
          readOnly: true
  volumes:
    - name: chio-keys
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: chio-keys

Cloud KMS / Key Vault / HSM (delegated signing)

For deployments that should not hold the secret half at all, chio exposes the SigningBackend trait in chio-core-types::crypto:

rust
pub trait SigningBackend: Send + Sync {
    fn algorithm(&self) -> SigningAlgorithm;
    fn public_key(&self) -> PublicKey;
    fn sign_bytes(&self, message: &[u8]) -> Result<Signature>;
}

The default Ed25519Backend wraps a local Keypair. Plug in your own implementation that calls AWS KMS, Google Cloud KMS, Azure Key Vault, an HSM via PKCS#11, or any other remote signer. The kernel never sees the secret half; it asks for signatures.

For hardware-authenticator and WebAuthn-backed custody, Chio exposes a named API, the chio-custody-hw crate. It produces HybridBackend-signed capabilities, keeps a replay-resistant nonce store, and cascades revocation. Reach for it when capability signing must be bound to a hardware authenticator rather than a process-held key.

TEE-bound key

The strongest custody pattern. The kernel signing keypair is generated inside the enclave, attested at launch, and dies with the enclave. A relying party verifies receipts under the TEE-attested public key, which makes the signing chain anchor on the platform attestation root rather than your operator practices. See /docs/deployment/tee for the attestation flow.


Rotation Runbook

Two key sets rotate independently: the kernel signing key (which signs new receipts) and the trusted issuer key set (which verifies new capability tokens). The runbook for each is different.

Kernel signing key

The control-plane API exposes rotate_authority_keypair in chio-control-plane: it generates a fresh keypair and atomically replaces the seed file. The corresponding admin command is part of the trust runbook in /docs/guides/rotate-keys-revoke.

Three properties hold across a kernel signing rotation:

  • Receipt continuity is preserved. Receipts signed by the old key remain verifiable. The checkpoint chain stitches across the rotation: each kernel checkpoint statement records the previous checkpoint hash and the kernel public key in use, so a verifier can reconstruct the key timeline.
  • No overlap window is required. A receipt is signed once, by whichever key was current at signing time. Rotating the live key flips the signer for subsequent receipts; nothing in the receipt store needs re-signing.
  • The rotation is observable. The control plane advances the authority generation and stamps a rotated timestamp, keeping the prior key in the trusted-key history. Auditors read the transition from the authority endpoint (GET /v1/authority).

Trusted issuer key set

The trusted issuer set is the Chio kernel's view of which public keys are allowed to mint capability tokens. It is loaded from CHIO_TRUSTED_ISSUER_KEY and CHIO_TRUSTED_ISSUER_KEYS at process start; restart-to-update is the simplest pattern. For zero-downtime rotation, run side-by-side processes during a short overlap and shift traffic.

bash
# Rolling the issuer set with one trusted key + one new key during overlap.
$ export CHIO_TRUSTED_ISSUER_KEYS="\
    a1b2c3...,\
    9f8e7d..."          # old + new

# Once all token producers have moved to the new key:
$ export CHIO_TRUSTED_ISSUER_KEYS="9f8e7d..."   # drop the old

Two-list overlap

During rotation keep both keys in the trusted set so any in-flight capability minted by either issuer continues to verify. Drop the old key only after the longest possible token TTL has elapsed, or after you have confirmed every token-issuer has moved.

Plugging In a Remote Signer

The SigningBackend trait is intentionally narrow: produce a public key and produce a signature over a byte slice. Three observations make remote signers practical:

  • Receipts are small. The canonical body of a chio receipt is on the order of a kilobyte. Round-tripping each one to a remote KMS is feasible even for hot paths if the KMS is co-located.
  • Checkpoints batch. Kernel checkpoints sign a Merkle root over a batch of receipts; even at high throughput the checkpoint signing rate is a few per minute. Remote-signed checkpoints are essentially free.
  • Algorithm is selectable. Under the fips feature, P-256 and P-384 ECDSA backends route through aws-lc-rs. If the KMS speaks one of those instead of Ed25519, configure the algorithm at backend construction time.

Free-function helper sign_canonical_with_backend canonicalises a serializable value and passes the bytes to the backend. This is the integration point you most often call from a custom signer wrapper.


Control Token

The trust-control plane and the API-protect proxy expose admin endpoints that mutate authority state, issue capabilities, or force revocations. Those endpoints require a bearer token. The token comes from CHIO_SIDECAR_CONTROL_TOKEN (with CHIO_API_PROTECT_CONTROL_TOKEN as a fallback alias).

bash
# Admin call with control token
$ curl -H "Authorization: Bearer $CHIO_SIDECAR_CONTROL_TOKEN" \
       https://chio-control:8443/admin/authority/status

Treat this token like a service-account credential: rotate on a schedule, scope per environment, and never check it in. If the token is missing, the admin API refuses each request that requires authentication.


Trusted Issuer Set

Three lifecycle operations on the trusted issuer set, each recorded in the receipt log:

  • Bootstrap. The first set is the publisher of the initial passport authority. Set CHIO_TRUSTED_ISSUER_KEY to that public key at first launch and the kernel will accept capabilities issued by it.
  • Expansion. To add a second issuer, switch to CHIO_TRUSTED_ISSUER_KEYS with the new key appended. The runtime parser deduplicates, so leaving the singular variable set is harmless.
  • Revocation. Removing a key from the set is the deployment-side response to a compromised issuer. The kernel rejects any capability that carries a signature from a non-trusted key. For per-token revocation (where the issuer is still trusted, but a specific capability needs to die), use the trust-control revocation store. See /docs/guides/rotate-keys-revoke.

Auditing Key Changes

Two data sources answer the audit question. The authority key timeline is read from the control plane; tool-call decisions are read from the receipt store. To inspect the current signing authority and its rotation history, query the control plane's authority endpoint (GET /v1/authority), which returns the current generation, the rotated timestamp, and the trusted-key history.

The receipt store records tool-call decisions for audit. Read it with chio receipt list, which emits one JSON receipt per line. The read path fails closed unless you scope it: pass --tenant <id> for a single tenant, or --admin-all to read across every tenant as an administrative operation.

bash
# List receipts in a window; --since/--until are Unix seconds.
$ chio receipt list --since 1719792000 --until 1722470400 --admin-all

# Narrow by decision outcome, or by the capability that authorized the call.
$ chio receipt list --since 1719792000 --admin-all --outcome deny
$ chio receipt list --capability <capability-id> --admin-all

The available filters are --capability, --tool-server, --tool-name, --outcome (allow, deny, cancelled, incomplete), --since/--until, --min-cost/--max-cost, and --cursor/--limit for pagination. There is no free-form event-type filter. See /docs/guard-platform/receipts for the exact field set.


Disaster Scenarios

Lost kernel signing key

If the seed file is lost or destroyed, the kernel cannot sign new receipts under its old identity. The recovery path is to rotate to a new keypair and re-anchor relying parties on the new public key. The old receipts remain verifiable because they were already signed; the checkpoint chain crosses the rotation boundary by referencing the previous checkpoint hash.

Compromised kernel signing key

Treat as urgent. Revoke the old public key from any consumer registry, then rotate. Receipts already signed by the compromised key are no longer trustworthy on their own; pair them with the next trusted-anchored checkpoint to bound how much of the log was at risk.

Compromised trusted issuer

Drop the issuer's public key from CHIO_TRUSTED_ISSUER_KEYS and restart. Capabilities minted under it stop verifying on the next request. Pair this with the trust-control revocation store if specific tokens were known to be issued during the compromise window.

Late discovery of compromise

The chio receipt log is append-only and signed; you can replay it forward from the last trusted checkpoint to find every decision the compromised key influenced. The replay infrastructure (and the verdict-drift Loki dashboard) is the operator-facing tool for this investigation. See /docs/deployment/observability.