Confidential Node
Run the Chio TEE sidecar inside an attested machine, verify its quote, and raise its mode from capture to enforcement.
On this page
A chio-tee sidecar in enforce mode inside an attested confidential VM or Nitro enclave, with a verified quote that lets any peer pin the kernel's signing key for one receipt history.
The sidecar runs beside the kernel and replays its decisions. It starts in verdict-only and moves to shadow, then enforce, as its replay earns your trust.
The quote commits to one key over one history
expect_report_data(kernel_pk, receipt_root) writes SHA256(kernel_pk_canonical_hex || receipt_root) into the first 32 bytes of the 64-byte binding slot and zero-pads the rest. A quote over the key alone could be replayed against another receipt history, and a quote over the history alone under another kernel. Receipts.
The quote is what vouches for the key outside the enclave. A relying party's trust rests on its binding and on the root the verifier is configured with.
1. Pull the chio-tee image
Every release publishes chio-tee as a signed image: chio-tee compiled against the locked workspace, on an Alpine runtime with tini as PID 1. Verify it with the cosign verify expression on Container Images; to build it yourself, read Build from source.
docker pull ghcr.io/backbay-labs/chio-tee:0.2.00.2.0: Pulling from backbay-labs/chio-tee Digest: sha256:7c41…e0b9 Status: Downloaded newer image for ghcr.io/backbay-labs/chio-tee:0.2.0
You should see the digest of the release image. Pin that digest in the machine definition, so the measurement names the binary you reviewed; chio release verify --evidence reproducible checks that the release's binaries rebuild byte for byte from its tagged source.
The Dockerfile the image is built from
# 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"]2. Provision an attested machine around the sidecar
Put a whole measured machine or enclave around the sidecar, on a platform a quote backend verifies: Intel TDX, AMD SEV-SNP, or AWS Nitro. Configure quote production before the container starts; a plain Docker launch establishes no attestation.
az vm create \
--resource-group chio-prod \
--name chio-tee-01 \
--image Ubuntu2204 \
--security-type ConfidentialVM \
--enable-secure-boot true \
--enable-vtpm true \
...{
"powerState": "VM running",
"resourceGroup": "chio-prod",
...
}You should see the VM running. On AWS the backend expects a Nitro Enclaves attestation document (see the platform attestation procedure). The enclave has no network device or persistent disk; its parent instance proxies traffic over vsock, and receipts reach the receipt store, on the parent or further out, through that channel.
3. Start the sidecar in verdict-only
Start chio-tee run with a runtime directory for its captures and signal file and a tenant signing key. It drains newline-delimited observation envelopes from stdin and appends a tenant-signed frame per observation.
docker run -d -i --name chio-tee \
-e CHIO_TEE_MODE=verdict-only \
-e CHIO_TEE_RUNTIME_DIR=/run/chio-tee \
-e CHIO_TEE_TENANT_KEY_FILE=/etc/chio/tenant.seed \
-v /etc/chio:/etc/chio:ro \
-v /var/lib/chio:/var/lib/chio \
ghcr.io/backbay-labs/chio-tee:0.2.0 run
docker logs chio-teetee.mode_resolved mode=verdict-only source=env capture=/run/chio-tee/captures/<run_id>.ndjson
You should see tee.mode_resolved name the mode and the layer that set it. The three modes form a lattice:
verdict-only. Captures kernel verdicts, with no replay-side enforcement. The default.shadow. Replays each decision in the TEE and records divergences without blocking.enforce. Replays each decision and fails closed when the replay disagrees with the kernel. For regulated workloads.
CHIO_TEE_MODE wins over the sidecar TOML at CHIO_TEE_CONFIG (default /etc/chio/tee.toml), which wins over the tenant manifest's tenant.tee.mode, which wins over the implicit verdict-only.
4. Verify the quote against the key and the receipt root
Hand the platform's quote and collateral to chio attest runtime-quote verify with the kernel public key and the receipt root it should commit to.
chio attest runtime-quote verify \
--tee-kind amd-sev-snp \
--quote quote.bin \
--collateral collateral.json \
--kernel-public-key "$KERNEL_PK" \
--receipt-root "$RECEIPT_ROOT"{
"accepted": true,
"expectedReportData": "9d02…e41b0000…0000",
"kernelPublicKey": "4f1c…a2e9",
"observedReportData": "9d02…e41b0000…0000",
"receiptRoot": "b7e4…05c1",
"schema": "chio.attest.runtime-quote.verification-report.v1",
"signedAtUnixSeconds": 1791734400,
"tcbStatus": "up-to-date",
"teeKind": "amd-sev-snp",
"verificationKind": "teeQuote",
"verificationState": "verified"
}You should see accepted: true and observedReportData equal to expectedReportData. The slot holds SHA256(kernel_pk_canonical_hex || receipt_root) in its first 32 bytes and zeros in the rest, and the backend compares all 64. The key uses the wire spec's algorithm-prefixed canonical hex, bare for Ed25519, so the binding is byte-stable. Both halves are required: a quote over the key alone could be replayed against another receipt history, and a quote over the history alone under another kernel.
A verified quote proves that a platform under your configured root, at an accepted TCB status, committed to this key over this receipt root; on SEV-SNP and Nitro, also that the launch measurement is the pinned one. The binary's correctness, the vendor's honesty, and operator side channels remain trust assumptions, listed in the trust model and the formal assumptions.
5. Raise the mode with an upgrade capability
Write the target mode and a chio:tee/upgrade@1 capability token to mode-request in the runtime directory, then send SIGUSR1. Moving down the lattice needs no token.
docker exec chio-tee sh -c \
"printf 'shadow\n%s\n' '$UPGRADE_TOKEN' > /run/chio-tee/mode-request"
docker kill --signal=USR1 chio-teechio-tee
You should see a shadow band on the dashboard's mode heatmap in step 6. When shadow shows no unexplained divergence, set the steady-state mode in the TOML:
[tee]
mode = "enforce"6. Watch verdicts on the chio-tee Loki dashboard
Import deploy/dashboards/loki/chio-tee.json into Grafana and point it at a Loki data source named Loki; that is the whole installation. The dashboard, titled “Chio TEE Loki receipt view”, filters by receipt id, tenant, trace id, verdict, and TEE mode. Every panel shares one LogQL pattern:
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]
)
)You should see records grouped by chio_verdict across chio-tee, chio-replay, and chio-otel-receipt-exporter. A stream without these attribute names is invisible to all four panels.
A restarted enclave signs with a new key that peers appraise again
With fresh-key provisioning, the kernel generates its signer inside the enclave through the same Keypair::generate() path the non-TEE kernel uses, and nothing outside the enclave can read the secret half. Retain the public key's trust evidence before shutdown: a restart brings a new signer and a new quote.
- Continuity. Keep the receipt store and the verified checkpoint head across restarts; the next checkpoint extends that head under the newly trusted signer. Verify key trust and history continuity separately, since a new quote cannot rebuild lost receipts.
- Long-lived identity. For a trail that verifies under one published key for years, anchor a trusted-issuer key outside the TEE and let the enclave key sign under it as a delegated signer. Persistent or sealed identities need their own provisioning, recovery, and rotation procedure.
Read more in Secrets & Signing Keys.
A failed check names itself in the report or the replay
The verifier reports accepted: false
Read failureCode. quote_verification_failed: the collateral, TCB status, slot, or measurement failed. provided_report_data_mismatch: a --report-data value disagreed with the verified quote. quote_evidence_missing: no --quote, and a binding alone verifies nothing. Refresh the collateral, or rebuild from pinned inputs if the measurement moved.
The report says tee_quote_feature_disabled
The three backends compile with the tee-quotes cargo feature, which the CLI forwards under the same name. Use a chio built with --features tee-quotes on the verifier.
Enforce refuses calls the kernel admitted
The replay disagreed with the kernel verdict. Downgrade to shadow with a tokenless mode-request and SIGUSR1, then find the divergence in the log stream. Production enclaves admit no debugger, so logs and receipts are the debugging surface; trace verbosely in pre-production.
A TEE earns its cost when no party can vouch for the host
A TEE moves the trust boundary onto the silicon vendor's root, the attestation service, the Chio source you built, and the verifier configuration. Most deployments meet their boundary with a hardened container, separate key custody, and the checkpointed receipt log. That suffices when you operate the host and trust its administrators, when all tenants are one legal entity, or when relying parties accept receipts under a KMS-managed key.
Evaluate one when the host operator may be adversarial, when a contract requires evidence about the environment holding the signing key, when two organizations share receipts without trusting each other's custody, or when receipts authorize high-value settlement. Each backend brings its own quote format, root, and provisioning workflow, and enclave transitions add per-request overhead to measure on your hardware; receipt signing stays once per decision.
Quote backends, modes, and dashboard panels
Search for TeeKind, SIGUSR1, or user_data.
Verification surfaces in chio-attest-verify
chio-attest-verify owns two attestation surfaces that 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 result states whether Rekor inclusion and the signed entry timestamp were verified. That is build provenance, and it compiles into every build of the crate.
QuoteVerifier is one trait over raw quote bytes and a QuoteVerificationContext carrying the kernel signing key and the receipt root; three backends implement it. TeeKind, QuoteTcbStatus, QuoteVerificationContext, VerifiedQuote, and expect_report_data are exported in every build; the backend modules compile with the tee-quotes feature, and the crate declares default = []. chio-tee does not depend on chio-attest-verify: the platform produces the quote, and verification runs in whichever process holds the verifier.
Quote backends
TeeKind has three variants and is non_exhaustive; a quote from any other TEE family has no backend here. All three refuse a QuoteTcbStatus outside UpToDate and ConfigurationNeeded and byte-compare the 64-byte binding slot, called report_data on TDX and SEV-SNP and user_data in the Nitro document. Nothing else differs in the Chio binary, the receipt format, or the kernel logic.
| Module | TeeKind | Collateral anchored at | Measurement compared | Typical hosting |
|---|---|---|---|---|
tdx | IntelTdx | The configured Intel root CA, over the PCK chain and the TCB-info issuer chain | None; checks collateral validity, the TCB recovery event id, the quote signature, and the binding | Azure CVM, on-prem TDX hosts |
sev_snp | AmdSevSnp | The AMD KDS root, over the VLEK or VCEK chain the envelope's key_select names | The 48-byte launch digest, against expected_ | Azure CVM, GCP Confidential VMs |
nitro | AwsNitro | The embedded AWS Nitro root, over the document's cabundle | PCR0, the launch-image digest, against expected_pcr0 | EC2 instances with Nitro Enclaves |
The control plane keeps its own runtime-attestation verifier families and appraises their evidence before it widens anything a node is granted; Runtime Assurance Tiers covers that boundary and the tier it resolves.
Image build choices
- Static OpenSSL. The builder installs
openssl-devandopenssl-libs-staticand builds withOPENSSL_STATIC=1, so the stripped runtime binary links no OpenSSL. The build copies the whole workspace (bench/,contracts/,integrations/,xtask/, and the rest) because manifest loading requires every workspace member the root manifest declares. - Fixed UID. The runtime user
chio:10001keeps volume ownership predictable. It sets ownership only; the enclave measurement comes from the platform. - Digest-pinned bases. Both
FROMlines carry an@sha256:digest beside the tag, so a moved tag cannot change what the enclave measures. - Conservative default. The image sets
CHIO_TEE_MODE=verdict-only, so a fresh container captures verdicts and enforces nothing.
Mode resolution and the hot toggle
The lattice lives in crates/trust/chio-tee/src/mode.rs: verdict-only < shadow < enforce. The TOML layer reads the [tee] mode key. On SIGUSR1 the sidecar reads ${CHIO_TEE_RUNTIME_DIR}/mode-request: the first line names the target mode and the second carries the capability token. A downgrade applies unconditionally; an upgrade without a non-empty chio:tee/upgrade@1 token is refused.
Dashboard panels
- TEE records by verdict. Records over time grouped by
chio_verdictacrosschio-tee,chio-replay, andchio-otel-receipt-exporter. - TEE mode and verdict density. A heatmap of
chio_tee_mode × chio_verdict, for verdict drift after a mode change. - Receipt, trace, and tee log stream. The raw log stream with prettified JSON, scoped to the same services.
- Receipt to trace lookup. The top 50 receipts as rows of receipt id, span id, trace id, policy ref, verdict, and deny reason, linked to Jaeger.