Skip to main content
External is the fully self-hosted topology. Every plane runs in your environment: control planes, data planes, the product console, and the analytics stack. Nothing leaves your network unless you explicitly enable hosted export, so there are no console-issued tokens and no NeuralTrust endpoints to allow. Choose External for air-gapped environments, strict data-residency mandates, or when policy forbids any outbound dependency on NeuralTrust SaaS. This page is the whole install: architecture, prerequisites, datastores, the step-by-step install, and high availability.

Architecture

External architecture: everything runs in your environment. Clients call TrustGate on :8081 for LLM traffic and :8082 for MCP, TrustGate calls TrustGuard on :8081, and TrustGuard calls the Firewall on :8000. An in-cluster control plane — console and API on :8000, AgentGateway admin on :8080, and the TrustGuard control plane on :8080 — serves configuration to TrustGate and TrustGuard. TrustGate and TrustGuard emit OTLP to a collector on :4317 and :4318 that writes to in-cluster ClickHouse; DataCore and AlertEngine read from it. PostgreSQL and Redis are recommended datastores outside the cluster. There is no DataAgent and no runtime dependency on NeuralTrust SaaS. Two properties shape everything else on this page:
  • Configuration comes from PostgreSQL, not from a config-sync stream. Each product’s data plane reads its configuration directly from that product’s database, which its own in-cluster control plane owns and migrates.
  • DataCore replaces DataAgent. There is no SaaS pushing queries over an outbound stream, so an in-cluster HTTP API serves tenant-scoped queries from ClickHouse instead, plus deployment metadata from its own datacore database.

What runs

A complete platform install. Every table below is deployed by the same umbrella chart in one release.

Request path

Control plane

Analytics

global.products is ignored in External mode — the full stack always deploys. DataAgent does not run at all.

Prerequisites

  • A Kubernetes cluster with an ingress controller, and Helm 3.8+
  • Roughly 4–5 workers at 8 vCPU / 16–32 GiB — the console and analytics stack on top of the data path. See Capacity
  • A base domain you can point at the cluster ingress
  • PostgreSQL, Redis, and ClickHouse — see Datastores
  • The registry key NeuralTrust sends you, or a mirror of the images for an air-gapped cluster — Container images
  • An SMTP or email provider, for console invitations and password resets

Decide the console hostname first

APP_URL and NEXTAUTH_URL drive email links, SSO redirects, and OAuth callbacks. Changing them later invalidates in-flight invitations and breaks configured SSO redirect URIs, so pick the hostname before installing. One limitation to know about: a handful of console values are NEXT_PUBLIC_*, which Next.js inlines into the JavaScript bundle at image build time. Helm cannot set them. In practice two matter, and both are cosmetic rather than blocking — the SCIM tenant URL shown to administrators and the meta.location returned to your IdP will show app.neuraltrust.ai. Enter your own host in your IdP rather than copying what the screen displays.

Network

External needs no outbound connectivity to NeuralTrust. There are no hostnames or IPs to allowlist and no inbound source IP to admit. What you do need reachable: Inbound, clients reach the TrustGate LLM and MCP hosts, and your operators reach the console — see Public routing.
Air-gapped installs mirror the images into an internal registry and set global.imageRegistry. Two collector images are pinned in full and need separate overrides — see Mirror to your own registry.

Datastores

Recommended for production: managed PostgreSQL and Redis outside the cluster, with ClickHouse in-cluster. The chart defaults to in-cluster PostgreSQL and Redis (deploy: true) so a proof of concept can helm install without provisioning anything first — that is convenience, not the production shape. ClickHouse is mandatory here, unlike a Hybrid data plane where analytics stay on the hosted path. Nearly everyone runs the in-cluster subchart, because it is telemetry storage the platform owns end to end. Each service owns its own PostgreSQL database and migrations. The endpoint is shared: since chart 2.6.0 an empty per-service host, port or sslMode (and Redis host / port / username / tls) inherits from global.postgresql / global.redis before falling back to the in-cluster Service names. Declare a managed datastore once on the global blocks; add a per-service database: / redis: block only to send one service somewhere else.

In-cluster PostgreSQL (proof of concept)

Leave global.postgresql.deploy: true. From chart 2.7.0 a control-plane-postgresql-bootstrap Job creates each service role and database using the password already in that service’s Secret, so you run no SQL by hand. Expect a few restarts on the Postgres-backed workloads while the Job waits for PostgreSQL, then they become Ready.

Managed PostgreSQL and Redis (production)

Name the endpoints once, disable the in-cluster stores you are replacing, and leave ClickHouse alone:
Pre-create every PostgreSQL role and database yourself — the bootstrap Job does not run against a managed instance (deploy: false, a named host, or IAM auth all stop it rendering). Required pairs: No datastore password belongs in the values file. Point each service at a Secret you created, including the control-plane role through global.postgresql.passwordSecret (chart 2.8.0+) — see External per-service datastore credentials. On chart 2.7.x and earlier the control-plane password had to stay inline as global.postgresql.password, because the chart composed the console’s connection string while rendering. For minimum sizes see Managed stores and values-managed-datastores.yaml.example, which ships with the chart. On AWS, RDS IAM authentication needs a per-gateway setting as well as the global one — see Cloud notes.
Running ClickHouse outside the cluster is supported but uncommon. Only if your policy requires it, add:

Install

External needs no console setup, no config-sync tokens, and no DataAgent enrollment. The platform is its own configuration source.
1

Create the namespace and image pull secret

Turn the registry key NeuralTrust sent you into the pull Secret every component expects, using the script from the chart sources:
2

Create the bootstrap administrator Secret

There is no hosted console to sign in from and no sign-up flow, so seed the first super-admin. A pre-created Secret keeps the credentials out of Helm release history:
The feature activates only when DEPLOYMENT_MODE=external and both values are non-empty. On password login the console checks these before normal user authentication, and on a match upserts the user with a verified email and grants OWNER on every team.Treat it as a break-glass account: it bypasses the normal user table by design. Create a personal administrator through the UI afterwards.
3

Write your values file

The chart ships values-external.yaml.example. Copy it and set your platform and domain:
With the defaults above, PostgreSQL and Redis stay in-cluster and need no extra Secrets beyond the pull secret and onprem-superadmin. For the production shape, start from values-managed-datastores.yaml.example and follow Managed PostgreSQL and Redis.
4

Install, and watch the ordering

Two things must complete before the console serves traffic:
  1. A pre-install Helm hook Job generates the MCP OAuth RSA signing key into a Secret. It checks for an existing key first, so upgrades do not rotate it and invalidate live access tokens. It only ever writes its own Secret — the Role is name-scoped.
  2. An init container runs prisma migrate deploy followed by the seed.
The console Deployment rolls with maxUnavailable: 0, so a failed migration blocks the rollout rather than serving against a half-migrated schema. control-plane-api reads the same schema and runs no migrations of its own — it assumes the console has already applied them.The chart registry is public; only the container images are private. Always pass --version so the release is reproducible.
On OpenShift the install is the same but the chart renders native Routes instead of Ingress — see OpenShift. Provider-specific ingress, certificate, and managed-store choices are in Cloud notes.

Verify

You should see the full inventory: the request path (agentgateway-proxy, agentgateway-mcp, trustguard-data-plane, firewall plus its workers, data-plane-api), the in-cluster control plane (agentgateway-admin, trustguard-control-plane, control-plane-api, control-plane-app), and the analytics stack (clickstack-collector, clickhouse, datacore, alertengine-api, alertengine-worker). No dataagent pod appears. In-cluster control-plane-postgresql and redis appear only while deploy: true. Data-plane readiness has no snapshot gate here, so Running and Ready track each other closely. Point DNS at the ingress address, sign in with the super-admin credentials, and create the organization.

Configuration comes from PostgreSQL

Config sync is off by default in External mode, and each product’s data plane reads configuration from its own database. Practical consequences:
  • There is no config-sync token to create and no snapshot cache to reason about
  • Each product needs its own database and role, not one shared credential
The chart still generates gRPC TLS material for the product control planes: a self-signed CA and a server certificate whose SANs cover the control-plane Service DNS name, preserved across upgrades. Nothing consumes it while config sync is off, but it means you can enable config sync later without provisioning certificates. If your policy requires your own PKI, supply a kubernetes.io/tls Secret including ca.crt through configSync.grpcTls.existingSecret; setting autoGenerate: false without one is rejected at render time. DataCore is reached by the console at DATACORE_URL with a JWT signed by a secret that must equal DataCore’s AUTH_JWT_HS256_SECRET. The chart wires both ends from a single generated value. If you override one by hand you must override the other, or every dashboard returns an authorization error.

Telemetry

Product telemetry stays in your in-cluster ClickHouse. To run with no outbound NeuralTrust telemetry — the air-gapped default — disable hosted export:
Disabling hosted export does not disable the in-cluster ClickStack pipeline; it only stops optional egress to NeuralTrust.

Public routing

AgentGateway exposes three surfaces in External mode: admin, proxy, and MCP. global.domain combines with default prefixes to render hostnames, and the chart can auto-add wildcard hosts (*.llm.<domain> / *.mcp.<domain>) for slug-based discovery. DNS, certificates, and cloud controller settings remain operator prerequisites — see Ingress.

What you have taken on

Worth stating plainly, since it is the real cost of running External:
  • Database migrations are yours to run and to roll back
  • Backups of PostgreSQL and ClickHouse are yours
  • Certificate rotation for anything you supplied yourself
  • The bootstrap credential is a standing break-glass path into every team
  • Email deliverability — invitations and password resets stop working silently if it breaks

Known rough edges

External is newer than Hybrid, and these are the issues you are most likely to meet. Each has a workaround in Troubleshooting.
  • Login lockout after repeated failed password attempts
  • SCIM setup screens display the SaaS host instead of yours
  • Alert evaluation does not currently run
  • Firewall complexity state silently disables itself without a Redis URL
  • Trace export returns 404 when the OTLP endpoint includes a signal path
  • imagePullSecrets is only honoured on the console under control-plane-app
  • IAM database auth needs setting per gateway, not only globally

High availability

Availability is a ladder, not a single design. Climb it only as far as the failure you actually have to survive. The three availability tiers side by side. Tier 1, one cluster multi-AZ: node pools in three availability zones, two or more replicas of TrustGate, TrustGuard and Firewall, and managed PostgreSQL and Redis with automatic failover inside the region; it survives node and zone loss and needs no promotion procedure or DNS work. Tier 2, twin clusters in one region: a serving cluster plus a second cluster to roll upgrades through, both against one PostgreSQL primary and one Redis primary shared inside the region; it survives cluster loss and bad upgrades. Tier 3, two regions active/passive: an active region running the only DataAgent, a warm passive region with no traffic, one writable PostgreSQL primary with a cross-region read replica, and region-local Redis per cluster; it survives regional loss and adds DNS promotion and a datastore runbook. Two invariants hold at every tier: exactly one writable PostgreSQL primary, and exactly one active DataAgent per gateway scope.

Tier 1 — one cluster across zones

Spread the node pool over three availability zones, run at least two replicas of the request path and both product control planes, and use managed PostgreSQL and Redis with automatic failover inside the region. No promotion procedure, no DNS work. This is enough for most deployments.

Tier 2 — twin clusters in one region

Two clusters against one PostgreSQL primary and one Redis primary. Sharing both stores inside a region costs no extra hop and keeps state identical from either side, which is what makes a blue/green upgrade safe here: External keeps its entire configuration state in PostgreSQL, so the second cluster is already looking at the same policies the moment it starts.

Tier 3 — two regions, active/passive

External in active/passive across two regions. A traffic manager you operate points the gateway, MCP and console hostnames at the active cluster. Each cluster holds a full copy of every component: the request path, both product control planes, the console and API, its own region-local Redis, and its own in-cluster ClickHouse. Both clusters read and write one shared PostgreSQL primary, which holds configuration, console data, payloads and detections, with a cross-region read replica promoted on failover. Four consequences of a promotion: ClickHouse does not follow it, so each cluster only has the telemetry its own collector received; rate limits are counted per region; console sessions live in the region that issued them; and nothing dials NeuralTrust, because both clusters are self-contained. The two stores are treated differently on purpose. PostgreSQL: one writable primary, one cross-region read replica. It holds every piece of durable state this mode has — configuration, console data, payloads, detections — so a single writer is what prevents split-brain. Both clusters run the same components against the same authoritative state, so there is nothing to reconcile after a promotion. Your RPO is the replication lag. Redis: region-local, one per cluster. Redis carries semantic cache, rate-limit counters and evaluation progress, and it has no persistence requirement. Crossing a region for that on every request costs latency on the hot path to protect data that is worth seconds. After a promotion the new active cluster rebuilds its counters in seconds.
Two consequences to accept: rate limits are counted per region, and console sessions live in the region that issued them, so a promotion signs operators out and they sign in again against the same PostgreSQL.
ClickHouse does not follow a promotion. It is an in-cluster store, so each cluster holds only the telemetry its own collector received. After a failover the console works and enforcement is unaffected, but dashboards show the promoted cluster’s history rather than the failed one’s. Either accept that, or configure ClickHouse replication between the two clusters.

Upgrades and migrations with two clusters

Both consoles run prisma migrate deploy in an init container against the same database. Upgrade one cluster at a time, active first, and let its rollout complete before starting the second. A migration is applied once and the second cluster’s init container no-ops, but running both simultaneously against one schema is worth avoiding — and the console rolls with maxUnavailable: 0, so a failed migration blocks that cluster rather than serving a half-migrated schema.

Promote the passive cluster

1

Detect and fence

Health check both request paths and the console. Remove the failed cluster from the traffic manager so it cannot take traffic during a partial recovery.
2

Point PostgreSQL at a writable primary

If the primary is in the failed region, promote the read replica and update global.postgresql.host in the surviving cluster. If the primary is unaffected, there is nothing to do here. Redis needs no attention either way, because the surviving cluster already has its own.
3

Switch traffic

Route the gateway, MCP, and console hostnames to the promoted cluster. Keep them together.
4

Verify

Sign in to the console, run representative allowed and blocked requests, and confirm new telemetry is arriving in the promoted cluster’s ClickHouse.

Before you rely on it

  • Both clusters are deployed and health checked against the same primary.
  • PostgreSQL is managed, with one primary and a cross-region read replica.
  • Each region has its own managed Redis, and you accept per-region counters and sessions.
  • Certificates and DNS cover the published hostnames from both clusters.
  • You have decided whether ClickHouse history needs replicating.
  • Backups of PostgreSQL and ClickHouse are running and have been restored once.
  • You have rehearsed a promotion, including the datastore step.

Next steps

Configuration

Managed stores, ingress, TLS, and every values switch.

Secrets

Per-service datastore credentials and what the chart generates.

Requirements

Dependencies, ports, and capacity.

Cloud notes

Ingress, certificates, and managed stores per provider.

Troubleshooting

Login, migration, and telemetry failures.