Kubernetes operator
Updated
Declare a KrypticSecret and the operator keeps a native Kubernetes Secret in
sync with a Kryptic project environment. No init containers, no sidecars, and no
secrets in your manifests.
Source: github.com/dev-kryptic/Kryptic.K8s.Operator (Apache 2.0). Install from the latest GitHub Release so the image tag matches a published build. The operator decrypts inside the cluster with Kryptic.Encryption.Go: the platform serves ciphertext, the machine client secret unwraps the operator's private key, that key opens the org key, and the org key opens each envelope.
Install
kubectl apply -f https://github.com/dev-kryptic/Kryptic.K8s.Operator/releases/latest/download/crd.yaml
kubectl apply -f https://github.com/dev-kryptic/Kryptic.K8s.Operator/releases/latest/download/operator.yaml
The operator image is ghcr.io/dev-kryptic/kryptic-operator. After the first
release, that GHCR package must be public or cluster pulls will 401.
Create a machine identity in the dashboard (vault
unlocked), grant it the organization key on Approvals if it is still
waiting, then store its credentials in the namespace where your
KrypticSecrets will live. That per-namespace Secret is the recommended
production path. Settings → Encryption is only for initializing
the org key and the Emergency Kit.
kubectl create secret generic kryptic-machine-credentials \
--from-literal=clientId=kmi_xxxxxxxxxxxxxxxx \
--from-literal=clientSecret=<the one-time secret>
Self-hosted platforms add --from-literal=apiUrl=https://pipelines.kryptic.example.com.
Optional cluster machine identity
For non-production clusters you can set one machine identity on the operator
and omit spec.auth on every KrypticSecret. Create the Secret once in
kryptic-system and wire it into the operator Deployment as
KRYPTIC_CLIENT_ID, KRYPTIC_CLIENT_SECRET, and optional KRYPTIC_API_URL
(the commented block in operator.yaml).
The cluster identity is opt-in per namespace: list the namespaces that may use
it in KRYPTIC_CLUSTER_NAMESPACES (comma-separated; "*" allows all; empty or
unset allows none). A KrypticSecret without spec.auth in a namespace outside
the list fails with a status message. apiUrl values must be https; for local
development only, KRYPTIC_ALLOW_INSECURE_API_URL=true permits http.
kubectl create secret generic kryptic-machine-credentials \
--namespace kryptic-system \
--from-literal=clientId=kmi_xxxxxxxxxxxxxxxx \
--from-literal=clientSecret=<the one-time secret>
apiVersion: kryptic.dev/v1
kind: KrypticSecret
metadata:
name: backend-secrets
spec:
projectId: proj_a1b2c3d4e5f6
environment: development
secretName: backend-env
Use this only on non-production clusters. One identity for the whole cluster
means every KrypticSecret shares the same blast radius. Prefer a Secret in
each application namespace for production. If spec.auth.secretRef is set,
that Secret always wins and a missing name does not fall back to the cluster
identity.
Declare a secret
apiVersion: kryptic.dev/v1
kind: KrypticSecret
metadata:
name: backend-secrets
spec:
projectId: proj_a1b2c3d4e5f6
environment: production
secretName: backend-env
refreshInterval: 5m
auth:
secretRef:
name: kryptic-machine-credentials
Consume it like any other Secret:
envFrom:
- secretRef:
name: backend-env
Checking status
kubectl get krypticsecrets
NAME PROJECT ENVIRONMENT SECRET KEYS READY AGE
backend-secrets proj_a1b2c3d4e5f6 production backend-env 3 True 2m
When READY is False, the Ready condition carries the reason:
kubectl get krypticsecret backend-secrets -o jsonpath='{.status.conditions[0]}'
Behavior you can rely on
| Situation | What happens |
|---|---|
You delete the KrypticSecret | The Secret is garbage-collected with it (owner reference) |
| A key is deleted in Kryptic | It disappears from the Secret on the next sync |
| The platform is unreachable | The Secret keeps its last known good values. A running workload is never emptied by an outage |
| Bad credentials or unknown project | Not Ready with ConfigurationError, backing off 10 minutes instead of hammering the API |
| A Secret with that name already exists | The operator refuses to overwrite it and reports why |
Kubernetes Secrets are base64-encoded, not encrypted, unless you have enabled encryption at rest in etcd. The operator delivers your secrets into the cluster's own storage model - harden etcd accordingly.
Options
| Field | Default | Notes |
|---|---|---|
spec.projectId | required | Project public id from kryptic.json |
spec.environment | required | Environment slug |
spec.secretName | resource name | Target Kubernetes Secret |
spec.refreshInterval | 5m | Values below 30s are ignored |
spec.auth.secretRef | cluster env, if set | Per-namespace credentials. Recommended in production. |
spec.keys | all | Restrict which keys are synced |
spec.template.type | Opaque | Type of the produced Secret |
spec.template.labels / .annotations | - | Merged onto the produced Secret |
The operator watches every namespace by default. Set WATCH_NAMESPACE on the
deployment to scope it to one.