Chio/Docs

Container Images

Three reference Dockerfiles ship in the chio repository, one per deployment shape. The default image gives you the chio CLI plus a built-in trust-control demo. The sidecar image is the canonical production-grade kernel runtime. The TEE image runs the shadow- replay binary inside a confidential-compute envelope. All three share the same multi-stage build pattern: compile cargo build --release --locked in a Rust builder, then ship the binary on a minimal runtime base.

Source

Verified against Dockerfile, Dockerfile.sidecar, Dockerfile.tee, deploy/sidecar/Dockerfile, and .github/workflows/sidecar-image.yml.

Image Overview

ImageDockerfileBinaryBaseExposed
Default kernel imageDockerfilechioAlpine 3.228940 (trust-demo target), 8931 (mcp-demo target)
Sidecar HTTP serviceDockerfile.sidecarchioAlpine 3.22 + tini9090 (default CHIO_LISTEN_ADDR)
TEE shadow runnerDockerfile.teechio-teeAlpine 3.22 + tininone (verdict-only by default)

A fourth Dockerfile, deploy/sidecar/Dockerfile, is the distroless variant of the sidecar image used by the published GHCR artifact. It shares the same build pattern but ships on gcr.io/distroless/cc-debian12:nonroot (uid 65532) and bakes a multi-arch curl for the HEALTHCHECK probe. See Sidecar HTTP Service for that path.


Default Kernel Image

Dockerfile builds a single-stage Rust binary plus an optional dashboard SPA, packaged as three compose-friendly targets: the bare CLI, a trust-control demo, and an MCP-server demo. The dashboard stage builds the chio-cli/dashboard Vite SPA so the trust-demo target can serve it.

Build

bash
# Build the bare CLI image (default target).
docker build -t chio:cli .

# Build the trust-control demo image (dashboard included).
docker build --target chio-trust-demo -t chio:trust-demo .

# Build the MCP-server demo image.
docker build --target chio-mcp-demo -t chio:mcp-demo .

Stages

  • rust-builder: rust:1.93-alpine3.22 with build-base, cmake, perl, and pkgconf. Builds cargo build --release --locked -p chio-cli --bin chio.
  • dashboard-builder: node:22-alpine. Builds the Vite dashboard for the trust-demo target.
  • chio: Alpine 3.22 runtime with ca-certificates, libgcc, and libstdc++. Default CMD is --help.
  • chio-trust-demo: extends chio, copies the dashboard dist/, exposes 8940, and starts chio trust serve with a demo service token.
  • chio-mcp-demo: extends chio, adds python3 and a mock MCP server, exposes 8931, runs chio mcp serve-http.

Env vars (demo targets only)

VariableDefaultUsed by
CHIO_SERVICE_TOKENdemo-tokentrust-demo
CHIO_CONTROL_URLhttp://chio-trust-demo:8940mcp-demo
CHIO_CONTROL_TOKENdemo-tokenmcp-demo
CHIO_AUTH_TOKENdemo-tokenmcp-demo

Demo targets are for examples, not production

The trust-demo and mcp-demo targets bake demo-token defaults so a bare docker compose up brings a working demo online. They are not safe to expose outside a development network. Production sidecars use Dockerfile.sidecar with secrets-backed env vars.

Sidecar Image

Dockerfile.sidecar is the canonical sidecar build. Two stages: an Alpine Rust builder produces a stripped, musl-linked chio binary; a minimal Alpine runtime ships only that binary plus tini and CA roots. Runs as a non-root chio:chio user (uid/gid 10001) with CHIO_HOME=/var/lib/chio.

Build

bash
docker build -f Dockerfile.sidecar -t chio-sidecar:local .

Stages

  • builder: rust:1.93-alpine3.22 (digest-pinned) with build-base, cmake, perl, pkgconf, musl-dev, and protoc. Runs cargo build --release --locked --package chio-cli --bin chio, strips the binary, copies it to /chio.
  • runtime: alpine:3.22 (digest-pinned) with ca-certificates and tini. Adds the chio user, copies the binary to /usr/local/bin/chio, and sets ENTRYPOINT ["/sbin/tini", "--", "/usr/local/bin/chio"].

Why protoc

chio-envoy-ext-authz, reachable transitively through the workspace, invokes tonic-build at compile time. CI installs protobuf-compiler for the same reason. The vendored Envoy protos consumed by tonic-build live under crates/chio-envoy-ext-authz/proto, so the existing COPY crates ./crates line covers them; no separate top-level COPY proto is required.

Default CMD

CMD ["--help"]. Both chio run and chio mcp serve-http require --policy plus positional input, so a bare docker run prints usage rather than crashing. Operators always override at deploy time.

Image size

Alpine + musl-linked binary keeps the runtime layer small. The runtime image carries only ca-certificates, tini, and the chio binary itself. Expect a compressed image around 20 to 40 MB depending on the build profile and feature set.


TEE Image

Dockerfile.tee mirrors the sidecar build pattern but builds the chio-tee binary, the confidential-compute shadow replay runner. It exposes no public port and runs in a verdict-only mode by default.

Build

bash
docker build -f Dockerfile.tee -t chio-tee:local .

Default env vars

VariableDefaultPurpose
CHIO_HOME/var/lib/chioWorking directory for spool and persisted state.
CHIO_TEE_CONFIG/etc/chio/tee.tomlPath to the TEE config file inside the image.
CHIO_TEE_MODEverdict-onlyReplay mode: emit only the allow/deny verdict, not the inputs.
RUST_LOGinfoTracing log level for the TEE runner.

The TEE runner reads /run/chio-tee as a runtime socket directory and writes spool data to /var/lib/chio/tee. Both are created and chown'd to chio:chio in the image. See TEE Deployment for the attestation flow and the verdict-only vs full-replay modes.


Build Strategy

Workspace-aware caching

All three Dockerfiles copy the entire chio workspace, not just the crates the binary directly depends on. The reason is Cargo.lock resolution: examples/, formal/, and tests/ are workspace members; their absence breaks cargo build --workspace even though the build runs --package chio-cli. wit/ is consumed at compile time by chio-wasm-guards through wasmtime::component::bindgen!. sdks/ is copied for forward-compatibility with future Rust members.

BuildKit cache mounts

The distroless variant under deploy/sidecar/Dockerfile uses --mount=type=cache for the cargo registry and the build target directory:

deploy/sidecar/Dockerfile
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/build/target \
    cargo build --release --locked -p chio-cli --bin chio

Combined with cache-from: type=gha in the GitHub Actions workflow, this pulls warm cache layers from previous runs. CI builds drop from the eight-minute cold cargo compile to under two minutes on a warm cache.

--locked is mandatory

Every build invocation passes --locked. The image must compile against the committed Cargo.lock; an image that picks up newer transitive deps at build time defeats reproducibility and the SBOM contract.


Multi-Arch

The published sidecar image is built for linux/amd64 and linux/arm64. The CI workflow uses docker/setup-qemu-action plus docker/setup-buildx-action and passes platforms: linux/amd64,linux/arm64 to the build step.

The distroless variant's healthcheck stage handles multi-arch explicitly: it derives the Debian multiarch directory from dpkg-architecture -qDEB_HOST_MULTIARCH so the curl shared-library copy works unmodified on both x86_64-linux-gnu and aarch64-linux-gnu.


Image Tagging

The published image lives at ghcr.io/backbay-labs/chio-sidecar. The CI workflow emits four tag families through docker/metadata-action:

TagTriggerExample
BranchPush to a branchmain
Semver fullTag push vX.Y.Z0.4.2
Semver minorTag push vX.Y.Z0.4
Short SHAEvery pushsha-1a2b3c4
latestDefault branch onlylatest

latest does not track tag pushes

The workflow intentionally does not emit :latest on tag pushes. Tagging an older patch (e.g. v0.1.1) after a newer minor (e.g. v0.2.0) would otherwise regress :latest. Versioned tags still ship via the semver patterns; pin to those for production.

Supply-Chain Signing

Every published sidecar manifest is keyless-signed with cosign through Sigstore Fulcio. The workflow exchanges its OIDC token for a short-lived Fulcio cert and signs the manifest by digest, which covers every tag pointing at the same content-addressed manifest.

bash
# Workflow excerpt: sign the digest, not the tag.
ref="${IMAGE_NAME}@${IMAGE_DIGEST}"
cosign sign --yes "${ref}"

Verification on the consumer side:

bash
cosign verify ghcr.io/backbay-labs/chio-sidecar:latest \
  --certificate-identity-regexp 'https://github\.com/backbay/chio/.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

The build also emits a SLSA provenance attestation (provenance: mode=max) and an SBOM (sbom: true), both attached to the image manifest by docker/build-push-action. Workspace advisory hygiene is enforced separately at CI time through cargo-deny; see deny.toml for the active advisories and the migration plan for any current ignores.


Runtime Posture

The published images are designed to slot into hardened orchestrator manifests without surprises.

Non-root by default

  • Alpine sidecar: USER chio:chio (uid / gid 10001).
  • Alpine TEE: same chio:chio user.
  • Distroless sidecar: USER 65532:65532 (distroless nonroot).

Read-only root filesystem

The reference ECS task definition sets readonlyRootFilesystem: true and the non-root user user: "65532:65532". Reverting either knob is a regression. The kernel writes only to /var/lib/chio and a tmpfs for in-flight buffers.

Linux capability drops

The chio binary needs none of CAP_SYS_ADMIN, CAP_NET_RAW, or CAP_SYS_PTRACE. Drop all capabilities and re-add only CAP_NET_BIND_SERVICE when binding below port 1024. The default sidecar listen address is 9090, so even that is unneeded in the standard manifest.

Distroless considerations

deploy/sidecar/Dockerfile ships on gcr.io/distroless/cc-debian12:nonroot because the kernel binary is dynamically linked against libgcc_s on glibc. Distroless has no shell, so the Dockerfile bakes a static-relocated curl for the HEALTHCHECK probe; an exec-form CMD is used because there is no /bin/sh to run shell-form directives.

No exec into a distroless container

Distroless images have no shell. You cannot docker exec -it ... sh. Debug by reproducing on the Alpine variant, or by attaching a tracing sidecar that reads the kernel's structured logs.

Next Steps

  • Sidecar HTTP Service for the runtime config (env vars, listen address, health endpoints, supervision).
  • TEE Deployment for the chio-tee image specifics (verdict-only mode, attestation flow, spool directories).
  • Cloud Run for the Knative-style multi-container manifest.
  • ECS Fargate for the task-definition manifest with dependsOn health gating.
  • Azure Container Apps for the Bicep template with startup and readiness probes.
Container Images · Chio Docs