Prerequisites
What you provision before an install starts — Postgres, Redis, cert-manager, DNS, TLS, ESO, registry.
Genesis Downloads · Secrets management
Genesis reads every credential from a single Kubernetes Secret named
ai-studio-secrets — 44 keys in total: 34
required plus 10 optional, feature-gated ones. This page is the complete
inventory, the four ways to populate it, and the rotation cadence per
key class.
ai-studio-secrets is the platform credential
bundle that every application pod reads. A second, much smaller
Secret — customer-genesis-secrets — carries the
PostgreSQL admin password and the OIDC client-secret reference used by
the ops chart, not by application pods. It is covered in the
install guide alongside the ExternalSecret
templates for all three clouds.
The umbrella chart supports four supply paths for
ai-studio-secrets. Pick one.
| Backend | When to choose | Setup |
|---|---|---|
| ESO + Azure Key Vault | Azure customers — the default | global.secrets.externalSecrets.enabled: true, global.secrets.azure.vaultUrl, Workload Identity wired |
| ESO + AWS Secrets Manager | AWS customers | global.secrets.externalSecrets.enabled: true, global.secrets.aws.region, IRSA wired |
| ESO + GCP Secret Manager | GCP customers | global.secrets.externalSecrets.enabled: true, GKE Workload Identity wired |
| Manual K8s Secret | Air-gapped clusters, dev clusters, no ESO | global.secrets.externalSecrets.enabled: false — you pre-create ai-studio-secrets yourself |
externalSecrets.enabled=true the umbrella renders one
ExternalSecret and one SecretStore per release,
mapping each key from your backend into ai-studio-secrets.
No client secrets are stored in the cluster — the SecretStore
authenticates with workload identity.
The mapping is defined in the umbrella chart's values.yaml
under secrets.data. Convention: vault keys are
kebab-case, Kubernetes Secret keys are snake_case. The platform
reads only the Kubernetes side.
secrets.data.
A deployment may legitimately override it with a smaller subset —
pinning the mapping to the keys a particular vault actually holds, so
the all-or-nothing sync cannot stall on a key nobody provisioned. If you
do that, every reference you drop must stay optional: true
on the consuming service.
| Category | Keys | Required when |
|---|---|---|
| Database & cache | 3 | Password auth / Redis AUTH enabled |
| Application encryption & sessions | 6 | Always |
| Keycloak / OIDC | 6 | Always |
| Keycloak SMTP | 8 | Keycloak sends user email — all 8 together |
| Azure services, Marketplace & AD federation | 8 | Per feature — the 2 Marketplace keys are optional |
| Gateway, service auth & data stores | 6 | Always, except the optional QDRANT_API_KEY |
| Observability & web tools | 5 | Optional — per feature |
| Optional — LLM providers | 2 | Optional — per enabled provider |
| Total | 44 | 34 required, 10 optional |
| Vault key | K8s key | Required when | Description |
|---|---|---|---|
DB-USER | DB_USER | global.database.authMethod: password | Postgres app user — omit for managed identity |
DB-PASSWORD | DB_PASSWORD | global.database.authMethod: password | Password for DB_USER — omit for managed identity. Must be alphanumeric. |
REDIS-PASSWORD | REDIS_PASSWORD | global.redis.auth: true | Redis AUTH password |
All six are required in every deployment.
| Vault key | K8s key | Description |
|---|---|---|
SECRET-KEY | SECRET_KEY | FastAPI session-signing key (32 random bytes. Generate with openssl rand -base64 32) |
AUTH-SECRET | AUTH_SECRET | Auth.js / NextAuth session secret on genesis-fe. Generate with openssl rand -hex 33 | head -c 64 |
CREDENTIAL-ENCRYPTION-KEY | CREDENTIAL_ENCRYPTION_KEY | AES-256 key wrapping connector credentials at rest. Generate with openssl rand -base64 32. |
CREDENTIALS-ENCRYPTION-KEY | CREDENTIALS_ENCRYPTION_KEY | Plural alias of the above (legacy compatibility). Keep both; the same value is fine |
SECURITY-ENCRYPTION-KEY | SECURITY_ENCRYPTION_KEY | AES-256 key wrapping organization-scoped secrets in Postgres. Generate with openssl rand -hex 33 | head -c 64 |
SECURITY-ENCRYPTION-SALT | SECURITY_ENCRYPTION_SALT | Salt used by SECURITY_ENCRYPTION_KEY. Generate with openssl rand -hex 33 | head -c 32 |
| Vault key | K8s key | Required | Description |
|---|---|---|---|
AUTH-KEYCLOAK-SECRET | AUTH_KEYCLOAK_SECRET | Always | Auth.js client secret for genesis-fe ↔ Keycloak. Generate with openssl rand -hex 33 | head -c 64 |
KEYCLOAK-CLIENT-SECRET | KEYCLOAK_CLIENT_SECRET | Always | OIDC client secret for backend services. Generate with openssl rand -hex 33 | head -c 64 |
KEYCLOAK-FRONTEND-CLIENT-SECRET | KEYCLOAK_FRONTEND_CLIENT_SECRET | Always | Browser-facing OIDC client secret. Generate with openssl rand -hex 33 | head -c 64 |
KC-BOOTSTRAP-ADMIN-USERNAME | KC_BOOTSTRAP_ADMIN_USERNAME | First install only | Initial Keycloak admin username (must be set to "admin"). |
KC-BOOTSTRAP-ADMIN-PASSWORD | KC_BOOTSTRAP_ADMIN_PASSWORD | First install only | Initial Keycloak admin password (must be alphanumeric.). |
KC-SPI-EVENTS-LISTENER-GENESIS-AUTHZ-EVENT-LISTENER-INTERNAL-API-KEY | KC_SPI_EVENTS_LISTENER_GENESIS_AUTHZ_EVENT_LISTENER_INTERNAL_API_KEY | Always | Shared secret on the Keycloak → genesis-authz event-sync webhook. Generate with openssl rand -hex 33 | head -c 64 |
Required when Keycloak sends user email (verification, password reset). Set all eight together — Keycloak fails if any one is missing.
| Vault key | K8s key | Description |
|---|---|---|
KC-SMTP-HOST | KC_SMTP_HOST | SMTP server hostname |
KC-SMTP-PORT | KC_SMTP_PORT | Usually 587 (STARTTLS) or 465 (SSL) |
KC-SMTP-USER | KC_SMTP_USER | SMTP username |
KC-SMTP-PASSWORD | KC_SMTP_PASSWORD | SMTP password |
KC-SMTP-FROM | KC_SMTP_FROM | From address — must be authorized by your SMTP relay |
KC-SMTP-FROM-DISPLAY-NAME | KC_SMTP_FROM_DISPLAY_NAME | Display name shown to recipients |
KC-SMTP-SSL | KC_SMTP_SSL | "true" for implicit SSL (port 465), else "false" |
KC-SMTP-STARTTLS | KC_SMTP_STARTTLS | "true" for STARTTLS (port 587), else "false" |
| Vault key | K8s key | Required when |
|---|---|---|
AZURE-OPENAI-API-KEY | AZURE_OPENAI_API_KEY | Azure OpenAI is the LLM backend |
AZURE-DOCUMENT-INTELLIGENCE-KEY | AZURE_DOCUMENT_INTELLIGENCE_KEY | Document Intelligence component used |
AZURE-SEARCH-API-KEY | AZURE_SEARCH_API_KEY | Search component used |
Consumed by genesis-tenant-mgmt for Azure Marketplace SaaS Fulfillment v2.
| Vault key | K8s key | Required when | Description |
|---|---|---|---|
AZURE-MARKETPLACE-CLIENT-SECRET | AZURE_MARKETPLACE_CLIENT_SECRET | SaaS billing via Azure Marketplace | Publisher AAD app secret from Partner Center technical configuration |
AZURE-MARKETPLACE-WEBHOOK-SECRET | AZURE_MARKETPLACE_WEBHOOK_SECRET | Future use | Operator-generated random value, reserved for webhook validation |
These configure end-user SSO via Azure AD as a Keycloak identity provider.
| Vault key | K8s key | Description |
|---|---|---|
AZURE-AD-CLIENT-ID | AZURE_AD_CLIENT_ID | App registration client ID |
AZURE-AD-CLIENT-SECRET | AZURE_AD_CLIENT_SECRET | App registration client secret |
AZURE-AD-TENANT-ID | AZURE_AD_TENANT_ID | Tenant ID (GUID) |
global.serviceAccount.azure.*. The
AZURE_CLIENT_ID / AZURE_TENANT_ID env vars it
uses are deliberately not in this inventory — the
Workload Identity webhook injects them into pods labelled
azure.workload.identity/use=true. The split avoids a
collision with the AD-federation values above.
| Vault key | K8s key | Required when | Description |
|---|---|---|---|
APISIX-ADMIN-KEY | APISIX_ADMIN_KEY | Always | Bearer token for the APISIX Admin API. Generate with openssl rand -hex 33 | head -c 64 |
INTERNAL-API-KEYS | INTERNAL_API_KEYS | Always | Platform-internal service-to-service API keys. Format is a JSON array — and it needs to include the value for GENESIS-API-KEY in the array. |
GENESIS-API-KEY | GENESIS_API_KEY | Always | Default platform API key, also used by genesis-authz internal calls. Generate with openssl rand -hex 33 | head -c 64 |
JWT-SECRET | JWT_SECRET | Always | Symmetric secret for internal JWT signing fallback (HS256). Production prefers JWKS via Keycloak. Generate with openssl rand -hex 33 | head -c 64 |
ADMIN-PASSWORD | ADMIN_PASSWORD | Always | Initial platform admin password. |
QDRANT-API-KEY | QDRANT_API_KEY | Optional: only needed when Qdrant cluster auth enabled. | Qdrant API key |
["<hex>"], not as a comma-separated
string. It is parsed as a JSON list, and a bare string fails settings
validation — genesis-be and genesis-authz will not start.
| Vault key | K8s key | Required when |
|---|---|---|
KAFKA-SASL-USERNAME | KAFKA_SASL_USERNAME | ModelHub trace ingestion enabled |
KAFKA-SASL-PASSWORD | KAFKA_SASL_PASSWORD | ModelHub trace ingestion enabled |
OTEL-EVENTHUB-CONNECTION-STRING | OTEL_EVENTHUB_CONNECTION_STRING | OTEL Azure Event Hub exporter |
OTEL-EVENTHUB-NAMESPACE | OTEL_EVENTHUB_NAMESPACE | OTEL Azure Event Hub exporter |
TAVILY-API-KEY | TAVILY_API_KEY | Tavily web-search tool enabled in agents |
The umbrella chart ships two optional mappings for LLM providers.
| Vault key | K8s key | Provider |
|---|---|---|
OPENAI-API-KEY | OPENAI_API_KEY | Direct OpenAI (non-Azure) |
ANTHROPIC-API-KEY | ANTHROPIC_API_KEY | Anthropic — Claude models |
Set global.secrets.externalSecrets.enabled: false and
pre-create the Secret yourself.
kubectl create secret generic ai-studio-secrets \ -n genesis \ --from-literal=DB_USER='postgres' \ --from-literal=DB_PASSWORD='<rotated>' \ --from-literal=REDIS_PASSWORD='<rotated>' \ --from-literal=SECRET_KEY='<32-rand-bytes>' \ --from-literal=AUTH_SECRET='<32-rand-bytes>' \ --from-literal=CREDENTIAL_ENCRYPTION_KEY='<32-rand-bytes>' \ --from-literal=CREDENTIALS_ENCRYPTION_KEY='<32-rand-bytes>' \ --from-literal=SECURITY_ENCRYPTION_KEY='<32-rand-bytes>' \ --from-literal=SECURITY_ENCRYPTION_SALT='<16-rand-bytes>' \ --from-literal=AUTH_KEYCLOAK_SECRET='<from-keycloak-client>' \ ... # repeat for all 34 required keys, plus any optional ones you enable
Generate random values with either of:
openssl rand -base64 32 python -c "import secrets; print(secrets.token_urlsafe(32))"
ai-studio-secrets to git. If you need it in a
GitOps repo, use SealedSecrets so only your cluster can decrypt it.
The most common configuration.
# values-customer-acme.yaml
global:
secrets:
provider: "kubernetes"
k8sSecretName: "ai-studio-secrets"
refreshInterval: "1h"
externalSecrets:
enabled: true
azure:
vaultUrl: "https://kv-acme-prod.vault.azure.net/"
secretStore:
name: "genesis-secret-store"
kind: "ClusterSecretStore"
managedIdentity:
enabled: true
provider: "azure"
azure:
clientId: "<workload-identity-client-id>"
tenantId: "<aad-tenant-id>"
secrets:
data:
- { secretKey: DB_USER, remoteKey: DB-USER }
- { secretKey: DB_PASSWORD, remoteKey: DB-PASSWORD }
- { secretKey: TEMPORAL_STORE_PASSWORD, remoteKey: DB-PASSWORD }
- { secretKey: TEMPORAL_VISIBILITY_STORE_PASSWORD, remoteKey: DB-PASSWORD }
... # repeat for all 44 mapped keys
Before you install, confirm:
genesis-platform-sa ServiceAccount carries the azure.workload.identity/use=true annotation.get and list on the vault's secrets.secrets.data exists in the vault
with a value populated — the 34 required ones at minimum.
The sync is all-or-nothing, so trim the mapping for any of the 10
optional keys you do not provision.global:
secrets:
externalSecrets:
enabled: true
aws:
region: "us-east-1"
managedIdentity:
enabled: true
provider: "aws"
serviceAccount:
aws:
enabled: true
roleArn: "arn:aws:iam::123456789012:role/genesis-platform-sa"
Before you install: IRSA is configured, and the role has
secretsmanager:GetSecretValue and
secretsmanager:DescribeSecret on the relevant secrets.
global:
secrets:
externalSecrets:
enabled: true
managedIdentity:
enabled: true
provider: "gcp"
Before you install: GKE Workload Identity binds the
Kubernetes ServiceAccount to a Google service account holding
roles/secretmanager.secretAccessor.
| Key class | Cadence | Disruption |
|---|---|---|
DB_PASSWORD, REDIS_PASSWORD |
90 days | None — hot-reloads on the next pool refresh (≤ 60s) |
SECRET_KEY, AUTH_SECRET, JWT_SECRET |
180 days | Active sessions invalidate. Rotate in a maintenance window |
CREDENTIAL_ENCRYPTION_KEY and the other two encryption keys |
Only via the key-rotation procedure | All stored credentials become unreadable if rotated without it |
KEYCLOAK_*_SECRET |
365 days, per Keycloak client | None — pods pull fresh on the next request |
KC_BOOTSTRAP_ADMIN_* |
Delete entirely after first install | None |
| Azure / OpenAI / Anthropic API keys | Per provider policy, typically 90–365 days | None |
INTERNAL_API_KEYS, GENESIS_API_KEY |
180 days | Service-to-service callers must update simultaneously |
KAFKA_*, OTEL_* |
Per provider policy | None — pods reconnect |
CREDENTIAL_ENCRYPTION_KEY,
CREDENTIALS_ENCRYPTION_KEY and
SECURITY_ENCRYPTION_KEY wrap data at rest
in PostgreSQL. Rotating any of them without the documented two-step
rotation procedure renders every stored connector credential and
organization-scoped secret permanently unrecoverable.
Open a support ticket for the procedure before you attempt it.
Once ESO syncs a new value into the Secret, pods must roll to pick it up.
ESO honours eso.external-secrets.io/refresh-time: "1h", so
most rotations land within the hour unattended. Force it when you need
immediate pickup:
kubectl rollout restart deploy -n genesis
| Check | Why it matters |
|---|---|
| All 34 required keys exist in the chosen backend | A missing key surfaces as CreateContainerConfigError, not a clear error |
| Of the 10 optional keys, only those for features you enabled exist — and no others | Unused entries generate key not found noise on every ESO refresh |
KC_BOOTSTRAP_ADMIN_* is scheduled for deletion after bootstrap | A live bootstrap admin credential is a standing privilege-escalation path |
| The three encryption keys have a documented escrow path | Losing them means losing the data — there is no recovery |
| Per-key rotation cadence is on a real calendar | 90 / 180 / 365 days as listed above |
ai-studio-secrets is not committed to git anywhere | Use SealedSecrets if it must travel through a GitOps repo |
ESO refreshInterval is set | The 1h default is fine |
genesis-platform-sa has minimum-scope read on the backend | No write, no delete — the platform never mutates your vault |
Keys are occasionally added or removed between releases. The authoritative per-release inventory ships with the release manifest; check it when upgrading rather than assuming this page's count.
What you provision before an install starts — Postgres, Redis, cert-manager, DNS, TLS, ESO, registry.
End-to-end runbook, including where the ExternalSecret CRs get applied in the sequence.
The non-secret half of configuration: which chart values every environment must change, and which to leave at their defaults.
ArgoCD / GitOps is the recommended path. Secrets are referenced, never committed.