Secrets
On Project Loom, Git is the source of truth for everything except secret values. Manifests reference Secrets by name, but the values themselves never land in the repo. There are three kinds of secret on the cluster.
Three kinds
Section titled “Three kinds”flowchart TD
subgraph InGit[in Git: names + wiring only]
Ref[manifests reference<br/>secrets by name]
end
CNPG[CNPG operator] -->|auto-generates| G1[(<app>-pg-app<br/>keycloak-pg-app<br/>infisical-pg-app)]
You([you, in a browser]) -->|type a value| Infisical[Infisical]
Infisical -->|synced by the K8s operator| G2[(<app>-api-kc<br/>owned end-to-end)]
Hand[you, via kubectl] -->|out of band, bootstrap only| G3[(infisical-secrets<br/><app>-pg-backup-creds<br/>keycloak-config-cli-sa)]
Ref -.secretKeyRef.-> G1
Ref -.envFrom.-> G2
Ref -.secretKeyRef.-> G3
1. Operator-generated (CNPG)
Section titled “1. Operator-generated (CNPG)”When you declare a CNPG Cluster, the
operator mints the DB credentials and writes the <cluster>-app
Secret. You never create these and never see the password — you just reference the generated key:
- name: DB_URL valueFrom: secretKeyRef: name: <app>-pg-app # created by CNPG, not by you key: urikeycloak-pg-app and infisical-pg-app work the same way for their respective databases.
See Operators.
2. Infisical-managed (the default for app secrets)
Section titled “2. Infisical-managed (the default for app secrets)”Every app’s runtime env vars — secret or not — are managed through
Infisical, a self-hosted GUI. You edit a value in the browser;
the Infisical Secrets Operator syncs it into a Kubernetes
Secret the app already reads via envFrom. No kubectl, no redeploy for a value change — the Rollout just needs its
pods to restart to pick it up (bump the
redeploy annotation, or let the next natural rollout do it).
envFrom: - secretRef: name: <app>-api-kc # owned end-to-end by the secrets operatorThree CRs wire an app’s namespace to Infisical — InfisicalConnection (where the Infisical server is), InfisicalAuth
(a Machine Identity credential, Universal Auth), and InfisicalStaticSecret (which project/environment to sync, and
into what Secret name, creationPolicy: Owner). Full recipe: Onboard → Secrets.
Not every env var belongs in Infisical. Two exclusions, both deliberate:
- Anything an operator mints and rotates on its own (
DB_URLfrom CNPG) — a copy in Infisical would silently go stale the moment CNPG rotates it. - Anything coupled to the Kubernetes manifest itself — a port that must match
containerPortand the Service’stargetPorthas no business being editable from a GUI independent of those two YAML fields; keep it hardcoded, next to them, in one file.
3. Out-of-band (kubectl-created)
Section titled “3. Out-of-band (kubectl-created)”What’s left after Infisical is small: the credentials needed to bootstrap Infisical and Keycloak’s automation themselves — chicken-and-egg items nothing else can generate for you. Created by hand, not in Git, recreated on rebuild:
| Secret | Namespace | What it holds |
|---|---|---|
infisical-secrets | infisical | Infisical server’s own AUTH_SECRET/ENCRYPTION_KEY/DB_CONNECTION_URI/REDIS_URL |
infisical-<app>-identity | <app>-staging, <app>-prod | Machine Identity clientId/clientSecret per env |
keycloak-config-cli-sa | keycloak-staging, keycloak-prod | config-cli service-account (see Operators) |
<app>-pg-backup-creds | matching <app>-<env> / keycloak ns | Oracle Object Storage S3 keys for that Cluster |
For the backup credentials, the access key is clean hex; the secret key contains
+/= — don’t swap them, or a / ends up in the SigV4 credential and breaks request signing.
The rule: no secret values in Git
Section titled “The rule: no secret values in Git”Manifests carry names and references, never values. This keeps the private
loom repo safe to read. A new node is Provision (Git + GUI values + a handful of
bootstrap Secrets). Restore after a live cluster: Restore.