Component configuration
All control plane settings use the EAI_ prefix and are defined in services/control-plane/src/eai/config.py or read by an integration adapter (see platform-integration.md). Non secret values live in controlPlane.config (ConfigMap); secrets in OpenBao kv/eai/<env>/<service>. Lists (EAI_CORS_ORIGINS) are written as JSON.
| Component | File | Key points |
|---|---|---|
| Control Plane | deploy/helm/enterprise-ai/values.yaml (controlPlane) | EAI_AUTH_DEV=0 in prod, EAI_AUTH_SPIFFE=1, integration URLs (EAI_OPENBAO_URL, EAI_TEMPORAL_URL, ...), pre-upgrade migration job, GET /metrics scraped by the ServiceMonitor |
| Background worker | values.yaml (worker) | python -m eai.worker, one replica, same image, ConfigMap and secrets as the API, dedicated NetworkPolicy |
| Portal | values.yaml (portal) | NEXT_PUBLIC_EAI_API_URL, public OIDC client portal (PKCE S256) |
| LiteLLM | deploy/litellm/config.example.yaml, litellm.config | keys through os.environ/..., router fallbacks; generated in prod by GET /v1/litellm/config and POST /v1/litellm/sync |
| vLLM | vllm.models | one entry per model: gpus, args, minReplicas/maxReplicas, KEDA threshold |
| Multimodal workers | workers.items | STT/TTS/video, KEDA nats-jetstream on consumer lag (stream and consumer passed as arguments) |
| Keycloak | deploy/keycloak/realm-enterprise-ai.json | clients portal (PKCE S256), control-plane (bearer, audience), cli (device flow), realm roles, sub, tenant, clearance, roles claims |
| OpenFGA | deploy/policies/openfga/model.fga | owner, editor, viewer, can_use, member on every type, objects prefixed by the tenant |
| OPA | deploy/policies/opa | eai.model_invocation, eai.tool_invocation, eai.gateway_access, eai.data_access, eai.agent_autonomy on the Policy Contract input, kill switch and thresholds in data.json |
| Kyverno | deploy/policies/kyverno/cluster-policies.yaml | cosign signatures, non-root, read-only rootfs, limits, no latest, sandbox RuntimeClass, tenant/owner labels |
| SPIRE | deploy/spire | trust domain enterprise-ai.internal, k8s_psat, OpenBao UpstreamAuthority |
| OpenBao | deploy/openbao | HCL policies per service (kv/data/tenants/*, transit keys tenant-<uuid>), kubernetes auth roles |
| cert-manager | deploy/cert-manager | internal CA, OpenBao PKI issuer, ACME for the gateway |
| Cilium | deploy/cilium | default deny, FQDN egress per provider (label eai.io/provider-<name>=enabled on LiteLLM) |
| APISIX | deploy/apisix/apisix.yaml | /v1 routes, OIDC, Redis rate limits, strips X-Dev-* and X-SPIFFE-ID; /metrics not published |
| KEDA | deploy/keda | LiteLLM, agent workers |
| Observability | deploy/observability | OTel PII filtering, SLO and cost rules on the eai_* metrics, OpenSearch logs and SIEM, Jaeger traces, Opik AI traces; no dashboard server (Navin Manager and portal, ADR-0169) |
| PostgreSQL | deploy/data/postgres/cluster.yaml | CNPG 3 instances, zone anti-affinity, WAL to S3 |
| Velero | deploy/velero/schedules.yaml | daily + hourly, PostgreSQL, Qdrant, OpenBao hooks |
| Wazuh | deploy/wazuh | audit JSON decoder, rules 100800-100808 |
Enabling an external provider
- Store the key:
bao kv put kv/eai/prod/providers/openai OPENAI_API_KEY=.... - Add it to
litellm.secrets.keysand the model to the generated config. - Label LiteLLM pods
eai.io/provider-openai=enabledto open FQDN egress. - Allow provider, model and region in the tenant policy (OPA
eai.model_invocation, overrides indata.eai.config.tenants.<uuid>.model_invocation).
Monitoring
SIEM, mutation audit, signed WORM export and erasure in Opik and ClickHouse: see
siem-audit.md (EAI_SIEM_*, EAI_AUDIT_SIGNING, EAI_AUDIT_WORM_RETENTION_DAYS).
| Variable | Default | Purpose |
|---|---|---|
EAI_OPIK_URL | empty | Opik backend (AI traces, OTLP /v1/private/otel, erasure and retention through the REST API). In prod: https, or plain http to a *.svc Service only, never credentials in the URL (startup refused otherwise) |
EAI_OPIK_UI_URL | empty | Opik frontend for the trace links of GET /v1/telemetry/traces/{id}; https required in prod |
EAI_GRAFANA_URL | empty | Grafana console run by the client (bring your own, never deployed by the platform because of its AGPL licence, ADR-0101), deep link of the portal operations space (GET /v1/platform-manager/console); https without credentials, otherwise the link is not shown |
EAI_KEYCLOAK_CONSOLE_URL | empty | Keycloak administration console, deep link of the portal; https without credentials |
EAI_LITELLM_UI_URL | empty | LiteLLM proxy administration UI, deep link of the portal; https without credentials |
EAI_OPENSEARCH_DASHBOARDS_URL | empty | OpenSearch Dashboards, deep link of the portal; https without credentials |
EAI_PLATFORM_MANAGER_CONSOLE_URL | empty (then EAI_PLATFORM_MANAGER_URL) | Platform Manager console targeted by the deep links of the Platform Manager space (ADR-0162) |
EAI_OPIK_WORKSPACE | default | Opik workspace (Comet-Workspace header) |
EAI_OPIK_PROJECT | eai-platform | Opik project of the platform spans |
EAI_OPIK_API_KEY | empty | API key, only when the Opik deployment authenticates its API (OpenBao secret) |
EAI_METRICS_TOKEN | empty | bearer token of GET /metrics, required outside dev and test (32 characters minimum) |
EAI_WORKER_METRICS_PORT | 9464 | metrics port of python -m eai.worker (0 disables) |
EAI_MEDIA_WORKER_METRICS_PORT | 9465 | metrics port of python -m eai.media_worker (0 disables) |
EAI_WORKER_GAUGES_INTERVAL_SECONDS | 30 | interval of the gauges job (global gauges computed from the database) |
EAI_WORKER_VERIFY_CHAIN_INTERVAL_SECONDS | 3600 | interval of the verify_chain job (audit chain of every tenant) |
Traces and logs:
| Variable | Role |
|---|---|
EAI_OTEL_ENABLED | Enables OpenTelemetry traces. A missing OpenTelemetry package refuses the API start. |
EAI_SLO_AVAILABILITY_TARGETS | Availability target per service, comma separated service=value, values in [0.5, 1). Default control_plane_api=0.995,model_gateway=0.995,tool_gateway=0.995,realtime=0.995. An invalid value is refused at production startup. |
EAI_SLO_LATENCY_TARGETS_SECONDS | Latency threshold per service in seconds, in (0, 300]. Default 1 for every service. |
EAI_SLO_LATENCY_OBJECTIVE | Share of requests that must stay under the latency threshold, in [0.5, 1). Default 0.95. |
EAI_SLO_RETENTION_MINUTES | How long each replica keeps its in memory measurements for GET /v1/telemetry/slo, between 60 and 10080. Default 1440. |
EAI_TELEMETRY_SPAN_MAX_DAYS | Longest retention of the span summaries (eai.telemetry_spans, ADR-0157) for every tenant, applied by the retention worker job, between 1 and 3650 days. The telemetry retention policy of a tenant can be shorter. Default 90. |
EAI_OTEL_EXPORTER_OTLP_ENDPOINT | OTLP/HTTP collector, port 4318 (/v1/traces is appended). |
EAI_OTEL_EXPORTER_OTLP_CERTIFICATE | Internal CA of an https collector (defaults to SSL_CERT_FILE). |
EAI_LOG_LEVEL | DEBUG, INFO, WARNING, ERROR or CRITICAL; JSON logs on stdout with trace_id, span_id and request_id. |
The AI path clients (LiteLLM, vLLM, multimodal adapters, collector) only reach the hosts declared in the EAI_*_URL and EAI_*_ENDPOINT settings.
LiteLLM gateway, model versions and isolation
| Setting | Role |
|---|---|
EAI_LITELLM_URL | Address of the LiteLLM OSS proxy (1.106.5); empty, the gateway calls the providers itself. |
EAI_LITELLM_MASTER_KEY | Proxy master key, injected from OpenBao, never exposed to agents. |
EAI_LITELLM_VENV | Test tooling only: venv of the real proxy created by bash tools/litellm/setup.sh (default /tmp/toolchains/litellm-venv). Without it tests/test_llm_litellm_proxy_real.py fails with a clear message. |
The isolated mode (local_only, air_gapped) is not an environment variable: it is set per tenant or per project with PUT /v1/llm/isolation and refuses every external route before any outbound call. Model versions, their progressive rollout and the rollback are driven by the API (see B-04); no setting activates a model without an approval.
RAG and embeddings
| Setting | Default | Purpose |
|---|---|---|
EAI_RAG_EMBEDDING_MODEL | local-hash-256 | registry embedding model (capability embeddings) of a new knowledge base; bge-m3-vllm in prod (values-prod.yaml, values-onprem-gpu.yaml) |
EAI_MEMORY_EMBEDDING_MODEL | local-hash-256 | embedding model of the agent memory; after a change the worker re-embeds the items |
EAI_RAG_RERANK_MODEL | empty | rerank model (capability rerank) used by rerank: true without rerank_model |
EAI_PADDLEOCR_URL | empty | PaddleOCR serving endpoint (POST /ocr) for scans; otherwise Docling, otherwise tesseract |
EAI_EMBEDDINGS_BATCH_SIZE | 64 | texts per Model Gateway call (ingestion, reindex) |
EAI_QDRANT_SHARD_NUMBER, EAI_QDRANT_REPLICATION_FACTOR, EAI_QDRANT_WRITE_CONSISTENCY_FACTOR | empty (Qdrant defaults) | shards, replicas per shard and write acknowledgements of every new Qdrant collection; 3, 2 and 1 on the 3 node Qdrant cluster (values.yaml, values-prod.yaml): losing one node loses no vector |
EAI_WORKER_EMBEDDINGS_REINDEX_INTERVAL_SECONDS | 60 | interval of the embeddings_reindex job (knowledge base reindex, memory re-embedding) |
Every embedding goes through the Model Gateway with the controls of the chosen model; EAI_EMBEDDINGS_URL no longer exists. local-hash-256 is a hashing of words, not a semantic model: it is accepted in dev and test only. In prod the startup refuses it for EAI_RAG_EMBEDDING_MODEL and EAI_MEMORY_EMBEDDING_MODEL, and outside dev and test creating a knowledge base with it returns 422 embedding_model_invalid.
Realtime and streaming DLP
| Setting | Default | Purpose |
|---|---|---|
EAI_REALTIME_WS_TOKEN_TTL_SECONDS | 300 | Lifetime of a single use ws_token |
EAI_REALTIME_WS_MAX_MESSAGE_BYTES | 1048576 | Maximum WebSocket message size (uvicorn ws_max_size and handler check, close 1009) |
EAI_REALTIME_WS_MAX_EVENTS_PER_SECOND | 50 | Sustained client event rate (close 4429) |
EAI_REALTIME_WS_BURST | 100 | Burst allowed above the rate |
EAI_REALTIME_WS_MAX_SESSION_SECONDS | 3600 | Maximum connection duration (close 4408) |
EAI_REALTIME_WS_IDLE_TIMEOUT_SECONDS | 120 | Idle timeout (close 4408) |
EAI_REALTIME_CONSENT_REQUIRED | unset (required outside dev and test) | Realtime transcription refused without an active participant consent (consent_required); false is refused at production startup. Recording always needs consent |
EAI_VAD_URL | unset | HTTP voice worker (route /vad, Silero VAD) called by the http-vad adapter through the egress guard |
EAI_SILERO_VAD_MODEL | unset | Silero VAD (MIT) ONNX file for the in process silero-onnx adapter (onnxruntime) |
EAI_KOKORO_MODEL, EAI_KOKORO_VOICES, EAI_KOKORO_VOICE | unset, af_heart | Kokoro v1.0 files (Apache-2.0 weights) of the reference TTS adapter kokoro (kokoro-onnx) |
EAI_STREAM_DLP_HOLDBACK_CHARS | 128 | Characters held back by the sliding streaming DLP buffer |
Redis (cache and coordination)
See redis.md. Redis is only a cache and a coordination aid: permissions, suspensions, quotas and evidence are always read from PostgreSQL.
| Setting | Default | Purpose |
|---|---|---|
EAI_REDIS_URL | empty | rediss://host:port/0 (TLS). Empty: in-memory rate limit counters, per replica. In prod the startup refuses redis:// without TLS |
EAI_REDIS_USERNAME | empty | Redis ACL user |
EAI_REDIS_PASSWORD_FILE | empty | File holding the ACL password (never the password itself). Required in prod when EAI_REDIS_URL is set |
EAI_REDIS_TLS_CA_FILE | empty | CA bundle verifying the rediss:// server certificate |
Licences
See licensing.md. None of these settings limits users, agents, applications, use cases or plugins.
| Setting | Default | Purpose |
|---|---|---|
EAI_LICENSE_PUBLIC_KEYS | empty | Publisher Ed25519 public keys, comma separated kid:x (several for a rotation). In prod the startup refuses a malformed value, a duplicate kid or a test-* kid |
EAI_LICENSE_EXPIRING_SOON_DAYS | 30 | Days before valid_until when the state becomes expiring_soon (1 to 365, checked at startup in prod) |
EAI_LICENSE_WARNING_DAYS | 30,7,1 | Warning thresholds of the license_check job (days before valid_until) |
EAI_WORKER_LICENSE_CHECK_SECONDS | 3600 | Interval of the license_check worker job |
Context Authority, delegations, mandates and break glass
See ../../security/en/identity-context-delegation.md. The private key of the Context Authority is never logged, never returned and never stored in the database: only its public key (JWK) is registered.
| Setting | Default | Purpose |
|---|---|---|
EAI_CONTEXT_SIGNING_KEY_REF | empty | OpenBao reference of the private key, openbao:<kv path>#<field> (Ed25519 or ECDSA P-256 PEM). Required in prod; changing the key is a rotation (the previous one still verifies for EAI_CONTEXT_MAX_TTL_SECONDS) |
EAI_CONTEXT_SIGNING_KEY | empty | PEM key in the environment, dev and test only: refused at startup in prod |
EAI_CONTEXT_ISSUER | urn:eai:context-authority | Issuer (iss) of signed contexts, checked by every verify |
EAI_CONTEXT_AUDIENCES | eai-control-plane | Accepted audiences, comma separated; the first one is the default audience |
EAI_CONTEXT_TTL_SECONDS | 900 | Default lifetime of a context |
EAI_CONTEXT_MAX_TTL_SECONDS | 3600 | Maximum lifetime of a context and verification window of a retired key |
EAI_CONTEXT_LEEWAY_SECONDS | 30 | Clock tolerance on iat and exp |
EAI_CONTEXT_REQUIRED | 0 | Every run gets a signed context; without a key the run is refused (503). Must be 1 in prod (checked at startup) |
EAI_DELEGATION_DEFAULT_TTL_SECONDS | 28800 | Lifetime of the implicit delegation of a run |
EAI_DELEGATION_MAX_TTL_SECONDS | 86400 | Maximum lifetime of a delegation |
EAI_MANDATE_MAX_TTL_DAYS | 365 | Maximum lifetime of a scheduled agent mandate |
EAI_BREAK_GLASS_MAX_SECONDS | 3600 | Maximum duration of a break glass grant |
EAI_WORKLOAD_SERVICE_ID | eai-control-plane | Technical identity of this workload carried by runs |
EAI_WORKLOAD_SPIFFE_ID | empty | SPIFFE ID of this workload when SPIRE issues one |
SOC, AI-SDR and Kill Switch
See ../../security/en/soc-runbook.md. The proportionality guard limits (confidence, blast radius, reactions needing an approval) live in deploy/policies/opa/data.json (soc_response), not in settings.
| Setting | Default | Purpose |
|---|---|---|
EAI_SOC_WEBHOOK_SECRET | empty | HMAC secret of POST /v1/soc/webhooks/{source} (Falco, Tetragon, OpenSearch) and POST /v1/soc/receipts (empty: every delivery refused with 401). 32 characters minimum in prod (checked at startup) |
EAI_SOC_CORRELATION_WINDOW_SECONDS | 900 | Window of the event correlator (same user, agent or tenant); 60 to 86400, checked at startup in prod |
EAI_WORKER_SOC_SCAN_SECONDS | 30 | Interval of the soc_scan worker job (audit chain to detections, policy baselines) |
EAI_WORKER_SOC_VERIFY_SECONDS | 120 | Interval of the soc_verify worker job (Containment Verifier, receipts, drift re-applied) |
EAI_MCP_RUNTIME_REAPER_SECONDS | 60 | Interval of the MCP Runtime controller loop, in the API pods (sleep, failures, probes, cleanups) |
EAI_MCP_INTERNAL_HOSTS | empty | Internal hosts of the MCP servers the gateway may call (host, host:port, *.suffix, CIDR); every public host is refused |
EAI_MCP_CATALOG_STORAGE_PROFILE | empty | Platform storage profile (SeaweedFS) of the company MCP catalogue; required in production |
EAI_MCP_CATALOG_KEY | mcp-runtime/templates.yaml | Object key of the MCP catalogue in that profile |
EAI_MCP_CATALOG_REFRESH_SECONDS | 300 | Refresh period of the MCP catalogue |
EAI_MCP_RUNTIME_IMAGE_REGISTRIES | <harbor-host> | Internal registries a template image may come from |
EAI_MCP_RUNTIME_WAKE_TIMEOUT_SECONDS | 90 | How long a call waits for a sleeping or recreated instance |
EAI_MCP_RUNTIME_FAILURE_LIMIT | 3 | Failures within the window before the failed state (explicit start) |
EAI_MCP_RUNTIME_FAILURE_WINDOW_SECONDS | 900 | Window in which the failures of an instance are counted |
EAI_MCP_RUNTIME_HEALTH_TIMEOUT_SECONDS | 3.0 | Timeout of the GET /health probe of a ToolHive proxy |
EAI_MCP_RUNTIME_OTEL_ENDPOINT | otel-collector.observability.svc.cluster.local:4318 | OTLP/HTTP collector (TLS) of the ToolHive proxies, host:port without scheme |
EAI_MCP_RUNTIME_OTEL_SAMPLING_RATE | 1.0 | Trace sampling of the ToolHive proxies |
EAI_MCP_RUNTIME_OTEL_CA_FILE | /etc/eai/ca/ca.crt | Internal CA copied to every MCP Runtime namespace; required outside dev and test |
Client object storage (brick E-13)
See ../../en/guides/storage-administration.md. The client chooses its storage (AWS S3, Azure Blob, Google Cloud Storage, IBM COS, SeaweedFS or another S3 compatible service) with a storage profile; secrets stay in OpenBao (tenant profiles) or in EAI_CRED_<NAME> variables (platform profiles), never in the profile. Private endpoints (on premises SeaweedFS or IBM COS appliance) must be listed in EAI_EGRESS_PRIVATE_ALLOWLIST.
| Setting | Default | Purpose |
|---|---|---|
EAI_AUDIT_WORM_STORAGE_PROFILE | empty | Name of the platform profile that receives the WORM audit exports of the tenants without an audit_worm profile; empty: legacy EAI_S3_* store (manifest worm.qualified: false) |
EAI_STORAGE_CA_BUNDLE | empty | PEM file of the certificate authority of an on premises endpoint; TLS verification stays on. In prod an unreadable file is refused at startup |
EAI_STORAGE_REQUEST_TIMEOUT_SECONDS | 30 | Timeout of a storage call (connection capped at 5 s) |
EAI_STORAGE_PRESIGN_MAX_SECONDS | 3600 | Longest presigned URL issued; 1 to 86400, checked at startup in prod (a presigned URL is a bearer credential) |
EAI_STORAGE_CAPABILITY_MAX_AGE_SECONDS | 86400 | Maximum age of a capability verification; older, the usage is refused (409 storage_capability_missing, reason stale) until the profile is verified again; 60 to 604800, checked at startup in prod |
Outside the dev and test environments, storage endpoints, token endpoints (IBM IAM, Microsoft Entra ID, Google OAuth2) and presigned URLs must be https (422 storage_tls_required).
gVisor sandbox
| Variable | Default | Purpose |
|---|---|---|
EAI_SANDBOX_RUNSC | /tmp/toolchains/runsc | runsc (gVisor) binary for untrusted tool code; when missing or unusable the code is refused (fail closed) |
Audit Gateway and Evidence Service (kit section 11)
See siem-audit.md. The prod startup checks refuse a missing EAI_AUDIT_SEAL_SIGNING_KEY_REF and any EAI_AUDIT_PAYLOAD_KEK other than openbao-transit.
| Variable | Default | Purpose |
|---|---|---|
EAI_AUDIT_GATEWAY_HTTP_URL | empty | SIEM HTTP collector: one POST per event, Idempotency-Key header equal to the event id, through the operator egress client |
EAI_AUDIT_GATEWAY_NATS_SUBJECT | audit.events | JetStream subject eai.<tenant>.<subject> (when EAI_NATS_URL is set), Nats-Msg-Id header for dedup |
EAI_AUDIT_GATEWAY_NATS_STREAM | EAI_AUDIT | JetStream stream created when missing (subjects eai.*.audit.>) |
EAI_AUDIT_GATEWAY_BATCH | 500 | Outbox rows handled per tenant and per run |
EAI_AUDIT_GATEWAY_MAX_ATTEMPTS | 12 | After that the row stays pending and an outbox_missing integrity alert is opened |
EAI_AUDIT_GATEWAY_BACKOFF_SECONDS | 2.0 | Base of the exponential backoff (base × 2^(attempts-1), capped at 900 s) |
EAI_WORKER_AUDIT_GATEWAY_SECONDS | 10 | Period of the audit_gateway job |
EAI_AUDIT_SEAL_SIGNING_KEY_REF | empty | OpenBao reference openbao:<kv path>#<field> of the Ed25519 seal private key (required in prod) |
EAI_AUDIT_SEAL_SIGNING_KEY | empty | Plain Ed25519 PEM key, dev and test only |
EAI_WORKER_AUDIT_SEAL_SECONDS | 300 | Period of the audit_seal job |
EAI_AUDIT_SEAL_MAX_EVENTS | 10000 | Most events per seal |
EAI_WORKER_AUDIT_INTEGRITY_SECONDS | 900 | Period of the audit_integrity job (chains, seals, signatures, timestamps, outbox coverage) |
EAI_AUDIT_CLOCK_SKEW_SECONDS | 300 | Largest accepted lead of event_time over the database clock; 0 to 3600, checked at startup in prod |
EAI_AUDIT_PAYLOAD_KEK | empty | Key encryption key of the sensitive payloads: openbao-transit (prod) or a base64 32 byte key (dev and test); empty: sensitive payloads are refused |
EAI_AUDIT_PAYLOAD_RETENTION_DAYS | 365 | Age after which the audit_retention job purges encrypted payloads outside legal holds |
EAI_WORKER_AUDIT_RETENTION_SECONDS | 86400 | Period of the audit_retention job |
EAI_OPA_DELIVERY | push | push: engine=opa policies are pushed to OPA on write; bundle: OPA loads the signed bundles published by POST /v1/policies/bundles (prod: signing key required) |
EAI_POLICY_BUNDLE_SIGNING_KEY_FILE | empty | PEM file of the RSA private key signing the bundles (RS256); required with EAI_OPA_DELIVERY=bundle, publication refused in prod without it |
EAI_POLICY_BUNDLE_KEY_ID | eai-policy | keyid of the signatures, same as the keys entry of the OPA configuration |
EAI_KMS_PROVIDER | none | Key management eai.crypto.kms: openbao (Transit), pkcs11 (client HSM) or none |
EAI_KMS_PKCS11_MODULE | empty | Path of the PKCS#11 library provided by the client (required with pkcs11) |
EAI_KMS_PKCS11_TOKEN_LABEL | empty | HSM token label (required with pkcs11) |
EAI_KMS_PKCS11_PIN_FILE | empty | Mounted file holding the user PIN, never the PIN in the environment (required with pkcs11) |