This page is the single source of truth for the feature flags that change what the chart deploys. Use it when you need to decide between in-cluster and external infrastructure, mirror images, customize storage, or pre-create secrets.
For end-to-end install walkthroughs, see the per-cloud guides. For the deployment-model decision (hybrid vs self-hosted), see Deployment models.
Component on/off toggles
The chart’s primary enabled flags decide which subcharts are rendered.
| Component | Values key | Default | Notes |
|---|
| Data Plane | neuraltrust-data-plane.dataPlane.enabled | true | API + worker + Kafka Connect |
| Control Plane | neuraltrust-control-plane.controlPlane.enabled | false | Off = hybrid (CP on SaaS). On = self-hosted. |
| TrustGate | trustgate.enabled | true | admin + gateway + actions + Redis |
| Firewall | neuraltrust-firewall.firewall.enabled | true | gateway + 5 workers — always on in supported topologies; the choice is CPU vs GPU workers (see Firewall deployment) |
| SIEM Connectors | neuraltrust-siem-connectors.siemConnectors.enabled | false | Off — opt-in |
| Watchdog (self-healing) | neuraltrust-watchdog.enabled | false | Off — opt-in |
| OpenTelemetry Collector | global.observability.enabled | false | Off — opt-in |
A zero-config helm install deploys Data Plane + TrustGate + Firewall + in-cluster infra, without Control Plane — i.e. a hybrid-shaped stack ready for SaaS enrollment.
PostgreSQL — in-cluster or external
PostgreSQL stores:
- Control Plane schema (RBAC, integrations, dashboards) — only when CP is enabled
- TrustGate schema (gateways, routes, plugins) — when TrustGate is enabled
- Customer prompts and responses are never stored here — those go to ClickHouse.
In-cluster (default)
| Values key | Default |
|---|
neuraltrust-control-plane.infrastructure.postgresql.deploy | true |
neuraltrust-control-plane.controlPlane.components.postgresql.persistence.storageClass | "" (cluster default) |
neuraltrust-control-plane.controlPlane.components.postgresql.secrets.user | neuraltrust |
neuraltrust-control-plane.controlPlane.components.postgresql.secrets.password | "" → auto-generated 32-char |
neuraltrust-control-plane.controlPlane.components.postgresql.secrets.database | neuraltrust |
neuraltrust-control-plane.controlPlane.components.postgresql.secrets.host | control-plane-postgresql |
The in-cluster Postgres deploys regardless of controlPlane.enabled — TrustGate uses the same instance even in hybrid mode.
External (managed service)
neuraltrust-control-plane:
infrastructure:
postgresql:
deploy: false
controlPlane:
components:
postgresql:
secrets:
name: "postgresql-secrets"
host: "<external-host>"
port: "5432"
user: "neuraltrust"
password: "<password>" # or pre-create the secret
database: "neuraltrust"
trustgate:
global:
env:
DATABASE_HOST: "<external-host>"
DATABASE_PORT: "5432"
DATABASE_USER: "trustgate"
DATABASE_PASSWORD: "<password>"
DATABASE_NAME: "trustgate"
DATABASE_SSL_MODE: "require" # "disable" only for in-cluster
Required databases & users when external
Create these before running helm upgrade. Most managed services let you create the cluster admin then run the CREATE DATABASE / CREATE USER statements below.
| Database | User | Required when | Permissions |
|---|
neuraltrust | neuraltrust | Always | ALL PRIVILEGES ON DATABASE neuraltrust — Prisma migrations need DDL on public schema |
trustgate | trustgate | trustgate.enabled: true | ALL PRIVILEGES ON DATABASE trustgate, schema DDL |
Recommended SQL:
-- as the cluster admin
CREATE USER neuraltrust WITH PASSWORD '<strong-password>';
CREATE DATABASE neuraltrust OWNER neuraltrust;
GRANT ALL PRIVILEGES ON DATABASE neuraltrust TO neuraltrust;
CREATE USER trustgate WITH PASSWORD '<strong-password>';
CREATE DATABASE trustgate OWNER trustgate;
GRANT ALL PRIVILEGES ON DATABASE trustgate TO trustgate;
-- connect to each DB and grant on public schema (Postgres 15+)
\c neuraltrust
GRANT ALL ON SCHEMA public TO neuraltrust;
\c trustgate
GRANT ALL ON SCHEMA public TO trustgate;
The chart’s trustgate-postgresql-init Job can create the trustgate database/user automatically if the credentials in postgresql-secrets have CREATE USER and CREATE DATABASE rights on the external instance. Most managed Postgres services restrict these — pre-creating the DB/user is the safer path.
Cloud-managed Postgres notes
| Provider | Notes |
|---|
| AWS RDS / Aurora PostgreSQL | Set DATABASE_SSL_MODE: require. Use IRSA for password rotation via Secrets Manager if desired. |
| Azure Database for PostgreSQL Flexible Server | Set DATABASE_SSL_MODE: require. Use private endpoint for VNet-internal access. |
| Cloud SQL for PostgreSQL | Use Private IP. Authorize the GKE subnet. Set DATABASE_SSL_MODE: require. |
| CloudNativePG (on-prem) | Run CREATE DATABASE … OWNER from a privileged session; chart-side init Job works if the bootstrap user has CREATE rights. |
Redis — in-cluster or external
TrustGate uses Redis for caching gateways, plugins, and rate-limit state. Data Plane and Control Plane do not use Redis.
In-cluster (default)
Redis ships bundled with the TrustGate subchart as trustgate-redis (single StatefulSet, 10 GiB PVC). No additional configuration needed.
| Values key | Default |
|---|
trustgate.redis.replica.replicaCount | 1 |
trustgate.redis.image.tag | 7.2.0-v20 |
trustgate.redis.bind | 0.0.0.0 -:: (dual-bind) |
External Redis
Point TrustGate at a managed Redis (ElastiCache, Memorystore, Azure Cache for Redis) by setting env vars:
trustgate:
redis:
enabled: false # config-only flag (see caveat below)
global:
env:
REDIS_HOST: "<external-host>"
REDIS_PORT: "6379"
REDIS_PASSWORD: "<password>" # required for managed Redis
REDIS_DB: "0"
Current chart caveat: setting trustgate.redis.enabled: false changes the TrustGate env vars to point external, but the in-cluster trustgate-redis StatefulSet still deploys (the template isn’t guarded on the flag). The unused pod is small (~500m / 1 GiB) but it does consume cluster resources. A fix is planned for an upcoming chart release; until then, expect the in-cluster Redis pod to exist even when you target an external one.
Kafka — in-cluster or external
Kafka is the event backbone between TrustGate, Data Plane API, the Data Plane worker, Kafka Connect, and the Control Plane scheduler (audit events).
In-cluster (default)
| Values key | Default |
|---|
infrastructure.kafka.deploy | true |
kafka.replicaCount | 1 |
kafka.persistence.size | 10Gi |
In-cluster Kafka has no auth — ClusterIP-only and never exposed outside the namespace.
External Kafka
infrastructure:
kafka:
deploy: false
external:
bootstrapServers: "kafka.example.com:9092"
# 'brokers' (a list) is also accepted and joined with commas
This sets the bootstrapServers for components that read from infrastructure.kafka.external.*. Per-component Kafka env vars must also be overridden for components that use a hardcoded default (see below).
Authentication for external Kafka
The chart does not wire SASL/SCRAM/mTLS automatically. The infrastructure.kafka.external.secretName / secretKey keys exist in values.yaml but are not consumed by templates today. Use extraEnv on each Kafka consumer to inject the auth env vars yourself.
neuraltrust-data-plane:
dataPlane:
components:
api:
extraEnv:
- name: KAFKA_BOOTSTRAP_SERVERS
value: "kafka.example.com:9093"
- name: KAFKA_SECURITY_PROTOCOL
value: "SASL_SSL"
- name: KAFKA_SASL_MECHANISM
value: "SCRAM-SHA-512"
- name: KAFKA_SASL_USERNAME
valueFrom:
secretKeyRef: { name: kafka-auth, key: username }
- name: KAFKA_SASL_PASSWORD
valueFrom:
secretKeyRef: { name: kafka-auth, key: password }
worker:
extraEnv: *kafka-auth-env # same as api.extraEnv
kafka:
connect:
bootstrapServers: "kafka.example.com:9093"
Pre-create the kafka-auth Secret in your release namespace.
Per-component Kafka overrides
| Component | Hardcoded default | Override path |
|---|
| Data Plane API | KAFKA_BOOTSTRAP_SERVERS=kafka:9092 | neuraltrust-data-plane.dataPlane.components.api.extraEnv |
| Data Plane Worker | KAFKA_BOOTSTRAP_SERVERS=kafka:9092 | neuraltrust-data-plane.dataPlane.components.worker.extraEnv |
| Kafka Connect | bootstrapServers=kafka:9092 | neuraltrust-data-plane.dataPlane.components.kafka.connect.bootstrapServers |
| Control Plane Scheduler | KAFKA_BROKERS=kafka:9092, KAFKA_TOPIC=audit_events | neuraltrust-control-plane.controlPlane.components.scheduler.config.kafkaBrokers / .kafkaTopic |
| Control Plane App | KAFKA_HOST=kafka, KAFKA_PORT=9092 | neuraltrust-control-plane.controlPlane.components.app.config.kafkaHost / .kafkaPort |
Cloud-managed Kafka notes
| Provider | Notes |
|---|
| AWS MSK (IAM) | Use IRSA + aws-msk-iam-sasl-signer-java on consumers — currently requires custom image; chart does not bundle it. |
| Confluent Cloud | SASL/PLAIN over TLS; inject KAFKA_SECURITY_PROTOCOL=SASL_SSL and KAFKA_SASL_MECHANISM=PLAIN plus API key/secret. |
| Azure Event Hubs (Kafka surface) | SASL/PLAIN over TLS on port 9093; username $ConnectionString, password = Event Hubs connection string. Inject via extraEnv. |
ClickHouse — in-cluster or external
ClickHouse is the analytics database for the Data Plane — prompts, responses, traces, evals, metrics. All customer telemetry lives here.
In-cluster (default)
| Values key | Default |
|---|
infrastructure.clickhouse.deploy | true |
clickhouse.auth.username | neuraltrust |
clickhouse.auth.password | "" → auto-generated 32-char in Secret clickhouse / key admin-password |
clickhouse.auth.database | neuraltrust |
clickhouse.persistence.size | 50Gi |
External ClickHouse
infrastructure:
clickhouse:
deploy: false
external:
host: "<external-host>"
port: "8123" # HTTP port; "8443" for ClickHouse Cloud
user: "neuraltrust"
password: "" # use --set or pre-created secret
database: "neuraltrust"
secretName: "clickhouse"
secretKey: "admin-password"
neuraltrust-data-plane:
dataPlane:
components:
clickhouse:
enabled: true # keep true to render clickhouse-secrets + migrations Job
host: "<external-host>"
port: "8123"
user: "neuraltrust"
database: "neuraltrust"
Required ClickHouse Cloud caveat
The Data Plane API runs an init container that connects to ClickHouse on native port 9000 to apply migrations — this is hardcoded in the chart today. ClickHouse Cloud exposes native on port 9440 (TLS) instead, so the migrations init container fails out of the box against Cloud.Workarounds: (a) pre-provision the schema yourself and disable the init container, (b) run a port-translating proxy that exposes 9000→9440 inside your VPC, or (c) deploy ClickHouse in-cluster instead. A configurable native port is on the chart roadmap.
Required databases & users when external
| Database | User | Permissions |
|---|
neuraltrust | neuraltrust | GRANT ALL ON neuraltrust.* TO neuraltrust |
The clickhouse-migrations initContainer applies the schema automatically the first time the Data Plane API starts.
Image registry — NeuralTrust GCR or your internal mirror
By default, every NeuralTrust image is pulled from
europe-west1-docker.pkg.dev/neuraltrust-app-prod/nt-docker/<name>.
Default (NeuralTrust GCR)
Pre-create the gcr-secret image pull secret in your release namespace. The chart does not create this secret — you have to do it yourself with the JSON key NeuralTrust provides:
kubectl create secret docker-registry gcr-secret \
--docker-server=europe-west1-docker.pkg.dev \
--docker-username=_json_key \
--docker-password="$(cat path/to/gcr-keys.json)" \
[email protected] \
-n neuraltrust
The chart references this secret from every subchart’s imagePullSecrets (defaults: ["gcr-secret"]).
Internal mirror (one override)
global:
imageRegistry: "registry.internal.example.com/neuraltrust"
The chart’s image helper strips the default GCP prefix and prepends yours — so
europe-west1-docker.pkg.dev/neuraltrust-app-prod/nt-docker/control-plane-api
becomes
registry.internal.example.com/neuraltrust/control-plane-api
with no other override required.
Three escalating customization levels
| You mirror images with… | What to override |
|---|
| Same short names, same tags | only global.imageRegistry |
| Same short names, custom tags | global.imageRegistry + per-component image.tag |
Renamed paths (e.g. my-registry.corp/cp-api) | global.imageRegistry + per-component image.repository + image.tag |
Per-component override paths:
| Path | Component |
|---|
neuraltrust-control-plane.controlPlane.components.{api,app,scheduler,postgresql}.image.{repository,tag} | Control Plane |
neuraltrust-data-plane.dataPlane.components.{api,worker}.image.{repository,tag} | Data Plane |
neuraltrust-data-plane.dataPlane.components.kafka.connect.image.{repository,tag} | Kafka Connect |
neuraltrust-firewall.firewall.gateway.image.* / workerDefaults.image.* | Firewall |
trustgate.global.image.{image,tag} | TrustGate |
trustgate.redis.image.{repository,tag} | Redis |
clickhouse.image.{repository,tag} | ClickHouse |
kafka.image.{repository,tag} | Kafka |
Mirroring for air-gapped installs
helm template neuraltrust-platform \
oci://europe-west1-docker.pkg.dev/neuraltrust-app-prod/helm-charts/neuraltrust-platform \
--version <VERSION> \
-f my-values.yaml \
| yq '.. | select(has("image")) | .image' \
| sort -u
Mirror each image to your internal registry with crane, skopeo, or docker pull/push, then set global.imageRegistry. Verify rendered images:
helm template . -f my-values.yaml | grep -E '^\s+image:' | sort -u
Storage class — cluster default or explicit
PVCs are created for ClickHouse, Kafka, PostgreSQL, Redis, and the optional OpenTelemetry collector buffer.
Cluster default (default behavior)
| Values key | Default |
|---|
global.storageClass | "" |
When empty, the chart omits storageClassName from PVC specs, which falls back to the cluster’s default StorageClass. Confirm one exists:
kubectl get storageclass
# default class is marked with (default)
Explicit, cluster-wide
global:
storageClass: "pd-balanced" # GKE example
# storageClass: "gp3" # AWS EKS
# storageClass: "managed-csi-premium" # Azure AKS
# storageClass: "longhorn" # bare metal
Per-component overrides
| Component | Values key | Recommended for prod |
|---|
| ClickHouse | clickhouse.persistence.storageClass | SSD-backed (pd-ssd, io2, managed-csi-premium) for high-throughput |
| Kafka | kafka.persistence.storageClass | SSD-backed |
| PostgreSQL | neuraltrust-control-plane.controlPlane.components.postgresql.persistence.storageClass | SSD-backed |
| Redis (TrustGate) | trustgate.global.storageClass (inherits umbrella global.storageClass) | Default |
| OTel collector buffer (optional) | global.observability.collector.bufferPVC.storageClass | Default |
Per-component override wins over global.storageClass. Empty per-component = inherit global.storageClass = inherit cluster default.
Secrets — auto-generated, explicit, or pre-created
Two top-level flags control how the chart manages secrets.
| Values key | Default | Behavior |
|---|
global.autoGenerateSecrets | true | Chart generates random secrets on first install; reuses existing on upgrade. |
global.preserveExistingSecrets | false | When true, all secret templates are skipped (parent + subcharts). You must pre-create everything. |
Use autoGenerateSecrets: true for quick installs and dev/test. Use preserveExistingSecrets: true when you manage secrets with Vault, Sealed Secrets, External Secrets Operator, SOPS, or any other secret manager.
Required secrets per scenario
Always required, regardless of mode:
| Secret | Type | Created by |
|---|
gcr-secret | docker-registry | You (manual, pre-install) |
Auto-generated by default (when autoGenerateSecrets: true):
| Scenario | Secret | Keys |
|---|
| Hybrid (CP off) | data-plane-jwt-secret | DATA_PLANE_JWT_SECRET |
| trustgate-secrets | DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME, DATABASE_SSL_MODE, DATABASE_URL, SERVER_SECRET_KEY, NEURAL_TRUST_FIREWALL_URL, NEURAL_TRUST_FIREWALL_SECRET_KEY |
| firewall-secrets | JWT_SECRET (optional HUGGINGFACE_TOKEN) |
| postgresql-secrets | POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_HOST, POSTGRES_PORT, DATABASE_URL, POSTGRES_PRISMA_URL |
| clickhouse | admin-password |
| clickhouse-secrets | CLICKHOUSE_HOST, CLICKHOUSE_PORT, CLICKHOUSE_USER, CLICKHOUSE_DATABASE |
| neuraltrust-ingress-tls (if global.ingress.tls.autoGenerate: true) | tls.crt, tls.key |
| Self-hosted (adds CP) | control-plane-secrets | CONTROL_PLANE_JWT_SECRET, TRUSTGATE_JWT_SECRET, FIREWALL_API_URL (optional Resend / LLM keys) |
| Optional (only if non-empty values) | openai-secrets, google-secrets, resend-secrets, huggingface-secrets | provider-specific |
Pre-creating secrets (preserveExistingSecrets: true)
Required for: production setups with external secret managers, GitOps flows where chart-rendered Secrets cause drift, or air-gapped environments where you bake secrets into your golden state.
global:
preserveExistingSecrets: true # chart will not render any secret templates
You must pre-create the secrets above before helm upgrade --install. The chart ships create-secrets.sh and SECRETS.md as a starting template.
For per-secret keys and a worked example, see Secrets management.
Topology cheat sheet
A condensed view of the toggles people set on day one:
global:
platform: "gcp" # aws | gcp | azure | openshift | kubernetes
domain: "platform.example.com"
storageClass: "" # "" = cluster default
imageRegistry: "" # "" = NeuralTrust GCR
autoGenerateSecrets: true # false + preserveExistingSecrets for prod
preserveExistingSecrets: false
neuraltrust-control-plane:
controlPlane:
enabled: false # true = self-hosted
infrastructure:
postgresql:
deploy: true # false = external
neuraltrust-data-plane:
dataPlane:
enabled: true
trustgate:
enabled: true
redis:
enabled: true # false + REDIS_HOST env = external (see Redis caveat)
neuraltrust-firewall:
firewall:
enabled: true # always on in supported topologies (CPU or GPU workers)
infrastructure:
clickhouse:
deploy: true # false = external
kafka:
deploy: true # false = external