Prerequisites
- A Kubernetes cluster (v1.24 or later) with
kubectlaccess, at least three worker nodes, and a persistent-storage provisioner. - NATS with JetStream and Consul, either in the cluster (the official NATS and Consul Helm charts are the recommended route) or external and reachable from it.
- On a secure workstation:
citadel-core-cli,age, andkubectl. - The citadel-core container image from Blockops, available to your cluster’s registry.
Generate key material offline
1
Peer registry
citadel-core-0, citadel-core-1, citadel-core-2. Edit peers.json so its node names match those pod names.2
Event initiator
3
Node identities
4
Chain code
chain_code in the ConfigMap.Create the namespace, secrets and configuration
Back up the share-store password immediately. BadgerDB encrypts the store under it at creation and it cannot be changed afterwards. In production, source both passwords from External Secrets Operator or Sealed Secrets rather than
--from-literal.Register peers in Consul
--peers=/app/peers.json to the node at start-up (as the manifest below does) and each node syncs the registry into Consul itself.
Deploy the StatefulSet
Restrict network access
Nodes need to reach each other, NATS and Consul, and nothing else:Verify
Connected to badger kv store, Loaded peers from consul, [READY] Node is ready and Starting consumers. In Consul’s key-value store, mpc_peers/ lists the three nodes. Then run a key generation from citadel-api to confirm the cluster signs end to end.
The image is distroless: there is no shell inside it. To inspect a running pod, attach an ephemeral debug container: kubectl debug -n citadel-core <pod> -it --image=busybox --target=citadel-core.

