Architecture
This page explains how Cosmopilot is put together: the components it ships, the
custom resources it reconciles, and what a running node actually looks like inside
your cluster. It is meant as a conceptual map — for field-level details see the
CRDs reference, and for flags and ports see the
CLI reference and Annotations & Ports reference.
Overview
Cosmopilot is a standard Kubernetes operator.
It watches two custom resources — ChainNode and ChainNodeSet — and continuously
reconciles the cluster state to match them. Everything a node needs (Pod, storage,
configuration, services, ingress, secrets) is created and kept in sync by the operator.
┌──────────────────────────────┐
│ Cosmopilot manager │
│ (Deployment, leader-elected) │
│ │
│ • ChainNode controller │
│ • ChainNodeSet controller │
│ • Admission webhooks │
└───────────────┬───────────────┘
│ watches & reconciles
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ ChainNode │ │ ChainNode │ │ ChainNode │ ← one Pod each
│ Pod │ │ Pod │ │ Pod │
│ + PVC │ │ + PVC │ │ + PVC │
│ + Service │ │ + Service │ │ + Service │
└────────────┘ └────────────┘ └────────────┘
Components
Cosmopilot is distributed as a Helm chart that installs a single manager
Deployment. The manager, in turn, deploys several helper components alongside each
node as needed.
Manager
The operator process itself. It runs both controllers and the admission webhook server in one binary:
- ChainNode controller — reconciles a single node: its Pod, PVC(s), Services, ConfigMaps, Secrets, ingress/gateway routes, snapshots and upgrades.
- ChainNodeSet controller — reconciles a set of nodes. It owns and manages
ChainNoderesources (one per instance, per group), plus group-level Services, ingresses and shared genesis ConfigMaps. - Admission webhooks — validate
ChainNodeandChainNodeSetresources on create/update (can be disabled withwebHooksEnabled=false).
The manager exposes a metrics endpoint and health probes, and supports leader election and worker sharding. See the CLI reference for the full list of flags and environment variables.
node-utils (sidecar)
A small sidecar container (node-utils, image ghcr.io/voluzi/node-utils) that runs
in every node Pod. It exposes an internal HTTP API on port 8000 that the
operator uses to drive and observe the node, including:
- reporting data directory size (used for auto-resize decisions);
- reporting the latest block height and whether the node is state-syncing;
- detecting when a governance upgrade height has been reached;
- gracefully shutting the node down for snapshots;
- proxying the TMKMS connection when enabled.
This API is internal to the operator and is not meant to be consumed directly. See Monitoring & Observability for the node metrics you can scrape.
CosmoGuard (optional)
When API exposure with fine-grained access control and caching is enabled,
Cosmopilot injects a CosmoGuard container
(image ghcr.io/voluzi/cosmoguard) in front of the node's API endpoints. See
CosmoGuard.
Cosmoseed (optional)
For dedicated seed nodes, Cosmopilot can deploy
Cosmoseed (image ghcr.io/voluzi/cosmoseed),
a lightweight seed-only implementation. See Cosmoseed.
TMKMS & vault-token-renewer (optional)
When a validator is configured to sign with TMKMS, a TMKMS container
is added to the validator Pod. If TMKMS is backed by HashiCorp Vault with a
non-root token, a vault-token-renewer sidecar keeps the Vault token renewed.
dataexporter (job)
A CLI tool used to upload snapshot tarballs to external storage (Google Cloud Storage) and to delete them. It runs as a short-lived job during snapshot export rather than as a long-running process. See the CLI reference.
Custom resources
| Resource | Scope | Purpose |
|---|---|---|
ChainNode | Single node | Deploy and manage one Cosmos node (full node, validator, sentry, or seed). |
ChainNodeSet | Group of nodes | Deploy and manage multiple nodes organized into groups, with shared genesis, services and ingresses. |
A ChainNodeSet is essentially a higher-level resource that produces and owns
several ChainNode resources. Deleting the set cleans up the nodes it owns.
Anatomy of a node Pod
Each ChainNode is backed by a single Pod (not a StatefulSet), so the operator
has fine-grained control over its lifecycle. A typical Pod contains:
app— the chain binary itself (your node image).node-utils— the helper sidecar (always present).cosmoguard— optional API firewall/cache.tmkms— optional remote signer for validators.vault-token-renewer— optional Vault token renewer for TMKMS.
Init containers handle one-time setup (data initialization, genesis retrieval, key provisioning) before the node starts.
Alongside the Pod, the controller manages:
- one or more PVCs for the node's data (with optional auto-resize);
- a Service exposing the node's ports (see ports);
- ConfigMaps for
config.toml,app.tomland other configuration; - Secrets holding the node key and, when generated, the consensus and account keys;
- optional Ingress/Gateway routes when endpoints are exposed.
Reconciliation flow
On every change to a ChainNode (and periodically), the controller runs an
idempotent reconcile that, broadly:
- ensures the node's Services exist;
- renders configuration and computes a config hash (changes to configuration trigger a controlled Pod restart);
- ensures the Pod exists and matches the desired spec (including the config hash);
- ensures PVC updates, such as auto-resize when usage crosses the configured threshold;
- handles higher-level lifecycle: genesis retrieval/creation, data initialization, state-sync, scheduled and governance upgrades, and snapshots.
The operator records Kubernetes Events on the resources throughout this process, which are a useful first stop when troubleshooting.
Configuration & state tracking
Cosmopilot stores operational state on the resources it manages using
annotations (for example: data height, genesis-downloaded, config hash, snapshot
status, VPA scaling history). These are documented in the
Annotations & Ports reference so you can inspect what the operator
is doing at any point.
Running multiple operator instances (sharding)
A single manager can run all reconciles, but for large fleets you can run multiple
operator instances and shard the work between them using workerName. Each instance
only reconciles resources labelled for it, and workerCount controls how many
concurrent reconciles a single instance performs. See
Configuration.