Documentation Index
Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
Use this file to discover all available pages before exploring further.
KaireonAI reads ~169 environment variables across the Next.js platform (platform/src/), the BullMQ worker (platform/src/worker/), the MCP server (platform/src/mcp/), the license-generator script (platform/scripts/), and the Python ml-worker (ml-worker/). This page is the canonical index — every variable below was verified by grepping the codebase against the cited file:line.
What it documents
Each variable below has a name, source file:line of its first read, default value (literal ||/?? fallback in the code, or — when there is none), and a one-line purpose. Variables are grouped by domain so an operator can configure a single subsystem without scrolling the whole page. Subsystem deep-dives live on /platform/ops/configuration-reference and /deployment/helm-reference; this page indexes everything in one place and cross-links to the deep-dive where one exists.
Quick start
The minimum to boot the platform locally:
DATABASE_URL=postgresql://user:pass@localhost:5432/kaireon
NEXTAUTH_SECRET=<random 32+ char string>
JWT_SIGNING_SECRET=<random 32+ char string>
CONNECTOR_ENCRYPTION_KEY=<random 32+ char string>
WEBHOOK_SIGNING_SECRET=<random 32+ char string>
API_KEY_PEPPER=<random 32+ char string>
DATABASE_URL is the only variable required outside production (platform/src/lib/env-validation.ts:18); the other five become required when NODE_ENV=production (platform/src/lib/env-validation.ts:19-23).
Production checklist (in addition to the six above):
REDIS_URL — required for caching, rate limiting, and the BullMQ worker queue (platform/src/worker/index.ts:27).
CORS_ALLOWED_ORIGINS — must be a non-empty, non-wildcard list in production or validateEnvFormats warns (platform/src/lib/env-validation.ts:91-93).
EVENT_PUBLISHER + the matching backend group (KAFKA_*, MSK_*, EVENTBRIDGE_*, KINESIS_*) — defaults to redis (platform/src/lib/infra/container.ts:50).
INTERACTION_STORE + the matching backend group (SCYLLA_*, DYNAMODB_*, KEYSPACES_*) — defaults to pg (platform/src/lib/infra/container.ts:123).
SEARCH_INDEX + OPENSEARCH_* when running OpenSearch — defaults to pg (platform/src/lib/infra/container.ts:186).
OTEL_EXPORTER_OTLP_ENDPOINT — when shipping traces (platform/src/lib/tracing.ts:63).
- A SIEM block (
SIEM_BACKEND + SIEM_ENDPOINT + optional SIEM_API_KEY/SIEM_SOURCETYPE/SIEM_INDEX) — when shipping audit logs to Splunk/Datadog/Elastic (platform/src/lib/audit/sink.ts:48-67).
How it works
platform/src/lib/env-validation.ts:35 runs at process startup (imported eagerly from prisma.ts). Two passes:
- Presence —
validateEnv() iterates the REQUIRED_ENV_VARS list at env-validation.ts:17-26. Production-required keys missing → throw. Development-required keys missing → warn-only.
- Format —
validateEnvFormats() at env-validation.ts:72 checks DATABASE_URL starts with postgresql://, REDIS_URL starts with redis:///rediss://, PORT is in [1, 65535], CORS_ALLOWED_ORIGINS is non-empty/non-wildcard in production, and any OPTIONAL_BOOLEAN_VARS are literally "true" or "false".
The build phase (NEXT_PHASE === "phase-production-build") skips validation entirely (env-validation.ts:37) so next build does not need production secrets.
Subsystem env reads happen lazily at first use:
- The DI container at
platform/src/lib/infra/container.ts reads EVENT_PUBLISHER, INTERACTION_STORE, and SEARCH_INDEX once-per-process to pick the backend, then reads the backend-group vars to construct the client.
- The Prisma pool at
platform/src/lib/prisma.ts:21 reads PG_POOL_MAX once at module load.
- Per-request env reads (e.g.
process.env.NEGOTIATION_GLOBAL_KILL at negotiate/apply/route.ts:121, process.env.ALLOW_UNSIGNED_WEBHOOKS at webhooks/delivery/route.ts:20) hit process.env on every call. Operators changing those vars need a process restart only when the read is module-scoped, not per-request.
Tier behavior:
NODE_ENV | What changes |
|---|
production | Required keys throw on missing. CORS wildcard rejected. CSP unsafe-eval removed. Encryption fallback key disabled (encryption.ts:36-42). |
development | Required keys warn-only. CSP allows unsafe-eval for React stack traces. Deterministic encryption fallback key allowed. |
test | Same as development plus rate limiter is disabled and RLS_AUTO_ENABLE is force-skipped (prisma.ts:81). |
Reference
Database and migrations
| Env Var | Default | Read From | Purpose |
|---|
DATABASE_URL | — | platform/src/lib/prisma.ts (via prisma.config.ts); also platform/src/app/api/v1/cron/cleanup/route.ts:105 | PostgreSQL connection string. Required in all tiers. |
PG_POOL_MAX | 50 | platform/src/lib/prisma.ts:21 | pg pool max connection count. |
RLS_AUTO_ENABLE | enabled (skipped only when "false") | platform/src/lib/prisma.ts:82 | Auto-enable Postgres Row-Level Security on all tenant tables at boot. Set to "false" to skip; never disable in production. |
Auth and MFA
| Env Var | Default | Read From | Purpose |
|---|
NEXTAUTH_URL | https://playground.kaireonai.com | platform/src/app/api/v1/auth/verify/route.ts:6; also platform/src/lib/email.ts:19 | Public canonical URL for NextAuth callbacks and outbound email links. |
NEXTAUTH_SECRET | — | platform/src/middleware.ts:200 | NextAuth.js session-cookie signing secret. Required in production. |
JWT_SIGNING_SECRET | — | platform/src/lib/oauth.ts:17 | HMAC key for OAuth2 JWT issue + verify. Required in production. Falls back to NEXTAUTH_SECRET when unset. |
API_KEY_PEPPER | — | platform/src/lib/oauth.ts:39 | HMAC pepper for client-secret hashing. Required in production. |
AUTH_URL | http://localhost:3000 | platform/src/lib/auto-seed.ts:42 | Auth.js base URL used by the auto-seed dispatcher when NEXTAUTH_URL is unset. |
GOOGLE_CLIENT_ID | — | platform/src/lib/auth.ts:34 | Google OAuth client id (sign-in via Google). |
GOOGLE_CLIENT_SECRET | — | platform/src/lib/auth.ts:34 | Companion to GOOGLE_CLIENT_ID. |
MFA_ENFORCEMENT_DISABLED | — | platform/src/middleware.ts:196 | Test-only flag. Disables MFA gate so integration tests can write without TOTP. Never set in production. |
CONNECTOR_ENCRYPTION_KEY | — (required in prod) | platform/src/lib/settings/platform-settings.ts:12 | AES-256 raw key for encrypting connector secrets at rest. SHA-256 derived to a 32-byte AES key (encryption.ts:26). |
CONNECTOR_ENCRYPTION_KEY_VERSION | 1 | platform/src/lib/encryption.ts:30 | Versioned-secret rotation pointer for the active key. |
CONNECTOR_ENCRYPTION_KEY_PREVIOUS | — | platform/src/lib/encryption.ts:31 | Previous key, used to decrypt rows still tagged with the prior version. |
CONNECTOR_ENCRYPTION_KEY_PREVIOUS_VERSION | 0 | platform/src/lib/encryption.ts:32 | Version number paired with CONNECTOR_ENCRYPTION_KEY_PREVIOUS. |
WEBHOOK_SIGNING_SECRET | — (required in prod) | platform/src/app/api/v1/journeys/callback/[token]/route.ts:77 | HMAC-SHA256 secret for outbound + inbound webhook signature verification. Also gates the delivery webhook endpoint. |
ALLOW_UNSIGNED_WEBHOOKS | — | platform/src/app/api/v1/webhooks/delivery/route.ts:20 | Dev-only override. When "true" and NODE_ENV !== "production", accepts unsigned webhooks. Hard-blocked in production. |
Network, CORS, and CSP
| Env Var | Default | Read From | Purpose |
|---|
NODE_ENV | — | platform/src/middleware.ts:47 | Standard Node runtime tier (production / development / test). Drives env-validation strictness, CSP, encryption fallback. |
NEXT_PHASE | — | platform/src/lib/env-validation.ts:10 | Next.js framework env. phase-production-build skips env-validation so next build does not need secrets. |
NEXT_RUNTIME | — | platform/src/instrumentation.ts:16 | Next.js framework env. Differentiates Edge runtime vs Node runtime when wiring instrumentation. |
NEXT_PUBLIC_APP_URL | http://localhost:3000 | platform/src/lib/ai/tools/studio-tools.ts:8 | Public app URL exposed to the browser bundle. Used by Studio AI tools for internal fetch base. |
NEXT_PUBLIC_BASE_URL | http://localhost:3000 | platform/src/lib/ai/tools/data-tools.ts:15 | Companion to NEXT_PUBLIC_APP_URL used by Data AI tools. |
PORT | — (Next.js default 3000) | platform/src/lib/env-validation.ts:85 | HTTP listen port. Validated as integer in [1, 65535]. |
CORS_ALLOWED_ORIGINS | — (deny all cross-origin) | platform/src/middleware.ts:33 | Comma-separated allowlist of cross-origin domains. Wildcard * rejected in production (middleware.ts:46-50). |
CSP_DISABLED | — | platform/src/middleware.ts:79 | Set to "true" to skip CSP header (dev only). |
CSP_POLICY | computed default | platform/src/middleware.ts:82 | Override the default Content Security Policy string. |
INTERNAL_SERVICE_SECRET | — | platform/src/middleware.ts:227 | Shared secret for service-to-service requests inside the cluster. |
INTERNAL_API_URL | — | platform/src/lib/seed-executor.ts:64 | Base URL for service-to-service API calls from the worker back into the platform. Falls back to NEXTAUTH_URL, then http://localhost:3000. |
API_KEY | — | platform/src/middleware.ts:235 | Outbound system API key used by internal jobs that talk to themselves over HTTP. |
Redis, caching, and rate limits
| Env Var | Default | Read From | Purpose |
|---|
REDIS_URL | — (cache disabled) | platform/src/lib/env-validation.ts:80; also platform/src/worker/index.ts:27 (default redis://localhost:6379) | Redis/Valkey connection string. Required for the BullMQ worker; optional for the platform (cache falls back to in-memory). |
RATE_LIMIT_TIER | — | platform/src/lib/api-rate-limit.ts:39 | Per-tenant rate-limit tier override. Used by the unified sliding-window limiter. |
SLOW_API_THRESHOLD_MS | 150 | platform/src/lib/api-instrumentation.ts:20 | Per-route warn threshold in milliseconds. Routes exceeding this log a slow_api warn. |
Event publisher (Kafka / MSK / EventBridge / Kinesis / Redpanda)
EVENT_PUBLISHER selects the backend; only the matching group is read.
| Env Var | Default | Read From | Purpose |
|---|
EVENT_PUBLISHER | redis | platform/src/lib/infra/container.ts:50 | Backend selector — redis / kafka / redpanda / msk / eventbridge / kinesis. |
KAFKA_BROKERS | — | platform/src/lib/infra/container.ts:56 | Comma-separated host:port list. |
KAFKA_CLIENT_ID | — | platform/src/lib/infra/container.ts:57 | KafkaJS client id. |
KAFKA_TLS_ENABLED | — | platform/src/lib/infra/container.ts:58 | "true" to enable TLS. |
KAFKA_SASL_MECHANISM | — | platform/src/lib/infra/container.ts:59 | plain / scram-sha-256 / scram-sha-512. |
KAFKA_SASL_USERNAME | — | platform/src/lib/infra/container.ts:60 | SASL username. |
KAFKA_SASL_PASSWORD | — | platform/src/lib/infra/container.ts:61 | SASL password. |
KAFKA_CONSUMER_GROUP_ID | — | platform/src/lib/infra/container.ts:62 | Consumer group id (for inbound consumer wiring). |
MSK_BROKERS | — | platform/src/lib/infra/container.ts:70 | AWS MSK bootstrap broker list. |
MSK_REGION | us-east-1 | platform/src/lib/infra/container.ts:71 | AWS region. |
MSK_AUTH_MODE | iam_role | platform/src/lib/infra/container.ts:72 | iam_role / sasl. |
MSK_ROLE_ARN | — | platform/src/lib/infra/container.ts:73 | IAM role ARN when MSK_AUTH_MODE=iam_role. |
MSK_SASL_USERNAME | — | platform/src/lib/infra/container.ts:74 | SASL username when MSK_AUTH_MODE=sasl. |
MSK_SASL_PASSWORD | — | platform/src/lib/infra/container.ts:75 | SASL password when MSK_AUTH_MODE=sasl. |
MSK_CONSUMER_GROUP_ID | — | platform/src/lib/infra/container.ts:76 | Consumer group id. |
MSK_TOPIC_PREFIX | — | platform/src/lib/infra/container.ts:77 | Topic name prefix. |
EVENTBRIDGE_AUTH_MODE | iam_role | platform/src/lib/infra/container.ts:85 | iam_role / access_key. |
EVENTBRIDGE_REGION | us-east-1 | platform/src/lib/infra/container.ts:86 | AWS region. |
EVENTBRIDGE_ROLE_ARN | — | platform/src/lib/infra/container.ts:87 | IAM role ARN. |
EVENTBRIDGE_ACCESS_KEY_ID | — | platform/src/lib/infra/container.ts:88 | Static credentials when auth_mode=access_key. |
EVENTBRIDGE_SECRET_ACCESS_KEY | — | platform/src/lib/infra/container.ts:89 | Companion to access key id. |
EVENTBRIDGE_BUS_NAME | — | platform/src/lib/infra/container.ts:90 | Target event bus name. |
EVENTBRIDGE_DETAIL_TYPE_PREFIX | — | platform/src/lib/infra/container.ts:91 | Prepended to every emitted detail-type. |
KINESIS_AUTH_MODE | iam_role | platform/src/lib/infra/container.ts:99 | iam_role / access_key. |
KINESIS_REGION | us-east-1 | platform/src/lib/infra/container.ts:100 | AWS region. |
KINESIS_ROLE_ARN | — | platform/src/lib/infra/container.ts:101 | IAM role ARN. |
KINESIS_ACCESS_KEY_ID | — | platform/src/lib/infra/container.ts:102 | Static credentials when auth_mode=access_key. |
KINESIS_SECRET_ACCESS_KEY | — | platform/src/lib/infra/container.ts:103 | Companion to access key id. |
KINESIS_STREAM_NAME | kaireon-events | platform/src/lib/infra/container.ts:104 | Target stream. |
KINESIS_PARTITION_KEY | — | platform/src/lib/infra/container.ts:105 | Field name extracted from event payload as the partition key. |
AI providers
The platform uses generic env keys AI_PROVIDER / AI_MODEL / AI_API_KEY / AI_BASE_URL rather than vendor-native keys (OPENAI_API_KEY, ANTHROPIC_API_KEY). The vendor-native keys are not read directly — apiKey is passed into the SDK constructor. Tenant-level overrides in the Settings UI take precedence over env (platform/src/lib/ai/provider.ts:20-23).
| Env Var | Default | Read From | Purpose |
|---|
AI_PROVIDER | google | platform/src/lib/ai/provider.ts:20-23, 46-49 (via AI_ENV.PROVIDER at types.ts:15) | Provider selector — google / anthropic / openai / ollama / lm_studio / bedrock. |
AI_MODEL | gemini-2.5-flash | platform/src/lib/ai/provider.ts:20-23, 46-49 (via AI_ENV.MODEL at types.ts:16) | Model id passed to the provider SDK. |
AI_API_KEY | — | platform/src/lib/ai/provider.ts:20-23, 46-49 (via AI_ENV.API_KEY at types.ts:17) | API key for the selected provider. |
AI_BASE_URL | — (Ollama default http://127.0.0.1:11434/api) | platform/src/lib/ai/provider.ts:20-23, 46-49 (via AI_ENV.BASE_URL at types.ts:18) | Override base URL — required for Ollama and LM Studio. |
ML_WORKER_URL | — | platform/src/app/api/v1/ai/analyze/[type]/route.ts:68 | Python ml-worker base URL (FastAPI service hosting ONNX scoring + LightGBM training). |
ML_WORKER_API_KEY | — | platform/src/lib/ml-worker-client.ts:95 | Bearer token for ml-worker requests. |
ML_WORKER_TIMEOUT_MS | — | platform/src/lib/ml-worker-client.ts:86 | Request timeout for ml-worker calls. |
Scoring and ONNX
| Env Var | Default | Read From | Purpose |
|---|
ONNX_BLOB_STORE_URL | — | platform/src/lib/scoring/onnx-blob-store.ts:161 | ONNX BYO model store URL. Supports file:// and s3:// schemes. Unset = inline storage in modelState JSON. Throws on unrecognized schemes. |
ONNX_INLINE_BYTES_LIMIT | 10485760 (10 MB) | platform/src/lib/scoring/onnx-blob-store.ts:187 | Threshold above which model bytes are offloaded to the blob store rather than inlined into modelState. |
RETRAIN_EVERY_N | 100 | platform/src/app/api/v1/respond/route.ts:48 | Per-tenant outcome-debounce. Bayesian, Thompson, epsilon-greedy, and online-learner models recompute every Nth outcome. Set to 1 for true real-time. |
ATTRIBUTION_TIMEOUT_MS | 5000 | platform/src/app/api/v1/respond/route.ts:647 | Timeout for the recommendation lookup that backs outcome attribution. |
Interaction store (Postgres / DynamoDB / Keyspaces / ScyllaDB)
INTERACTION_STORE selects the backend; only the matching group is read.
| Env Var | Default | Read From | Purpose |
|---|
INTERACTION_STORE | pg | platform/src/lib/infra/container.ts:123 | Backend selector — pg / dynamodb / keyspaces / scylla / cassandra. |
SCYLLA_CONTACT_POINTS | localhost | platform/src/lib/infra/container.ts:129 | Comma-separated host list. |
SCYLLA_LOCAL_DATACENTER | datacenter1 | platform/src/lib/infra/container.ts:130 | Local datacenter for token-aware routing. |
SCYLLA_KEYSPACE | kaireon | platform/src/lib/infra/container.ts:131 | Target keyspace name. |
SCYLLA_USERNAME | — | platform/src/lib/infra/container.ts:132 | Auth username. |
SCYLLA_PASSWORD | — | platform/src/lib/infra/container.ts:133 | Auth password. |
SCYLLA_TLS_ENABLED | — | platform/src/lib/infra/container.ts:134 | "true" to enable TLS. |
SCYLLA_REPLICATION_FACTOR | — | platform/src/lib/infra/container.ts:135 | Keyspace replication factor (integer). |
SCYLLA_CONSISTENCY_LEVEL | — | platform/src/lib/infra/container.ts:138 | e.g. LOCAL_QUORUM. |
SCYLLA_POOL_SIZE | — | platform/src/lib/infra/container.ts:139 | Driver connection pool size. |
SCYLLA_REQUEST_TIMEOUT_MS | — | platform/src/lib/infra/container.ts:142 | Per-request timeout. |
DYNAMODB_TABLE_NAME | kaireon-interactions | platform/src/lib/infra/container.ts:151 | Target DynamoDB table. |
DYNAMODB_AUTH_MODE | iam_role | platform/src/lib/infra/container.ts:152 | iam_role / access_key. |
DYNAMODB_REGION | us-east-1 | platform/src/lib/infra/container.ts:153 | AWS region. |
DYNAMODB_ROLE_ARN | — | platform/src/lib/infra/container.ts:154 | IAM role ARN. |
DYNAMODB_ACCESS_KEY_ID | — | platform/src/lib/infra/container.ts:155 | Static credentials. |
DYNAMODB_SECRET_ACCESS_KEY | — | platform/src/lib/infra/container.ts:156 | Companion to access key id. |
KEYSPACES_KEYSPACE | kaireon | platform/src/lib/infra/container.ts:164 | AWS Keyspaces target keyspace. |
KEYSPACES_USERNAME | — | platform/src/lib/infra/container.ts:165 | Service-specific credentials username. |
KEYSPACES_PASSWORD | — | platform/src/lib/infra/container.ts:166 | Service-specific credentials password. |
KEYSPACES_REGION | us-east-1 | platform/src/lib/infra/container.ts:167 | AWS region. |
KEYSPACES_AUTH_MODE | access_key | platform/src/lib/infra/container.ts:168 | access_key / iam_role. |
KEYSPACES_REQUEST_TIMEOUT_MS | — | platform/src/lib/infra/container.ts:169 | Per-request timeout. |
Search index (Postgres / OpenSearch)
| Env Var | Default | Read From | Purpose |
|---|
SEARCH_INDEX | pg | platform/src/lib/infra/container.ts:186 | Backend selector — pg / opensearch. |
OPENSEARCH_NODE_URL | https://localhost:9200 | platform/src/lib/infra/container.ts:191 | OpenSearch node URL. |
OPENSEARCH_USERNAME | — | platform/src/lib/infra/container.ts:192 | Basic auth username. |
OPENSEARCH_PASSWORD | — | platform/src/lib/infra/container.ts:193 | Basic auth password. |
OPENSEARCH_TLS_ENABLED | enabled (skip when "false") | platform/src/lib/infra/container.ts:194 | TLS toggle. |
OPENSEARCH_TLS_REJECT_UNAUTHORIZED | enabled (skip when "false") | platform/src/lib/infra/container.ts:195 | Cert-validation toggle. |
OPENSEARCH_INDEX_PREFIX | kaireon- | platform/src/lib/infra/container.ts:196 | Prepended to every index name. |
OPENSEARCH_REQUEST_TIMEOUT_MS | — | platform/src/lib/infra/container.ts:197 | Per-request timeout. |
OPENSEARCH_MAX_RETRIES | — | platform/src/lib/infra/container.ts:200 | SDK-level retry count. |
OPENSEARCH_AUTH_MODE | basic | platform/src/lib/infra/container.ts:203 | basic / iam_role. |
OPENSEARCH_REGION | — | platform/src/lib/infra/container.ts:204 | AWS region (for iam_role mode against AWS-managed OpenSearch). |
OPENSEARCH_ROLE_ARN | — | platform/src/lib/infra/container.ts:205 | IAM role ARN. |
OPENSEARCH_ACCESS_KEY_ID | — | platform/src/lib/infra/container.ts:206 | Static credentials. |
OPENSEARCH_SECRET_ACCESS_KEY | — | platform/src/lib/infra/container.ts:207 | Companion to access key id. |
Storage and attachments
| Env Var | Default | Read From | Purpose |
|---|
STORAGE_BACKEND | local | platform/src/lib/ai/import/storage/factory.ts:15 | AI-import attachment backend — local or s3. Unrecognized values throw. |
ATTACHMENT_S3_BUCKET | — | platform/src/lib/ai/import/storage/factory.ts:18 | S3 bucket for attachments when STORAGE_BACKEND=s3. Required when backend is s3. |
ATTACHMENT_STORAGE_PATH | /var/kaireon/attachments | platform/src/lib/ai/import/storage/factory.ts:38 | Local filesystem base path when STORAGE_BACKEND=local. |
AWS_REGION | us-east-1 | platform/src/lib/email.ts:14 | Default AWS region for SES + S3 SDK clients. |
Email and outbound
| Env Var | Default | Read From | Purpose |
|---|
SES_FROM_EMAIL | support@kaireonai.com | platform/src/lib/email.ts:17 | From-address used by all SES-sent transactional emails. |
Governance, approvals, and license
| Env Var | Default | Read From | Purpose |
|---|
APPROVAL_MAX_AGE_HOURS | 168 (7 days) | platform/src/app/api/v1/cron/approvals-expire/route.ts:91 | Pending approvals older than this are auto-expired by the cron sweep. Falls back on invalid input rather than failing. |
NEGOTIATION_GLOBAL_KILL | — | platform/src/app/api/v1/decisions/[id]/negotiate/apply/route.ts:121 | Global kill switch for the agent-negotiation apply path. Set to "true" to reject every apply across all tenants. |
KAIREON_LICENSE_PRIVATE_KEY | — (auto-generates RSA pair when unset) | platform/scripts/generate-license.mjs:67 | RSA private key (PEM, PKCS8) for signing customer licenses. The matching public key ships embedded in the platform for verification. |
Worker, outbox, and seed
| Env Var | Default | Read From | Purpose |
|---|
WORKER_CONCURRENCY | 5 | platform/src/worker/index.ts:28 | BullMQ worker concurrency per process. |
WORKER_METRICS_PORT | 9091 | platform/src/worker/index.ts:29 | Worker Prometheus exporter listen port. |
WORKER_SECRET | worker-internal | platform/src/lib/seed-executor.ts:71 | Worker→API shared secret (X-Worker-Secret header). The default is a known string — override in production. |
_SEED_FROM_WORKER | — | platform/src/lib/seed-executor.ts:60 | Internal flag the seed-executor sets at :60 and deletes at :100. Currently has no reader — reserved for future API-side reentrancy detection. Operators should not set this. |
OUTBOX_LIVENESS_FILE | /tmp/outbox-publisher.alive | platform/src/worker/outbox-publisher.ts:58 | Liveness file the outbox publisher touches each loop. K8s liveness probe reads its mtime. |
OUTBOX_POLL_INTERVAL_MS | 2000 | platform/src/worker/outbox-publisher.ts:53 | Outbox polling interval in ms. |
OUTBOX_SHUTDOWN_DRAIN_TIMEOUT_MS | 15000 | platform/src/worker/outbox-publisher.ts:54-57 | Max time to wait for in-flight publishes to drain on shutdown. |
OUTBOX_REAPER_STALENESS_SECONDS | 300 | platform/src/app/api/v1/cron/outbox-reaper/route.ts:74 | Outbox rows older than this are reset by the reaper sweep. Falls back on invalid input. |
Cron tier
| Env Var | Default | Read From | Purpose |
|---|
CRON_SECRET | — | platform/src/app/api/v1/cron/siem-ship/route.ts:18 | Bearer token gating /api/v1/cron/* endpoints (k8s CronJobs). |
CRON_TOKEN | — | platform/src/app/api/cron/tick/route.ts:62 | Bearer token gating the legacy single /api/cron/tick endpoint. Distinct from CRON_SECRET. |
Observability — tracing, metrics, logs
| Env Var | Default | Read From | Purpose |
|---|
LOG_LEVEL | info | platform/src/lib/logging.ts:13 | Log level threshold for the structured logger (silent / error / warn / info / debug). |
OTEL_EXPORTER_OTLP_ENDPOINT | — | platform/src/lib/tracing.ts:63 | OTLP traces+metrics exporter endpoint. Tracing disabled when unset. |
The structured logger pairs with the logError(context, err, meta?) helper at platform/src/lib/api-error.ts:118. logError mints a per-call UUID errorId, sanitizes the error message, attaches the meta block, and returns the errorId for the caller to surface (HTTP response body, downstream worker telemetry, audit row). Use it for any caught error a SIEM or operator may need to correlate later. Adoption is incremental — the API-route layer and the Outbox publisher worker already mint errorId per failed tick (platform/src/worker/outbox-publisher.ts:92); other workers still emit bare getLogger().error() calls and are tracked in .planning/RESIDUALS_2026-04-29.md residual #18 for migration.
Audit + SIEM
| Env Var | Default | Read From | Purpose |
|---|
SIEM_BACKEND | — | platform/src/lib/audit/sink.ts:49 | SIEM target — splunk / datadog / elastic. Unset = no SIEM ship-out. Unknown values warn-and-disable. |
SIEM_ENDPOINT | — | platform/src/lib/audit/sink.ts:55 | SIEM ingest endpoint URL. Required when SIEM_BACKEND is set. |
SIEM_API_KEY | — | platform/src/lib/audit/sink.ts:63 | Bearer token for SIEM ingest (Splunk HEC token, Datadog API key, etc.). |
SIEM_SOURCETYPE | — | platform/src/lib/audit/sink.ts:64 | Splunk-only sourcetype name. |
SIEM_INDEX | — | platform/src/lib/audit/sink.ts:65 | Elastic-only index name (Splunk uses sourcetype). |
Provenance, supply chain, and signing
| Env Var | Default | Read From | Purpose |
|---|
GIT_SHA | — | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:148 | Build-time git SHA stamped into Decision Provenance Bundles. SLSA attestation skipped when missing. |
GIT_REPO | — | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:149 | Build-time git repo URL. |
IMAGE_NAME | — | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:150 | Container image name (e.g. ghcr.io/kaireonai/platform). |
IMAGE_DIGEST | — | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:151 | Container image digest (sha256:…). |
SBOM_DIGEST_SHA256 | — | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:152 | Hex digest of the published SBOM. Pinned into the SLSA statement. |
BUILDER_ID | kaireon.platform.runtime | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:164 | SLSA builder.id. |
BUILDER_VERSION | unknown | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:165 | SLSA builder.version. |
BUILD_STARTED_ON | request-time fallback | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:166 | SLSA metadata.buildStartedOn. |
BUILD_FINISHED_ON | request-time fallback | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:167 | SLSA metadata.buildFinishedOn. |
GITHUB_RUN_ID | request id fallback | platform/src/app/api/v1/decisions/[id]/provenance/route.ts:168 | SLSA invocationId. |
COSIGN_BINARY | cosign | platform/src/lib/supply-chain/cosign-sign-blob.ts:43 | Cosign CLI binary path. Override when not on PATH. |
COSIGN_KEY | — | platform/src/lib/supply-chain/__tests__/cosign-sign-blob.test.ts:65 | Cosign signing-key file path. |
Webhooks and inbound channels
| Env Var | Default | Read From | Purpose |
|---|
WHATSAPP_APP_SECRET | — | platform/src/app/api/v1/webhooks/whatsapp/route.ts:87 | Meta WhatsApp app secret used to verify the X-Hub-Signature-256 header on inbound. |
WHATSAPP_WEBHOOK_VERIFY_TOKEN | — | platform/src/app/api/v1/webhooks/whatsapp/route.ts:32 | Token returned during the Meta hub.verify_token handshake. |
Multi-region and tenant routing
| Env Var | Default | Read From | Purpose |
|---|
KAIREON_REGION | us-east-1 | platform/src/lib/tenant/region-router.ts:28 | Region binding for the running process (e.g. us-east-1). Used by the multi-region router to decide whether a tenant request is local or must redirect. |
MULTI_REGION_ENABLED | — | platform/src/lib/tenant/region-router.ts:32 | "true" enables multi-region routing. |
PLATFORM_OWNER_TENANT_ID | — | platform/src/app/api/v1/platform-settings/route.ts:10 | Tenant id treated as the platform owner — gates write access to platform-wide settings. |
SINGLE_TENANT_MODE | — | platform/src/lib/tenant.ts:18 | Single-tenant build mode toggle (test-asserted; production semantics depend on caller). |
MCP server (CLI / SDK side)
| Env Var | Default | Read From | Purpose |
|---|
KAIREON_API_URL | http://localhost:3000 | platform/src/mcp/auth.ts:5 | KaireonAI API base URL the MCP tools call. |
KAIREON_API_KEY | — | platform/src/mcp/auth.ts:6 | API key used by the MCP server when calling v1 routes. |
KAIREON_TENANT_ID | default | platform/src/mcp/auth.ts:7 | Tenant id sent in the X-Tenant-Id header by MCP tool calls. |
MCP_ALLOW_WRITES | — | platform/src/mcp/tools/types.ts:28 | Set to "true" to enable write-mode MCP tools. Read-only by default. See /integrations/mcp for the full tool list. |
Flow streaming
| Env Var | Default | Read From | Purpose |
|---|
FLOW_STREAMING_ENABLED | — | platform/src/lib/flow/runtime/streaming/feature-flag.ts (also asserted in __tests__/feature-flag.test.ts) | Feature flag for Flow streaming consumers (Kafka / Kinesis / Pulsar). Phase 6.4 — disabled by default; enable only on self-host with a running broker. |
These four endpoint names are declared as the registry the external-model-call transform consults at runtime (lib/flow/runtime/transforms/external-model-call.ts:47-59). The actual process.env lookup happens inside the call helper. Operators must set the corresponding env var before invoking the transform; if unset, the transform throws MissingExternalModelEndpointError.
| Env Var | Default | Read From | Purpose |
|---|
GEO_RESOLVE_ENDPOINT | — | platform/src/lib/flow/runtime/transforms/external-model-call.ts:51 | External geo-resolution model URL. |
LANGUAGE_DETECT_ENDPOINT | — | platform/src/lib/flow/runtime/transforms/external-model-call.ts:59 | External language-detection model URL. |
SENTIMENT_SCORE_ENDPOINT | — | platform/src/lib/flow/runtime/transforms/external-model-call.ts:55 | External sentiment scoring URL. |
VECTOR_EMBED_ENDPOINT | — | platform/src/lib/flow/runtime/transforms/external-model-call.ts:47 | External embedding-vector URL. |
Playground and demo
| Env Var | Default | Read From | Purpose |
|---|
PLAYGROUND_MODE | — | platform/src/lib/auto-seed.ts:17 | "true" triggers Starbucks auto-seed for new tenants on registration. |
DEMO_MODE | — | platform/src/app/api/v1/tenant/status/route.ts:19 | Demo-environment toggle. Validated as boolean by validateEnvFormats (env-validation.ts:29). |
Python ml-worker
The Python FastAPI service under ml-worker/ reads only two env vars (ml-worker/app/config.py lines 6-7).
| Env Var | Default | Read From | Purpose |
|---|
DATABASE_URL | postgresql://localhost:5432/kaireon | ml-worker/app/config.py:6 | Postgres connection string for the ml-worker process. Distinct from the platform’s DATABASE_URL only in that it can be overridden per-process. |
ML_WORKER_PORT | 8000 | ml-worker/app/config.py:7 | FastAPI listen port. |
Test-only
These keys are read only by the test runner. Operators should not set them in production.
| Env Var | Default | Read From | Purpose |
|---|
TEST_API_KEY | test-ch7-key | platform/src/__tests__/integration/block4-decision-flow.integration.test.ts | Integration-test API key. |
TEST_TENANT_ID | 5a9904b9-4f75-4c4b-a04d-ff826… | platform/src/__tests__/integration/block4-decision-flow.integration.test.ts | Integration-test tenant id. |
TEST_BASE_URL | http://localhost:3000 | platform/src/__tests__/integration/sample-data-e2e.integration.test.ts | Base URL used by integration test HTTP calls. |
VITEST | — | platform/src/lib/connector-executors/types.ts:75 | Set by Vitest. Production code branches on it to skip side effects in tests. |
Configuration
Loading order
Next.js picks up env files in this order, last-write-wins (per Next.js framework defaults):
.env
.env.local (gitignored — local overrides)
.env.${NODE_ENV} (e.g. .env.production)
.env.${NODE_ENV}.local
- Process environment (
process.env)
platform/.env.example is the template. Copy it to .env.local for local dev and to a secret manager (Kubernetes Secret, AWS SSM, etc.) for production.
Kubernetes — ConfigMap vs Secret
In the Helm chart at helm/kaireon/:
- Non-secret runtime config (
NODE_ENV, LOG_LEVEL, EVENT_PUBLISHER, INTERACTION_STORE, SEARCH_INDEX, RLS_AUTO_ENABLE, WORKER_CONCURRENCY, OUTBOX_*, SLOW_API_THRESHOLD_MS, RETRAIN_EVERY_N, OPENSEARCH_INDEX_PREFIX, OPENSEARCH_TLS_ENABLED) lives in helm/templates/configmap.yaml.
- Secrets (
DATABASE_URL, REDIS_URL, NEXTAUTH_SECRET, JWT_SIGNING_SECRET, CONNECTOR_ENCRYPTION_KEY*, WEBHOOK_SIGNING_SECRET, API_KEY_PEPPER, WORKER_SECRET, INTERNAL_SERVICE_SECRET, CRON_SECRET, CRON_TOKEN, KAFKA_SASL_PASSWORD, MSK_SASL_PASSWORD, *_SECRET_ACCESS_KEY, *_PASSWORD, SIEM_API_KEY, WHATSAPP_APP_SECRET, KAIREON_LICENSE_PRIVATE_KEY, COSIGN_KEY) live in helm/templates/secrets.yaml.
See /deployment/helm-reference for the full chart values map.
Rotation guidance
| Variable | Rotation strategy |
|---|
CONNECTOR_ENCRYPTION_KEY | Set the new key as CONNECTOR_ENCRYPTION_KEY + bump CONNECTOR_ENCRYPTION_KEY_VERSION. Keep the old key as CONNECTOR_ENCRYPTION_KEY_PREVIOUS + matching _PREVIOUS_VERSION until all rows are re-encrypted by a background sweep. |
JWT_SIGNING_SECRET | Rotate inside a maintenance window. All issued tokens become invalid the moment the new secret takes effect. |
API_KEY_PEPPER | Cannot be rotated without invalidating every stored API-key hash. Plan for a hard cutover. |
WEBHOOK_SIGNING_SECRET | Rotate by overlapping — accept either old or new for the rotation window via a transitional verifier (currently single-key; overlap window is an open op). |
NEXTAUTH_SECRET | Logs out every active session. Rotate during low traffic. |
Cloud provider keys (AWS_*, *_ACCESS_KEY_ID) | Rotate through the cloud provider’s secret manager; pod restart picks up the new value (no in-process rotation). |
Honest limits
- TS-only vs Python ml-worker — Of the documented variables, only
DATABASE_URL is read by both the TypeScript platform and the Python ml-worker. ML_WORKER_PORT is Python-only. The platform talks to the ml-worker via ML_WORKER_URL + ML_WORKER_API_KEY (HTTP); the two processes do not share env vars beyond DATABASE_URL.
- Once-at-boot vs per-request reads — Most env vars are read once during module load (DI container, Prisma pool, AI provider config, encryption keys). A handful are read per-request:
NEGOTIATION_GLOBAL_KILL (negotiate/apply/route.ts:121), ALLOW_UNSIGNED_WEBHOOKS (webhooks/delivery/route.ts:20). For the rest, a process restart is required after changing the value.
- Deprecated / soft-deprecated —
CRON_TOKEN is the legacy auth token for /api/cron/tick. New cron endpoints under /api/v1/cron/* use CRON_SECRET; both are kept until the legacy tick is removed.
- Internal-only flags — operators should not set —
_SEED_FROM_WORKER is set by the seed-executor itself before its internal HTTP call. Setting it manually breaks reentrancy detection.
- Test-only flags in production —
MFA_ENFORCEMENT_DISABLED, ALLOW_UNSIGNED_WEBHOOKS, VITEST, TEST_API_KEY, TEST_BASE_URL, TEST_TENANT_ID must not appear in any production environment. ALLOW_UNSIGNED_WEBHOOKS is hard-blocked when NODE_ENV=production (webhooks/delivery/route.ts:20); the others are not enforced — operator discipline only.
KAIREON_LICENSE_PRIVATE_KEY is script-side — Read only by platform/scripts/generate-license.mjs:67, never by the running platform. The platform verifies licenses with the matching public key embedded in source. When unset, the script auto-generates a fresh RSA key pair on each run rather than failing.
- AI provider env keys are tenant-overridable — Tenant-level AI settings in the Settings UI take precedence over
AI_PROVIDER / AI_MODEL / AI_API_KEY / AI_BASE_URL (provider.ts:20-23). The env vars are only the fallback when no DB-level setting is configured.
- Environment variables not yet wired for the AI sidebar metadata —
AI_SIDEBAR_ENABLED and AI_RATE_LIMIT_PER_MINUTE are declared in AI_ENV at types.ts:19-20 but no process.env[AI_ENV.ENABLED] or process.env[AI_ENV.RATE_LIMIT] read currently exists in the platform. They are reserved keys.