Skip to main content
The NeuralTrust Platform Helm chart can manage every required secret for you, or stay completely out of the way and let your existing secret management own the lifecycle. Pick the mode that fits your environment.
ModeFlag(s)BehaviorBest for
Auto-generated (default)global.autoGenerateSecrets: trueHelm creates and preserves secretsDev, staging, quick starts
Explicit valuesglobal.autoGenerateSecrets: true + values setYour values override auto-generationControlled environments
Pre-generatedglobal.preserveExistingSecrets: trueHelm never touches secretsProduction with Vault, Sealed Secrets, or External Secrets Operator

Auto-generated secrets (default)

No action required. Deploy and all platform secrets are created automatically:
helm upgrade --install neuraltrust-platform \
  oci://europe-west1-docker.pkg.dev/neuraltrust-app-prod/helm-charts/neuraltrust-platform \
  --version <VERSION> \
  --namespace neuraltrust --create-namespace
SecretKubernetes SecretKey
TrustGate server keytrustgate-secretsSERVER_SECRET_KEY
TrustGate DB passwordtrustgate-secretsDATABASE_PASSWORD
Control Plane JWTcontrol-plane-secretsCONTROL_PLANE_JWT_SECRET
TrustGate JWT (synced)control-plane-secretsTRUSTGATE_JWT_SECRET
Data Plane JWTdata-plane-jwt-secretDATA_PLANE_JWT_SECRET
PostgreSQL passwordpostgresql-secretsPOSTGRES_PASSWORD
ClickHouse admin passwordclickhouseadmin-password
SERVER_SECRET_KEY (TrustGate) and TRUSTGATE_JWT_SECRET (Control Plane) are always synchronized to the same value automatically. You can set either one explicitly and the other will follow.

How it works

  1. On first install, random 64-character alphanumeric values are generated for every required secret.
  2. On helm upgrade, existing values are read from the cluster via Helm lookup and reused — secrets are never overwritten.
  3. Explicit (non-empty) values in your values file always take priority and become the source of truth.

Overriding a specific secret

trustgate:
  global:
    env:
      SERVER_SECRET_KEY: "my-explicit-key"   # overrides auto-generation

Pre-generated secrets

For environments where Helm must not create or update secrets — e.g. Vault, Sealed Secrets, or External Secrets Operator owns the lifecycle:
global:
  autoGenerateSecrets: false
  preserveExistingSecrets: true   # Helm will NOT create or update secrets
When this flag is set, all required secrets must already exist in the namespace before deployment. Missing secrets cause workloads to fail.

Required secrets when preserveExistingSecrets: true

Kubernetes SecretRequired keys
clickhouseadmin-password
postgresql-secretsPOSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, DATABASE_URL, POSTGRES_PRISMA_URL
control-plane-secretsCONTROL_PLANE_JWT_SECRET, TRUSTGATE_JWT_SECRET (must equal TrustGate SERVER_SECRET_KEY)
data-plane-jwt-secretDATA_PLANE_JWT_SECRET
trustgate-secretsSERVER_SECRET_KEY, DATABASE_PASSWORD (and NEURAL_TRUST_FIREWALL_URL, NEURAL_TRUST_FIREWALL_SECRET_KEY if using the Firewall)
gcr-secretdocker-registry type — for pulling NeuralTrust images
Optional (only if used): openai-secrets, google-secrets, resend-secrets, huggingface-secrets, firewall-secrets.
With preserveExistingSecrets: true, you are responsible for keeping SERVER_SECRET_KEY (in trustgate-secrets) and TRUSTGATE_JWT_SECRET (in control-plane-secrets) identical. The chart no longer enforces this for you.

Full secret reference

Data Plane

Kubernetes SecretKeyRequiredDescription
data-plane-jwt-secretDATA_PLANE_JWT_SECRETAuto-generatedJWT for Data Plane API auth
openai-secretsOPENAI_API_KEYNoOpenAI API key
google-secretsGOOGLE_API_KEYNoGoogle API key
resend-secretsRESEND_API_KEYNoResend email API key
huggingface-secretsHUGGINGFACE_TOKENNoHugging Face model access

Control Plane

Kubernetes SecretKeyRequiredDescription
control-plane-secretsCONTROL_PLANE_JWT_SECRETAuto-generatedJWT for Control Plane API auth
control-plane-secretsTRUSTGATE_JWT_SECRETAuto-generatedSynced with SERVER_SECRET_KEY
control-plane-secretsFIREWALL_JWT_SECRETIf Firewall enabledJWT shared with the firewall
control-plane-secretsMODEL_SCANNER_SECRETNoModel scanner service secret
control-plane-secretsOPENAI_API_KEYNoOpenAI API key
control-plane-secretsresend-api-keyNoResend API key
control-plane-secretsresend-alert-senderNoAlert notification email
control-plane-secretsresend-invite-senderNoInvitation email

PostgreSQL

Kubernetes SecretKeyRequiredDescription
postgresql-secretsPOSTGRES_HOSTIf pre-generatingDatabase hostname
postgresql-secretsPOSTGRES_PORTIf pre-generatingDatabase port
postgresql-secretsPOSTGRES_USERIf pre-generatingDatabase username
postgresql-secretsPOSTGRES_PASSWORDAuto-generatedDatabase password
postgresql-secretsPOSTGRES_DBIf pre-generatingDatabase name
postgresql-secretsDATABASE_URLIf pre-generatingConnection URL (URL-encoded)
postgresql-secretsPOSTGRES_PRISMA_URLIf pre-generatingPrisma-compatible URL

TrustGate

Kubernetes SecretKeyRequiredDescription
trustgate-secretsSERVER_SECRET_KEYAuto-generatedServer secret key (= TRUSTGATE_JWT_SECRET)
trustgate-secretsDATABASE_HOSTExternal DBPostgreSQL host
trustgate-secretsDATABASE_PORTExternal DBPostgreSQL port
trustgate-secretsDATABASE_USERExternal DBPostgreSQL user
trustgate-secretsDATABASE_PASSWORDAuto-generatedPostgreSQL password
trustgate-secretsDATABASE_NAMEExternal DBPostgreSQL database
trustgate-secretsDATABASE_URLExternal DBConnection URL
trustgate-secretsNEURAL_TRUST_FIREWALL_URLFirewall integrationFirewall base URL
trustgate-secretsNEURAL_TRUST_FIREWALL_SECRET_KEYFirewall integrationFirewall API key

Firewall

Created when neuraltrust-firewall.firewall.enabled: true:
Kubernetes SecretKeyRequiredDescription
firewall-secretsJWT_SECRETYesShared with services calling the firewall
firewall-secretsHUGGINGFACE_TOKENNoHF token for model weights
Align controlPlane.secrets.firewallJwtSecret (FIREWALL_JWT_SECRET) with firewall-secrets.JWT_SECRET so the Control Plane validates firewall tokens correctly.

Docker registry

Kubernetes SecretTypeRequiredDescription
gcr-secretdocker-registryYesCredentials for NeuralTrust container images

Secret references in values

You can embed a secret value directly in values (less secure) or reference an existing secret (recommended).
neuraltrust-data-plane:
  dataPlane:
    secrets:
      dataPlaneJWTSecret: "your-secret-value"

TrustGate ↔ Firewall integration

TrustGate calls the NeuralTrust Firewall using two keys stored in trustgate-secrets:
trustgate:
  global:
    env:
      NEURAL_TRUST_FIREWALL_URL: "http://firewall-gateway:8000"
      NEURAL_TRUST_FIREWALL_SECRET_KEY: "your-secret-key"
When global.autoGenerateSecrets: true, both keys are auto-populated. With preserveExistingSecrets: true, you must add them to a pre-created trustgate-secrets. After changing firewall secrets, restart TrustGate so pods pick up the new values:
kubectl rollout restart deployment/trustgate-control-plane -n neuraltrust
kubectl rollout restart deployment/trustgate-data-plane -n neuraltrust
kubectl rollout restart deployment/trustgate-actions -n neuraltrust

External Secrets Operator

Example syncing the Data Plane JWT from Vault:
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: data-plane-jwt-secret
  namespace: neuraltrust
spec:
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: data-plane-jwt-secret
  data:
    - secretKey: DATA_PLANE_JWT_SECRET
      remoteRef:
        key: neuraltrust/data-plane/jwt-secret
Apply equivalent ExternalSecret resources for every entry in the reference table you intend to manage externally, then deploy the chart with global.preserveExistingSecrets: true.

Pre-generation script

For environments that require all secrets created before deployment, the chart repo ships a helper script:
git clone --branch v<VERSION> --depth 1 https://github.com/NeuralTrust/neuraltrust-platform.git
cd neuraltrust-platform

# Interactive prompts
./create-secrets.sh --namespace neuraltrust

# Non-interactive via environment variables
export DATA_PLANE_JWT_SECRET="..."
export CONTROL_PLANE_JWT_SECRET="..."
export TRUSTGATE_JWT_SECRET="..."
export SERVER_SECRET_KEY="$TRUSTGATE_JWT_SECRET"   # must match
export POSTGRES_PASSWORD="..."
./create-secrets.sh --namespace neuraltrust

# Replace any existing secrets without prompting
./create-secrets.sh --namespace neuraltrust --replace-existing
After the script completes, deploy with preserveExistingSecrets: true.

Security best practices

  1. Default to auto-generation. It’s the safest starting point and the chart preserves existing secrets on every upgrade.
  2. Use external secret management for production. Vault, Sealed Secrets, or External Secrets Operator give you rotation, audit, and centralized control.
  3. Never commit secret values to git. Don’t store real values in version-controlled values files; use --set or pre-created secret references.
  4. Rotate secrets regularly — especially JWT secrets. Restart the affected workloads after each rotation.
  5. Restrict access with RBAC. Limit who can get/list/watch Kubernetes Secrets in the neuraltrust namespace.

Troubleshooting

Secret not found

kubectl get secret <secret-name> -n neuraltrust
kubectl get secrets -n neuraltrust
kubectl get secret <secret-name> -n neuraltrust -o yaml

TrustGate firewall env vars not updating

TrustGate reads NEURAL_TRUST_FIREWALL_URL and NEURAL_TRUST_FIREWALL_SECRET_KEY from trustgate-secrets at pod startup. After helm upgrade, restart TrustGate deployments to reload them (see TrustGate ↔ Firewall integration above).

Wrong secret value

kubectl patch secret <secret-name> -n neuraltrust \
  --type='json' \
  -p='[{"op": "replace", "path": "/data/<key>", "value": "<base64-encoded-value>"}]'

Secret format issues

When creating secrets manually:
  • URL-encode passwords inside DATABASE_URL.
  • Base64-encode all values stored in Kubernetes Secrets.
  • Avoid trailing newlines or whitespace — they break authentication silently.