Appearance
Environment variables
Every process (API, worker, migrate) validates these at start-up with EnvSchema and refuses to start on an invalid value. Required means no default and not optional; an optional variable with no default is unset when absent. Regenerate with pnpm env:reference.
Database
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
DATABASE_URL | url | yes | ||
DATABASE_APP_URL | url | no | Non-owner connection used by the API and worker so row-level security applies. |
Temporal
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
TEMPORAL_ADDRESS | string | localhost:7233 | no | |
TEMPORAL_NAMESPACE | string | default | no | |
TEMPORAL_TASK_QUEUE | string | aletheia-workflows | no | |
TEMPORAL_BACKTEST_TASK_QUEUE | string | aletheia-backtests | no | Long-running backtests run on their own queue so they never starve decisions. |
TEMPORAL_APP_TASK_QUEUE | string | aletheia-apps | no | App calls (through the app runner) and document scans run on their own queue so vendor latency never starves decisions. |
Apps
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
APP_CATALOG_DIR | string | no | Where the API finds the catalogue's index.json and modules (catalog/dist in development, /app/catalog in the image); unset, no platform apps are published at start-up. | |
APP_RUNNER_URL | url | no | Where the API and the worker reach the app runner (POST /v1/invoke and the other calls); unset, apps are off: nothing is installed or run, and the catalogue is not published. | |
APP_RUNNER_TOKEN | string (min 32 chars) | no | Bearer the API, the worker and the runner share: every call to the runner carries it, and the runner presents it to the API's internal routes. Unset, the runner accepts every request (development only; in production it refuses to start without one). | |
APP_CALL_TOKEN_SECRET | string (min 32 chars) | no | Signs the tokens a call reads the tenant's documents with (GET /internal/app-calls/…); held by the API and the worker only, never by the runner. 32 characters at least. | |
APP_BLOCKED_SHA256 | string | "" | no | Hex SHA-256 hashes of modules the operator blocked, comma-separated: every call of one fails with app_blocked and GET /apps/health lists them. |
APP_RUNNER_API_URL | url | http://localhost:4000 | no | Where the runner fetches modules and documents from: the API's internal routes. |
APP_RUNNER_HOST | string | 0.0.0.0 | no | |
APP_RUNNER_PORT | integer > 0 | 4100 | no | |
APP_RUNNER_CONCURRENCY | integer ≥ 1 | 8 | no | Calls one runner process runs at once; past it the runner answers 429 busy. |
APP_RUNNER_TENANT_CONCURRENCY | integer ≥ 1 | 4 | no | Calls one tenant may have in flight on one runner process at once. |
APP_RUNNER_CACHE_DIR | string | no | Where the runner keeps fetched modules on disk; defaults to aletheia-app-runner under the temp directory. | |
APP_RUNNER_CACHE_MODULES | integer ≥ 1 | 32 | no | Compiled modules the runner keeps in memory; the least recently used go first. |
APP_RUNNER_ALLOW_PRIVATE_NETWORKS | true | false | false | no | Lets apps reach http:// URLs and private, loopback and link-local addresses. For development stacks only: in production vendors are https and public. |
API
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
API_HOST | string | 0.0.0.0 | no | |
API_PORT | integer > 0 | 4000 | no | |
API_INTERNAL_PORT | integer ≥ 1 | 4001 | no | The API's second listener, for the app runner only (GET /internal/app-modules/…, GET /internal/app-calls/…): never behind the ingress or a public Service port. |
API_CORS_ORIGINS | string | "" | no | Browser origins allowed to call the API directly; comma-separated. None by default: the admin console and the collection terminal call it through their own origin. |
API_RATE_LIMIT_PER_MINUTE | integer ≥ 0 | 600 | no | Requests a minute one caller may make before the API answers 429 rate_limited with Retry-After; per API instance. A caller is a tenant's person or service user, an applicant's submission, or on the public routes (vendor webhooks, flow previews) a client address; health checks are not counted. 0 turns the limit off. |
Zitadel (identity)
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
ZITADEL_ISSUER | url | no | Identity provider (Zitadel). The API refuses to start without the required subset. | |
ZITADEL_PROJECT_ID | string | no | ||
ZITADEL_API_CLIENT_ID | string | no | ||
ZITADEL_API_CLIENT_SECRET | string | no | ||
ZITADEL_DIRECTORY_PAT | string | no | PAT of the read-only machine user the reviewer directory uses; unset disables the directory. | |
ZITADEL_MANAGEMENT_PAT | string | no | PAT of the machine user that manages service users and their keys (Connect › API keys) and the tenants' people (Settings › Team); it needs user and user-grant management in the tenant organisations. Unset disables those routes. | |
ZITADEL_SIGNUP_PAT | string | no | PAT of the aletheia-signup machine user (instance role IAM_ORG_MANAGER): POST /signup creates a workspace's organisation, grants it the project and creates its admin. Signup is off without it, RESEND_API_KEY, SIGNUP_EMAIL_FROM and SIGNUP_CONSOLE_URL. |
Signup
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
RESEND_API_KEY | string | no | Resend API key; the signup email carries the new admin's temporary password. | |
SIGNUP_EMAIL_FROM | string (min 3 chars) | no | Sender of the signup email, Name <address> on a domain verified in Resend. | |
SIGNUP_EMAIL_REPLY_TO | string | no | Reply-To of the signup email; the sender when unset. | |
SIGNUP_CONSOLE_URL | url | no | The admin console's public address, in the signup email and the answer to the form. | |
TURNSTILE_SECRET_KEY | string | no | Cloudflare Turnstile secret. When set, POST /signup requires a token that passes; unset accepts the form without a bot check (development only). | |
SIGNUP_MAX_PER_HOUR | integer ≥ 0 | 20 | no | Workspaces signup may create per hour across the deployment; 0 turns signup off. |
Submission tokens
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
SUBMISSION_TOKEN_SECRET | string (min 32 chars) | no | Signs the links end customers use to open a collection flow. |
Collection flow
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
COLLECTION_FLOW_URL | url | no | Public base URL of the collection terminal, used to build those links. | |
COLLECTION_TERMINAL_TENANT_NAME | string | no | The name the collection terminal shows for tenants without their own branding (Settings › Branding). A tenant's own name or logo replaces it and the logo below together. | |
COLLECTION_TERMINAL_LOGO_URL | url | no | The logo for those tenants (https). | |
COLLECTION_TERMINAL_PRIMARY_COLOR | string | no | The brand color for those tenants, #rgb or #rrggbb. | |
COLLECTION_TERMINAL_COLOR_SCHEME | light | dark | system | no | light, dark or system (the applicant's device) for those tenants. | |
COLLECTION_TERMINAL_SUPPORT_URL | url | no | Where "Help" leads for those tenants (https); a tenant's own support page or address replaces this and the address below together. | |
COLLECTION_TERMINAL_SUPPORT_EMAIL | string | no | The support address for those tenants. | |
COLLECTION_TERMINAL_PRIVACY_URL | url | no | The privacy notice for those tenants (https). | |
COLLECTION_TERMINAL_TERMS_URL | url | no | The terms for those tenants (https). | |
COLLECTION_TERMINAL_RETURN_URL | url | no | Where "Back to …" leads after an approval, for those tenants (https). | |
COLLECTION_TERMINAL_SHOW_DECISION | true | false | false | no | true shows applicants the decision itself (approved, not approved) for flows that set no outcome visibility; false, where their application stands. |
Webhooks
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
WEBHOOK_PUBLIC_URL | url | no | Public base URL vendors call back on (<url>/webhooks/apps/<app>). | |
WEBHOOK_ALLOW_PRIVATE_NETWORKS | true | false | false | no | Lets outbound webhooks reach http:// URLs and private, loopback and link-local addresses. For development stacks only: in production endpoints are https and public. |
Object storage
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
STORAGE_ENDPOINT | url | no | S3-compatible object storage for documents (Garage in the dev stack). Unset disables documents. | |
STORAGE_PUBLIC_ENDPOINT | url | no | Endpoint browsers reach for direct uploads and downloads; defaults to STORAGE_ENDPOINT. | |
STORAGE_REGION | string | garage | no | |
STORAGE_BUCKET | string | aletheia-documents | no | |
STORAGE_ACCESS_KEY | string | no | ||
STORAGE_SECRET_KEY | string | no | ||
STORAGE_CORS_ORIGINS | string | no | Comma-separated browser origins allowed to upload directly (the collection terminal and the admin console). |
Documents
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
DOCUMENT_SCANNER | none | none | no | Document scanner engine; none marks every document clean (ClamAV is deferred). |
OpenTelemetry
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | url | no | OTLP/HTTP collector base URL; unset disables tracing and metric export entirely. | |
OTEL_SERVICE_NAME | string | no | Overrides the default service name (aletheia-api, aletheia-worker). | |
OTEL_TRACES_SAMPLER | string | no | ||
OTEL_TRACES_SAMPLER_ARG | string | no | ||
OTEL_SDK_DISABLED | boolean | false | no | |
OTEL_PG_STATEMENTS | boolean | false | no | Include SQL statement text on database spans (postgres.js). |
Metrics
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
METRICS_PORT | integer > 0 | no | Serves Prometheus /metrics on this port when set (API and worker use their own). |
Worker
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
WORKER_HEALTH_PORT | integer > 0 | no | The worker's GET /healthz port; unset disables the health server. |
Schema check
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
SCHEMA_CHECK | strict | lenient | no | What to do when the database schema is behind the bundled migrations: strict refuses to start (production default), lenient logs a warning (development default). |
General
| Variable | Type | Default | Required | Description |
|---|---|---|---|---|
NODE_ENV | development | test | production | development | no | |
LOG_LEVEL | trace | debug | info | warn | error | fatal | info | no | |
SECRET_STORE_KEY | string | no | 32 random bytes, base64: seals the secrets written through the API, app secrets and the signing secrets of outbound webhooks. Unset disables those writes (and outbound webhooks). | |
TRACE_URL_TEMPLATE | string | no | A link to one trace in your tracing backend, with {traceId} where the id goes, such as https://grafana.example/explore?traceId={traceId}. A run's page links the trace of its start there for callers with ops:read; unset or empty, there is no link. | |
DEPLOYMENT_REGION | string | no | Where this deployment keeps its data, as a label such as eu-fra; GET /tenants/me reports it as the tenant's region (Settings › Tenant). Unset or empty reports none. |