Skip to content

Post-install configuration

Once the chart has installed and you have signed in as the first administrator (see Install with Helm), most day-to-day configuration moves into the admin console rather than back into a values file. This page is the map of what still lives where.

A few chart values feed many components. Set each once; a component’s own value, when you set it, wins.

ValueDefaultWhat it fills where that value is empty
global.publicUrlemptyThe origin browsers open, for example https://elitea.example.com (a scheme, a host and an optional port; no path). It fills main.env.DEPLOYMENT_URL; main.env.OIDC_REDIRECT_URI as <publicUrl>/auth/oidc/callback, when main.env.OIDC_ISSUER_URL is set; the authentication document’s public_origin, when the chart renders a document; llmGateway.env.GATEWAY_SELF_LLM_ORIGINS as <publicUrl>/llm,http://elitea-main:8080/llm; worker.runtime.publicBaseUrl; worker.runtimeSession.expectedIdentity as spiffe://<host>/ns/<release namespace>/sa/agent-worker; and the host of every main.ingress.hosts and tls[].hosts entry, when that list is empty.
global.trustedProxyCidrs[]The proxies whose X-Forwarded-For elitea-main believes. It fills main.env.ELITEA_TRUSTED_PROXY_CIDRS (comma-joined) and the authentication document’s trusted_proxy_cidrs. On GKE: the pod CIDR, 35.191.0.0/16 and 130.211.0.0/22.
global.otelEndpointemptyAn OTLP/HTTP collector outside the chart. See Observability.
certManager.issuers.internalelitea-internal-ca, kind emptyThe issuer of the LLM gateway’s, DeepWiki’s and Inventory’s mTLS Certificates (*.mtls.issuerRef).
certManager.issuers.runtimeelitea-runtime-ca, kind emptyThe issuer of elitea-vector’s and Qdrant’s Certificates, the DeepWiki and Inventory callback client certificates and the egress proxy’s audit certificate.
certManager.issuers.createfalsetrue: the chart makes both, namespaced (see below).

An empty kind means ClusterIssuer, as before, or Issuer when create is true. With create: true the chart renders, in Argo CD wave -15: a self-signed Issuer, a CA Certificate and the elitea-internal-ca Issuer over it (ECDSA P-256, duration 43800h, renewBefore 8760h); and the elitea-runtime-ca Issuer over the existing Secret certManager.issuers.runtime.caSecretName. The chart never generates the runtime CA: the worker certificate and elitea-main’s runtime listeners are signed by it, so a new CA would fail every handshake. With externalSecrets on, that Secret is an ExternalSecret too (<caSecretName>/tls.crt and tls.key, or runtime-ca.crt and runtime-ca.key with externalSecrets.sharedProperties). create: true with a ClusterIssuer kind is refused.

Placeholders. The chart refuses to render while any value still holds a <...> placeholder of an example values file (values-gke.yaml has several), and the message lists every one by its path.

Shared secret properties. With externalSecrets.remoteKey (one flat JSON secret), externalSecrets.sharedProperties: true reads the values that several Secrets carry from one property each: runtime-ca.crt, runtime-ca.key, command-signing-keyring.json and gateway-identity-secret. A key’s own property still wins. deploy/scripts/mint-secrets.sh mints that JSON for a new install; see Deploy on GKE with Argo CD.

Configure identity providers under Configuration › Authentication in the admin console — see Authentication for the click-through. What matters at the deployment level:

  • Sign-in is OIDC or SAML. The local username/password (Form) sign-in is off by default: main.env.ELITEA_FORM_LOGIN_ENABLED must be "true" for the Form users file to be a sign-in path. With it off, the authentication document is still read (the gateway edge and the runtime depend on it), but no Form login page is mounted and no configured password is accepted. Every Form user needs a real address (email, or attributes.email); one without is refused at sign-in. See docs/UPGRADING.md in the platform repository.
  • The first global admin is a chart/environment setting, not a console one — identity.initial_global_admins in the auth document, or main.env.ELITEA_INITIAL_GLOBAL_ADMINS. See the Helm install guide.
  • An OIDC-only install now gets the configuration plane. Before, only a Form authentication document (ELITEA_AUTH_CONFIG_FILE) composed the principal validator the configuration write path needs; now env.OIDC_ISSUER_URL alone composes it too, as long as ELITEA_AI_PROJECT_ID is set. A deployment with no authentication at all is still refused, in both the chart and the binary.
  • SCIM provisions accounts and groups at /api/v2/scim/v2. It authenticates with a dedicated SCIM client credential (a bearer secret, or OAuth2 client credentials at /api/v2/scim/oauth/token), not with a personal access token. Create the client and bind groups to project roles in the console (see Authentication). ELITEA_SCIM_ACCESS_TOKEN_TTL sets the access token lifetime (default 1h).

A fresh database seeds only dev@elitea.ai. On a single-sign-on deployment, this account has no password. Set identity.initial_global_admins or the equivalent environment variable before you rely on the admin console.

SMTP_HOST, SMTP_PORT, SMTP_USERNAME, SMTP_PASSWORD, SMTP_TLS, EMAIL_FROM, EMAIL_REPLY_TO and PUBLIC_BASE_URL are a default layer, not the whole configuration. Admin → E-mail writes a second layer into the database, and internal/emailsettings merges the two field by field on every send — the database wins where it has a value.

  1. Set the bootstrap defaults

    Ship EMAIL_FROM and PUBLIC_BASE_URL at minimum through main.env; SMTP_PASSWORD belongs in the secrets: block (a Secret), never as a plaintext parameter.

  2. Let an administrator name the relay

    A change in the console needs no restart or re-sync — the merge runs per message.

  3. Check what resolved

    Terminal window
    curl -s -H "Authorization: Bearer $ADMIN_PAT" \
    https://elitea.example.com/api/v2/admin/email/administration | jq

    .settings is what the console stored, .effective is the merged document the next message uses, .sources names database/environment/unset per field, and .configured/.reason say whether a message can be sent.

Separate allowlists guard separate outbound paths:

Names the hosts a saved provider credential’s api_base may point at. It is the floor: the gateway also reads egress_allowlist rows from gateway.governance_config (edit them at Admin → LLM Governance → New entry, type Egress allowlist, no re-sync needed) and enforces the union. An authored row can only add a destination — nothing an admin authors can withdraw a host the chart named. For a host that a running deployment needs, add a governance entry first; change this variable for hosts that every deployment of the configuration needs.

When the list refuses a host, Test connection names the host and both places to add it. See When the egress allowlist refuses a provider. Vertex AI needs its regional host, <location>-aiplatform.googleapis.com, and the token host oauth2.googleapis.com.

One grammar for both sources: a bare host, host:port, a *.domain wildcard, or a CIDR block. The first entry anywhere turns the restriction on — with none in either source, a credential may name any public host.

A private endpoint needs its address named, not only a hostname — bifrost’s SSRF-safe dialer refuses RFC 1918 and loopback destinations unless an entry explicitly names a private address or block, because the gateway never resolves a name before deciding (that would reopen the DNS-rebinding race the allowlist exists to close). Link-local addresses (169.254.0.0/16, fe80::/10 — cloud metadata endpoints) are refused no matter what the allowlist says.

GET /governance/status on the gateway (proxied at GET /api/v2/admin/gateway/status, and shown on Admin → LLM Proxy → Status) reports the merged list by source and whether a private destination is actually reachable.

Admin → Budgets writes spend-ceiling rows, but the LLM gateway is what enforces them, through a NATS JetStream counter — GATEWAY_NATS_URL names that server. In Kubernetes the chart derives it from the top-level nats block as tls://elitea-nats.<namespace>.svc.cluster.local:4222, in the platform’s own namespace, and mounts the gateway’s NATS client certificate beside it; an override must also be tls:// and carry no credential.

Worker implementation: python (default) or rust

Section titled “Worker implementation: python (default) or rust”

worker.implementation selects which agent worker image the chart runs; the values are not interchangeable and the chart handles the difference rather than the operator.

Every catalogued toolkit type and every internal chat-agent tool is available, at the cost of the larger interpreter/dependency image. This is the default in both the Helm chart and standalone-stack.sh.

runtime.json — the worker’s own configuration document — is byte-identical either way; only the process and its capability surface differ.

main.env.STORAGE_BACKEND / STORAGE_CONTAINER point at an S3-compatible bucket. It must exist before the first sync — the readiness probe tolerates a missing bucket, but the very next boot step (setting the bucket’s retention lifecycle) does not, so a missing bucket looks like a green install that then crash-loops. objectInit.enabled: true creates it for a self-hosted S3-compatible endpoint you administer directly (RustFS, MinIO); a managed cloud object store almost always withholds CreateBucket from an application-scoped credential, so create it out of band there.

Both are the same pattern — a Go subapp-host serving the SPI, reaching an engine sidecar over a Unix socket (for DeepWiki the Rust-native elitea-deepwiki-engine-native image, for Inventory the Rust-native elitea-inventory-engine image). Both are on by default, and each is one switch: deepwiki.enabled / inventory.enabled. The chart derives elitea-main’s side from the provider’s block — ELITEA_DEEPWIKI_ENABLED / ELITEA_INVENTORY_ENABLED, the git allowlist (copied from deepwiki.env / inventory.env), the client-certificate paths and mounts, and the platform gRPC target — so the two halves cannot drift apart. A main.env value that agrees is kept; one that disagrees fails the render with the switch to set instead. To leave either out, set enabled: false explicitly. The one value the chart cannot default is the git allowlist (ELITEA_DEEPWIKI_GIT_ALLOWLIST, ELITEA_INVENTORY_GIT_ALLOWLIST): the render lists it with every other missing value in one message. Their callback client certificates (elitea-deepwiki-callback-client, elitea-inventory-callback-client) are issued by cert-manager from the elitea-runtime-ca issuer, and their identity Secrets have fixed names (elitea-deepwiki-secrets, elitea-inventory-secrets, key identity-secret).

Each provider’s scratch space (deepwiki.scratch 8Gi, inventory.scratch 20Gi by default) is ephemeral clone/index space, not durable storage — a wiki publishes to PostgreSQL, and a replica that dies mid-generation only loses work already being redone. ELITEA_DEEPWIKI_GIT_ALLOWLIST / the Inventory equivalent are read fail-closed by both halves (the facade and the provider); two values that disagree mean an invocation that starts and then fails.

See DeepWiki and Inventory for the product-facing side.

Traces flow over OpenTelemetry to the chart’s own otelCollector (on by default, otel/opentelemetry-collector-contrib). The shipped pipeline exports to debug (log-only) — swap the otelCollector.config exporter for a real sink (an OTLP/HTTP endpoint, a vendor backend) before relying on traces outliving the pod. There is no bundled Langfuse or third-party APM integration.

The chart sets each component’s OTEL_EXPORTER_OTLP_ENDPOINT (elitea-main, the scheduler, the LLM gateway, DeepWiki, Inventory, elitea-vector and the worker) where you have not set it or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT or OTEL_SDK_DISABLED:

  1. global.otelEndpoint, when set;
  2. else the bundled collector, http://<collector fullname>:4318, when otelCollector.enabled;
  3. else OTEL_SDK_DISABLED=true, so no component exports to localhost. The gateway does not read that variable; with no endpoint it exports nothing.

The worker has no free-form env block, so it always gets this value.

Set any of these as main.env keys. Both endpoints empty (the default) means export is off: no relay starts, no listener opens, nothing dials out. A bad value refuses the boot. What the export does and does not guarantee is in Audit trail; the event format is in the audit event schema reference.

KeyDefaultMeaning
ELITEA_AUDIT_EXPORT_OTLP_ENDPOINTemptyOTLP logs receiver. For http, the collector’s base URL (/v1/logs is appended); for grpc, host:port or a URL whose scheme picks TLS (https) or plaintext (http).
ELITEA_AUDIT_EXPORT_OTLP_PROTOCOLhttphttp or grpc.
ELITEA_AUDIT_EXPORT_OTLP_HEADERS_FILEemptyAbsolute path of a JSON object of request headers (an API key for a hosted SIEM).
ELITEA_AUDIT_EXPORT_WEBHOOK_URLemptySigned webhook receiver. https, or http only when the allowlist below names the receiver. No credentials in the URL.
ELITEA_AUDIT_EXPORT_WEBHOOK_KEYS_FILEemptyAbsolute path of the signing keys file. Required with the webhook URL.
ELITEA_AUDIT_EXPORT_EGRESS_ALLOWLISTemptySame grammar as ELITEA_WEBHOOK_EGRESS_ALLOWLIST. A receiver on a private address must be named here; link-local, multicast, reserved and cloud-metadata addresses stay refused.
ELITEA_AUDIT_EXPORT_AUDIT_EVENTStrueAlso export the admin audit trail (centry.audit_events), not only security audit events.
ELITEA_AUDIT_EXPORT_AUDIT_EVENTS_FROMnownow starts at the newest audit trail row; beginning replays the table on the first run.
ELITEA_AUDIT_EXPORT_BATCH_SIZE200Events per pass, 1 to 5000. The webhook sends 100 per request.
ELITEA_AUDIT_EXPORT_INTERVAL5sPause between audit trail passes, 1s to 1h.
ELITEA_AUDIT_EXPORT_MAX_PERMANENT_ATTEMPTS5How many times the receiver may refuse the same batch as malformed (400, 413, 422) before it is dead-lettered.
ELITEA_AUDIT_EXPORT_METRICS_ADDR:9464Listener for the export’s Prometheus /metrics. Empty disables it.

The webhook keys and OTLP headers are files, never environment values. Put them in a Kubernetes Secret and let the chart mount it:

Terminal window
kubectl -n elitea create secret generic audit-export \
--from-file=webhook-keys.json --from-file=otlp-headers.json
helm upgrade elitea ./elitea --reuse-values \
--set main.auditExport.secretName=audit-export \
--set-string main.env.ELITEA_AUDIT_EXPORT_WEBHOOK_URL=https://siem.example.com/elitea \
--set-string main.env.ELITEA_AUDIT_EXPORT_WEBHOOK_KEYS_FILE=/etc/elitea/audit-export/webhook-keys.json

The keys file is read at start and again every 30 seconds, so a rotated key needs no restart:

{"keys":[{"id":"2026-10","secret":"<at least 32 characters>","created":"2026-10-01T00:00:00Z"}]}

To rotate, give your receiver the new key first, then list it beside the old one with a later created. Elitea signs with the newest key and names it in the X-Elitea-Key-Id header; the receiver verifies with whichever it holds. Remove the old key once nothing needs it. At most two keys are accepted.

Off by default. main.env.ELITEA_ENTERPRISE_CREDENTIAL_GOVERNANCE_ENABLED pins every new run to a governed profile and an immutable policy revision, mounts the agent identity admin API, and can require an external Vault/OpenBao store. See Enterprise credential governance before you turn it on.

Both are console-editable under Configuration, not chart settings: a dismissible banner with its own icon/style/message, and a maintenance mode that answers 503 with a splash screen to every caller without administration access (the admin console and authentication routes stay reachable, so the window is reversible from inside itself). See Features and configuration for the click-through.