CLI reference
Command arguments, flag types, defaults, requirements, and effects for the public C8s CLI and its operational components.
Tables list the registered flags for the public commands and operational components below. Grouped sections identify which subcommand takes each flag; hidden conformance commands are excluded. Required conditions include runtime validation, not just CLI annotations. Defaults show registered values; descriptions identify effective fallbacks and conditional requirements.
The c8s binary also responds to the symlink aliases get-cert, armtls-mesh, and
nri-image-policy (each auto-prepends the matching subcommand). Installed as c8s-runc, it is
the measured OCI runtime wrapper of the node image; pass runc commands and flags directly,
without a c8s command prefix.
--image-policy-file takes a complete JSON image policy. A CDS pin binds both its image and
the exact measured launch file (cds-pin.json). Node and router policies, such as server.json,
identify those targets independently. Flat measurement flags exist only where listed.
c8s install
Install the C8s operator, CRDs, attestation-api, and component charts via Helm. Flags are
registered with cobra/pflag. node.baked=false is the chart default; node.baked=true selects
chart artifacts baked into the measured node image and settings from its signed launch file.
router.enabled=true (the default) enables the router and its routing listener.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--namespace | string | c8s-system | No | namespace to install into |
--release | string | c8s | No | Helm release name |
-f, --values | string slice | nil | No | values files (repeatable) |
--wait | bool | true | No | wait for the release to become ready (helm --wait) |
--install-crds | bool | true | No | install chart CRDs (false passes helm --skip-crds) |
--webhook-cert-fs-group | int64 | 65532 | No | fsGroup for injected certificate volume |
--webhook-get-cert-renew-interval | duration | 6h | No | renewal interval for injected workload certificates. Applied only when passed: unset, the chart default webhook.getCert.renewInterval (2h) stands |
--webhook-get-cert-run-as-user | int64 | 65532 | No | runAsUser for injected get-cert containers |
--webhook-get-cert-run-as-group | int64 | 65532 | No | runAsGroup for injected get-cert containers |
--webhook-get-cert-run-as-non-root | bool | true | No | set runAsNonRoot for injected get-cert containers |
--volumes | bool | false | No | deploy volumed, the node agent that opens a pod's encrypted volume devices, and pin its image into the NRI allowlist. Off by default: it runs privileged, with hostPID and a writable bind of the kubelet directory |
--cvm-mode | string | "" | Yes | Deployment shape: bare-metal (self-managed confidential node), gke (GKE confidential nodes), or aks (Azure vTPM nodes). All modes use one node CVM as the trust domain. |
--hardware-platform | string | "" | Yes | CPU-level TEE hardware (orthogonal to --cvm-mode): sev-snp (/dev/sev-guest) or tdx (Intel TDX, /dev/tdx-guest). Under --cvm-mode=aks both ride the Azure vTPM (/dev/tpm0, no guest device): sev-snp selects the az-snp shape, tdx selects az-tdx |
--image-tag | string | "" | No | component image tag to resolve digests at (default: the CLI build version, or main for an unstamped build); override to pin a specific branch/tag/release |
--resolve-digests | bool | true | No | resolve each component image tag to its registry digest (via crane), pin it, and add the resolved images to the NRI allowlist. Also reads each pinned image config, the router nginx image included (docker.io unless router.nginx.image.repository names a mirror). Pass --resolve-digests=false when supplying digests via -f |
--attest | bool | true | No | serve the router's attestation endpoints under /.well-known/c8s/ (browser and CLI verification via c8s-verify); --attest=false sets router.attest.enabled=false and turns them off, while the cds-attest sidecar keeps routing application requests |
--image-pull-secret | string | "" | No | name of an existing kubernetes.io/dockerconfigjson Secret in the release namespace; the chart appends it to every component's imagePullSecrets, so all pods pull the C8s images from an authenticated registry from first start. The Secret is never created or managed by the install — install fails fast when it is missing or has the wrong type |
--workload-ref | string slice | nil | No | existing workload to adopt as a confidential workload, as <cw-id>=<namespace>/<kind>/<name>[:<port>]; repeatable. Kind is any resource exposing a pod template at spec.template (deployment, statefulset, daemonset, or an operator CRD such as <kind>.<group>); install patches the pod template with confidential.ai/cw=<cw-id> once C8s is ready. The optional :<port> is the ordinary application TCP port carried by its injected mesh endpoint, required on the ref --upstream selects |
--upstream | string | "" | No | confidential.ai/cw id of the adopted --workload-ref workload to route / to: writes a route manifest naming c8s-<id>.<ns>.svc.cluster.local:<port> (that ref's :<port>, carried by the injected mesh endpoint) and the allowlist entry <id>, for every router.san DNS name |
--operator-key | string | "" | No | operator EC private-key PEM file path (env C8S_OPERATOR_KEY) signing the --upstream route manifest; publish to the external CDS using --cds-address and --cds-pin. Requires --upstream; without this key, write the manifest to --routes-out |
--routes-out | string | routes.json | No | file the --upstream route manifest is written to when no --operator-key signs it, or when install cannot store it on CDS |
--routes-revision | uint64 | 0 | No | revision of the --upstream route manifest, above the one CDS stores; default the later of the stored revision plus one and the current Unix time. After a CDS reset, pass one above the highest revision any router applied |
--routes-max-age | duration | 24h | No | maxAgeSeconds of a new --upstream route manifest: how long the router admits new attested sessions by it without a successful fetch from CDS; application routing goes on regardless. A manifest CDS already stores keeps its own |
--operator-keys | string | "" | Conditional | PEM public-key bundle file path embedded in cds.operatorKeys for router route verification; install preflight requires this flag, a -f values file, or --force. CDS write authority comes from its own measured configuration |
--force | bool | false | No | bypass guarded install prompts for missing --operator-keys/values files and a component policy that would deny platform pods; chart validation still applies |
--cds-address | string | "" | Conditional | external CDS <host>:<port> (DNS or IPv4); sets cds.address. With node.baked=false, supply this and --cds-pin, or set cds.address and cds.measurementsConfig in -f values |
--cds-pin | string | "" | Conditional | path to cds-pin.json, binding the external CDS image and exact launch file; embeds its contents in cds.measurementsConfig |
--allowlist-seed-out | string | "" | No | write the release/options-matched component allowlist seed and stop before cluster mutation; input to c8s cds config new --allowlist-seed |
c8s uninstall
Uninstall the C8s Helm release and sweep C8s host artifacts, including image-policy and mesh state. Requires Helm and kubectl on PATH.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--namespace | string | c8s-system | No | namespace the release was installed into |
--release | string | c8s | No | Helm release name |
--wait | bool | true | No | wait for the release deletion to complete (helm --wait); the host sweep waits for its host pods to be gone either way |
--host-sweep | bool | true | No | after the release is deleted, sweep C8s host artifacts (NRI image-policy plugin, armtls-mesh netfilter state, RKE2 prep template, idle c8s volume attach disks) off every node through a short-lived privileged DaemonSet. Runs for every release shape |
--host-sweep-only | bool | false | No | skip the helm uninstall and run only the host sweep — for a cluster whose release is already gone (e.g. a previous bare helm uninstall) but whose nodes still carry C8s artifacts. Uses the chart defaults and the distro detected from the cluster when the release values are unavailable |
--force | bool | false | No | uninstall while pods hold C8s encrypted volumes; the pre-delete hook cannot close a mapping a live pod holds, and fails naming it |
--delete-crds | bool | false | No | also delete the ConfidentialWorkload CRD — this deletes every ConfidentialWorkload object in the cluster with it |
--delete-namespace | bool | false | No | also delete the release namespace (and everything left in it, e.g. an operator-created image pull Secret) |
c8s render-values
Print the resolved Helm values an install would apply, to stdout — without contacting a
cluster. Useful for feeding a GitOps consumer (e.g. a Flux HelmRelease's valuesFrom) instead
of recomputing digests and device mappings. Unlike install, the host distro is not autodetected —
pass --distro to pin it. Still needs the registry reachable for the default digest resolution
(--resolve-digests=true). The consuming chart defaults to node.baked=false and needs
cds.address and cds.measurementsConfig; supply the flags here or layer these values in the
consuming Helm configuration. node.baked=true selects measured-image artifacts with signed
launch inputs. router.enabled=true (default) enables routing; its keys come from launch inputs,
cds.operatorKeys, or the verified CDS. Route manifests are not Helm values: store them with
c8s routes.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--distro | string | "" | No | host Kubernetes distro (k8s | rke2); install autodetects it, render-values does not, so pass it to pin the value (unset leaves the chart default) |
--volumes | bool | false | No | emit volumed.enabled=true, deploying the node agent that opens encrypted volumes |
--cvm-mode | string | "" | Yes | Deployment shape: bare-metal (self-managed confidential node), gke (GKE confidential nodes), or aks (Azure vTPM nodes). All modes use one node CVM as the trust domain. |
--hardware-platform | string | "" | Yes | CPU-level TEE hardware (orthogonal to --cvm-mode): sev-snp (/dev/sev-guest) or tdx (Intel TDX, /dev/tdx-guest). Under --cvm-mode=aks both ride the Azure vTPM (/dev/tpm0): sev-snp renders az-snp, tdx renders az-tdx |
--resolve-digests | bool | true | No | resolve each component image tag to its registry digest (via crane), pin it, and enable the NRI allowlist derivation |
--image-tag | string | "" | No | component image tag to resolve digests at (default: the CLI build version, or main) |
--image-pull-secret | string | "" | No | name of an existing dockerconfigjson Secret the chart wires into every component's imagePullSecrets |
--operator-keys | string | "" | No | PEM public-key bundle file path; embeds its contents in cds.operatorKeys for router route verification. Without configured or measured launch keys, the router fetches keys from verified CDS |
--install-crds | bool | true | No | emit values for chart CRDs (false sets statusMirror.enabled=false, matching install --install-crds=false) |
--cds-address | string | "" | Conditional | external CDS <host>:<port> (DNS or IPv4); emits cds.address. Supply with --cds-pin, or layer both CDS values in the consuming Helm configuration; this command accepts no values-file input |
--cds-pin | string | "" | Conditional | path to cds-pin.json, binding the external CDS image and exact launch file; embeds its contents in cds.measurementsConfig |
c8s operator
Run the C8s controller-manager and admission webhook.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--metrics-bind-address | string | :8080 | No | address for Prometheus metrics |
--health-probe-bind-address | string | :8081 | No | address for health/readyz probes |
--leader-elect | bool | true | No | enable leader election for HA |
--leader-election-namespace | string | c8s-system | No | namespace holding the leader-election Lease |
--status-mirror-enabled | bool | true | No | enable CRD-backed ConfidentialWorkload status mirror controller |
--get-cert-image | string | "" | No | image reference the webhook injects for get-cert containers (empty = webhook disabled) |
--attestation-api-url | string | "" | No | attestation-api endpoint (empty = no verification) |
--exclude-namespaces | string slice | nil | No | extra namespaces the startup reinject sweep skips (mirrors webhook.extraExcluded) |
--webhook-config-name | string | "" | No | MutatingWebhookConfiguration to patch caBundle (empty = skip) |
--webhook-service-name | string | "" | Conditional | webhook Service name; required when --webhook-config-name requests webhook configuration |
--webhook-service-namespace | string | "" | No | webhook Service namespace (defaults to --leader-election-namespace) |
--cert-fs-group | int64 | 65532 | No | fsGroup applied to injected pods when unset; enabled mesh injection requires a nonnegative value |
--get-cert-renew-interval | duration | 2h | No | renewal interval for injected workload certificates |
--workload-claims-host-dir | string | "" | Conditional | host directory holding the NRI inventory socket; required for injected certificates, whose get-cert container redeems a sandbox token |
--mesh-image | string | "" | Conditional | pod mesh endpoint image; required with --get-cert-image |
c8s cds
Run the Certificate Distribution Service (CDS) from a measured c8s-cds/v1 configuration.
A verified self-report must bind its exact bytes. The host-readable configuration holds public
inputs, including operator keys, configured node pins and policy; it carries no private keys or secrets.
Subcommands are ca, config new,
ha, and verify (see c8s verify).
A singleton holds its mesh CA and secrets in memory. An HA configuration selects replicated volatile state and requires explicit Genesis. A replica restart need not lose cluster state; losing all replica memory does. Surviving memory is retained when quorum loss stops serving. CA certificates renew under the same key after half their lifetime; certificate fingerprint changes alone do not establish a new CA key.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--host | string | 0.0.0.0 | No | listen address; auto selects this VM's primary IPv4 address |
-p, --port | int | 8443 | No | listen port |
--log-level | string | info | No | log level: debug, info, warn, error |
--attestation-api-url | string | "" | Yes | this VM's attestation API; must use a literal loopback IP address |
--ca-common-name | string | c8s Mesh CA | No | singleton mesh CA common name; HA uses the built-in common name |
--ca-cert-validity | duration | 8760h | No | singleton mesh CA certificate lifetime; HA uses the built-in lifetime. Both renew after half-life under the same key |
--jwt-clock-skew | int64 | 30 | No | operator JWT clock-skew tolerance in seconds; singleton startup without --allowlist-persistent also delays operator-token requests for this interval |
--cert-ttl | duration | 24h | No | TTL of the leaf and EAR /attest issues, capped at the built-in 24h leaf ceiling |
--named-cert-ttl | duration | 6h | No | upper bound on the TTL of a leaf carrying a matched-workload stamp (never applied to membership-only leaves); must be positive and may only shorten the built-in 6h ceiling |
--challenge-ttl | duration | 1m | No | lifetime of an attestation challenge nonce; issuance and /secrets keep separate pools |
--request-timeout | duration | 5s | No | per-request timeout on /attest and on the inventory callback (0 disables) |
--max-request-size | int64 | 65536 | No | maximum body bytes on /attest, /authenticate and workload secret routes; must be positive |
--read-timeout | duration | 10s | No | HTTP server read timeout |
--read-header-timeout | duration | 5s | No | HTTP server read-header timeout |
--write-timeout | duration | 10s | No | HTTP server write timeout |
--idle-timeout | duration | 20s | No | HTTP server idle timeout |
--max-header-bytes | int | 1048576 | No | maximum HTTP request header bytes |
--san-validation | bool | true | No | require CSR IP SANs to equal the request source IP (false rejects CSRs carrying IP SANs) |
--allowed-cn-pattern | string | "" | No | regex the CSR Subject CN must match in full (empty disables) |
--readiness-interval | duration | 10s | No | interval of the attestation-api health check behind /readyz; must be positive |
--min-ca-validity | duration | 1h | No | /readyz fails when the loaded mesh CA has less than this remaining lifetime |
--allowlist-db | string | "" | Yes | path to the allowlist SQLite database |
--allowlist-persistent | bool | false | No | declare the singleton allowlist SQLite database durable; does not persist its mesh CA or secret memory. Refused with an HA configuration, whose state is replicated and volatile |
--rate-limit | float64 | 10 | No | max requests per second per source IP on attestation endpoints |
--rate-burst | int | 20 | No | max burst size per source IP |
--rate-limiter-max-entries | int | 10000 | No | max entries in the per-IP rate limiter; once full, a new source evicts the least recently seen one |
--rate-limiter-evict-interval | duration | 1m | No | interval for per-IP rate limiter eviction sweep |
--rate-limiter-idle-timeout | duration | 5m | No | idle duration before a per-IP rate limiter entry is evicted |
--secrets-max-paths | int | 1024 | No | max distinct secret paths held in memory across every workload; must be positive |
--secrets-max-paths-per-workload | int | 64 | No | max secret paths one allowlist entry may hold; must be positive and below --secrets-max-paths |
--secrets-max-value-bytes | int | 4096 | No | max bytes in one secret value; must be at least 32, the size of every value the CDS generates |
--sandbox-ledger-max-entries | int | 10000 | No | max sandbox-to-inventory bindings held in memory; must be positive |
--armtls-cert-ttl | duration | 24h | No | TTL of the RA-TLS serving certificate CDS presents |
--config | string | "" | Yes | path to the exact measured CDS launch document, c8s-cds/v1 |
c8s cds config
new takes no positional arguments and writes cds-config.yaml and cds-pin.json to a new
directory. The pin binds the exact configuration bytes and CDS guest image; all HA replicas
boot those same bytes. Generate the release/options-matched component seed first with
install --allowlist-seed-out, then generate and verify the external CDS before installation.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--out | string | "" | Yes | output directory; must not exist |
--cluster-id | string | "" | Yes | cluster name, an RFC 1123 label |
--operator-keys | string | "" | Yes | PEM public-key bundle file path authorizing policy writes and HA decrees |
--allowlist-seed | string | "" | Yes | JSON component allowlist matching the release and deployment options |
--workloads | string | "" | No | tenant allowlist added to the seed; names cannot overwrite component entries |
--node-pins | string | "" | Yes | complete node launch identities for singleton admission and the default HA Genesis node set |
--node-cidr | string array | nil | Yes | allowed node inventory subnets; repeatable |
--pod-cidr | string array | nil | Yes | pod ranges, excluded from node inventory ranges; repeatable |
--service-cidr | string array | nil | Yes | service ranges, excluded from node inventory ranges; repeatable |
--leaf-dns-name | string array | nil | No | allowed literal leaf DNS SAN; repeatable |
--leaf-dns-pattern | string array | nil | No | full-match DNS SAN regex; repeatable. If this flag is omitted, uses ^[a-z0-9-]+[.][a-z0-9-]+[.]svc$ (for example api.tenant.svc), including when --leaf-dns-name is supplied |
--image-manifest | string | "" | Yes | exact CDS guest image's manifest.json |
--platform | string | "" | Yes | CDS TEE: sev-snp or tdx |
--vcpus | int slice | nil | Conditional | SEV-SNP vCPU counts, one pin per shape; at least one required for SNP; repeatable |
--replica | string array | nil | Conditional | HA discovery DNS name or IPv4 address, no port or load-balanced address; repeatable; requires --voters |
--voters | int | 0 | Conditional | HA initial voter seats, odd 1–9; requires replicas. Omitting both HA flags selects singleton mode |
HA discovery reaches distinct replica identities on their client port and peer port 8444. Up to 16 nonvoters can join. Replicated state has a 256 MiB bound with headroom; this is not available secret capacity. Workloads and voter count initialize live state. Configuration node pins supply the default for HA Genesis; signed Genesis selects the initial admitted node set. Signed changes can update live state. Pinning the configuration does not freeze the live node policy forever.
c8s cds ha
Inspect and administer the replicated CDS. All leaf commands take no positional arguments.
Reads verify each reached CDS against --cds-pin. Decrees are signed under an operator key in
the measured configuration and posted to POST /ha/decrees.
| Subcommand | Purpose |
|---|---|
status | read every selected replica's GET /ha/status; print serving state and alarms |
decrees | read GET /ha/decrees from the first answering replica and check every retained operator signature; at most 256 applied decrees are retained |
pin-policy | read GET /ha/pins, check its signature, cluster and launch-file binding, and optionally write the resulting pins and signed decree; 404 before a pin decree exists |
decree genesis | bind an unjoined process and its prospective CA to the initial CDS/node pins; explicit Genesis starts an HA cluster |
decree pins | replace the complete accepted CDS image set; overlapping old/new sets support image rolls |
decree node-images | replace the complete node identity set CDS certifies, independently of CDS image pins |
decree voters | set the odd target voter-seat count; seats change one at a time while preserving a majority |
decree rotate-key | replace the mesh CA key; recreate pods for new credentials and update client CA anchors |
decree recover | sign and deliver a survivor-bound Recover intent; delivery can hold it, but production recovery is not wired to apply it |
decree dismiss-recover | sign a decree naming a Recover doubt to dismiss; does not supply an automatic fencing or recovery mechanism |
decree admit | let one unjoined process temporarily admit the serving cluster's applied CDS image set; each restart needs a fresh decree |
status exits unsuccessfully for an unreachable, nonserving or wrong-cluster replica, or
version/CA disagreement. Not every printed alarm changes its exit status. Only roll_safe and
divergence are leader-specific; followers also report voters and checkpoints. Diagnostics remain
reachable when ordinary APIs return 503 because a replica lacks a valid leader-confirmed serving
lease or applied state.
Decree delivery reports held, applied at version, or refused. A successful CLI exit for a held decree proves acceptance only. Production held-decree consumers handle Genesis and Admit; Recover has no operational consumer or automatic Doubt/fencing proposer. Preserve surviving VMs when permanent quorum loss halts service. Recover acceptance is not a recovery procedure, and total memory loss cannot restore secrets or the CA key.
Before planned removal and between replacements, require the leader's roll_safe result. A VM
already known destroyed may be replaced first while a majority serves; its dead vote itself
makes the roll gate false. Unreachability does not prove destruction. Image rolls require overlap,
per-process Admit and majority preservation. rotate-key changes mesh CA authority only;
operator authorization keys stay launch-bound. Clients can adopt an independently signed,
strictly newer provisional pin decree over unverified transport; an initial refresh failure keeps
the launch pins. New connections fail across changed CA keys;
established streams are not guaranteed to terminate immediately.
Flags shared by the read commands and/or decree commands are listed once with their scope:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--config | string | "" | Yes | all leaf commands: exact CDS measured configuration |
--cds-pin | string | "" | Yes | all leaf commands: CDS image/launch-file policy used for every endpoint read or delivery; include both images during a roll |
--url | string array | nil | No | status, decrees, pin-policy: <host>[:8443] replicas overriding config discovery; repeatable |
--json | bool | false | No | status, decrees: print full JSON reports or records with signature-check results |
--out | string | "" | No | pin-policy: write reconstructed pin document; every decree command: write signed decree in addition to posting |
--decree-out | string | "" | No | pin-policy: write the signed pin decree for launch-config add-agent --pin-decree |
--operator-key | string | "" | Yes | every decree command: EC private-key PEM file path matching measured operator authority |
--expires-in | duration | 10m | No | every decree command: positive adoption validity interval |
--base-version | uint64 | 0 | Yes | pins, node-images, voters, rotate-key, recover, dismiss-recover: current applied version, explicitly supplied even if 0 |
--target | string | "" | Yes | genesis, recover, admit: one process at <host>[:8443]; must resolve to exactly one IPv4 address |
--to | string | "" | Yes | pins, node-images, voters, rotate-key, dismiss-recover: replica to receive the decree and forward to its leader |
--platform | string | "" | Yes | genesis, pins: CDS platform, sev-snp or tdx |
--image-manifest | string array | nil | Yes | genesis, pins: accepted CDS guest-image manifests; repeatable |
--vcpus | int slice | nil | Conditional | genesis, pins: accepted SEV-SNP CPU counts; required for SNP; repeatable |
--nodes | string | "" | Conditional | node policy document; optional for genesis (defaults to measured config), required for node-images |
--resume-version | uint64 | 0 | Yes | genesis, recover: highest known prior version (explicit 0 for first Genesis); Recover requires at least --base-version |
--alone | bool | false | No | genesis: allow single-replica start without discovery peers; a host hiding other replicas could cause a separate cluster |
--voters | int | 0 | Yes | voters: target voter seats, odd 1–9 |
--i-understand-rotation-forks-the-cluster | bool | false | Yes | rotate-key: must be true to acknowledge the new mesh CA key requires fresh workload credentials |
--i-understand-recover-is-break-glass | bool | false | Yes | recover: must be true to acknowledge missing committed writes; does not make Recover operational |
--recover-intent | string | "" | Yes | dismiss-recover: Recover intent ID reported as doubt |
--cluster | string | "" | Yes | admit: serving replica <host>[:8443] supplying the applied pin decree |
c8s cds ca
Read the mesh CA bundle CDS serves at GET /ca and write it as PEM to --out, or to stdout. It is
the anchor c8s secrets put --mesh-ca, c8s volume create --mesh-ca and c8s verify --mesh-ca
take. The read travels the attested connection those commands use and requires endpoint pins. A
launch measurement identifies the image, not the instance, so the SHA-256 of each certificate is
printed on stderr for comparison on a later read. A singleton restart or loss of all replica
memory replaces the mesh CA key. A surviving HA
cluster retains it across individual restarts. Same-key certificate renewal also changes certificate
fingerprints; compare the key separately when judging cluster continuity. The router front door does not relay
/ca, and a URL that serves a router discovery document is refused; point --url at CDS directly.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--url | string | "" | Yes | direct CDS HTTPS base URL; plaintext and router discovery endpoints are refused |
--image-policy-file | string | "" | Yes | complete CDS image and launch-file pin, such as cds-pin.json; required for HTTPS, including reads |
--timeout | duration | 15s | No | per-request timeout |
--operator-key | string | "" | No | inherited operator credential flag (env C8S_OPERATOR_KEY); the CA read does not require a write token |
--insecure | bool | false | No | inherited connection flag; cds ca still requires HTTPS |
--out | string | "" | No | write the bundle here instead of stdout |
c8s allowlist
Read and mutate the CDS-served allowlist that nri-image-policy enforces on every node. It is a
set of named entries, each pinning an init/main container set with per-container argv, mount,
environment and path policy. An image admitted under any command line is an entry whose command
and args policy are both any; add writes one. Reads are unauthenticated; writes are signed with
the operator EC private key whose public half is in the CDS measured configuration. See The Allowlist for usage.
| Subcommand | Arguments | Purpose |
|---|---|---|
list | — | list the current allowlist entries |
get | <name> | print one entry as canonical JSON |
export | [file] | write the full allowlist as canonical JSON to a file (default stdout) for backup or re-upload |
diff | <file> | show how an allowlist file differs from the live allowlist |
lint | <file|-> | validate an allowlist file and report semantic findings; never contacts the CDS |
inspect-image | <ref> | resolve an image reference via crane and print its digest plus baked Entrypoint/Cmd; registry-only |
derive | <name> <file|-> | print an entry built from a Pod, Deployment, StatefulSet or DaemonSet given as JSON; may read image argv from the registry with crane. Injected containers are dropped |
add | <digest> <image> | write the entry <image basename>-<first 12 hex of digest>: one container at <digest> with command and args policy any (operator key required) |
apply | <file|-> | replace the named entries whole in a signed full-document update (operator key required) |
edit | <name> | fetch an entry, edit it in $EDITOR, and apply the result (operator key required) |
delete | <name> [<name>...] | delete one or more entries (operator key required) |
upload | <file> | atomically replace the entire allowlist with the contents of a file; CDS assigns the new version (operator key required) |
For add, <digest> is sha256: plus 64 hexadecimal digits; <image> is a nonempty informational label (* is refused)
(such as a tagged image reference), not a registry lookup. For example, this prints a dry run:
c8s allowlist add sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef example/app:1 --url https://cds.example:8443 --dry-runThe router front door relays /allowlist; commands that contact CDS can use that route.
Entry edits (add, apply, edit, delete) canonicalize and sign a complete policy and use
conditional whole-document writes with bounded conflict retries. upload refuses a racing write
for review. Exact canonical bytes retain the signature; seed and noncanonical policies may be
unsigned. derive prints a bare name-to-entry map and has no egress flag.
Signed egress policy admits at most eight unique TCP exits per workload: masked IPv4 /16 or
narrower, or IPv6 /48 or narrower, excluding protected ranges. It requires pinned argv and mounts
plus a unique, complete sandbox main-container match. Environment follows the selected policy,
which may be any. Exits remain unavailable until observed containers satisfy every main-container policy of the matching workload entry. Exits bypass capture without
encrypting payloads; changed or removed exits
require pod recreation. See The Allowlist.
HA allowlist history is periodically pruned toward 1000 entries and 64 MiB, preserving the current
document; these targets can be exceeded before pruning. Removed objects and signatures become
unavailable. HA writes can fail with 503 or 507 state_capacity.
Persistent flags, registered on every subcommand:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--url | string | "" | Yes | router or direct CDS base URL; required by every subcommand that contacts the CDS (lint, derive and inspect-image do not). A router front door is accepted only when its discovery document reports public_tls.mode=cds; a WebPKI front door is refused. A direct URL is verified by RA-TLS on the CDS serving cert. Plaintext http:// is refused without --insecure |
--measurements | string slice | nil | No | flat launch-digest pins for the attested endpoint; cannot establish direct CDS launch-file binding. Unpinned HTTPS reads and writes are refused |
--measurements-file | string | "" | No | file of allowed launch measurements, one hex digest per line; merged with --measurements |
--image-policy-file | string | "" | Conditional | complete endpoint policy; direct CDS requires cds-pin.json with launch-file binding. Router discovery uses its independently intended node policy. Excludes --measurements and --measurements-file |
--timeout | duration | 15s | No | per-request timeout |
--operator-key | string | "" | No | operator EC private key PEM file whose public key is pinned in the CDS measured configuration (env C8S_OPERATOR_KEY; the flag wins); required for writes |
-o, --output | string | text | No | output format: text or json |
--insecure | bool | false | No | dev/test only: allow a plaintext http:// CDS URL, skipping RA-TLS attestation of CDS |
Per-subcommand flags:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--exit-code | bool | false | No | diff only: exit non-zero when the file and the live allowlist differ |
--online | bool | false | No | lint only: also check each digest exists in its registry via crane |
--seed | string | "" | No | lint only: the release/options-matched component allowlist seed; lint then names each chart-generated entry the seed now generates under another name, safe to delete once no pod runs it |
--strict | bool | false | No | lint, upload: treat lint warnings as fatal (lint errors already fail on their own) |
--cvm-mode | string | "" | No | upload: bare-metal, gke, or aks; bare-metal excludes both nri-image-policy and attestation-api from the required components because the measured node supplies them |
--dry-run | bool | false | No | add: print the intended entry without calling CDS; apply: show the diff without writing any entry; upload: show the diff without replacing the allowlist |
--force | bool | false | No | upload only: upload even if core C8s components are missing from the file's image labels. Bypasses that check alone |
--require | string slice | nil | No | upload: override required component identifiers; default armtls-mesh, nri-image-policy, attestation-api, nginx |
--label | string | "" | No | derive only: optional label written into the derived entry |
--secret-read | string array | nil | No | derive only: grant read on this secret path (repeatable); with none the derived entry carries no secrets block. A grant requires every container to pin argv (exact or deny) and mounts (deny, or exact with no host rule) |
--env | string | "" | One of two | derive only: environment policy for every container, any or deny; exactly one of --env and --env-file is required |
--env-file | string | "" | One of two | derive only: JSON file mapping container names to explicit environment policies, including exact and match |
--mounts | string | "" | No | derive: any or deny; omitted with no --mounts-file, uses deny (platform mounts only). Mutually exclusive with --mounts-file |
--mounts-file | string | "" | No | derive only: JSON file mapping container names to explicit mount policies, covering every derived init and main container; exact mount rules go here |
For a bare-metal node, use upload --cvm-mode bare-metal to check the appropriate component set.
--force bypasses the missing-component check and requires a separate review of the policy.
c8s secrets
Write operator-supplied values into the CDS secret store, and report why a sandbox does or does
not receive them. CDS releases a value to a pod when the containers running in that pod's sandbox
match a workload entry whose secrets grant covers the path; the grant is written with
c8s allowlist apply. Values are held in singleton memory or replicated HA memory. A singleton restart or loss of
all replica memory empties the store; preserve operator escrow for restoration. See Secrets.
| Subcommand | Arguments | Purpose |
|---|---|---|
put | <path> | store a value at a secret path, read from stdin or --from-file |
explain | — | print the release decision CDS reaches for one sandbox |
put sends the bytes exactly as read, including any trailing newline, and prints the byte count.
Both subcommands sign their request with the operator EC private key whose public half the CDS
pins in its measured configuration. The router front door does not relay
/secrets or /ca, and both subcommands refuse a URL that serves a router discovery document
before they send anything: point --url at the external CDS client address and pin its image and exact launch file with cds-pin.json.
Persistent flags, registered on every subcommand:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--url | string | "" | Yes | direct external CDS HTTPS base URL; router discovery endpoints are refused. Plaintext HTTP requires --insecure |
--image-policy-file | string | "" | Conditional | complete CDS image and launch-file pin, such as cds-pin.json; required for HTTPS, including reads |
--timeout | duration | 15s | No | per-request timeout |
--operator-key | string | "" | No | operator EC private key PEM file whose public key is pinned in the CDS measured configuration (env C8S_OPERATOR_KEY; the flag wins). Both subcommands sign, so one of the two must be set |
--insecure | bool | false | No | dev/test only: allow a plaintext http:// CDS URL, skipping RA-TLS attestation of CDS |
Per-subcommand flags:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--from-file | string | "" | No | put only: read the value from this file instead of stdin |
--overwrite | bool | false | No | put only: replace a value already at the path. Without it an occupied path is refused, and the error names what is there |
--mesh-ca | string | "" | Conditional | put only: PEM bundle file path for the mesh CA this CDS must serve, the same anchor c8s verify --mesh-ca takes; c8s cds ca reads it. The write is refused unless every certificate CDS serves at /ca is in the bundle. Required unless --force; a plaintext --insecure endpoint is exempt |
--force | bool | false | No | put only: write without checking the CDS mesh CA against --mesh-ca. Unrelated to --overwrite |
--dry-run | bool | false | No | put only: print the intended change without calling CDS |
--sandbox | string | "" | Yes | explain only: sandbox ID to report on. It is carried on the pod's certificate; c8s verify prints it |
--json | bool | false | No | explain only: print the raw report as JSON |
c8s volume
Build an encrypted volume, store its key in the CDS secret store, and present the image to a node
as a disk. create packages --source into an erofs image, formats a dm-verity tree over it,
encrypts the pair to --out, writes the key blob to --escrow-out, and puts the key at --path
in the store; with --mutable it builds a writable ext4 of --size instead, with no integrity
protection. It then prints the pod annotations and the allowlist grant to apply; it modifies no
workload. The image is ciphertext and can reach the node by any route, including through the
untrusted host, attached as a raw block device with disk serial c8s-vol-<name>. create needs
mkfs.erofs and veritysetup on PATH, or mkfs.ext4 with --mutable. The operator provisioning process generates the key outside the TEE and writes escrow before
publishing it to CDS memory. A key path is create-only: CDS
refuses a --path that already holds a value. See
Volumes.
| Subcommand | Arguments | Purpose |
|---|---|---|
create | — | build an encrypted volume image and store its key |
attach | <name> | present a volume image on this node as a disk with serial c8s-vol-<name>, through LIO's loopback target |
detach | <name> | remove a volume's disk from this node, leaving the image and the stored key alone |
attach and detach require a Linux node with LIO support and root; the C8s node image does
not support LIO attachment. A QEMU/KVM node receives a disk with the required serial from its
hypervisor.
Persistent flags, registered on every subcommand:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--url | string | "" | Conditional | direct CDS base URL, verified as for c8s secrets; a router front door is refused before the build. Required by create and not read under --dry-run. attach and detach inherit it and never contact CDS |
--image-policy-file | string | "" | Conditional | complete CDS image and launch-file pin, such as cds-pin.json; required for HTTPS, including reads |
--timeout | duration | 15s | No | per-request timeout |
--operator-key | string | "" | No | operator EC private key PEM file whose public key is pinned in the CDS measured configuration (env C8S_OPERATOR_KEY; the flag wins); required for the store write |
--insecure | bool | false | No | dev/test only: allow a plaintext http:// CDS URL, skipping RA-TLS attestation of CDS |
create flags:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--name | string | "" | Yes | volume name; forms the device serial c8s-vol-<name>, so a DNS-1123 label of at most 12 characters |
--source | string | "" | Conditional | directory whose contents become the volume; required for an immutable volume, an optional preload with --mutable |
--mutable | bool | false | No | build a writable ext4 volume instead of an immutable, dm-verity-protected one. A mutable volume has no integrity protection: the host can flip bits or roll it back undetected |
--size | string | "" | Conditional | mutable filesystem size, e.g. 50Gi; inferred from --source when one is given, required with --mutable and no --source, and refused without --mutable |
--out | string | "" | Yes | path the encrypted image is written to; must not exist |
--path | string | "" | Yes | secret-store path for the key, e.g. /tenant-a/volumes/weights; absolute, clean, no wildcards |
--escrow-out | string | "" | Yes | create: key blob output, mode 0600; must not exist. Keep operator escrow for restoration |
--mesh-ca | string | "" | Conditional | PEM mesh-CA bundle file path checked as for c8s secrets put --mesh-ca; required for publishing the key unless --force, --dry-run, or plaintext HTTP with --insecure applies |
--force | bool | false | No | write the volume key without checking the CDS mesh CA against --mesh-ca |
--node | string | "" | No | node holding the device; emitted as a nodeSelector on the printed annotations |
--work-dir | string | "" | No | directory for build intermediates (default: a temp dir); they are removed either way |
--dry-run | bool | false | No | build the image and write the escrow file, but do not call CDS |
--jobs | int | 0 | No | create: parallel encryption workers; 0 selects min(GOMAXPROCS, 16), a positive value selects that count, and negative values are refused; output format is unchanged |
attach and detach flags:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--image | string | "" | Yes | attach only: path on this node to the encrypted image. The device serves the file opened here, not the path |
--config-root | string | "" | No | attach, detach: configfs mount; empty selects /sys/kernel/config |
--json | bool | false | No | attach only: print the name and serial as JSON |
c8s routes
Read and write the signed route manifest (c8s.routes/v1) CDS stores at /routes. The router
routes application requests by this manifest alone, and c8s install --upstream
writes the first one for an adopted workload. A write must
carry a revision greater than the stored one, and is signed with the operator key, as for
c8s allowlist. add and rm fetch the stored manifest, verify it against
--operator-keys, change one route, and write the next revision: the later of the stored revision
plus one and the current Unix time, or --revision. They set notBefore to now, or keep a later
stored value, and print every route the new revision carries before signing. The router does not
relay /routes: point --url at CDS itself, with no path. The route digest show and digest
print is the value c8s verify --route-digest takes.
| Subcommand | Arguments | Purpose |
|---|---|---|
show | — | print the stored manifest, its revision, notBefore, maxAgeSeconds, route digest and routes |
digest | <file> | validate a manifest file and print its route digest, the hex SHA-256 of its exact bytes |
put | <file> | sign a manifest file's exact bytes and store them (operator key required) |
add | — | add or replace one route and store the next revision (operator key and --operator-keys required) |
rm | — | remove one route and store the next revision; a host left with no routes is removed (operator key and --operator-keys required) |
Persistent flags, registered on every subcommand:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--url | string | "" | Conditional | direct CDS base URL; required for show, add, rm (including their dry runs), and put without --dry-run. digest and put --dry-run are offline. HTTPS verifies CDS; HTTP requires --insecure |
--measurements | string slice | nil | No | flat launch-digest pins for the attested endpoint; cannot establish direct CDS launch-file binding. Unpinned HTTPS reads and writes are refused |
--measurements-file | string | "" | No | file of allowed launch measurements, one hex digest per line; merged with --measurements |
--image-policy-file | string | "" | Conditional | complete endpoint policy; direct CDS requires cds-pin.json with launch-file binding. Router discovery uses its independently intended node policy. Excludes --measurements and --measurements-file |
--timeout | duration | 15s | No | per-request timeout |
--operator-key | string | "" | No | operator EC private key PEM file whose public key is pinned in the CDS measured configuration (env C8S_OPERATOR_KEY; the flag wins); required for writes |
--insecure | bool | false | No | dev/test only: allow a plaintext http:// CDS URL, skipping RA-TLS attestation of CDS |
--operator-keys | string | "" | No | PEM file of trusted operator public keys; verifies the write token of every fetched route manifest. show verifies when it is given; add and rm require it |
Per-subcommand flags:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--host | string | "" | Yes | add, rm: request host, lower-case without port |
--path | string | "" | Yes | add, rm: route path, beginning with / |
--match | string | add: prefix; rm: "" | No | add, rm: exact or prefix; rm requires a choice when both exist at --path |
--address | string | "" | Yes | add only: backend host:port |
--workload | string | "" | One of two schemes | add: expected backend workload name; its leaf must carry this name and the same allowlist digest as the router's leaf, or routing fails. Select this alone or both --server-name and --ca; mixing schemes is refused |
--server-name | string | "" | Conditional | add: address-backend TLS server name; required with --ca, mutually exclusive with --workload |
--ca | string | "" | Conditional | add: trust-anchor PEM file path; required with --server-name, mutually exclusive with --workload; must not include the CDS mesh CA |
--new | bool | false | No | add only: start a new manifest when CDS stores none (the first route of a cluster) |
--max-age | duration | 24h | No | add only: maxAgeSeconds of a manifest --new starts; otherwise add keeps the stored value |
--revision | uint64 | 0 | No | add, rm: revision to write, above the stored one; default the later of the stored revision plus one and the current Unix time |
--dry-run | bool | false | No | add, rm: fetch the stored manifest and print the new one without storing it; put: validate the file and print its revision and digest without calling CDS |
-o, --output | string | text | No | show only: text, or json for the envelope CDS serves |
--raw | bool | false | No | show only: print the manifest bytes only, ready to edit and put |
After a CDS reset that lost its database, put the last manifest rather than starting a new one.
c8s cds-attest
Run the router routing, attestation and over-encryption sidecar (the c8s-verify/v1 server
side). It always runs in the router pod. It polls the operator-signed
route manifest from CDS (GET /routes) over RA-TLS, verifies it under the operator
keys, and routes by it: plain application requests nginx hands to its loopback routing
listener, and requests decrypted from the tunnel. With the attestation endpoints on (chart value
router.attest.enabled), it serves the LB's attestation at /.well-known/c8s/attest-pq (encrypted
session) and /.well-known/c8s/attest-lb (ordinary TLS); both bind the digest of the applied
manifest into their evidence. Until it applies a manifest, routed requests get 503 and handshakes
are refused with 503 routes_unavailable; once maxAgeSeconds passes without a successful poll,
handshakes are refused with routes_stale while routing goes on. A request whose host is not a
manifest host, or whose path matches no route, gets 404; a backend TLS failure gets 502. It's a
chart-managed component, not normally run by hand — see
Consumer Verification for the protocol.
Mesh identity credentials are fixed at /etc/c8s/certs/tls.crt, tls.key and ca.crt. Applications serve ordinary TCP/HTTP; the
injected endpoint holds their private mesh credentials. Workload backends must present the
expected name and the same allowlist digest as the router leaf; mismatches fail routing. The
ordinary nginx-routed HTTP body cap defaults to 1048576 bytes (oversize requests return 413).
The attest-pq tunnel instead caps the encoded sealed record at 8 MiB, including overhead,
so usable application payload is smaller. Decoded requests go directly to the backend without
that ordinary HTTP cap. Backend limits, tunnel response buffering, and aggregate/per-client
memory limits also apply.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--session-max-age | duration | 5h | No | absolute session lifetime: a session's keys retire this long after establishment, however busy it is |
--host | string | 127.0.0.1 | No | listen host (loopback: nginx proxies to it) |
-p, --port | int | 8800 | No | listen port |
--log-level | string | info | No | log level: debug, info, warn, error |
--attestation-api-url | string | "" | Yes | local attestation API used for CDS verification and production evidence, required even with a fixture; node-mounted pins require the node verifier. $(NODE_IP) reads /run/c8s/node-ip; $(HOST_IP) reads the environment |
--platform | string | "" | Conditional | TEE platform: snp, az-snp, az-tdx, or tdx; required when attestation endpoints are enabled |
--generation | string | genoa | No | AMD processor generation for the browser's bare-SNP verifier (--platform snp only, ignored otherwise): milan | genoa | turin |
--front-door-mode | string | "" | Conditional | cds or acme uses a TEE-held serving key; webpki uses a host-visible Secret and refuses attest-lb with external_tls while retaining attest-pq. Required with enabled attestation unless --front-door-mode-file supplies it |
--front-door-mode-file | string | "" | No | file holding the front-door mode, read at start; mutually exclusive with --front-door-mode |
--serving-cert-file | string | "" | No | path to the LB serving-leaf PEM (the cert nginx presents); in cds front-door mode /.well-known/c8s/attest-lb binds report_data to this exact leaf DER, re-read per request to follow get-cert rotation |
--acme-serving-cert-file | string | "" | No | serving-leaf PEM nginx presents in acme front-door mode; used instead of --serving-cert-file when --front-door-mode-file reads acme |
--expected-workload | string | "" | No | gate /readyz on the mesh identity leaf carrying a matched-workload stamp with this exact name; empty keeps /readyz unconditionally 200 |
--evidence-fixture | string | "" | No | dev only: serve recorded TEE evidence from this file instead of the attestation-api |
--session-ttl | duration | 5m | No | established-session idle TTL |
--read-header-timeout | duration | 5s | No | HTTP read-header timeout |
--tunnel-io-timeout | duration | 30s | No | how long a tunnel upload, or its sealed response download, may make no progress; must be positive |
--attest-endpoints | bool | true | No | serve attest-pq, attest-lb and the tunnel; false serves only the routing listener and probes, and requires --routing-port. The chart sets it from router.attest.enabled |
--cds-url | string | "" | Conditional | CDS HTTPS origin for route polling; required unless the node mounts its endpoint; an explicit override of a mounted endpoint is refused |
--image-policy-file | string | "" | Conditional | complete CDS image and launch-file pin; required when node-mounted pins are absent. Node-mounted pins take precedence and this flag is then ignored |
--operator-keys-file | string | "" | No | PEM public-key file path for route signatures when measured node keys are absent; otherwise measured keys take precedence. Unset, fetch keys from the verified CDS |
--routes-poll-interval | duration | 30s | No | positive interval between signed route-manifest polls |
--routing-host | string | 127.0.0.1 | No | routing listener host (loopback: nginx proxies application requests to it) |
--routing-port | int | 0 | No | routing listener port; 0 disables the listener (the tunnel still routes by the applied manifest) |
--backend-timeout | duration | 1h | No | how long a request or upgraded connection on the routing listener may go with no data moving either way; a tunnelled request, whose response is buffered whole, gets this long in total |
--reserved-paths | string slice | the router's own endpoints | No | paths the router serves itself; a request under one matches no manifest route. Default: /allowlist, /.well-known/c8s/, /.well-known/cds-cert.pem, /.well-known/mesh-ca.pem, /.well-known/acme-challenge/, /v1/discovery, /readyz, /healthz. Each entry must be a normalized path |
--tunnel-budget | quantity | "" | No | buffer budget, e.g. 32Gi; unset uses 75% of the container memory limit or 32Gi without one, floored at 152 MiB. Explicit budgets must be at least 152 MiB. Per-client allowance is min(budget, max(budget/4, 152 MiB)); excess requests get 503 tunnel_busy |
--publish-routes | bool | false | No | serve the applied signed route manifest at /.well-known/c8s/routes, for clients that verify it under pinned operator keys; off, the path answers 404. Requires --attest-endpoints=true. The chart sets it from router.routePublication.enabled |
--measured-policies-file | string | "" | No | node TDX policy journal included in attestation bundles; empty omits the list; requires --attest-endpoints=true |
c8s acme
Run the router's in-guest ACME sidecar (acme front-door mode). It runs beside nginx in the router
pod and keeps the full chain at /etc/c8s-acme-tls/cert.pem, with private and account keys
under /etc/c8s-acme-key, issued over ACME HTTP-01: nginx's :80 server proxies /.well-known/acme-challenge/
to the loopback challenge listener. Configured domains whose challenge paths do not yet reach this
router are added once they do, without blocking the others. The certificate renews at 2/3 of its
lifetime; nginx reads renewed credentials on subsequent handshakes, while open connections
retain their negotiated leaf. On a node image the inputs come
from the signed launch file through the *-file flags; an empty domains file means no ACME, and
nginx serves the mesh leaf from --standby-cert-dir instead. A chart-managed component, not run by
hand.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--domains | string slice | nil | Conditional | certificate FQDNs; repeatable/comma-separated; mutually exclusive with --domains-file. One domain source is required |
--domains-file | string | "" | Conditional | file of whitespace-separated domains, read at start; mutually exclusive with --domains. An empty file requires --standby-cert-dir |
--acme-email | string | "" | No | contact email registered with the ACME account |
--acme-email-file | string | "" | No | file holding --acme-email; an empty file keeps the flag value |
--acme-directory-url | string | https://acme-v02.api.letsencrypt.org/directory | No | ACME directory URL; point it at a staging directory when testing |
--acme-directory-url-file | string | "" | No | file holding --acme-directory-url; an empty file keeps the flag value |
--standby-cert-dir | string | "" | Conditional | with an empty --domains-file, do no ACME: report ready once this directory holds the tls.crt and tls.key nginx serves instead |
--challenge-port | int | 8402 | No | loopback port answering ACME HTTP-01 challenges |
--http-port | int | 8080 | No | loopback port of nginx's :80 server, probed round-trip before each order |
--ready-port | int | 0 | No | port serving GET /healthz, and GET /readyz, 200 once cert.pem and key.pem exist (0 disables it) |
--log-level | string | info | No | log level: debug, info, warn, error |
c8s allowlist-proxy
Publish the CDS allowlist API through router. Public TLS terminates at the router nginx; this
process makes the second trust hop, verifying CDS's RA-TLS serving certificate before forwarding
the request. Path, query, and Authorization header are forwarded byte-for-byte, so the operator
token's method/path/body binding survives the hop. It routes /allowlist and /allowlist/ to
CDS, answers /healthz itself, and binds loopback only. A chart-managed component, not run by
hand — see c8s allowlist for the client side.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--host | string | 127.0.0.1 | No | listen host; must be a loopback IP (nginx is the public listener) |
-p, --port | int | 8801 | No | listen port; 1–65535 |
--cds-url | string | "" | Conditional | HTTPS CDS origin; required unless the node supplies the endpoint. Node-mounted endpoint/pins must be paired and cannot be overridden by flags |
--image-policy-file | string | "" | Conditional | complete CDS image and launch-file pin; required unless the node supplies pins, which refuse a flag override |
--attestation-api-url | string | "" | No | attestation-api URL used to verify CDS evidence. A literal $(NODE_IP) is replaced by the address in /run/c8s/node-ip and $(HOST_IP) by the HOST_IP environment variable; startup fails when that source is missing or not an IP address |
--request-timeout | duration | 30s | No | timeout for one request to CDS; must be positive |
--read-header-timeout | duration | 5s | No | HTTP request-header timeout; must be positive |
c8s attest-proxy
Expose the pod-loopback attestation API through a node-local Unix socket. Socket ownership and
mode restrict reachability; the socket is not a routable attestation endpoint. With no subcommand,
serve until stopped. healthcheck performs one GET /health over its socket with a 3s timeout
and exits 0 only on success; it registers only --socket. Both accept no positional arguments.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--socket | string | "" | Yes | absolute socket path to serve (mode 0660) or, for healthcheck, probe |
--socket-gid | int | 65532 | No | server only: group owning the socket; 0 keeps the process group |
--upstream | string | http://127.0.0.1:8400 | No | server only: HTTP(S) attestation API base URL; deployment uses pod loopback |
--health-addr | string | "" | No | server only: TCP address for GET /healthz; empty disables |
--read-header-timeout | duration | 5s | No | server only: positive socket-listener header timeout |
c8s verify
Verify a deployed component's TEE attestation evidence (AMD SEV-SNP or Intel TDX) against the
hardware signature chain plus a measurement, TCB, and policy check. Verification runs
in-process with attestation-go; no container runtime is needed; a bare SEV-SNP report needs outbound HTTPS to AMD KDS
(kdsintf.amd.com) to fetch its VCEK, bounded by --timeout. Takes a
[target] (a URL or host:port) or --url; one target is required unless --from-file supplies
evidence. If both target forms are given, the positional argument wins. With --from-file,
evidence is read from the file even if a target is supplied. c8s cds verify is the
same command with CDS presets (--kind cds, default port 8443); --mode stays auto, which
resolves to armtls-cert for a CDS target. See the
verification guide for the end-to-end workflow.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--url | string | "" | Conditional | target URL or host:port; required unless a positional target or --from-file is supplied. A positional target overrides this flag |
--kind | string | auto | No | component being verified: cds, lb, workload, or auto |
--mode | string | auto | No | evidence mode: auto, armtls-cert, discovery, attest-pq, or attest-lb; any other value is a usage error. auto resolves to armtls-cert for --kind cds, discovery for --kind lb, and otherwise tries discovery, then the serving cert |
--discovery-path | string | /v1/discovery | No | path of the LB discovery document (discovery mode) |
--server-name | string | "" | No | TLS SNI server name (for port-forward / routed domains) |
--timeout | duration | 15s | No | per-attempt timeout (evidence fetch and AMD KDS collateral fetch) |
--from-file | string | "" | No | verify evidence from a saved PEM certificate or attestation-response JSON instead of dialing; with --mode attest-lb, also pass --observed-serving-cert and --attestation-nonce |
--observed-serving-cert | string | "" | No | with --mode attest-lb --from-file: PEM serving leaf observed on the connection that fetched the receipt |
--attestation-nonce | string | "" | No | with --mode attest-lb --from-file: unpadded base64url nonce sent to fetch the receipt |
--measurements | string slice | nil | No | allowed SHA-384 hex launch measurement(s) (repeatable / comma-separated); empty = no pinning (UNSAFE). On TDX this pins MRTD only, so it covers the firmware alone; cannot be combined with --image-manifest or --image-policy-file |
--measurements-file | string | "" | No | file of allowed launch measurements, one hex digest per line; feeds the same allowlist as --measurements and carries the same exclusions |
--image-manifest | string | "" | No | build-artifact manifest of the expected TDX guest image (JSON object with mrtd, rtmr1, rtmr2, each 96 lowercase hex chars); pins all three registers exactly against that one manifest. Cannot be combined with --measurements, --measurements-file, --image-policy-file, or a by-hand --rtmr 1=/--rtmr 2=. TDX evidence only — a policy error against SEV-SNP evidence |
--expected-rtmr3 | string | "" | No | DEPRECATED — use --rtmr 3=<sha384-hex>, which is the identical pin under identical rules. Retained so existing invocations keep working |
--operator-pkey | string | "" | No | operator public-key PEM file path; seed RTMR[3] replay from its exact bytes and replay measured policy history supplied by evidence. Requires --image-manifest; excludes other RTMR[3] pins; TDX only |
--rtmr | string slice | nil | No | expected TDX runtime measurement register(s) as <index>=<sha384-hex> (repeatable / comma-separated). Index 0 is rejected, 1 and 2 cannot be combined with --image-manifest, and 3 requires it. TDX evidence only — a policy error against SEV-SNP evidence |
--image-policy-file | string | "" | Conditional | complete target identity policy; required for --kind cds, using launch-file-bound cds-pin.json. Node/router targets use their independently approved policy. Excludes --measurements, --measurements-file, and --image-manifest |
--served-policy-file | string | "" | No | explicitly require the CDS /measurements set to equal this independently intended current node policy; use peers.json only while it matches the intended set. HA's initial set comes from Genesis, its signed initialization decree; c8s cds ha decree genesis --nodes can override that set, and later node-images decrees can change it. Endpoint pinning alone does not request this check |
--operator-keys | string | "" | No | PEM bundle file path of expected operator public keys. --kind cds: verification fails unless the key set the attested target serves at /operator-keys matches it. --mode attest-pq/attest-lb: the route manifest the router publishes at /.well-known/c8s/routes must verify under these keys, and its digest must be the one the evidence binds; refused with --kind cds in those modes |
--route-digest | string slice | nil | No | expected route digest(s) (repeatable / comma-separated), the hex SHA-256 of the route manifest bytes as c8s routes show prints it. attest-pq and attest-lb evidence binds one, so evidence binding any other digest fails. These modes require this or --operator-keys, not both; a saved attest-pq bundle or attest-lb receipt (--from-file) requires this |
--routes-floor | string | "" | No | with --operator-keys in attest-pq/attest-lb: <revision>:<route digest hex> last accepted; a published manifest with a lower revision, or the same revision and another digest, fails (rollback) |
--sandbox-id | string | "" | No | expected CRI pod sandbox ID on the target's leaf; requires --mesh-ca. Refused with --kind cds, including default c8s cds verify |
--workload | string | "" | No | expected matched-workload name on the target's leaf; requires --mesh-ca, since CDS's signature on the leaf is what vouches for the stamp. Refused with --kind cds, including default c8s cds verify |
--allowlist | string | "" | No | file holding the exact canonical allowlist bytes as served by GET /allowlist; the leaf's stamped policy digest must equal SHA-256 of those bytes and the stamped name must resolve in the document. Requires --mesh-ca. Refused with --kind cds, including default c8s cds verify |
--mesh-ca | string | "" | No | PEM bundle file path for the CDS mesh CA; when set, the target's leaf must chain to it, which is what authenticates the reported sandbox ID. In attest-pq and attest-lb mode it also upgrades a responder-chosen chain anchor from a partial verdict to a verified one. c8s cds ca reads it. Refused with --kind cds, including default c8s cds verify |
--init-data | string | "" | No | expected init-data digest: SHA-256 hex of the init-data document the target guest must carry; verification fails unless the evidence commits exactly this digest |
--allow-debug | bool | false | No | accept debug-enabled guests |
--min-tcb-bootloader | uint | 0 | No | minimum bootloader TCB component (0–255); SEV-SNP evidence only, a policy error against TDX evidence |
--min-tcb-tee | uint | 0 | No | minimum TEE TCB component (0–255); SEV-SNP evidence only, a policy error against TDX evidence |
--min-tcb-snp | uint | 0 | No | minimum SNP firmware TCB component (0–255); SEV-SNP evidence only, a policy error against TDX evidence |
--min-tcb-microcode | uint | 0 | No | minimum microcode TCB component (0–255); SEV-SNP evidence only, a policy error against TDX evidence |
--expected-report-data | string | "" | No | hex REPORTDATA / TPM-nonce anchor override for bare evidence files (1–64 bytes, exactly as bound by the producer); refused in --mode attest-pq and attest-lb |
-o, --output | string | text | No | output format: text or json |
--show-evidence | bool | false | No | print the raw report fields |
--pin-policy | string slice | nil | No | accepted sha256:<hex> policy digests; every replayed digest and the leaf's issuing policy must be accepted. Requires --operator-pkey or an image policy with approver_key; excludes --trust-operator; proving the leaf stamp requires --mesh-ca |
--trust-operator | bool | false | No | accept history and issuing policies only after fetching, re-hashing, and checking their signed write tokens against --trust-operator-keys; excludes --pin-policy, requires an approver-key replay anchor; seed/noncanonical policies may be unsigned |
--trust-operator-keys | string | "" | Conditional | PEM public-key bundle file path for policy signatures; required with --trust-operator and refused without it. Separate from --operator-keys route/CDS checks |
--fetch-allowlists | string | "" | No | save digest-checked replayed policy documents as <hex>.json in this directory; failed fetch/write fails verification. Requires --operator-pkey or an image policy with approver_key |
Exit codes are a CI contract: 0 verified · 1 usage · 2 verification/policy failed · 3
evidence unavailable (unreachable / unparseable) · 4 partially verified — the evidence verified,
but a property it presents is not proven (a serving key the evidence does not attest, no observable
handshake, a responder-chosen chain anchor, or unjudged policy history). Policy measurement is
optional and TDX-only; the stock baked image disables it, so its policy-history verification is
not guaranteed. --pin-policy and --trust-operator fail when replayed history is absent.
Certificate, discovery, or offline history alone is at best partial even after policy checks;
fresh nonce-bound attest-pq or attest-lb evidence is needed to establish current history.
History objects have bounded retention.
c8s measurements
Work with measurements config files. A measurements config pins the VM images a cluster
accepts; each entry is one image, matched as a whole — a launch digest together with the
runtime registers measured from the same build. One file describes one platform: tee is
either sev-snp or tdx; one policy document cannot mix the two platforms.
derive reads the manifest.json of each built confidential-os-builder image and writes the
config. An SEV-SNP image contributes one entry per vCPU variant, named <image>-smp<N>, because
the launch digest covers one VMSA page per vCPU — so even a single-image SNP cluster needs a
multi-entry config. A TDX image contributes one entry: its MRTD with RTMR[1] and RTMR[2].
RTMR[0] is never pinned, since it varies with the vCPU and memory shape.
derive takes one or more <image-dir|manifest.json> arguments. lint <measurements.json> checks a
config for problems and takes no flags.
derive flags:
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--tee | string | "" | No | platform to derive for, sev-snp or tdx; required when an image is built for both |
--out | string | "" | No | write the config to this path instead of stdout |
c8s get-cert
Obtain a CDS-signed certificate over RA-TLS using a fresh ECDSA P-256 private key per issuance.
The chain, key and CA form one validated generation on the private credential volume. Resolve
current once and read its directory for a coherent generation; stable file paths are replaced
individually. All three output paths are required and must share a directory. Workload claims
are enabled by default. Select exactly one of --san, --san-file, or --no-san.
Renewal follows CA certificate renewal under the same key. A replacement CA key requires pod recreation because each pod binds its issuer key for its lifetime. Private credentials are for platform consumers; ordinary applications use their injected mesh endpoint.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--ca-watch-interval | duration | 1m | No | in renewal mode, poll CDS for same-key CA certificate or policy changes and renew; otherwise defaults off. 0 disables, positive values must be at least 1s. Replacement CA keys require pod recreation |
--cds-url | string | "" | Conditional | HTTPS CDS base URL; required unless the node mounts its endpoint. An explicit URL is refused when the endpoint is mounted |
--image-policy-file | string | "" | Conditional | complete CDS image and launch-file pin; required unless the node supplies pins; excludes --image-policy-json and node-mounted pin overrides |
--image-policy-json | string | "" | No | inline complete JSON image policy, in the same format as --image-policy-file; only one of the two may be set |
--attestation-api-url | string | "" | Yes | URL of the node-local attestation-api (e.g. http://localhost:8400, or unix:// plus the on-node socket path the chart wires). A literal $(NODE_IP) is replaced by the address in /run/c8s/node-ip and $(HOST_IP) by the HOST_IP environment variable; startup fails when that source is missing or not an IP address |
--san | string | "" | One of three | certificate IP or RFC 1123 DNS SAN; wildcards refused; select this, --san-file, or --no-san |
--san-file | string | "" | One of three | file holding the certificate SAN; mutually exclusive with --san and --no-san |
-v, --verbose | bool | false | No | enable debug logging |
--renew-interval | duration | 0 | No | re-obtain the certificate at this interval (0 = run once and exit) |
--renew-jitter-percent | int | 20 | No | shorten each renewal delay by a random amount up to this percentage so certificates issued together do not renew in lockstep; 0 disables jitter (valid range: 0–99) |
--unnamed-renew-interval | duration | 30s | No | with workload claims and --renew-interval, renew while the installed leaf carries no matched-workload stamp, at most this far apart (plus jitter), starting at 2s and doubling; settles to --renew-interval once named. Poll timing never changes the match decision. 0 disables the fast poll |
--expected-workload | string | "" | No | allowlist entry this pod should be stamped with; requires workload claims and --renew-interval. A leaf stamped with any other name stays on the --unnamed-renew-interval fast poll instead of settling. Issuance never fails on a mismatch (empty = any stamp settles) |
--initial-retry-timeout | duration | 2m | No | retry the first certificate request in-process for up to this long before failing, so a transient CDS/mesh outage during a roll does not crash the init container into kubelet backoff (0 = try once) |
--initial-retry-interval | duration | 2s | No | initial request retry delay; CDS/inventory 403 and CDS 429 back off with jitter, doubling up to 5m |
--continue-on-initial-error | bool | false | No | in renewal mode, keep running when the first certificate request fails, retrying on a capped backoff until a certificate is issued; requires --renew-interval |
--discovery-out | string | "" | No | path to write JSON discovery metadata for the issued certificate and attestation evidence |
--discovery-cds-cert-url | string | "" | No | public URL path where the CDS certificate PEM is served |
--discovery-mesh-ca-url | string | "" | No | public URL path where the mesh CA PEM is served |
--discovery-public-tls-mode | string | cds | No | public TLS mode to report in discovery metadata: cds, webpki, or acme |
--discovery-public-tls-mode-file | string | "" | No | file holding the public TLS mode to report, read at start; replaces --discovery-public-tls-mode |
--discovery-public-tls-hostnames-file | string | "" | No | file holding launch-selected public hostnames; discovery reports the first one without changing the certificate SAN |
--workload-claims-timeout | duration | 5s | No | timeout for the admission inventory request |
--cert-path | string | "" | Yes | published certificate chain PEM; same directory as the other credential paths |
--key-path | string | "" | Yes | published private key PEM, mode 0600 (0640 in a shared setgid directory); memory-backed filesystem required; same directory as the other credential paths |
--ca-path | string | "" | Yes | published mesh CA PEM; same directory as the other credential paths |
--no-san | bool | false | One of three | request no SAN for an unnamed pod; CDS derives the subject from verified workload identity |
--no-workload-claims | bool | false | No | disable the default sandbox-token assertion for a node without an inventory; the leaf then has no workload instance binding. Injected workloads retain claims |
--measured-policies-file | string | /run/c8s/workload-claims/measured-policies | No | mounted node TDX policy journal listed in CSR and attestation requests; empty lists none |
c8s get-secret
Fetch the secrets this pod is granted and write each one to a file under --out-dir. Injected by
the webhook as a native sidecar. It authenticates to CDS with the pod's CDS-issued certificate and
a sandbox token redeemed from the node's admission inventory at a compiled-in endpoint, and CDS
releases only once every main container in the sandbox is running — earlier attempts are refused
and retried, so the files appear shortly after the workload starts and a consumer must wait for
them. A path the store does not hold yet is created with a CDS-generated value, which is lost with singleton restart or total replica memory
loss. Publication replaces each file atomically, not the whole set. A 404 can mean a denied read or
missing value; secrets explain diagnoses release, and secrets put or escrow restore supplies
operator values. After writing, the process idles until the pod is torn down. See
Secrets.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--cds-url | string | "" | Conditional | HTTPS CDS base URL; required unless the node mounts its endpoint. An explicit URL is refused when the endpoint is mounted |
--attestation-api-url | string | "" | Yes | resolved local HTTP(S) attestation API URL or unix:// plus an absolute socket path, used to verify CDS; the CLI does not expand $(NODE_IP) or $(HOST_IP) |
--image-policy-file | string | "" | Conditional | complete CDS image and launch-file pin; required unless the node supplies pins; excludes --image-policy-json and node-mounted pin overrides |
--image-policy-json | string | "" | No | inline complete JSON image policy, in the same format as --image-policy-file; only one of the two may be set |
--cert | string | /run/c8s/certs/tls.crt | No | the pod's CDS-issued certificate, presented to CDS |
--key | string | /run/c8s/certs/tls.key | No | private key for --cert |
--secret | string slice | nil | Yes | NAME=/store/path to fetch; NAME is the filename written under --out-dir (repeatable). At least one is required, each NAME must be distinct and must not be a path |
--out-dir | string | /run/c8s/secrets | No | directory the secret files are written to; must be memory-backed |
--file-mode | string | 0640 | No | octal mode for the written files |
--attempts | int | 0 | No | maximum attempts; 0 retries without limit, negative values refused; release waits for all main containers to run |
--retry-interval | duration | 5s | No | initial retry delay, doubles to 30s; an explicitly larger interval stays constant; must be positive |
--request-timeout | duration | 10s | No | per-request timeout against CDS; must be positive |
--inventory-timeout | duration | 5s | No | timeout for redeeming a sandbox token from the node's admission inventory; must be positive |
c8s get-volume
Fetch the key for each encrypted volume this pod is granted and hand it to
c8s volumed, which opens the device and mounts it into this pod — read-only for
an immutable volume, read-write for a mutable one. Injected by the webhook as a native sidecar,
with the same authentication, release rule, and retry behavior as
c8s get-secret: the volume appears shortly after the workload starts. The key
must already be in the store, put there by c8s volume create; nothing here creates
one. The node agent's socket directory is compiled in (/run/c8s/workload-claims), so a pod
cannot point the key handoff elsewhere. Linux only — the subcommand is registered only in Linux builds of the CLI. See
Volumes.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--cds-url | string | "" | Conditional | HTTPS CDS base URL; required unless the node mounts its endpoint. An explicit URL is refused when the endpoint is mounted |
--attestation-api-url | string | "" | Yes | resolved local HTTP(S) attestation API URL or unix:// plus an absolute socket path, used to verify CDS; the CLI does not expand $(NODE_IP) or $(HOST_IP) |
--image-policy-file | string | "" | Conditional | complete CDS image and launch-file pin; required unless the node supplies pins; excludes --image-policy-json and node-mounted pin overrides |
--image-policy-json | string | "" | No | inline complete JSON image policy, in the same format as --image-policy-file; only one of the two may be set |
--cert | string | /run/c8s/certs/tls.crt | No | the pod's CDS-issued certificate, presented to CDS |
--key | string | /run/c8s/certs/tls.key | No | private key for --cert |
--volume | string slice | nil | Yes | NAME=/store/path to open; NAME selects the device by serial c8s-vol-<name> and names the mounted volume (repeatable). At least one is required and each NAME must be distinct |
--attempts | int | 0 | No | maximum attempts; 0 retries without limit, negative values refused; release waits for all main containers to run |
--retry-interval | duration | 5s | No | initial retry delay, doubles to 30s; an explicitly larger interval stays constant; must be positive |
--request-timeout | duration | 10s | No | per-request timeout against CDS and the node agent; must be positive |
--inventory-timeout | duration | 5s | No | timeout for redeeming a sandbox token from the node's admission inventory; must be positive |
c8s volumed
Agent that opens encrypted volumes for the pods it serves. An injected
c8s get-volume sidecar hands it a key over volumed.sock, created inside the
admission inventory's socket directory; the daemon resolves the calling pod from kernel peer
credentials, opens dm-crypt, stacks dm-verity for an immutable volume, and mounts the result into
that pod and no other — read-only erofs when verified, read-write ext4 when mutable. The mount
target is never taken from what the caller reports about itself. Teardown is driven by the pod's
cgroup slice disappearing. CDS checks release grants; volumed checks the key blob and target.
Writer exclusion is node-local. Verity authenticates immutable content, not freshness; mutable
dm-crypt provides neither integrity nor rollback protection.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--socket-dir | string | "" | Yes | host directory holding the admission inventory's socket, where this daemon creates volumed.sock. No default: the in-pod path a sidecar sees is not where the daemon serves |
--kubelet-root | string | /var/lib/kubelet | No | kubelet's root directory, holding per-pod volume directories |
--cgroup-root | string | /sys/fs/cgroup | No | cgroup mount, where a pod's slice going away is what triggers teardown |
--reap-interval | duration | 15s | No | how often to tear down volumes whose pod has gone; must be positive |
--max-mounts | int | 64 | No | maximum volumes open on this node at once; must be positive |
c8s cred-release
Issue a short-lived kube client certificate to a caller that proves possession of the operator key
whose public half was bound into the CVM's launch identity. The same key may instead request a
log-reader certificate (pods and their logs, nothing else) to hand on to someone who should not
hold the operator role. Authorization is the cluster's RBAC on the issued group: the C8s node image
bakes ClusterRoleBindings for the default --cert-org and --log-cert-org; elsewhere, create
them or pass groups the cluster already binds. It serves POST /release-credential
over RA-TLS — the endpoint c8s get-kubeconfig consumes — signing the
caller's CSR with the cluster's client CA and returning a kubeconfig anchored to the serving CA.
Both TEE platforms are served, and only the binding differs: on TDX the staged operator public key
seeds RTMR[3] as SHA-384(0x00*48 ‖ SHA-384(pubkey)), followed by the measured-policy
journal replay, checked against the guest's own tdx_guest sysfs; on SEV-SNP it must equal the HOSTDATA the launcher
committed at launch, SHA-256(pubkey), taken from a self-report the local attestation-api
verified. Startup fails closed on a mismatch, and --platform is required because RA-TLS is
mandatory here. Baked as a systemd unit in the C8s node image; not run by hand. The CA defaults are
the RKE2 paths — on kubeadm, set all three to /etc/kubernetes/pki/ca.crt and ca.key.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--listen | string | :8443 | No | HTTPS (RA-TLS) bind address |
--attestation-api-url | string | http://127.0.0.1:8400 | No | local attestation-api base URL: the source of the RA-TLS serving cert's quote and, on SEV-SNP, of the verified HOSTDATA self-report |
--platform | string | "" | Yes | TEE platform: tdx or snp; selects the operator-key binding checked at startup |
--client-ca-cert | string | /var/lib/rancher/rke2/server/tls/client-ca.crt | No | cluster client-CA cert that signs kube client certs |
--client-ca-key | string | /var/lib/rancher/rke2/server/tls/client-ca.key | No | cluster client-CA key |
--server-ca-cert | string | /var/lib/rancher/rke2/server/tls/server-ca.crt | No | CA that signs the apiserver serving cert; embedded in the released kubeconfig |
--cert-ttl | duration | 168h | No | lifetime of issued operator certs |
--cert-org | string | c8s:node-operators | No | Kubernetes group (cert Subject O) for the issued operator cert; the node image binds the default to the baked, bounded c8s-node-operator ClusterRole |
--cert-cn | string | operator | No | Kubernetes user (cert Subject CN) for the issued cert |
--log-cert-ttl | duration | 168h | No | lifetime of issued log-reader certs |
--log-cert-org | string | c8s:log-readers | No | Kubernetes group (cert Subject O) for log-reader certs; the node image binds the default to the baked c8s-log-reader ClusterRole |
--log-cert-cn | string | log-reader | No | Kubernetes user (cert Subject CN) for log-reader certs |
--measured-policies-file | string | /run/nri-image-policy/measured-policies | No | TDX allowlist policy journal replayed after the operator-key seed during the binding check; empty replays none |
c8s get-kubeconfig
Obtain an operator or log-reader kubeconfig from a measured C8s CVM. The --image-manifest shape selects the
platform and the gate pins every value it carries: on TDX the image tuple (MRTD, RTMR[1], RTMR[2])
plus RTMR[3] replay from the operator-key seed, measured-policy history and any explicitly
expected --workload-image digest extensions. Fresh response runtime extensions feed replay; on SEV-SNP the reported launch digest must be one of the manifest's per-vCPU-count
snp_variants digests and HOSTDATA must equal SHA-256 of the operator public key. SEV-SNP has no
runtime-extend register, so --workload-image is rejected there. A locally generated CSR is then
exchanged for a short-lived kube client certificate over the node's
cred-release endpoint. New --out files are created with mode 0600; existing files are overwritten with
their permissions preserved. Verification runs in-process with attestation-go, including the RA-TLS check on the
cred-release dial; fresh attestation is operator-authenticated over the cred-release endpoint,
so the guest's raw attester can stay on loopback. Requires --operator-key, --image-manifest,
and --out, plus either --node, --vmi, or both --release-url and --apiserver-url.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--node | string | "" | No | guest host/IP that fills --release-url and --apiserver-url with the standard ports (8443/6443); each explicit URL flag overrides it. Mutually exclusive with --vmi |
--vmi | string | "" | No | guest as a KubeVirt VMI [namespace/]name; its address is resolved through the current kubeconfig and used as --node, with the namespace defaulting to the kubeconfig context's. Mutually exclusive with --node |
--attest-url | string | "" | No | optional legacy attestation-api /attest URL; by default attestation runs operator-authenticated over --release-url |
--release-url | string | "" | No | cred-release base URL |
--apiserver-url | string | "" | No | apiserver URL written into the kubeconfig |
--operator-key | string | "" | Yes | operator ECDSA private-key PEM file path; its public half is the one bound into the node's launch identity (TDX RTMR[3], SEV-SNP HOSTDATA), and it signs the credential-release request |
--image-manifest | string | "" | Yes | provenanced build-artifact manifest of the expected guest image — TDX: mrtd/rtmr1/rtmr2; SEV-SNP: snp_variants. Its shape selects the platform, and every value it carries is pinned |
--workload-image | string array | nil | No | digest-pinned image ref (sha256:<hex> or name@sha256:<hex>; tags rejected) the node's measurer is expected to have extended into RTMR[3]; repeatable, in first-extend order, each image given once. Omit for a node that runs no measured workloads. TDX only |
--context | string | c8s | No | kubeconfig cluster/context/user name |
--tls-server-name | string | c8s-cvm | No | kubeconfig tls-server-name: pins apiserver certificate verification to this SAN (baked into the node image's tls-san) instead of the dialed IP. Empty omits the field |
--out | string | "" | Yes | kubeconfig output path; new files are created 0600, existing files are overwritten with their permissions preserved |
--role | string | operator | No | identity the released cert carries: operator (tenant workloads cluster-wide, bounded so it cannot reach the node's guards) or log-reader (pods and their logs only) |
--timeout | duration | 30s | No | per-step network timeout |
--release-wait | duration | 2m | No | how long to retry refused connections to cred-release during attestation and credential release; 0 fails on the first refused dial |
c8s keys
Manage the operator EC keypair (P-256). The private key signs allowlist, secret, volume and route
writes, and authenticates c8s get-kubeconfig; CDS pins its operator public keys in its measured launch configuration. Helm operator keys
configure router verification. Changing CDS authority requires a new measured configuration
and pin; HA rotate-key changes the mesh CA key only.
| Subcommand | Arguments | Purpose |
|---|---|---|
new | — | generate an operator keypair; neither file is ever overwritten, and the private key is written 0600 |
sign-launch | <launch.yaml> | sign a launch configuration, writing the detached signature to <launch.yaml>.sig beside it |
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--out | string | "" | Yes | new only: private key output path |
--pub-out | string | "" | Yes | new only: public key output path |
--key | string | "" | Yes | sign-launch only: operator private-key PEM file path (c8s keys new --out) |
--force | bool | false | No | sign-launch only: replace an existing signature, after editing the document |
c8s launch-config
Create, extend and stage a measured node's signed c8s-launch/v2 configuration. c8s node launch-config
is the same command. Node documents reject workloads; the generator uses --tenant-workloads
for CDS policy. Signed node fields include registry mirrors, optional nodePassword, and router
body limits. See the node CVM launch contract.
| Subcommand | Purpose |
|---|---|
new | create a cluster's launch bundle: launch keys, fresh RKE2 join tokens, a signed launch.yaml per node, and the client policy that pins the server |
add-agent | derive an agent launch.yaml from the bundle's server document, sign it with the bundle's agent key, and write <bundle>/<name>; nothing the server booted with changes |
stage | on the guest: verify the signed launch configuration and stage the selected node role |
new writes:
| Path | Content |
|---|---|
<out>/server.key | generated server/operator key; omitted when --operator-key supplies an existing key |
<out>/cds-config.yaml | measured configuration the external CDS boots |
<out>/cds-pin.json | CDS image and exact configuration pin, independent of server/peer node policies |
<out>/agent.key | agent launch key; add-agent reuses it |
<out>/server.json | image policy pinning the server node; distinct from the CDS identity pin |
<out>/peers.json | singleton node policy and default HA Genesis node set; use the independently intended current policy for c8s cds verify --served-policy-file |
<out>/server/ | pubkey, launch.yaml, launch.yaml.sig: the server's opkeydata |
<out>/<agent>/ | the same three files for each --agent |
Attach each node directory as its opkeydata disk: an ISO with volume label opkeydata, or a
KubeVirt Secret volume with volumeLabel: opkeydata. On SEV-SNP, also set HOST_DATA to the SHA-256
of that node's pubkey file.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--out | string | "" | Yes | new only: bundle directory to create; must not exist |
--cluster-id | string | "" | Yes | new only: cluster name, an RFC 1123 label |
--image-manifest | string | "" | Yes | new only: manifest.json published with the exact node image to boot |
--vcpus | int | 0 | Conditional | new: SNP node vCPU count; required if the manifest contains multiple SNP variants, otherwise selects its sole variant; refused for TDX |
--server-name | string | "" | No | new only: server node name; empty selects <cluster-id>-server |
--server-address | string | "" | Conditional | new: server IPv4 address every node can reach; required with agents, otherwise the server autodetects it. add-agent: defaults to the server document's, which must then be explicit |
--agent | string array | nil | No | new only: agent node name; repeatable |
--tls-san | string | "" | No | new only: DNS name for the front-door certificate; empty selects c8s.local |
--router-hostname | string array | nil | No | new only: public DNS name the router serves with an ACME certificate; repeatable |
--acme-email | string | "" | No | new only: contact email for the router's ACME account; requires --router-hostname |
--acme-directory-url | string | "" | No | new only: ACME directory URL, e.g. a staging directory for tests; requires --router-hostname (default Let's Encrypt) |
--bundle | string | "" | Yes | add-agent only: bundle directory created by launch-config new |
--name | string | "" | Yes | add-agent only: agent node name, an RFC 1123 label |
--config | string | "" | Yes | stage only: signed launch.yaml |
--signature | string | "" | Yes | stage only: detached launch.yaml.sig |
--platform | string | "" | Yes | stage only: image TEE platform, tdx or snp |
--attestation-api-url | string | http://127.0.0.1:8400 | No | stage only: trusted node-local attestation API |
--tenant-workloads | string | "" | No | new: tenant allowlist JSON merged into the CDS component seed; component-name collisions refused |
--cds-address | string | "" | Yes | new: external CDS <host>:<port> (DNS or IPv4) |
--cds-image-manifest | string | "" | Yes | new: exact CDS guest image manifest |
--cds-platform | string | "" | Yes | new: CDS platform sev-snp or tdx, independent of node platform |
--cds-vcpus | int slice | nil | Conditional | new: SEV-SNP CDS CPU counts, at least one for SNP; repeatable |
--cds-node-cidr | string array | nil | Yes | new: node inventory subnets the CDS may dial; repeatable |
--cds-allowlist-seed | string | "" | Yes | new: release-matched allowlist-seed-<platform>.json component policy |
--cds-replica | string array | nil | Conditional | new: HA DNS names or IPv4 addresses, no port or load-balanced address; repeatable; requires --cds-voters |
--cds-voters | int | 0 | Conditional | new: odd initial HA voter seats, 1–9; requires replicas; omit both HA inputs for singleton |
--registry-mirror | string array | nil | No | new: signed registry=http://ipv4[:port] mirror mappings; one per registry, repeatable |
--router-max-request-body-bytes | int64 | 0 | No | new: signed ordinary nginx-routed HTTP body limit; 0 selects 1048576 bytes, positive values override; oversize requests return 413. Decoded tunnel requests bypass this limit; encoded sealed records have an 8 MiB cap including overhead, and backends may impose limits |
--operator-key | string | "" | No | new: existing P-256 private-key PEM file path used as server/operator key; not copied into the bundle |
--operator-pubkey | string | "" | No | new: public-key PEM file path; its bytes must equal the public key derived from --operator-key, which is required |
--pin-decree | string | "" | No | add-agent: checked signed CDS pin decree; embeds its images and the floor max(base_version, resume_version), not its resulting applied version |
--measured-policies-file | string | /run/nri-image-policy/measured-policies | No | stage: TDX policy journal for operator-key binding replay; empty replays none |
c8s node-image
Build measured node-image integration artifacts at image build time. node-services (prepare,
node-ip) is its in-guest counterpart: baked units that prepare the image's authenticated
bootstrap inputs, with no flags.
| Subcommand | Purpose |
|---|---|
render | render the bundled chart as complete Kubernetes manifests for the node image; needs Helm at build time, and none in the guest. Authenticated launch settings are mounted read-only from /run/c8s-node at boot |
check-images | read the config of every image render listed from its preloaded OCI layout, and check that the bootstrap seed admits each baked container's complete launch environment; run it before the image is sealed |
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--output-dir | string | "" | Yes | render: directory for c8s-integration.yaml, allowlist-seed.json, images.txt and router-nginx/; check-images: the directory render wrote |
--hardware-platform | string | "" | Yes | render only: CPU TEE, tdx or sev-snp |
--kube-version | string | "" | Yes | render only: Kubernetes semantic version of the baked RKE2 release |
--chart-dir | string | "" | No | render only: chart source directory; empty uses the chart bundled in this binary |
--image-digest | string | "" | Yes | render only: digest of the operator/get-cert image (sha256:...) |
--image-repository | string | "" | No | render only: operator/get-cert repository; empty uses the chart default |
--armtls-mesh-image-digest | string | "" | Yes | render only: digest of the mesh image |
--armtls-mesh-image-repository | string | "" | No | render only: mesh repository; empty uses the chart default |
--router-image-digest | string | "" | No | render only: digest of the nginx image; empty uses the pinned chart default |
--router-image-repository | string | "" | No | render only: nginx repository; empty uses the chart default |
--layouts | string | "" | Yes | check-images only: directory holding each image's OCI layout as oci-<sha256 hex> |
c8s armtls-mesh
Run one pod's injected TCP mesh endpoint. Applications serve ordinary TCP/HTTP inside the pod;
the endpoint handles TLS 1.3 using a CDS-issued chain, purpose, ALPN and sandbox identity. It
performs certificate validation, not hardware attestation on each connection, and supplies no
generic destination-workload ACL. Covered pods are injected whether or not they have a cw
name; the name expresses workload/service intent.
Credentials stay on the platform's private volume at /etc/c8s/certs in injected pods. Nonempty
credential path/volume overrides and application credential mounts are refused by injection.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--cert-path | string | "" | Yes | CDS-issued pod certificate chain PEM |
--key-path | string | "" | Yes | matching private key PEM |
--ca-path | string | "" | Yes | issuing mesh CA PEM; all credential paths share one directory |
c8s nri-image-policy
Run the nri-image-policy plugin. Uses the standard Go flag package, so the help output
prints single-dash forms and both -config and --config parse; most configuration comes
from the YAML config file rather than CLI flags.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--config | string | /etc/nri/conf.d/image-policy.yaml | No | path to config file |
--health-addr | string | :8080 | No | health check listen address |
--read-timeout | duration | 5s | No | HTTP server read timeout |
--write-timeout | duration | 10s | No | HTTP server write timeout |
c8s probe-file
Exit 0 if <path> exists and is a non-empty file — a file-existence helper for distroless
containers, where /bin/test is not available. One-shot by default (for a kubelet exec probe);
with --wait it blocks until the path passes (or --timeout elapses), so it can be the
entrypoint of an init container that gates a workload on a file another container writes — the
exec-free equivalent of a startup probe when exec probes are
denied by policy. The check rejects directories and zero-length files; a nonempty file can still be incomplete.
| Flag | Type | Default | Required | Description |
|---|---|---|---|---|
--wait | bool | false | No | block until <path> passes the check instead of probing once |
--poll-interval | duration | 1s | No | how often to re-check <path> in --wait mode |
--timeout | duration | 0 | No | give up (non-zero exit) after this long in --wait mode; 0 waits forever |