Secrets
A KMSSecret CRD names a KMS scope and a target Secret; an operator pulls the values from KMS over the native binary protocol and writes them into the k8s Secret — never plaintext in the manifest.
Abstract
A secret lives in KMS. A pod needs it as a Kubernetes Secret. A KMSSecret
custom resource is the bridge: it names a KMS scope to read and a Secret to
write, and an operator reconciles the second from the first. The manifest
carries the reference, never the value — the secret material never appears
in git, in a CR, or in any plaintext field.
This is the read side of machine identity (LP-7110): the CR's
credentialsRef points at the machine credentials that authorize the KMS
scope; KMS (LP-7336, M-Chain custody LP-7100) holds the material.
Invariant
A
KMSSecretdeclares a KMS scope and a target Secret. The operator's only job is to make the target equal the scope. The secret value transits KMS → operator → k8s Secret; it is never written in the CR.
Why (first principles)
The alternative — a Secret checked into a manifest — puts the value in
git, in CI logs, in every clone. The only safe place for the value is KMS;
the only thing safe to commit is a pointer to it. So the CR is a pointer:
group secrets.lux.network, version v1alpha1, kind KMSSecret, holding a
scope (projectSlug/envSlug/secretsPath) and a target
(managedSecretReference).
Reconciliation is convergence, not a one-shot copy. The operator re-reads the scope on an interval and rewrites the target, so a rotated secret in KMS becomes a rotated Secret in the cluster without a redeploy. The loop is the whole design — declare the desired equality once, let the operator hold it.
The transport is the native binary protocol, not HTTP. In-cluster the
operator dials KMS over luxfi/zap on port 9999 — "no HTTP, no auth tokens,
no REST round-trips" — so the value crosses one binary hop between two
in-cluster components and lands in the Secret, never serialized through a
REST surface.
Enforcement
The kind is fixed. The operator reconciles exactly
secrets.lux.network/v1alpha1, resource kmssecrets
(kms/cmd/kms-operator/main.go:43-45).
The loop is four steps, stated at the top of the command and run on
RESYNC_INTERVAL: list KMSSecret CRs, dial KMS at the CR's host over ZAP,
list then get every secret in the scope, write or update the named k8s
Secret (kms/cmd/kms-operator/main.go:1-11, :79-85). One reconcile reads
authentication.universalAuth.secretsScope
(envSlug/secretsPath), pulls each value over ZAP, and creates or updates
the managedSecretReference Secret with that data
(kms/cmd/kms-operator/main.go:117-198).
The value is never in the CR. The reconcile reads from the ZAP client and
writes data into the Secret it creates; nothing in the spec carries secret
material — only credentialsRef (the machine identity, a reference to
another Secret), the scope, and the target. The target defaults to the CR's
own namespace when secretNamespace is omitted
(kms/cmd/kms-operator/main.go:132-134); the written Secret is labelled
app.kubernetes.io/managed-by: lux-kms-operator and back-linked to its
source CR (kms/cmd/kms-operator/main.go:161-172).
Copyright
Copyright and related rights waived via CC0.