@@ -130,11 +130,11 @@ Generate the peer configuration file that defines all nodes in your MPC cluster:
130130mpcium-cli generate-peers -n 3
131131
132132# This creates peers.json with structure:
133- # [
134- # {"id ": "node0_<random>", "name": "node0"} ,
135- # {"id ": "node1_<random>", "name": "node1"} ,
136- # {"id ": "node2_<random>", "name": "node2"}
137- # ]
133+ # {
134+ # "node0 ": "<uuid>" ,
135+ # "node1 ": "<uuid>" ,
136+ # "node2 ": "<uuid>"
137+ # }
138138```
139139
140140** Important** : Keep this ` peers.json ` file secure. You'll need it for generating identities and registering peers.
@@ -148,16 +148,16 @@ The event initiator is authorized to trigger MPC operations (keygen, signing, re
148148mpcium-cli generate-initiator --encrypt
149149
150150# Output files:
151- # - initiator_identity. json (public key)
152- # - initiator_private .key.age (encrypted private key)
151+ # - event_initiator.identity. json (public key and metadata )
152+ # - event_initiator .key.age (encrypted private key)
153153```
154154
155155** Save the decryption password securely** - you'll need it for applications that initiate MPC operations.
156156
157157Extract the public key:
158158
159159``` bash
160- cat initiator_identity .json | jq -r ' .public_key'
160+ cat event_initiator.identity .json | jq -r ' .public_key'
161161# Example output: 6cdddd50b0e550f285c5e998cb9c9c88224680cd5922307b9c2e3c395f78dabc
162162```
163163
@@ -196,10 +196,8 @@ Each identity directory contains:
196196
197197```
198198identity/node0/
199- ├── node0_identity.json
200- ├── node0_private.key.age (encrypted private key)
201- ├── node1_identity.json
202- └── node2_identity.json
199+ ├── node0_identity.json (public key and metadata)
200+ └── node0_private.key.age (encrypted private key)
203201```
204202
205203### Step 5: Prepare Secrets
@@ -244,36 +242,34 @@ Deploy NATS and Consul if not using external services. For production, use the o
244242
245243Create secrets for each node containing identity files and passwords:
246244
247- Command to generate storng Badger password :
245+ Generate and store passwords directly as a Kubernetes secret (passwords never touch disk or shell history) :
248246
249- ` ` `
250- < /dev/urandom tr -dc 'A-Za-z0-9!@#$^&\*()-\_=+[]{}|;:,.<>?/~' | head -c 32; echo
247+ ` ` ` bash
248+ kubectl create secret generic mpcium-secrets \
249+ -n mpcium \
250+ --from-literal=mpcium-db-password.cred="$(< /dev/urandom tr -dc 'A-Za-z0-9!@#' | head -c 32)" \
251+ --from-literal=mpcium-identity-password.cred="$(< /dev/urandom tr -dc 'A-Za-z0-9!@#' | head -c 32)"
251252```
252253
253- ``` yaml
254- apiVersion : v1
255- kind : Secret
256- metadata :
257- name : mpcium-secrets-node0
258- namespace : mpcium
259- type : Opaque
260- stringData :
261- mpcium-db-password.cred : " <badger_password>"
262- mpcium-identity-password.cred : " <identity_decrypt_password>"
254+ To retrieve a password later:
255+
256+ ``` bash
257+ kubectl get secret mpcium-secrets -n mpcium \
258+ -o jsonpath=' {.data.mpcium-db-password\.cred}' | base64 -d; echo
263259```
264260
265261``` bash
266- # Create identity secret from files
267- kubectl create secret generic mpcium-identity-node0 \
262+ # Create identity secret containing all node identity files
263+ kubectl create secret generic mpcium-identity \
268264 -n mpcium \
269265 --from-file=identity/node0/node0_identity.json \
270266 --from-file=identity/node0/node0_private.key.age \
271- --from-file=identity/node0/node1_identity.json \
272- --from-file=identity/node0/node2_identity.json
267+ --from-file=identity/node1/node1_identity.json \
268+ --from-file=identity/node1/node1_private.key.age \
269+ --from-file=identity/node2/node2_identity.json \
270+ --from-file=identity/node2/node2_private.key.age
273271```
274272
275- Repeat for node1 and node2.
276-
277273Create a ConfigMap containing the shared ` peers.json ` file (required by the distroless image at runtime):
278274
279275``` bash
@@ -304,6 +300,7 @@ data:
304300 mpc_threshold: 2
305301 event_initiator_pubkey: "<your-initiator-public-key>"
306302 event_initiator_algorithm: "ed25519"
303+ chain_code: "<64-char-hex-string>" # Required: 32-byte hex for HD key derivation
307304 backup_enabled: false
308305 backup_period_seconds: 300
309306` ` `
@@ -335,52 +332,25 @@ mpcium-cli register-peers \
335332 --config ./register-config.yaml
336333` ` `
337334
338- # ## Step 6: Create Persistent Volume Claims
339-
340- Each node needs persistent storage :
341-
342- ` ` ` yaml
343- apiVersion: v1
344- kind: PersistentVolumeClaim
345- metadata:
346- name: mpcium-data-node0
347- namespace: mpcium
348- spec:
349- accessModes:
350- - ReadWriteOnce
351- resources:
352- requests:
353- storage: 5Gi
354- ` ` `
355-
356- This stores :
357-
358- - BadgerDB data
359- - Backups (`/app/data/backups`)
360-
361- Repeat for node1 and node2.
335+ # ## Step 6: Deploy mpcium StatefulSet
362336
363- # ## Step 7: Deploy mpcium Nodes
364-
365- Create a Deployment for each node :
337+ Use a StatefulSet so each pod gets a stable name (`mpcium-0`, `mpcium-1`, etc.) that maps to the peer entries in Consul. The StatefulSet also manages PersistentVolumeClaims automatically via `volumeClaimTemplates`.
366338
367339` ` ` yaml
368340apiVersion: apps/v1
369- kind: Deployment
341+ kind: StatefulSet
370342metadata:
371- name: mpcium-node0
343+ name: mpcium
372344 namespace: mpcium
373345spec:
374- replicas: 1
375- strategy:
376- type: Recreate
346+ replicas: 3
377347 selector:
378348 matchLabels:
379- app: mpcium-node0
349+ app: mpcium
380350 template:
381351 metadata:
382352 labels:
383- app: mpcium-node0
353+ app: mpcium
384354 spec:
385355 securityContext:
386356 runAsNonRoot: true
@@ -390,16 +360,22 @@ spec:
390360 containers:
391361 - name: mpcium
392362 image: fystacklabs/mpcium-prod:1.0.0
363+ env:
364+ - name: POD_NAME
365+ valueFrom:
366+ fieldRef:
367+ fieldPath: metadata.name
393368 args:
394369 - "start"
395- - "--name=node0"
396- - "--config=/config.yaml"
370+ - "--name=$(POD_NAME)"
371+ - "--config=/app/config.yaml"
372+ - "--peers=/app/peers.json"
397373 - "--password-file=/app/secrets/mpcium-db-password.cred"
398374 - "--identity-password-file=/app/secrets/mpcium-identity-password.cred"
399375 - "--decrypt-private-key"
400376 volumeMounts:
401377 - name: config
402- mountPath: /config.yaml
378+ mountPath: /app/ config.yaml
403379 subPath: config.yaml
404380 readOnly: true
405381 - name: peers
@@ -431,34 +407,32 @@ spec:
431407 name: mpcium-peers
432408 - name: identity
433409 secret:
434- secretName: mpcium-identity-node0
410+ secretName: mpcium-identity
435411 defaultMode: 0400
436412 - name: secrets
437413 secret:
438- secretName: mpcium-secrets-node0
414+ secretName: mpcium-secrets
439415 defaultMode: 0400
440- - name: data
441- persistentVolumeClaim:
442- claimName: mpcium-data-node0
443416 - name: tmp
444417 emptyDir: {}
418+ volumeClaimTemplates:
419+ - metadata:
420+ name: data
421+ spec:
422+ accessModes: ["ReadWriteOnce"]
423+ resources:
424+ requests:
425+ storage: 5Gi
445426` ` `
446427
447- Duplicate this manifest for `node1`, `node2`, etc., updating :
448-
449- - Deployment/resource names (`mpcium-nodeX`)
450- - CLI flag `--name=nodeX`
451- - Secret and PVC references (`mpcium-identity-nodeX`, `mpcium-secrets-nodeX`, `mpcium-data-nodeX`)
452- - Labels (`app : mpcium-nodeX`) to keep pods discoverable by node name
453-
454- Deploy all nodes :
428+ Deploy :
455429
456430` ` ` bash
457- kubectl apply -f node0-deployment.yaml
458- kubectl apply -f node1-deployment.yaml
459- kubectl apply -f node2-deployment.yaml
431+ kubectl apply -f mpcium-statefulset.yaml
460432` ` `
461433
434+ Pod names will be `mpcium-0`, `mpcium-1`, `mpcium-2` — make sure your `peers.json` and Consul entries use these names (e.g. `{"mpcium-0" : " <uuid>" , ...}`).
435+
462436# # Post-Deployment Verification
463437
464438# ## Step 1: Check Pod Status
@@ -565,12 +539,11 @@ healthcheck:
565539 address: "0.0.0.0:8080" # default
566540` ` `
567541
568- Sample probes once enabled :
542+ **Important**: The `/health` endpoint returns HTTP 503 when the node is not yet ready (e.g. during peer ECDH key exchange setup). Do **not** use it as a `livenessProbe` — Kubernetes would kill pods that are still initializing. Use it only for `readinessProbe`, and use a TCP socket probe for liveness :
569543
570544` ` ` yaml
571545livenessProbe:
572- httpGet:
573- path: /health
546+ tcpSocket:
574547 port: 8080
575548 initialDelaySeconds: 30
576549 periodSeconds: 10
@@ -617,11 +590,9 @@ kubectl cp mpcium/<pod-name>:/app/data/backups/backup-<timestamp>.bak ./backup.b
6175903. Perform rolling update :
618591
619592` ` ` bash
620- kubectl set image deployment /mpcium-node0 \
593+ kubectl set image statefulset /mpcium \
621594 -n mpcium \
622- mpcium=fystack/mpcium:v0.3.4
623-
624- # Repeat for other nodes
595+ mpcium=fystacklabs/mpcium-prod:v0.3.4
625596` ` `
626597
627598**Note**: For critical updates affecting consensus, consider:
@@ -747,7 +718,7 @@ rules:
747718 resources: ["pods", "pods/log"]
748719 verbs: ["get", "list", "watch"]
749720 - apiGroups: ["apps"]
750- resources: ["deployments "]
721+ resources: ["statefulsets "]
751722 verbs: ["get", "list", "watch", "update", "patch"]
752723` ` `
753724
@@ -776,8 +747,8 @@ kubectl describe pod -n mpcium <pod-name>
776747**Solutions**:
777748
778749` ` ` bash
779- # Check if BadgerDB is corrupted
780- kubectl exec -n mpcium -it <pod-name> -- ls -la /data/db/
750+ # Check if BadgerDB is corrupted (use debug container since distroless has no ls)
751+ kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium -- ls -la /proc/1/root/app /data/db/
781752
782753# Check password is correct
783754kubectl get secret mpcium-secrets-node0 -n mpcium -o json | \
@@ -816,8 +787,8 @@ kubectl get secret mpcium-identity-node0 -n mpcium -o json | \
816787# Verify Consul is running
817788kubectl get pods -n mpcium -l app=consul
818789
819- # Test connectivity from pod
820- kubectl exec -n mpcium -it <pod-name> -- \
790+ # Test connectivity from a debug container
791+ kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium -- \
821792 wget -O- http://consul:8500/v1/kv/mpc_peers/
822793
823794# Verify peers are registered
@@ -835,8 +806,8 @@ curl http://localhost:8500/v1/kv/mpc_peers/?keys
835806# Verify NATS is running
836807kubectl get pods -n mpcium -l app=nats
837808
838- # Test connectivity
839- kubectl exec -n mpcium -it <pod-name> -- \
809+ # Test connectivity from a debug container
810+ kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium -- \
840811 nc -zv nats 4222
841812
842813# Check NATS logs
@@ -863,19 +834,18 @@ kubectl get configmap mpcium-config -n mpcium -o yaml
863834
864835# ## Debugging Commands
865836
837+ The mpcium image uses distroless (no shell, no `ls`, no `env`). Use an ephemeral debug container for filesystem inspection, or call the mpcium binary directly for commands it supports :
838+
866839` ` ` bash
867- # Launch an ephemeral debug container (distroless base image has no shell)
840+ # Launch an ephemeral debug container that shares the pod's process namespace
868841kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium
869842
870- # Check environment variables
871- kubectl exec -n mpcium <pod-name> -- env | grep -i mpcium
872-
873- # Inspect data/identity mounts
874- kubectl exec -n mpcium <pod-name> -- ls -la /app
875- kubectl exec -n mpcium <pod-name> -- ls -la /app/identity
876- kubectl exec -n mpcium <pod-name> -- ls -la /app/data
843+ # Inside the debug container, inspect mounts:
844+ ls -la /proc/1/root/app
845+ ls -la /proc/1/root/app/identity
846+ ls -la /proc/1/root/app/data
877847
878- # Verify mpcium binary
848+ # Verify mpcium binary version (this works directly since it's a Go binary)
879849kubectl exec -n mpcium <pod-name> -- /app/mpcium version
880850` ` `
881851
@@ -902,34 +872,29 @@ kubectl logs -n mpcium <pod-name> > node0.log
902872
903873` ` ` bash
904874# 1. Stop all nodes
905- kubectl scale deployment mpcium-node0 --replicas=0 -n mpcium
906- kubectl scale deployment mpcium-node1 --replicas=0 -n mpcium
907- kubectl scale deployment mpcium-node2 --replicas=0 -n mpcium
875+ kubectl scale statefulset mpcium --replicas=0 -n mpcium
908876
909877# 2. Wait for shutdown
910- kubectl wait --for=delete pod -l component =mpcium -n mpcium --timeout=60s
878+ kubectl wait --for=delete pod -l app =mpcium -n mpcium --timeout=60s
911879
912880# 3. Start all nodes
913- kubectl scale deployment mpcium-node0 --replicas=1 -n mpcium
914- kubectl scale deployment mpcium-node1 --replicas=1 -n mpcium
915- kubectl scale deployment mpcium-node2 --replicas=1 -n mpcium
881+ kubectl scale statefulset mpcium --replicas=3 -n mpcium
916882` ` `
917883
918884# ### Lost Node Recovery
919885
920886` ` ` bash
921887# If a node's data is lost but identity is intact:
922888
923- # 1. Delete the PVC
924- kubectl delete pvc mpcium-data-node0 -n mpcium
925-
926- # 2. Recreate PVC (will provision new volume)
927- kubectl apply -f node0/pvc.yaml
889+ # 1. Delete the specific pod (StatefulSet will recreate it)
890+ kubectl delete pod mpcium-0 -n mpcium
928891
929- # 3. Restart deployment
930- kubectl rollout restart deployment/mpcium-node0 -n mpcium
892+ # 2. If the PVC is corrupted, delete it and the pod
893+ kubectl delete pvc data-mpcium-0 -n mpcium
894+ kubectl delete pod mpcium-0 -n mpcium
895+ # StatefulSet will recreate both the pod and PVC
931896
932- # 4 . Node will start fresh (key shares may need resharing)
897+ # 3 . Node will start fresh (key shares may need resharing)
933898` ` `
934899
935900# # Additional Resources
0 commit comments