Secrets
No chart contains a credential. Every one is referenced by name. This page is explains what must exist, with which keys, before the platform starts.
The secrets you create
| Secret | Namespace | Keys | Created in |
|---|---|---|---|
gen0sec-registry | Both | .dockerconfigjson | Install step 2 |
gen0sec-object-store | gen0sec-system | RUSTFS_ACCESS_KEY, RUSTFS_SECRET_KEY | Install step 2 |
gen0sec-download-proxy | gen0sec | DOWNLOAD_PROXY_API_KEY | Install step 2 |
The pull secret is needed in both namespaces. The application runs in gen0sec; the operators,
object store and cache run in gen0sec-system.
The twenty the script creates
make-db-secrets.sh composes one secret per service from the passwords the Postgres operator
generated.
| Secret | Contains | Role it uses | Endpoint |
|---|---|---|---|
g0s-db-<service> | DATABASE_URL, plus object store keys for services that use it | Its own per-service role, read-only where it only reads | The connection pooler |
gen0sec-db-migrate | DATABASE_URL | postgres, superuser | The primary, unpooled |
g0s-db-ui | DATABASE_URL, BETTER_AUTH_SECRET, object store keys | frontend | The connection pooler |
Every service connects as its own role. The migration job needs a superuser on the primary because it
runs CREATE EXTENSION, which an owner role cannot do.
g0s-db-ui carries the dashboard's session signing secret. Re-running the script keeps the existing
value, so sessions and invite links stay valid across a re-run. Nothing else regenerates it.
Four services need no database connection and get no DATABASE_URL: the GeoIP updater, the GeoIP
database updater, the model publisher and the rules validator.
Where the database passwords come from
The Postgres operator creates a credentials secret per role it provisions, named after the role:
<role>.core.credentials.postgresql.acid.zalan.do
Underscores in the role become dashes in the secret name.
Compose each connection string from that role's password:
postgres://<role>:<password>@core-pooler.gen0sec-system.svc.cluster.local:5432/ax_prod?sslmode=require
| Consumer | Role | Endpoint |
|---|---|---|
| Each service | Its own per-service role | core-pooler |
| Migration job | postgres | core, the primary, with the on-prem marker appended |
| Dashboard | frontend | core-pooler |
The dashboard's connection string uses sslmode=no-verify. Its client library verifies the certificate
chain for sslmode=require and rejects the pooler's self-signed certificate; the Go library every
other service uses reads the same value as encrypt-without-verify.
Do not copy no-verify onto the other services. Their driver rejects it outright.
For production: use your secret manager
make-db-secrets.sh is fine for evaluation. In production, treat its output as a specification
rather than a tool, and have your secret manager produce secrets of the same shape.
Two reasons:
- The script writes plain Kubernetes secrets from operator-generated passwords. Your manager should be the source of truth, with the cluster holding copies.
- Rotation is then a manager operation rather than a re-run plus a rollout.
An external secret can template the connection string from the operator-generated password, so nothing is copied by hand:
# shape, not a working manifest: adapt to your operator
data:
- secretKey: DATABASE_URL
remoteRef:
key: g0s-auth-api-ro.core.credentials.postgresql.acid.zalan.do
property: password
# then template: postgres://<role>:{{ .password }}@core-pooler...:5432/ax_prod?sslmode=require
To see the exact list your install needs, read it out of the values file rather than from this page:
grep -oE 'secretRef:\s*\{?\s*name:\s*[a-z0-9-]+' \
$B/values/gen0sec-platform-values-onprem.yaml | sort -u
Or inspect your live installation inside your cluster.
Rotation
| Credential | How |
|---|---|
| Database role passwords | Rotate through the Postgres operator, then restart the consumers |
| Object store keys | Update both gen0sec-object-store and every g0s-db-<service> that carries them, then restart the consumers |
| Registry pull credentials | Recreate gen0sec-registry in both namespaces. Existing pods keep running; new pulls use the new value |
| Your Gen0Sec API key | Recreate gen0sec-download-proxy, then restart the relay |
| Dashboard session key | Rotating invalidates every session and every outstanding invite link |
The object store keys appear as RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY to the object store itself,
and as AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to the services that read it. Same values, two
naming conventions. Rotating means updating both places.
Verifying
Nothing should be missing before you install the platform:
$KIT/scripts/preflight.sh
After install, confirm the count:
kubectl -n gen0sec get secret | wc -l # ~20 service secrets plus the ones you created
kubectl -n gen0sec-system get secret | grep credentials | wc -l # expect 6
What is never in a secret
| Where it is instead | |
|---|---|
| The registry host and namespace | The values file |
| The relay upstream URL | The values file |
| The cache connection string | The values file, deliberately with no credential in it |
If you enable authentication on the cache, remove that line from the values file and put the connection string in each service's secret instead.