On-prem multi-tenant setup: URLs, environment and enrollment
This guide covers an organization running multiple tenants on its own infrastructure,
with Cortega AI Booster, Endpoint Guard and the VS Code AI Booster Pack. Multi-tenancy
is independent of hosting: an on-prem installation can serve multiple tenants.
AI Verifier is no longer a standalone desktop product; use Cortega AI Booster.
Existing ai_verifier, verifier_seats, ai_verifier_user and /api/v1/verifier/*
identifiers remain in the implementation. Do not rename configuration or API paths.
1. Choose the configuration file
| Install path | File the operator edits | What produces runtime .env |
|---|---|---|
| Existing Docker hosts / on-prem | Copy deploy/.env.example to repository-root .env | Operator, including addressing and secrets |
| AWS scripts | deploy/aws/.env, copied from deploy/aws/env.example | deploy/aws/deploy.sh |
| Terraform | Cloud-specific .env from deploy/terraform/env.example, plus Terraform inputs | deploy/terraform/deploy.sh |
| Local source development | Repository-root .env from .env.example | Operator; localhost defaults are development settings |
For on-prem, run compose from the repository root with --env-file .env.
Container env_file: ../.env is relative to deploy/. LLM provider environment keys
belong in repository-root .env.provider-keys, consumed only by gateway containers.
Pin the Cortega images to the release you deploy and use its matching gateway pin.
Generate separate secrets for CORTEGA_ENCRYPTION_KEY, JWT_SECRET, INTERNAL_SECRET,
OIDC_COOKIE_SECRET and CORTEGA_CLIENT_TELEMETRY_TOKEN; openssl rand -hex 32
produces 64 hex characters. Match POSTGRES_PASSWORD to the password in DATABASE_URL.
Keep MANAGEMENT_BACKEND_URL, REDIS_URL, CLICKHOUSE_URL, PROMETHEUS_URL and
OTEL_COLLECTOR_URL as private service addresses, adjusted for your network topology.
Never substitute the public app URL for a private database or backend address.
2. Reserve DNS names and TLS certificates
Use Cortega's hosted naming pattern as the concrete example: open
live.cortega.ai to see the application. The app,
gateway frontends and telemetry each use one subdomain directly under cortega.ai.
There is no extra ai level and no tenant name in these shared service URLs.
For your on-prem installation, replace cortega.ai with a domain you control:
live.example.com, gw.example.com, mcp-gw.example.com,
http-gw.example.com and telemetry.example.com. Do not point customer devices at
Cortega's hosted service when enrolling them into your own installation. Your DNS
names must be reachable from employee browsers, native services and developer tools
(including VPN users). External here means reachable by clients; private DNS is fine.
| Client URL / hostname | Route at your TLS nginx / load balancer | Configuration |
|---|---|---|
https://live.cortega.ai | Core edge-ingress on WEBAPP_HTTP_PORT, then webapp nginx → webapp-backend → management-backend for /api/v1/*; SPA for / | CORTEGA_APP_ORIGIN, CORS_ORIGINS; native cortega_url; VS Code cortega.ai.serverUrl |
https://gw.cortega.ai | Gateway ingress, LLM frontend (gateway port 4001); /v1/* is preserved | Gateway suffix, LLM template and advertised scheme/port |
https://mcp-gw.cortega.ai | Gateway ingress, MCP frontend (port 3001); preserve /mcp and OAuth discovery paths | MCP host template |
https://http-gw.cortega.ai | Gateway ingress, HTTP frontend (port 5001), including configured browser/OIDC routes | HTTP host template |
https://telemetry.cortega.ai | Core telemetry-ingest host bind on 4320; container listens on 4318 | CORTEGA_CLIENT_TELEMETRY_URL, CLIENT_TELEMETRY_HTTP_BIND |
App /api/v1/edge/sso/oauth/callback | Same app API chain; preserve host, scheme and path | Register the full URL in each applicable IdP OAuth client |
App /api/v1/auth/sso/callback | Same app API chain | SSO_REDIRECT_URL for console login |
App /api/v1/verifier/* | Same app API chain, including browser sign-in, token polling, config, device registration and Projects/Legal MCP | Existing API paths used by AI Booster Pack; these are not the gateway /mcp frontend |
| Insights public URL, when enabled | Your Superset reverse proxy, with embedding supported | SUPERSET_PUBLIC_BASE_URL, SUPERSET_EMBED_ALLOWED_DOMAINS; private SUPERSET_BASE_URL stays server-side |
For multiple gateway groups, reserve all three frontend names per group. With the
example templates, group engineering adds engineering.cortega.ai,
mcp-engineering.cortega.ai and http-engineering.cortega.ai.
Do not assume nginx supports arbitrary tenant subdomains or tenant URL prefixes:
tenant assignment comes from authenticated identities, domains, invitations and CAs.
A shared app origin and shared gateway hosts can serve multiple tenants.
With this naming pattern, a wildcard certificate for *.example.com can cover
all five customer hostnames, including live.example.com. Multi-tenancy does not
require a second level of subdomains. Keep the single-level templates below and
check each generated hostname against your DNS and certificate SANs.
3. Set public addressing and tenant mode
The following shows the shared cortega.ai hostname pattern. For your on-prem
runtime .env, replace every cortega.ai with your own domain before applying it,
and add the images, secrets and datastore settings. These values illustrate URL
configuration; they are not a complete copy of Cortega's production .env.
CORTEGA_MULTI_TENANCY_ENABLED=true
CORTEGA_APP_ORIGIN=https://live.cortega.ai
CORS_ORIGINS=https://live.cortega.ai
SESSION_MODE=cookie
COOKIE_SECURE=true
CORTEGA_COOKIE_SECURE=true
CORTEGA_COOKIE_DOMAIN=
SELF_SERVE_SIGNUP_ENABLED=false
CORTEGA_GATEWAY_HOST_SUFFIX=cortega.ai
CORTEGA_GATEWAY_PUBLIC_SCHEME=https
CORTEGA_GATEWAY_HTTP_PORT=443
CORTEGA_GATEWAY_EXTERNAL_ENABLED=yes
CORTEGA_INSTALL_NGINX=yes
GATEWAY_INGRESS_HOST_SUFFIX=cortega.ai
GATEWAY_INGRESS_PUBLIC_SCHEME=https
GATEWAY_INGRESS_HTTP_PORT=443
GATEWAY_INGRESS_LLM_HOST_TEMPLATE={gateway}.{suffix}
GATEWAY_INGRESS_MCP_HOST_TEMPLATE=mcp-{gateway}.{suffix}
GATEWAY_INGRESS_HTTP_HOST_TEMPLATE=http-{gateway}.{suffix}
CORTEGA_CLIENT_TELEMETRY_URL=https://telemetry.cortega.ai
EDGE_TRUST_PROXY_CLIENT_CERT_HEADER=false
EDGE_CLIENT_CERT_HEADER=X-Cortega-Edge-Client-Cert
The CORTEGA_GATEWAY_* values are managed-install inputs; gateway-compose.sh
reads the GATEWAY_INGRESS_* runtime values. Keep both sets consistent when filling
an on-prem file manually. Advertised HTTPS/443 does not itself install a TLS
listener: nginx routing, certificate files and published host binds must be supplied.
GATEWAY_INGRESS_BIND in the gateway compose file controls its private nginx listener,
and GATEWAY_PUBLIC_HTTP_BIND controls its other published listener; neither is the
advertised client port. WEBAPP_HTTP_PORT is the core ingress bind, not the public origin.
GATEWAY_INGRESS_ADVERTISE_GROUP can choose the group advertised to clients. For
separate private gateway addressing, set GATEWAY_INGRESS_INTERNAL_HOST_SUFFIX,
GATEWAY_INGRESS_INTERNAL_SCHEME, GATEWAY_INGRESS_INTERNAL_PORT and the three
GATEWAY_INGRESS_INTERNAL_*_HOST_TEMPLATE values. They default to public addressing;
verify that server-side callers can actually resolve and reach those URLs.
For one host, choose non-conflicting binds: for example core WEBAPP_HTTP_PORT=127.0.0.1:8080,
gateway GATEWAY_PUBLIC_HTTP_BIND=127.0.0.1:8081, private gateway
GATEWAY_INGRESS_BIND=127.0.0.1:18081, and telemetry
CLIENT_TELEMETRY_HTTP_BIND=127.0.0.1:4320. A host nginx can route to these ports.
If nginx runs on a different host, use reachable private-IP binds and firewall rules
allowing that proxy. Loopback binds cannot be reached across machines. Use
INSTALL_TOPOLOGY=multi only when gateways are on separate hosts; it is unrelated
to the number of tenants. Configure sidecar management, Redis and OTEL reachability
for those hosts as described in the load balancer guide.
Multi-tenant mode enables per-tenant model access controls and disables Pass-Through Routing, MCP OAuth, and gateway Basic Auth/OIDC identities. It does not disable console SSO or device SSO enrollment. Keep email self-serve signup disabled for an admin-provisioned enterprise rollout: that separate flow has its own tenant-creation behavior and is not the device enrollment flow below.
4. Configure nginx, rather than only editing .env
The AWS and Terraform deploy scripts generate app, gateway, telemetry and optional
Superset nginx routes. Running the existing-environment compose files alone does not
run those generators. In particular docker-compose.aws-lb.yml mounts
deploy/nginx/lb.d: supply the configuration before starting that service. Gateway
compose mounts deploy/nginx/conf.d, whose default has no per-group routes until
rendered by gateway-compose.sh. Render/start the desired gateway groups using that
script with the correct base compose file and network settings. Use deploy/gateway-compose.sh plan gw=1 to review the desired group, then
deploy/gateway-compose.sh up gw=1 with the appropriate
CORTEGA_COMPOSE_FILE for that host. See the load balancer guide for the deployed release.
For an existing host nginx, these are the essential proxy directives for each host
(use the upstream from the URL table; this example is the app on the same host).
Replace live.cortega.ai with your own app hostname before installing it:
server {
listen 443 ssl;
server_name live.cortega.ai;
ssl_certificate /etc/nginx/certs/server.pem;
ssl_certificate_key /etc/nginx/certs/server-key.pem;
client_max_body_size 40m;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}
Use separate host blocks for gateway and telemetry routes. Preserve authorization
headers and paths, disable buffering for LLM/MCP streaming, and support WebSocket
upgrade for frontends that need it. If another trusted proxy terminates TLS first,
preserve its verified external scheme instead of replacing it with the internal
HTTP hop's scheme. Wrong scheme/host headers generate wrong OAuth callback URLs.
Keep database, Redis, gateway admin/metrics, and the raw OTEL collector private.
Client telemetry must use telemetry-ingest, which derives tenant/person identity
from the device credential; do not expose the collector or share its internal token
with users. Set CORTEGA_CLIENT_TELEMETRY_TOKEN before starting the packaged core:
the client collector requires it even before a tenant enables collection.
For certificate enrollment, use deploy/nginx/edge-mtls.conf.example as the mTLS
reference. Configure the trusted Enrollment CA bundle and optional client
certificates so ordinary browsers can still log in. Set
EDGE_TRUST_PROXY_CLIENT_CERT_HEADER=true only after the TLS terminator validates
client certificates and overwrites X-Cortega-Edge-Client-Cert with its verified,
escaped certificate. Prevent access around that terminator and strip spoofable
client-cert headers on other ingress paths. For an ALB certificate header use
EDGE_CLIENT_CERT_HEADER=X-Amzn-Mtls-Clientcert. Enrollment CA trust is separate
from the local interception CA installed on a workstation.
EDGE_OBSERVABILITY_URL is an optional fallback. The backend first chooses a
registered public gateway HTTP URL, then this fallback, then the request origin.
Check the actual observability_url returned to an enrolled device; a fallback
will not override an already registered public gateway URL.
5. Prepare tenants and their sign-in providers
- Activate the installation license with the required Endpoint Guard/AI Booster features and sufficient installation Edge seats.
- As Tenant 0/platform admin, create an active tenant for each organization or business unit. Enable the products, AI Booster package/seat capacity and agents that tenant needs. Installation licensing and tenant entitlements both apply.
- Assign tenant teams, provider/model access, budgets and the default routing choice. Configure Endpoint Guard → AI Assistants separately for each tenant: which tools are routed, usage telemetry, prompt/response collection and optional repository inventory. Sign-in alone does not turn every collection option on.
- Create users in the correct tenant, or assign its permitted SSO registration domains in Tenant Management. Domain lists are edited by Tenant 0 admins. When several tenants share one email domain, pre-provision exact users or use tenant-specific invitations/certificates; a domain alone cannot distinguish them.
- Configure a tenant SSO provider under Endpoint Guard → Enrollment, or the
installation fallback via
SSO_ENABLED,SSO_ISSUER_URL,SSO_CLIENT_ID,SSO_CLIENT_SECRETandSSO_REDIRECT_URL. Register the Edge OAuth callback in the OAuth client used for device/Pack sign-in, even when console login works.
Device SSO resolves an existing user's tenant before matching registration domains. Unknown, unmatched emails become pending access requests; device SSO does not silently create a new tenant. Pending Tenant 0 requests require administrator approval or assignment before retrying. Suspended users and inactive tenants cannot enroll.
6. Register AI Booster, Endpoint Guard and VS Code
| Client | Enrollment | What is recorded |
|---|---|---|
| AI Booster desktop | Enterprise sign-in with your app origin or a tenant-specific invitation, then browser SSO | Native person-device registration in Endpoint Guard → Seats |
| Endpoint Guard headless | MDM installs package, per-device certificate/private key and cortega_url; alternatively an administrator imports a generated identity | Native registration; trusted Enrollment CA identifies the tenant |
| AI Booster Pack in VS Code | Set cortega.ai.serverUrl to app origin, then Cortega AI Booster Pack: Sign in | Separate VS Code person-computer registration, labeled VS |
For AI Booster invitations, create a link for the intended email and tenant under
Enrollment. The link has the form
https://live.cortega.ai/api/v1/edge/sso/i/<code>. Paste it into the
native app's Enterprise sign-in field. Browser SSO must prove the invited email;
the invitation carries the tenant. For domain-based enrollment enter the bare app
origin. Never leave the server blank on an on-prem client, since client defaults
point to the hosted service.
For headless Endpoint Guard, follow the certificate import/MDM instructions in the
Endpoint Guard deployment guide. Shared EDGE_AGENT_ENROLLMENT_KEY enrollment is a
legacy compatibility path: it proves possession of a shared secret, not a unique
managed identity. Prefer tenant-specific certificates for a multi-tenant fleet.
The VS Code Pack uses /api/v1/verifier/auth/start: it asks for an email, displays a
user code and opens the verification page. Confirm the browser code matches. The
backend chooses that user's tenant IdP, else a domain-matched tenant IdP, else the
installation IdP. SSO must return the typed email. The Pack polls
/api/v1/verifier/auth/token, receives a session/rotating refresh credential, and
calls POST /api/v1/verifier/me/device with VS Code's computer ID. It configures
supported local AI tools from /api/v1/verifier/me/config; run Sync AI tools with
Cortega and restart the tools. See the coding-tools guide for adapter coverage,
shared profiles, undo and repair.
There are two capacity checks for the Pack: sign-in grants the legacy
ai_verifier_user role, limited by the tenant's verifier_seats; VS device
registration also checks Endpoint Guard/AI Booster tenant capacity and the
installation edge_devices limit. A repeated sign-in on the same computer by the
same person reuses the VS registration; another person or computer takes another.
Native and VS registrations have different device identities, so deploying both
can create separate Seats rows. Plan capacity for the actual client combination.
Removing a VS row alone does not disable the user; the next sync can recreate it.
Removing the role frees the role-based allocation; disable the account to stop
further sign-ins. A full allocation returns 402 seats_full.
The Pack configures tools and usage reporting; it does not install a machine-wide TLS interception service. Native Endpoint Guard supplies that enforcement. The AI Booster desktop app exposes Projects, usage, query analysis/log and settings; verification notifications/document-check screens are no longer its primary UI.
7. Optional service URLs and on-prem egress
| Setting | Purpose and requirement |
|---|---|
ASSIST_MCP_BASE_URL, ASSIST_MCP_INTERNAL_TOKEN | Core → separately deployed Cortega Assist REST API. Blank URL hides Diagnose. Token must match Assist's server configuration. |
Assist public /mcp | Separate MCP client route; deploy behind a dedicated governed gateway frontend with rate limits. Core app nginx does not automatically host Assist. |
MODEL_INTEL_BASE_URL, MODEL_INTEL_API_TOKEN | Core → independent Model Intelligence replica; optional recommendations/capability metadata. |
COURTLISTENER_BASE_URL, COURTLISTENER_API_TOKEN | Legal agent case-law lookup, when enabled. Optional fallback URL/token and LEGAL_DATA_BASE_URL/LEGAL_DATA_API_TOKEN cover replica/additional-source use. |
CORTEGA_CCM_URL | License activation/phone-home; default Cloud Manager endpoint is https://cloudmgr.cortega.ai. Plan reachability for activation. |
SSO_ISSUER_URL / tenant IdP | Core and browser reachability to the chosen identity provider. |
EMAIL_FROM + Resend or EMAIL_SMTP_* | Email delivery for workflows that send mail; optional unless those workflows are used. |
Assist, Model Intelligence and Legal Data are independent deployments, not services included in the core release package. Internal service URLs need only be reachable by the caller; public MCP endpoints need client DNS/TLS. An on-prem deployment is not automatically an air-gapped deployment: also account for image downloads, configured LLM providers, IdP, license activation and any enabled external sources.
8. Acceptance check before customer rollout
- From a workstation on the actual client network, verify app, each gateway
frontend and telemetry DNS/TLS. Confirm
/api/v1/healthworks through the app proxy; confirm nginx upstreams and public advertised URLs match. - Sign in to the console, then AI Booster and VS Code using test users from two tenants. Verify both OAuth callbacks, correct tenant assignment and Seats rows. Test an unmatched email, a full tenant and a disabled tenant.
- Route one supported assistant through the gateway. Verify the selected team/model, tenant, key attribution and budget/guardrail behavior in observability, not only that configuration was saved. Test that the other tenant cannot use its resources.
- Enable tenant usage telemetry and confirm it reaches the correct tenant/person; prompt text appears only when collection is enabled. Verify native captured traffic separately from configured assistant gateway traffic.
- Enroll one MDM/certificate pilot and inspect its identity method and tenant. Confirm browsers still work without a client certificate and spoofed headers cannot bypass the terminator.
- If using Assist, confirm its
/readyz, console Diagnose and an MCP query about this setup. Deploy a rebuilt knowledge pack with the customer release.
Troubleshooting
| Symptom | Likely cause / action |
|---|---|
| Console works but device/Pack SSO fails | Edge OAuth callback is missing from the applicable tenant or fallback IdP, or nginx changes host/scheme. |
| Sign-in goes to hosted Cortega | Native server field or cortega.ai.serverUrl still uses its default; set your full app origin. |
403 account_pending | Unknown domain or pending Tenant 0 request; assign/approve the user, then retry SSO. |
402 seats_full | Check role-based AI Booster allocation, person-device registrations and installation Edge limit. |
| App loads but AI tools fail | App and gateway URLs use separate nginx routes; check the advertised LLM frontend and streaming support. |
| No assistant usage | Check tenant telemetry toggle, returned telemetry URL, telemetry-ingest route and internal collector token. |
| Certificates enroll into the wrong tenant | Verify the issuing Enrollment CA and the explicit user assignment; avoid a shared domain as the only tenant selector. |
| Proxy cannot reach a healthy container | Host bind is loopback on a different machine, port conflict, wrong Docker network, or private firewall rule. |