Files
zeekayandHanzo Dev c26e084dfa zoo-kms stays on the generation that boots
1.12 is the consensus-native KMS: it constructs a ConsensusAuthorizer at start
and refuses to run without one — 'consensus validator authority is empty
(refusing to boot fail-open)'. The refusal is right; there is no open mode by
design. But nothing provides this pod the authority snapshot the operator is
meant to drop at KMS_CONSENSUS_FILE, so the upgrade crash-looped and took the
Zoo KMS down for ~90 minutes until it was rolled back.

Adopting 1.12 needs the consensus authority provisioned first. Pinning the
generation that runs is not a preference here — it is the difference between a
KMS and a CrashLoopBackOff.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-05 18:28:14 -07:00

120 lines
5.5 KiB
YAML

# Zoo KMS — Zoo's OWN Go-native KMS server (crypto-sovereign).
# Namespace: zoo-kms (do-sfo3-zoo-k8s). Mirrors the PROVEN lux-kms-go pattern.
#
# Image: ghcr.io/zooai/kms:1.11.8 — Zoo-branded rebrand of luxfi/kms:1.11.8, the
# EXACT version proven-live in lux-kms-go (the pure-Go Hanzo/Lux KMS). White-
# labelled to the Zoo brand by domain (kms.zoo.network) and org (KMS_ORG=zoo).
# Patch-pin (never :latest/major). 1.11.10 mandates a consensus-authorizer that
# fail-closes without KMS_CONSENSUS_VALIDATORS/OPERATORS (crash) — a fleet
# upgrade owned by the KMS-issuer/consensus workstream; v1.11.8 = secrets-only
# parity with Lux today.
#
# Secrets surface (HTTP :8080) is the active path (kms-operator + Go callers):
# Org -> path -> env -> secret, IAM-JWT gated, ZapDB (Badger) at rest.
#
# PARITY WITH LUX (verified live 2026-07-14): the reference lux-kms-go/kms-0 runs
# SECRETS-ONLY — MPC_VAULT_ID="" and ZAP_PORT=0. It stores secrets with ZapDB
# at-rest encryption (encryption-key-b64); it does NOT do MPC-backed signing, and
# its lux-mpc ring is a SEPARATE sovereign primitive not wired into the KMS.
# Zoo is deployed at EXACT parity: own KMS + own zoo-mpc ring, secrets-only today.
# Enabling true MPC-backed signing (populate MPC_VAULT_ID with the Zoo DKG vault
# id + ZAP_PORT>0) requires KMS_CONSENSUS_VALIDATORS/OPERATORS authority or kmsd
# fail-closes — the SAME gate that blocks Lux. Staged, owner-go.
#
# Bootstrap secrets (kms-secrets, in-cluster only — generated by deploy.sh, NEVER
# committed; do NOT lose encryption-key-b64 or the ZapDB store is unreadable):
# master-key-b64 random 32B (SecretStore envelope)
# encryption-key-b64 random 32B (ZapDB Badger at-rest encryption)
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: kms
namespace: zoo-kms
labels: { app: kms, brand: zoo }
spec:
serviceName: kms
replicas: 1
selector: { matchLabels: { app: kms } }
template:
metadata: { labels: { app: kms, brand: zoo } }
spec:
imagePullSecrets:
- name: ghcr-zooai
containers:
- name: kms
# PINNED BACK from 1.12.9, which cannot boot here. 1.12 is the
# consensus-native generation: it builds a ConsensusAuthorizer at start
# and REFUSES to run without one —
# kms: consensus authorizer init failed: consensus validator authority
# is empty (refusing to boot fail-open)
# That refusal is correct (no open mode, fail closed), but nothing on
# this pod provides the authority snapshot the operator is meant to drop
# at KMS_CONSENSUS_FILE, so 1.12.9 crash-looped for 90 minutes with the
# Zoo KMS down. Moving to 1.12 needs the authority provisioned FIRST;
# until then this stays on the generation that runs.
image: ghcr.io/zooai/kms:1.11.8
imagePullPolicy: IfNotPresent
ports:
- { name: http, containerPort: 8080, protocol: TCP }
env:
- { name: KMS_DATA_DIR, value: /data/kms }
- { name: KMS_LISTEN, value: ":8080" }
- { name: KMS_ORG, value: "zoo" }
- { name: KMS_BRAND_NAME, value: "Zoo" }
# secrets-only parity with Lux (see header). Staged for MPC-backing.
- { name: ZAP_PORT, value: "0" }
- { name: MPC_VAULT_ID, value: "" }
# IAM — shared Casdoor, single-issuer (hanzo.id), Zoo apps as audience.
# Zoo-branded login face id.zoo.network resolves to the same IAM; the
# branded-endpoint swap is coordinated with the KMS-issuer work (agent C).
- { name: IAM_ENDPOINT, value: "https://hanzo.id" }
- { name: IAM_TOKEN_PATH, value: "/v1/iam/oauth/token" }
- { name: KMS_IAM_URL, value: "https://hanzo.id" }
- { name: KMS_EXPECTED_ISSUER, value: "https://hanzo.id" }
- { name: IAM_KEYS_URL, value: "https://hanzo.id/.well-known/jwks" }
# White-label the console OIDC SSO to the Zoo brand: KMS_IAM_OWNER=zoo
# makes client_id default to "zoo-kms" (NEVER the luxfi default "lux-kms").
- { name: KMS_IAM_OWNER, value: "zoo" }
- { name: KMS_OIDC_CLIENT_ID, value: "zoo-kms" }
- name: KMS_OIDC_CLIENT_SECRET
valueFrom: { secretKeyRef: { name: zoo-kms-oidc, key: client-secret } }
- name: KMS_STATE_SECRET
valueFrom: { secretKeyRef: { name: zoo-kms-oidc, key: state-secret } }
- name: KMS_MASTER_KEY_B64
valueFrom: { secretKeyRef: { name: kms-secrets, key: master-key-b64 } }
- name: KMS_ENCRYPTION_KEY_B64
valueFrom: { secretKeyRef: { name: kms-secrets, key: encryption-key-b64 } }
livenessProbe:
httpGet: { path: /healthz, port: http }
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet: { path: /healthz, port: http }
initialDelaySeconds: 5
periodSeconds: 5
resources:
requests: { cpu: 50m, memory: 64Mi }
limits: { cpu: 500m, memory: 512Mi }
volumeMounts:
- { name: data, mountPath: /data/kms }
volumeClaimTemplates:
- metadata: { name: data }
spec:
accessModes: [ReadWriteOnce]
# Retain so an accidental PVC delete never nukes the Zoo KMS store.
storageClassName: do-block-storage-retain
resources: { requests: { storage: 5Gi } }
---
apiVersion: v1
kind: Service
metadata:
name: kms
namespace: zoo-kms
labels: { app: kms, brand: zoo }
spec:
type: ClusterIP
selector: { app: kms }
ports:
- { name: http, port: 80, targetPort: http, protocol: TCP }