Prepare launch inputs
Select approved images, create operator keys, and generate the documents that bind CDS and workers to your deployment.
Prepare these files on a trusted operator machine. The next step boots CDS replicas and admits the first worker using these exact files.
Before you start
You need:
- A substrate that launches Intel TDX or AMD SEV-SNP VMs with the published measured boot artifacts, read-only launch disks, primary IPv4 addresses, and working hardware attestation.
- For TDX, host quote generation (
qgsd), platform registration, and Intel certificate collateral access. - Three independent CDS VM slots and at least one worker slot. Spread CDS replicas across failure domains if you need host-failure availability.
- An encrypted scratch backing disk of at least 64 GiB for each worker.
- An external Kubernetes control plane compatible with the worker's
v1.36.4kubelet, itsadmin.kubeconfig, API endpoint ashost:port, and one PEM serving-CA certificate saved asca.crt. - Bootstrap token authentication, kubelet CSR signing/approval/rotation, and node pod-CIDR allocation enabled on that control plane. Disable its bundled CNI, kube-proxy, CoreDNS, ingress, and service load balancer: workers supply Cilium, kube-proxy, and CoreDNS.
- A bootstrap token in the form
abcdef.0123456789abcdef, kept privately for the worker launch. For k3s/RKE2, extract the token after theK10<hash>::prefix. - Worker, pod, and Service IPv4 CIDRs; a cluster DNS IP inside the Service CIDR; DNS for three CDS replicas, a CDS client endpoint, and the router's TLS name.
- Go
1.27.3, Git, the GitHub CLI, Helm4.2.4, ORAS, OpenSSL,sha256sum, andgenisoimageonPATH. Registry credentials must permit pulling the selected artifacts.
For a k3s server, the documented control-plane settings are:
disable-agent: true
egress-selector-mode: disabled
flannel-backend: none
disable-kube-proxy: true
disable:
- coredns
- traefik
- servicelb
- local-storage
- metrics-serverConfigure those before starting the server.
RKE2 also requires its bundled ingress-nginx removed; that substrate is less tested. Keep control-plane installation and credentials under your normal Kubernetes operating procedures.
Build the matching CLI
Use a new working directory for this deployment. Build the CLI and its bundled chart from the same source revision as these instructions:
gh repo clone confidential-dot-ai/C8s c8s-source
git -C c8s-source switch --detach 640b2d64afacb5696e66752eb5c834433336e4ed
mkdir bin
(cd c8s-source && go build -o ../bin/c8s ./cmd/c8s)
export PATH="$PWD/bin:$PATH"Keep this directory and shell for the remaining tasks.
Acquire the approved artifacts
Obtain independently approved guest OCI digests. The matching v0.99.1
release chart supplies the operator/get-cert and armtls-mesh container
digests used by this worker build.
The published artifact candidates for this source are:
- Worker:
ghcr.io/confidential-dot-ai/guests/worker:v0.99.1-tdx. - CDS:
ghcr.io/confidential-dot-ai/guests/cds:sha-640b2d6.
For SEV-SNP use worker tag v0.99.1-sev-snp. CDS has a commit tag. The
-kubevirt tags contain CDI import images; the commands here pull ORAS build
artifacts with their manifests and boot files.
Replace the angle-bracket values below, then run this in Bash:
set -euo pipefail
WORKER_REPO=ghcr.io/confidential-dot-ai/guests/worker
CDS_REPO=ghcr.io/confidential-dot-ai/guests/cds
WORKER_DIGEST='<APPROVED_WORKER_OCI_DIGEST>'
CDS_DIGEST='<APPROVED_CDS_OCI_DIGEST>'
helm pull oci://ghcr.io/confidential-dot-ai/charts/c8s-cloud \
--version 0.99.1 --untar --untardir release
OPERATOR_DIGEST=$(helm show values release/c8s-cloud --jsonpath '{.image.digest}')
MESH_DIGEST=$(helm show values release/c8s-cloud --jsonpath '{.armtlsMesh.image.digest}')
test "$(oras resolve "$WORKER_REPO:v0.99.1-tdx")" = "$WORKER_DIGEST"
test "$(oras resolve "$CDS_REPO:sha-640b2d6")" = "$CDS_DIGEST"
oras pull "$WORKER_REPO@$WORKER_DIGEST" -o artifacts/worker
oras pull "$CDS_REPO@$CDS_DIGEST" -o artifacts/cds
cp artifacts/worker/manifest.json worker-manifest.json
cp artifacts/cds/manifest.json cds-manifest.json
sha256sum worker-manifest.json cds-manifest.json > manifest-checksums.txtEach digest has the form sha256: followed by 64 lowercase hex digits.
ORAS checks downloaded content against its OCI digests.
Approve the producer and immutable digest independently before downloading: deriving pins from an unapproved manifest only pins an unapproved image.
Verify the release statement
using the release verification instructions,
then compare the chart digest printed by helm pull with its chart field.
A downloaded chart alone does not establish publisher approval.
Retain that statement, the chart, OCI references, manifests, and boot artifacts together.
Create the operator key
The private key authorizes CDS policy changes and signed cluster decrees. Keep it on the trusted operator machine, with a secure offline recovery copy. Only its public half belongs in VM launch inputs.
umask 077
openssl ecparam -name prime256v1 -genkey -noout -out operator.key
openssl ec -in operator.key -pubout -out operator.pubGenerate the launch documents
Set these values from your control plane and network plan. The example DNS
names in the commands must resolve in your deployment; replace them together.
NODE_CIDR contains worker primary addresses and must not overlap pod or
Service ranges.
NODE_CIDR='<WORKER_IPV4_CIDR>'
POD_CIDR='<POD_IPV4_CIDR>'
SERVICE_CIDR='<SERVICE_IPV4_CIDR>'
CLUSTER_DNS='<CLUSTER_DNS_IPV4>'
API_SERVER='<KUBERNETES_API_HOST:PORT>'
c8s node-image render \
--hardware-platform tdx --kube-version v1.36.4 \
--image-digest "$OPERATOR_DIGEST" \
--armtls-mesh-image-digest "$MESH_DIGEST" --output-dir render
c8s cds config new --out cds \
--cluster-id example --operator-keys operator.pub \
--allowlist-seed render/allowlist-seed.json \
--node-cidr "$NODE_CIDR" --pod-cidr "$POD_CIDR" \
--service-cidr "$SERVICE_CIDR" --leaf-dns-name api.example.com \
--replica cds-1.example.com --replica cds-2.example.com \
--replica cds-3.example.com --voters 3 \
--image-manifest cds-manifest.json --platform tdx
c8s worker new --out worker.yaml \
--cds-address cds.example.com:8443 --cds-pin cds/cds-pin.json \
--tls-san api.example.com --api-server "$API_SERVER" \
--ca-certificate ca.crt --pod-cidr "$POD_CIDR" \
--service-cidr "$SERVICE_CIDR" --cluster-dns "$CLUSTER_DNS"
c8s measurements derive --tee tdx \
--out worker-image.json worker-manifest.json
c8s measurements lint worker-image.json
c8s measurements lint cds/cds-pin.json
c8s allowlist lint render/allowlist-seed.json --strictcds/ and worker.yaml must not already exist. Use the same CLI/chart and
component digests as the selected worker build.
For SEV-SNP, change
tdx to sev-snp, including the artifact tag check, and add --vcpus 4 to
cds config new only if that CDS manifest contains the four-vCPU variant.
Select the matching IGVM variant when launching each VM.
The generated documents have distinct roles:
render/allowlist-seed.jsonsupplies platform workload admission.- The CDS configuration fixes operator public keys, replica discovery, permitted networks, and leaf DNS names.
cds/cds-pin.jsonbinds the approved CDS image to those exact configuration bytes.worker.yamlembeds that CDS pin and the external API's CA, endpoint, and cluster settings.
worker-image.json contains the worker build's expected measurements without
a launch binding. Genesis adds the digest of worker.yaml when it admits the
first worker. Keep that document byte-for-byte unchanged after approval.
Keep these generated files together:
operator.keyandoperator.pub.- Both artifact directories,
worker-manifest.json, andcds-manifest.json. render/allowlist-seed.json.cds/cds-config.yamlandcds/cds-pin.json.worker.yamlandworker-image.json.
Keep admin.kubeconfig, ca.crt, and the bootstrap token for the worker step.
Continue to Start policy enforcement.