Installing it
The operator is installed once per cluster. Each environment you then want projected is a small resource you apply — see Projecting an environment.
Before you start
You need a cluster you can install into, Helm, and a kyvlt.sh service token for the environment you want. Mint the token from Integrations → Kubernetes, which also generates everything below filled in for you.
Install the operator
helm install kyvlt-connector oci://ghcr.io/bluepawlabs/charts/kyvlt-connector \ --version 0.10.0 \ --namespace kyvlt-operator --create-namespace \ --set servedNamespaces='{default}' \ --set reload.enabled=trueThe operator runs in
kyvlt-operatorand writes eachSecretbeside the resource that asked for it — in the namespaces named underservedNamespaces, and only those. The command above names the one your resources will live in; see Where it may write for how the grant works.Value Default servedNamespaces [] (the operator's own namespace) The namespaces the operator serves — one Role each, never cluster-wide. Below. kyvlt.apiBaseUrl — Checked for a supported contract at startup. Each resource still names its own. reload.enabled false Grants patchon Deployments in the served namespaces. Restarting workloads.registry.existingSecret — A pull secret you already own, for a private or mirrored registry. image.tag the chart's appVersion Pinned by the chart. There is no latest.resources 50m / 96Mi, 256Mi limit Give the operator its token
kubectl create secret generic kyvlt-token \ --from-literal=token=PASTE_TOKEN_HEREMinting from Integrations → Kubernetes hands you this command with the value already in it.
Minted for thirty days. The operator renews it from fourteen days out and writes the successor into the same
Secret, so nothing here is a calendar entry. See Service tokens.Check the operator is running
kubectl -n kyvlt-operator get podsA running pod means the operator started and understood the kyvlt.sh API it was pointed at. If it refuses to start it names the API version it expected and the one it found, rather than syncing against an API it does not understand.
Letting kyvlt.sh wire projections
With the cluster registered and
--set managed.enabled=true (plus kyvlt.apiBaseUrl), projections are
assigned in the interface instead of applied
by hand — the operator materialises and removes them itself, inside the
served namespaces and never beyond. Everything below still applies: the
fence, the token path, and hand-authored resources all keep working
unchanged.
Where it may write
servedNamespaces is the complete answer. Each entry grants the operator one
Role in exactly that namespace; there is no ClusterRole over secrets in
any configuration, and a projection always lands in its resource's own
namespace — target.namespace is refused at apply time. So "who may put a
secret here" is who may create a KyvltSync here, which is your cluster's
own RBAC and nobody else's grant.
Empty serves the operator's own namespace alone — the smallest install, and
almost never what you want. Name every namespace that will hold a
KyvltSync:
helm install ... --set servedNamespaces='{checkout,billing}'| What happens | ||
|---|---|---|
| A KyvltSync in a served namespace | Projects, beside itself | |
| A KyvltSync anywhere else | Refused, NamespaceNotServed, naming the exact helm upgrade remedy | |
| Adding a namespace | helm upgrade by whoever owns the release — a deliberate act, because it is a grant | |
| A named namespace that does not exist | The install fails with the API server's own “namespaces not found” |
Why not cluster-wide, when Doppler and External Secrets are. Versions
0.8–0.9 shipped that shape and 0.10.0 reversed it: the operator is a workload
too, and one holding cluster-wide Secret write is the single credential
that defeats every namespace boundary in the cluster if it is ever
compromised. The helm upgrade per new namespace is the cost, and it is
paid by the person the boundary protects.
A mirror, or an air-gapped registry
The image and the chart are public, so an ordinary install needs no credential. If you pull from somewhere that does, create the pull secret yourself and name it:
kubectl -n kyvlt-operator create secret docker-registry ghcr-pull \
--docker-server=ghcr.io \
--docker-username=YOUR_GITHUB_USERNAME \
--docker-password=YOUR_READ_PACKAGES_TOKENhelm install ... --set registry.existingSecret=ghcr-pullThe chart accepts no registry credentials and builds no pull secret. One it built for you would be a second credential living in your cluster — stored, rotated and eventually leaked — and naming one you already own keeps it under whatever rotation you already apply.
Verifying what you installed
Every image and chart is signed, and the signature needs no key from us:
cosign verify ghcr.io/bluepawlabs/kyvlt-connector:0.9.0 \
--certificate-identity-regexp '^https://github.com/bluepawlabs/kyvlt.sh/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.comThe same command works for ghcr.io/bluepawlabs/charts/kyvlt-connector.
There is no latest tag. A tag that moves under a running cluster is a
version nobody can name during an incident, so every install names one.