Sign inSign up

blinklabs/dingo-operator

By blinklabs

•Updated about 18 hours ago

Kubernetes operator for Dingo Cardano nodes

Image
0

2.8K

blinklabs/dingo-operator repository overview

⁠dingo-operator

A Kubernetes operator for Dingo⁠, the Go Cardano node. It manages the full lifecycle of Cardano relays and block producers — including KES/operational-certificate monitoring and rotation, Mithril bootstrap, topology (with external relays), and active/standby failover — aimed at secure, production mainnet block production for enterprise stake pool operators.

Status: early development (v1alpha1). The API may change.

⁠Features

  • One CRD, two roles. DingoNode runs as a relay or a blockProducer.
  • Env-var-driven Dingo workloads as StatefulSets with a persistent DB volume, headless + client Services, and an optional PodMonitor.
  • Mithril bootstrap via an init container running dingo mithril sync (native Go client, no external binary).
  • Topology management — auto-wire in-cluster block-producer/relay peering, plus static/external relays merged into local roots.
  • Block-producer key handling — stages KES/VRF/opcert from a Secret into a memory-backed /keys volume with mode 0600, sets the CARDANO_SHELLEY_* env, and surfaces KES period / opcert state from the node's metrics. A delivered bundle is validated (opcert signature, pool binding, KES-key binding, counter, KES period) before the operator rolls the pod onto it; a refused bundle sets KeysValid=False and Degraded=True and is not rolled out. The live pod keeps its staged keys when the Secret changes. Any later pod recreation stages the Secret's current contents, so fix a refused bundle rather than leaving it in place.
  • On-chain counter floor — with blockProducer.nodeToClient.enabled, the operator reads the pool's authoritative opcert counter from the node over node-to-client local-state-query, publishes it as status.opcert.onChainCounter, and refuses a delivered certificate numbered below it (the chain would reject blocks forged with it). It fails open: an unreachable or unsynced node falls back to the operator's own last accepted counter, and the OnChainCounterAvailable condition says which applies. Reaching the port also needs the label dingo.blinklabs.io/node-to-client=allowed on the client pod — see the NetworkPolicy note below.
  • OpCert rotation — MonitorOnly, Assisted, or full Auto issuance via a pluggable cold-signer (Bursa) that keeps cold keys out of the cluster.
  • Safe HA — SingleActive (default) or ActiveStandby with fenced, lease-based promotion so exactly one node ever forges.
  • Secure by default — non-root, dropped capabilities, least-privilege RBAC, and a default-deny NetworkPolicy for block producers. Node-to-client (port 3002) is closed unless a client opts in: label the client pod dingo.blinklabs.io/node-to-client=allowed, and its namespace too when it is not the node's own. That label is the only way in — peers named in topology.relayRefs get the node-to-node port (3001) and nothing else. Dingo also binds node-to-client to loopback until blockProducer.nodeToClient.enabled is set, so both the listener and the policy have to be opened deliberately.

⁠Installation

Install the operator via its Helm chart (published to GHCR as an OCI artifact):

helm install dingo-operator \
  oci://ghcr.io/blinklabs-io/helm-charts/charts/dingo-operator \
  --namespace dingo-operator-system --create-namespace

The chart installs the DingoNode CRD, the operator Deployment, RBAC, and (optionally) a PodMonitor. See the chart in blinklabs-io/helm-charts⁠.

⁠Quick start

Create a namespace and a relay:

kubectl create namespace cardano
kubectl apply -f config/samples/dingo_v1alpha1_dingonode_relay.yaml
kubectl get dn -n cardano

For a block producer, first create a Secret with your pool credentials in cardano-cli text-envelope form, then apply the sample:

kubectl create secret generic producer-pool-keys -n cardano \
  --from-file=vrf.skey --from-file=kes.skey --from-file=opcert.cert
kubectl apply -f config/samples/dingo_v1alpha1_dingonode_blockproducer.yaml

⁠DingoNode at a glance

apiVersion: dingo.blinklabs.io/v1alpha1
kind: DingoNode
spec:
  role: blockProducer          # relay | blockProducer
  network: mainnet
  storageMode: core
  mithril: { enabled: true }
  topology:
    relayRefs: [relay]
    externalRelays:
      - { address: relay.example.com, port: 3001, valency: 1, trustable: true }
  blockProducer:
    keys: { secretRef: producer-pool-keys }
    rotation: { mode: MonitorOnly, renewBeforePeriods: 8 }
    ha: { strategy: SingleActive, failover: Automatic }

See CLAUDE.md⁠ for the design summary, rotation/HA models, delivery roadmap, and the upstream issues that unlock full automation, and AGENTS.md⁠ for coding and review standards.

⁠Development

make manifests generate   # regenerate CRDs, RBAC, deepcopy
make build                # build the operator binary
make test                 # run tests (uses envtest)
make lint                 # golangci-lint + nilaway + modernize
make run                  # run the operator against the current kubecontext

Requires Go 1.26+. A local Cardano devnet for end-to-end testing lives in the Dingo repo⁠ under internal/test/devnet/.

⁠License

Apache-2.0. See LICENSE⁠.

Tag summary

Content type

Image

Digest

sha256:f92758c20…

Size

20.2 MB

Last updated

about 18 hours ago

docker pull blinklabs/dingo-operator:main