Chio/Docs
LOGIN · JOIN

PlatformConfiguration & Custody

Node

Confidential Node

Run the node inside a Trusted Execution Environment so its signing identity is bound to a measured enclave, and what that costs.


TEE guarantees

A TEE provides these guarantees beyond ordinary container isolation:

  • Hardware-isolated execution. Encrypted-memory enclaves keep the kernel's working state opaque to the host operating system, the hypervisor, and the cloud control plane. A privileged operator on the host machine cannot read receipts, capability tokens, or signing material out of process memory.
  • Attestation-bound signing keys. The kernel generates its Ed25519 signing keypair inside the TEE and never exports the secret. A relying party can verify, before trusting any receipt, that the public half was produced by a measured Chio binary running in a measured TEE.
  • Replay separation. The TEE side can re-evaluate kernel decisions independently and flag divergences. In enforce mode it fail-closes when its replay disagrees with the kernel verdict.

TEE trust boundary

TEE moves the trust boundary; it does not eliminate it. You are still trusting the silicon vendor, the attestation root, and the chio binary measurement. Read /docs/formal/assumptions for the full list of what stays trusted even with a TEE.
The audited assumption registry: audited_crypto, ASSUME-ED25519, ASSUME-SHA256; audited_external, ASSUME-CHAIN-FINALITY; audited_observability, ASSUME-TRACE-OBSERVER; audited_platform, ASSUME-OS-CLOCK, ASSUME-SUBPROCESS-ISOLATION, ASSUME-WASM-ENGINE; audited_serialization, ASSUME-CANONICAL-JSON; audited_service, ASSUME-EXTERNAL-REGISTRIES, ASSUME-FINDING-STATUS-OPERATOR-COMPLETENESS, ASSUME-FINDING-SELLER-TOOL-SERVER; audited_storage, ASSUME-SQLITE-ATOMICITY; audited_transport, ASSUME-TLS, ASSUME-NETWORK-TRANSPORT, ASSUME-GOSSIP-FAIRNESS-PARTITION-BOUNDaudited_crypto · 2ASSUME-ED25519: Ed25519 verification and signing are assumed to satisfy standard unforgeability for trusted public keys.ED25519properties P2 P3 P4ASSUME-SHA256: SHA-256 and Merkle hash collision resistance are assumed for concrete receipt and checkpoint evidence.SHA256properties P4 P7audited_external · 1ASSUME-CHAIN-FINALITY: External chain and oracle finality are assumed only after the configured confirmation/finality policy accepts the evidence.CHAIN-FINALITYproperties P4 P7audited_observability · 1ASSUME-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.TRACE-OBSERVERproperties P2 P4 P10audited_platform · 3ASSUME-OS-CLOCK: The injected clock is assumed to report operator-accepted Unix time within the deployment tolerance.OS-CLOCKproperties P2 P3 P8ASSUME-SUBPROCESS-ISOLATION: Tool-server subprocess isolation and OS process boundaries are assumed for effects outside the pure kernel decision core.SUBPROCESS-ISOLATIONproperties P3 P6ASSUME-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.WASM-ENGINEproperties P3audited_serialization · 1ASSUME-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.CANONICAL-JSONproperties P4 P7 P10audited_service · 3ASSUME-EXTERNAL-REGISTRIES: Hosted package, certification, DID, and registry services are assumed to return state they have durably accepted or else fail closed.EXTERNAL-REGISTRIESproperties P9 P10ASSUME-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.FINDING-STATUS-OPERATOR-COMPLETENESSproperties P4 P7 P10ASSUME-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.FINDING-SELLER-TOOL-SERVERproperties P3 P6 P10audited_storage · 1ASSUME-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.SQLITE-ATOMICITYproperties P2 P4 P6 P7audited_transport · 3ASSUME-TLS: TLS endpoint authentication and channel confidentiality are assumed for configured remote control and hosted HTTP surfaces.TLSproperties P8 P9ASSUME-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.NETWORK-TRANSPORTproperties P2 P8 P9ASSUME-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.GOSSIP-FAIRNESS-PARTITION-BOUNDproperties P215 assumptions in 8 classes, every id prefixed ASSUME- and every class prefixed audited_a class names what kind of thing is assumed, not how far it is trusted, so the classes carry no order and the figure draws no edges
The audited assumption registry, banded by the class it assigns each entry. The classes name what kind of thing is assumed, so they carry no order and the figure draws no edges between them.
sourceformal/assumptions.toml:23-39at fe56570

Two verification surfaces, one crate

chio-attest-verify owns both attestation surfaces, and they answer different questions. SigstoreVerifier checks a Sigstore bundle over artifact bytes: the bytes hash to the signed digest, the signing certificate chains to the embedded Fulcio trust root, the OIDC issuer matches exactly, the certificate identity SAN matches the caller's anchored regexp, and the returned value states truthfully whether Rekor inclusion and the signed entry timestamp were verified. That is provenance for a build, and it compiles into every build of the crate.

The TEE quote surface is the other one. QuoteVerifier is a single trait taking raw quote bytes plus a QuoteVerificationContext carrying the kernel signing key and the receipt root, and three backends implement it.

The quote backends are off by default

TeeKind, QuoteTcbStatus, QuoteVerificationContext, VerifiedQuote and expect_report_data are exported unconditionally, but the three backend modules sit behind the tee-quotes cargo feature and the crate declares default = []. A build that does not pass --features tee-quotes has the shapes and none of the verifiers. The CLI forwards the same feature under the same name.

TeeKind has three variants and is non_exhaustive. A quote from any other TEE family has no backend in this crate, so it cannot be verified here whatever the hardware supports.

Backend moduleTeeKindCollateral anchored atMeasurement the backend comparesTypical hosting
tdxIntelTdxThe configured Intel root CA, over both the PCK chain and the TCB-info issuer chainNone. It checks collateral validity, the TCB recovery event id, the quote signature, and the bindingAzure CVM, on-prem TDX hosts
sev_snpAmdSevSnpThe AMD KDS root, over whichever of the VLEK or VCEK chains the envelope's key_select flag namesThe 48-byte launch digest, against expected_launch_digestAzure CVM, GCP Confidential VMs
nitroAwsNitroThe embedded AWS Nitro root, over the document's cabundlePCR0, the launch-image digest, against expected_pcr0EC2 instances with Nitro

All three refuse a QuoteTcbStatus outside UpToDate and ConfigurationNeeded, and all three byte-compare the full 64-byte binding slot before returning a VerifiedQuote. The slot is called report_data on TDX and SEV-SNP and user_data in the Nitro attestation document. Nothing else differs in the Chio binary, the receipt format, or the kernel logic.

The control plane keeps a separate set of runtime-attestation verifier families and appraises their evidence before it will widen anything a node is granted; that boundary, and the tier it resolves, is Runtime Assurance Tiers.


The Dockerfile.tee image

deploy/docker/Dockerfile.tee is a two-stage build that mirrors deploy/docker/Dockerfile.sidecar so the path dependencies and Cargo.lock resolution stay identical to CI. Stage one compiles the chio-tee binary against the locked workspace; stage two ships an Alpine runtime with tini as PID 1.

deploy/docker/Dockerfile.teedockerfile
# syntax=docker/dockerfile:1.7
# Chio TEE sidecar image. This mirrors Dockerfile.sidecar's workspace build
# pattern so path dependencies and Cargo.lock resolution stay identical to CI.

ARG RUST_VERSION=1.94.1
ARG ALPINE_VERSION=3.22

############################
# Stage 1: build the binary
############################
FROM rust:${RUST_VERSION}-alpine${ALPINE_VERSION}@sha256:797631f9efd6957d0013f200e410478c380907eee3b469c6f80d89022df28bc7 AS builder
RUN apk add --no-cache build-base cmake openssl-dev openssl-libs-static perl pkgconf musl-dev protoc
ENV OPENSSL_STATIC=1 \
    OPENSSL_DIR=/usr
WORKDIR /workspace

# Copy the full workspace because chio-tee reaches path dependencies under
# crates/ and Cargo resolves every workspace member against the committed lock.
# Top-level directories listed here must match every prefix declared as a
# workspace member in the root `Cargo.toml`. chio-web3-bindings also embeds
# reviewed contract artifacts with include_str!, so contracts/ must be present
# even when only the chio-tee package is being built. Missing any one of these
# inputs aborts manifest loading or Rust compilation.
COPY Cargo.toml Cargo.lock ./
COPY crates ./crates
COPY bench ./bench
COPY contracts ./contracts
COPY integrations/editors ./integrations/editors
COPY wit ./wit
COPY examples ./examples
COPY formal ./formal
COPY integrations ./integrations
COPY tests ./tests
COPY sdks ./sdks
COPY xtask ./xtask

RUN cargo build --release --locked --package chio-tee --bin chio-tee \
 && strip target/release/chio-tee \
 && cp target/release/chio-tee /chio-tee

############################
# Stage 2: minimal runtime
############################
FROM alpine:${ALPINE_VERSION}@sha256:310c62b5e7ca5b08167e4384c68db0fd2905dd9c7493756d356e893909057601 AS runtime

RUN apk add --no-cache ca-certificates tini \
 && addgroup -S -g 10001 chio \
 && adduser  -S -u 10001 -G chio -h /home/chio -s /sbin/nologin chio \
 && mkdir -p /var/lib/chio/tee /etc/chio /run/chio-tee \
 && chown -R chio:chio /var/lib/chio /etc/chio /run/chio-tee /home/chio

COPY --from=builder /chio-tee /usr/local/bin/chio-tee
RUN chmod 0755 /usr/local/bin/chio-tee

USER chio:chio
WORKDIR /home/chio
ENV CHIO_HOME=/var/lib/chio \
    CHIO_TEE_CONFIG=/etc/chio/tee.toml \
    CHIO_TEE_MODE=verdict-only \
    RUST_LOG=info

LABEL org.opencontainers.image.title="chio-tee" \
      org.opencontainers.image.description="Chio TEE sidecar runtime" \
      org.opencontainers.image.licenses="Apache-2.0" \
      org.opencontainers.image.source="https://github.com/backbay-labs/chio"

ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/chio-tee"]
CMD ["--help"]

Four things in this image matter for TEE operators:

  • Static OpenSSL in the builder. The builder installs openssl-dev and openssl-libs-static and builds with OPENSSL_STATIC=1, so the stripped binary copied into the runtime stage links no OpenSSL at run time. The build also copies the full workspace: bench/, contracts/, integrations/, xtask/, and the rest, because the top-level directories have to match every prefix the root manifest declares as a workspace member or manifest loading aborts.
  • Non-root, deterministic UID. The runtime user chio:10001 is fixed so that volume mounts attest the same uid:gid every time. Enclave measurement of process state stays deterministic.
  • Digest-pinned base layers. Both FROM lines carry an @sha256: digest beside the tag, so a moved tag cannot change what the enclave measures. Builds resolve to the same two base layers, and the measured image contents are bounded.
  • Default mode is conservative. CHIO_TEE_MODE=verdict-only ships in the image. The TEE side captures verdicts but does not enforce; you upgrade through the mode lattice deliberately.

TEE modes

The TEE shadow runner participates in kernel decisions at three escalation levels. The lattice is enforced in crates/trust/chio-tee/src/mode.rs: downgrades are unconditional, upgrades require a chio:tee/upgrade@1 capability token.

ModeBehaviorUse
verdict-onlyCapture kernel verdicts; no replay-side enforcement.Default; safe rollout.
shadowReplay decisions inside the TEE; record divergences but do not block.Detection without blast risk.
enforceReplay decisions and reject when the TEE-side replay disagrees with the kernel verdict. Fail-closed.Regulated workloads.

Mode resolution follows a four-layer precedence: CHIO_TEE_MODE wins, then sidecar TOML at CHIO_TEE_CONFIG (default /etc/chio/tee.toml), then the per-tenant manifest tenant.tee.mode, then the implicit verdict-only default. SIGUSR1 hot-toggles the resolved mode at runtime via ${CHIO_TEE_RUNTIME_DIR}/mode-request.

bash
# Set mode at launch
$ docker run --rm \
    -e CHIO_TEE_MODE=shadow \
    -v /etc/chio:/etc/chio:ro \
    chio-tee:latest

# Or via sidecar TOML
$ cat /etc/chio/tee.toml
[tee]
mode = "enforce"

What the quote commits to

The binding is one function. expect_report_data(kernel_pk, receipt_root) computes SHA256(kernel_pk_canonical_hex || receipt_root) into the first 32 bytes of the 64-byte slot and right-pads the rest with zeros. Backends byte-compare the entire slot, padding included, so a quote that stuffs unrelated bytes into the tail is refused. The canonical form is the algorithm-prefixed hex the wire spec locks, bare for Ed25519 and prefixed otherwise, so the binding is byte-stable across the wire.

Both halves are required, and the module states why: a quote that commits only to the signing key can be replayed against a different receipt history, and one that commits only to the history can be replayed under a different kernel. The measurement is not in this slot. It is a separate per-backend comparison against a value the verifier is configured with, and the TDX backend makes none, which is why the binding is the part an operator has to get right.

Producing the quote is the platform's job, not the sidecar's: chio-tee does not depend on chio-attest-verify at all. Verification happens in whatever process holds the verifier, and a relying party that gets Ok(VerifiedQuote) may pin the kernel public key as a receipt signer for that receipt root, and for nothing else.

A verified quote proves a narrow property: the quote came from a platform whose attestation root you configured, its TCB status is one of the two acceptable ones, and its binding slot commits to exactly this signing key over exactly this receipt root. On the two backends that compare one, it also proves the launch measurement is the one you pinned. It does not prove that the binary is bug-free, that the platform vendor is honest, or that the operator running the enclave has not granted side-channel access. Those are separate trust assumptions.

What stays trusted

Even with a TEE you still trust: the silicon vendor's root key, the platform attestation service, the chio source you built, and the verifier configuration that pins the collateral roots and, on the backends that compare one, the expected measurement. See /docs/concepts/trust-model for the precise list.

Signing key lifecycle

Inside a TEE deployment the Chio kernel signing key is ephemeral by design. It is generated inside the enclave at launch, attested to the platform quote, and dies when the enclave terminates. There is no off-enclave key custody and no rotation runbook for the kernel signing material itself: a new enclave gets a new key, attested fresh.

  • Generation. The keypair is produced by the same Keypair::generate() path used by the non-TEE kernel; the difference is that the process memory holding the secret half is never readable from outside the enclave.
  • Binding. The quote's binding slot is a hash over the public key and the receipt root together, so a verified quote commits to this key over this history, not to the binary alone.
  • Continuity. Receipt continuity across enclave restarts comes from the Merkle checkpoint chain, not from the signing key. A new enclave gets a new key; the kernel checkpoint emitted by the old enclave references the previous checkpoint hash, so the chain stitches across the key transition.

For deployments that need long-lived signing identity (for example, an audit trail that must verify under one published key for years), the pattern is to anchor an external trusted-issuer key outside the TEE and have the enclave key sign under it as a delegated signer. See /docs/deployment/secrets-keys for the trusted-issuer key set and delegation patterns.


The chio-tee Loki dashboard

The dashboard at deploy/dashboards/loki/chio-tee.json ships with the repo. Title: Chio TEE Loki receipt view. It is the operator-facing view for verdicts emitted by the TEE side and is filtered by receipt id, tenant, trace id, verdict, and TEE mode.

PanelWhat it shows
TEE records by verdictTime series of records grouped by chio_verdict across chio-tee, chio-replay, and chio-otel-receipt-exporter services.
TEE mode and verdict densityHeatmap of chio_tee_mode × chio_verdict. Useful for spotting verdict drift right after a mode upgrade.
Receipt, trace, and tee log streamFilterable raw log stream with prettified JSON, scoped to the same service set.
Receipt to trace lookupTop-50 receipt rows pivoted to columns: receipt id, span id, trace id, policy ref, verdict, deny reason. Click through to Jaeger.

The query backbone is a structured Loki LogQL pattern that all four panels share. The first panel uses:

bash
sum by (chio_verdict) (
  count_over_time(
    {service_name=~"chio-tee|chio-replay|chio-otel-receipt-exporter"}
      | json
      | chio_tenant_id=~"$tenant_id"
      | chio_receipt_id=~"$receipt_id"
      | chio_verdict=~"$verdict"
    [$__interval]
  )
)

Every panel filters on the same attribute names, so a stream that does not carry them is invisible to all four. Importing the dashboard into Grafana and pointing it at a Loki backend named Loki is the entire installation.


Deployment patterns

Two patterns follow the three backends the verifier ships. Both put a whole measured machine or enclave around the sidecar rather than a process-level enclave, because a process-level enclave family with no TeeKind variant produces a quote nothing here can check.

Confidential VM

The simplest path. Provision a confidential VM (Azure CVM, GCP Confidential VM, AWS EC2 with Nitro, or an on-prem TDX host), install Docker, and run the deploy/docker/Dockerfile.tee image as you would any sidecar. The platform produces the attestation document; chio-tee includes its public key in the binding.

bash
# Azure SEV-SNP example
$ az vm create \
    --resource-group chio-prod \
    --name chio-tee-01 \
    --image Ubuntu2204 \
    --security-type ConfidentialVM \
    --enable-secure-boot true \
    --enable-vtpm true \
    ...

# Then on the VM
$ docker run --rm -d \
    --name chio-tee \
    -e CHIO_TEE_MODE=shadow \
    -v /etc/chio:/etc/chio:ro \
    -v /var/lib/chio:/var/lib/chio \
    chio-tee:latest

Nitro enclave

On AWS, the enclave runs as a sibling EC2 process with no network device and no persistent disk. The parent EC2 instance proxies traffic in and out via vsock. Receipts produced inside the enclave reach the receipt store through that vsock channel; the store itself can live on the parent instance or further out.


Trade-offs

Account for these TEE costs before choosing a deployment.

  • Throughput. Encrypted memory and enclave transitions add per-request overhead, and how much depends on the backend and the workload, so measure it on the hardware you will run. Receipt signing is the part that does not scale with payload size: it happens once per decision.
  • Debugging. Production enclaves do not let you attach a debugger or read process memory. You debug by structured logs and receipts, which is what the chio-tee Loki dashboard exists to support. Plan for verbose tracing in pre-production and conservative tracing in production.
  • Hardware lock-in. Each backend has its own quote format, attestation root, and provisioning workflow. Multi-cloud TEE requires separate verifier configuration for each backend.
  • Build reproducibility. Attestation cannot establish the intended binary if its measurement is not reproducible. Pin base images, lock toolchains, and build in CI rather than on a developer laptop.

When a TEE is overkill

Most Chio deployments do not need a TEE. The default trust model of a hardened container, a separate signing key custody path, and the receipt log and checkpoint chain may meet the required trust boundary. Skip TEE when:

  • You operate the host yourself and trust your own privileged operators.
  • Your tenants are within the same legal entity (single-org SaaS, internal platform).
  • The auditing relying parties accept signed receipts under a KMS-managed key, without a per-process attestation.

When a TEE is essential

On the other hand, four scenarios genuinely require it:

  • Hostile multi-tenant infrastructure. You are running on infrastructure where the operator is adversarial or potentially adversarial. Confidential cloud deployments and bring-your-own-host scenarios are the obvious examples.
  • Regulated industries with explicit TEE mandates. Some financial-services and healthcare frameworks now name TEE attestation as a required control for AI-mediated PHI or settlement-relevant decisions.
  • Cross-organization receipts with no shared trust root. Two organizations want to share signed receipts but neither trusts the other's key custody. A TEE-attested public key is a third-party-rooted signing identity both sides can verify.
  • High-value bilateral settlement. When receipts are economic instruments, the cost of a forged receipt is high enough that the throughput penalty stops mattering.

  • Container Images for the non-TEE Dockerfile lineage and how Dockerfile.tee relates to Dockerfile.sidecar.
  • Secrets & Signing Keys for trusted-issuer key sets, KMS integration, and the rotation runbook for non-TEE keys.
  • Trust Model for what stays trusted in any Chio deployment, with or without a TEE.
  • Formal Assumptions for the precise list of assumptions the Chio proofs leave unproved, including the TEE attestation root.
Confidential Node · Chio Docs