Configuration & Environment Variables
Central reference for configuring the Heimdall platform: the Rust API (and bots)
load layered TOML files with environment-variable overrides via the heimdall-config
crate, while the Next.js apps (backend, id, policies) are configured purely
through .env files.
Config Resolution Order (API)
The API uses heimdall-config (crates/heimdall-config/src/lib.rs). Configuration is
built from four sources, where later sources override earlier ones :
config/default.toml — base defaults (required , load fails if missing)
config/{APP_ENV}.toml — run-mode overrides, e.g. production.toml (optional)
config/local.toml — local developer overrides, git-ignored (optional)
HEIMDALL__* environment variables — runtime overrides (highest priority)
The run mode comes from the APP_ENV environment variable and defaults to
"development" when unset. The API config lives in platform/api/config/
(default.toml, staging.toml, production.toml, local.toml).
crate :: load ( "APP_ENV" , "HEIMDALL" )
Environment-variable override convention
Verified from heimdall-config:
Prefix : HEIMDALL
Prefix separator : __ (double underscore)
Nesting separator : __ (double underscore)
So a TOML key maps to an env var by uppercasing the path and joining sections with
__:
TOML Environment variable database.urlHEIMDALL__DATABASE__URLserver.portHEIMDALL__SERVER__PORTemail.smtp.hostHEIMDALL__EMAIL__SMTP__HOSTstorage.upload_limits.images.max_size_mbHEIMDALL__STORAGE__UPLOAD_LIMITS__IMAGES__MAX_SIZE_MB
List-valued keys are parsed as comma-separated strings. The following keys are
registered for list parsing (others stay scalar):
cors.allowed_origins
email.supported_locales
storage.upload_limits.images.allowed_types
storage.upload_limits.documents.allowed_types
storage.upload_limits.videos.allowed_types
storage.upload_limits.general.allowed_types
Example: HEIMDALL__CORS__ALLOWED_ORIGINS="https://a.com,https://b.com".
Code defaults vs. shipped
default.tomlThe Default column below is the compiled-in default from the Rust struct
(#[serde(default = ...)]). The shipped platform/api/config/default.toml sometimes
sets a different value (e.g. GraphQL path is "/gql" in default.toml but "/graphql"
is the struct default). Where a field has no struct default it is required and must
be supplied by default.toml (which is always loaded).
API Configuration Reference
All structs live in crates/heimdall-config/src/common.rs (top-level Settings).
[server]
Key Type Default Description Env override hoststring required (127.0.0.1)Bind address HEIMDALL__SERVER__HOSTportu16 required (3000)Bind port HEIMDALL__SERVER__PORTworkersusize? none (4)Actix worker threads HEIMDALL__SERVER__WORKERSkeep_aliveu64 75Keep-alive timeout (seconds) HEIMDALL__SERVER__KEEP_ALIVEpublic_urlstring http://localhost:3000Public base URL for OAuth redirects / external links HEIMDALL__SERVER__PUBLIC_URL
[database] (PostgreSQL)
Key Type Default Description Env override urlstring required PostgreSQL connection URL HEIMDALL__DATABASE__URLmax_connectionsu32 required (10)Max pool connections HEIMDALL__DATABASE__MAX_CONNECTIONSmin_connectionsu32 required (2)Min idle connections HEIMDALL__DATABASE__MIN_CONNECTIONSconnect_timeoutu64 required (30)Connect timeout (seconds) HEIMDALL__DATABASE__CONNECT_TIMEOUTidle_timeoutu64 required (600)Idle timeout (seconds) HEIMDALL__DATABASE__IDLE_TIMEOUTmax_lifetimeu64 1800Max connection lifetime (seconds) HEIMDALL__DATABASE__MAX_LIFETIME
[timescale] (TimescaleDB — GPS & audit time-series)
Key Type Default Description Env override urlstring required TimescaleDB connection URL (default port 5433) HEIMDALL__TIMESCALE__URLmax_connectionsu32 5Max pool connections HEIMDALL__TIMESCALE__MAX_CONNECTIONSmin_connectionsu32 1Min idle connections HEIMDALL__TIMESCALE__MIN_CONNECTIONSconnect_timeoutu64 30Connect timeout (seconds) HEIMDALL__TIMESCALE__CONNECT_TIMEOUTidle_timeoutu64 600Idle timeout (seconds) HEIMDALL__TIMESCALE__IDLE_TIMEOUTmax_lifetimeu64 1800Max connection lifetime (seconds) HEIMDALL__TIMESCALE__MAX_LIFETIMEcompression_after_hoursu32 168Compress chunks older than N hours (7 days) HEIMDALL__TIMESCALE__COMPRESSION_AFTER_HOURS
[redis]
Key Type Default Description Env override urlstring required Redis connection URL HEIMDALL__REDIS__URLpool_sizeusize required (10)Max pool connections HEIMDALL__REDIS__POOL_SIZEtimeoutu64 5Connection timeout (seconds) HEIMDALL__REDIS__TIMEOUT
[graphql]
Key Type Default Description Env override playgroundbool trueEnable GraphQL playground HEIMDALL__GRAPHQL__PLAYGROUNDpathstring /graphql (toml: /gql)GraphQL endpoint path HEIMDALL__GRAPHQL__PATHmax_depthu32 10Max query depth HEIMDALL__GRAPHQL__MAX_DEPTHmax_complexityu32 100Max query complexity HEIMDALL__GRAPHQL__MAX_COMPLEXITY
[websocket]
Key Type Default Description Env override pathstring /wsWebSocket endpoint path HEIMDALL__WEBSOCKET__PATHheartbeat_intervalu64 5Heartbeat interval (seconds) HEIMDALL__WEBSOCKET__HEARTBEAT_INTERVALclient_timeoutu64 10Client inactivity timeout (seconds) HEIMDALL__WEBSOCKET__CLIENT_TIMEOUTmax_message_sizeusize 65536Max message size (bytes, 64 KB) HEIMDALL__WEBSOCKET__MAX_MESSAGE_SIZEmax_frame_sizeusize 16384Max frame size (bytes, 16 KB) HEIMDALL__WEBSOCKET__MAX_FRAME_SIZEmax_channels_per_sessionusize 100Max channels per WS session HEIMDALL__WEBSOCKET__MAX_CHANNELS_PER_SESSION
[auth]
Key Type Default Description Env override jwt_secretstring required (empty)JWT signing secret — must be overridden HEIMDALL__AUTH__JWT_SECRETjwt_expirationi64 86400Access token TTL (seconds, 24h) HEIMDALL__AUTH__JWT_EXPIRATIONrefresh_token_expirationi64 2592000Refresh token TTL (seconds, 30d) HEIMDALL__AUTH__REFRESH_TOKEN_EXPIRATIONtotp_encryption_keystring ""Base64 AES-256 key for TOTP secrets (openssl rand -base64 32) HEIMDALL__AUTH__TOTP_ENCRYPTION_KEY
[cors]
Key Type Default Description Env override allowed_originsstring[] required Allowed origins (comma-separated for env) HEIMDALL__CORS__ALLOWED_ORIGINSallow_credentialsbool trueAllow credentials in CORS HEIMDALL__CORS__ALLOW_CREDENTIALSmax_ageu64 3600Preflight cache max-age (seconds) HEIMDALL__CORS__MAX_AGE
[rate_limiting]
Key Type Default Description Env override max_requestsu64 100Max requests per window HEIMDALL__RATE_LIMITING__MAX_REQUESTSwindow_secsu64 60Window length (seconds) HEIMDALL__RATE_LIMITING__WINDOW_SECS
[logging]
Key Type Default Description Env override levelstring required (debug)Log level: trace/debug/info/warn/error HEIMDALL__LOGGING__LEVELformatstring required (pretty)Log format: json or pretty HEIMDALL__LOGGING__FORMAT
[app]
Key Type Default Description Env override environmentstring required (development)App environment: development/staging/production HEIMDALL__APP__ENVIRONMENTdebugbool trueDebug mode HEIMDALL__APP__DEBUGnamestring required (Heimdall API)Application name HEIMDALL__APP__NAME
[apps]
Frontend URLs used for OAuth client redirect URIs on first launch.
Key Type Default Description Env override backend_urlstring http://localhost:3001Backend/console app URL HEIMDALL__APPS__BACKEND_URLid_urlstring http://localhost:3002ID (login) app URL HEIMDALL__APPS__ID_URLpolicies_urlstring http://localhost:3004Policies app URL HEIMDALL__APPS__POLICIES_URL
[bots]
Optional bot health-check endpoints.
Key Type Default Description Env override discord_bot_urlstring? none Discord bot health URL (e.g. http://localhost:3006/health) HEIMDALL__BOTS__DISCORD_BOT_URLtwitch_bot_urlstring? none Twitch bot health URL (e.g. http://localhost:3007/health) HEIMDALL__BOTS__TWITCH_BOT_URL
[scheduler]
The whole section is optional (Option<SchedulerConfig>).
Key Type Default Description Env override enabledbool trueEnable background scheduler HEIMDALL__SCHEDULER__ENABLEDdeletion_cronstring? 0 */15 * * * *Account-deletion job cron (every 15 min) HEIMDALL__SCHEDULER__DELETION_CRONintegration_refresh_cronstring? 0 */5 * * * *Integration token-refresh cron (every 5 min) HEIMDALL__SCHEDULER__INTEGRATION_REFRESH_CRONintegration_refresh_buffer_minutesi64? 15Refresh this many minutes before token expiry HEIMDALL__SCHEDULER__INTEGRATION_REFRESH_BUFFER_MINUTESchannel_stats_refresh_cronstring? 0 0 */6 * * *Channel-stats refresh cron (every 6h) HEIMDALL__SCHEDULER__CHANNEL_STATS_REFRESH_CRONchannel_stats_stale_minutesi64? 360Minutes until channel stats are stale (6h) HEIMDALL__SCHEDULER__CHANNEL_STATS_STALE_MINUTES
Cron format is 6-field: sec min hour day month day-of-week. The defaults listed are
the documented defaults; default.toml ships deletion_cron = "0 */1 * * * *".
[email]
Key Type Default Description Env override enabledbool falseEnable email sending HEIMDALL__EMAIL__ENABLEDproviderstring consolesendgrid, smtp, or consoleHEIMDALL__EMAIL__PROVIDERfrom_emailstring noreply@elcapitano.comSender address HEIMDALL__EMAIL__FROM_EMAILfrom_namestring elcapitano IdentitySender display name HEIMDALL__EMAIL__FROM_NAMEtoken_expiry_hoursu32 24Verification-link TTL (hours) HEIMDALL__EMAIL__TOKEN_EXPIRY_HOURSdefault_localestring enFallback email locale HEIMDALL__EMAIL__DEFAULT_LOCALEsupported_localesstring[] ["en","de"]Supported email locales (comma-separated for env) HEIMDALL__EMAIL__SUPPORTED_LOCALES
[email.sendgrid]
Key Type Default Description Env override api_keystring ""SendGrid API key HEIMDALL__EMAIL__SENDGRID__API_KEY
[email.smtp]
Key Type Default Description Env override hoststring localhostSMTP host HEIMDALL__EMAIL__SMTP__HOSTportu16 587SMTP port (587 STARTTLS / 465 SSL / 25 plain) HEIMDALL__EMAIL__SMTP__PORTusernamestring ""SMTP username HEIMDALL__EMAIL__SMTP__USERNAMEpasswordstring ""SMTP password HEIMDALL__EMAIL__SMTP__PASSWORDtlsbool trueUse TLS/STARTTLS HEIMDALL__EMAIL__SMTP__TLStimeoutu64 30Connection timeout (seconds) HEIMDALL__EMAIL__SMTP__TIMEOUT
[turnstile]
Key Type Default Description Env override enabledbool falseEnable Cloudflare Turnstile verification HEIMDALL__TURNSTILE__ENABLEDsecret_keystring ""Turnstile server-side secret key HEIMDALL__TURNSTILE__SECRET_KEY
[sentry]
Key Type Default Description Env override enabledbool falseEnable Sentry error tracking HEIMDALL__SENTRY__ENABLEDdsnstring ""Sentry DSN HEIMDALL__SENTRY__DSNenvironmentstring developmentSentry environment name HEIMDALL__SENTRY__ENVIRONMENTtraces_sample_ratef32 1.0Transaction sample rate (0.0–1.0) HEIMDALL__SENTRY__TRACES_SAMPLE_RATEsample_ratef32 1.0Error-event sample rate (0.0–1.0) HEIMDALL__SENTRY__SAMPLE_RATEdebugbool falseSentry SDK debug mode HEIMDALL__SENTRY__DEBUGattach_stacktracebool trueAttach stack traces to messages HEIMDALL__SENTRY__ATTACH_STACKTRACEsend_default_piibool falseSend PII in error reports HEIMDALL__SENTRY__SEND_DEFAULT_PIImax_breadcrumbsusize 100Max breadcrumbs captured HEIMDALL__SENTRY__MAX_BREADCRUMBS
[geoip]
MaxMind GeoLite2 IP-to-location lookups used for audit enrichment.
Key Type Default Description Env override database_pathstring? none Path to GeoLite2 City .mmdb HEIMDALL__GEOIP__DATABASE_PATHaccount_idstring? none MaxMind account ID (Privacy Exclusions API) HEIMDALL__GEOIP__ACCOUNT_IDlicense_keystring? none MaxMind license key (auto-update / Privacy Exclusions) HEIMDALL__GEOIP__LICENSE_KEYauto_updatebool falseAuto-update DB on startup (needs license key) HEIMDALL__GEOIP__AUTO_UPDATEupdate_interval_daysu32 7DB update interval (days) HEIMDALL__GEOIP__UPDATE_INTERVAL_DAYSprivacy_exclusions_enabledbool falseEnable Privacy Exclusions API HEIMDALL__GEOIP__PRIVACY_EXCLUSIONS_ENABLEDprivacy_exclusions_refresh_hoursu32 24Refresh interval for exclusions (hours) HEIMDALL__GEOIP__PRIVACY_EXCLUSIONS_REFRESH_HOURSprivacy_exclusions_cache_pathstring data/geoip-privacy-exclusions.jsonLocal cache file path HEIMDALL__GEOIP__PRIVACY_EXCLUSIONS_CACHE_PATH
[integrations]
Streaming-platform OAuth (Twitch, YouTube, Kick, Trovo) — separate from user auth .
Key Type Default Description Env override token_encryption_keystring ""AES-256-GCM key for stored OAuth tokens (openssl rand -base64 32) HEIMDALL__INTEGRATIONS__TOKEN_ENCRYPTION_KEYinternal_service_keystring ""Bot-to-API auth key (openssl rand -hex 32) HEIMDALL__INTEGRATIONS__INTERNAL_SERVICE_KEY
Each platform sub-section ([integrations.twitch], [integrations.youtube],
[integrations.kick], [integrations.trovo]) has the same two keys:
Key Type Default Description Env override (example: twitch) client_idstring ""OAuth client ID HEIMDALL__INTEGRATIONS__TWITCH__CLIENT_IDclient_secretstring ""OAuth client secret HEIMDALL__INTEGRATIONS__TWITCH__CLIENT_SECRET
Swap TWITCH for YOUTUBE, KICK, or TROVO for the other platforms.
[storage] (S3-compatible)
Key Type Default Description Env override enabledbool falseEnable storage service HEIMDALL__STORAGE__ENABLEDendpointstring http://localhost:9000S3 endpoint URL HEIMDALL__STORAGE__ENDPOINTregionstring us-east-1 (toml: auto)S3 region HEIMDALL__STORAGE__REGIONaccess_keystring ""S3 access key ID HEIMDALL__STORAGE__ACCESS_KEYsecret_keystring ""S3 secret access key HEIMDALL__STORAGE__SECRET_KEYbucketstring heimdallDefault bucket HEIMDALL__STORAGE__BUCKETpath_stylebool falseUse path-style URLs (required for MinIO) HEIMDALL__STORAGE__PATH_STYLEpublic_urlstring? none Public CDN URL if different from endpoint HEIMDALL__STORAGE__PUBLIC_URLpresigned_expiry_secondsu32 3600Presigned URL TTL (seconds) HEIMDALL__STORAGE__PRESIGNED_EXPIRY_SECONDS
[storage.upload_limits.*]
Four categories — images, documents, videos, general — each with the same keys:
Key Type Default (images / documents / videos / general) Description max_size_mbu64 5 / 25 / 500 / 100Max upload size (MB) allowed_typesstring[] jpeg,png,webp,gif / pdf,plain,json / mp4,webm,quicktime,x-msvideo,x-matroska / [] (all) Allowed MIME types
Env overrides follow the nested rule, e.g.
HEIMDALL__STORAGE__UPLOAD_LIMITS__IMAGES__MAX_SIZE_MB and the comma-separated
HEIMDALL__STORAGE__UPLOAD_LIMITS__IMAGES__ALLOWED_TYPES.
[pegelonline] (WSV water-level monitoring)
Key Type Default Description Env override enabledbool falseEnable Pegelonline polling HEIMDALL__PEGELONLINE__ENABLEDbase_urlstring https://pegelonline.wsv.de/webservices/rest-api/v2Pegelonline REST API base HEIMDALL__PEGELONLINE__BASE_URLradius_kmf64 30.0Search radius around GPS position (km) HEIMDALL__PEGELONLINE__RADIUS_KMstationsstring[] []Station whitelist (exact API shortnames); empty = radius-based HEIMDALL__PEGELONLINE__STATIONS
[ais] (vessel tracking)
Key Type Default Description Env override enabledbool falseEnable AIS tracking HEIMDALL__AIS__ENABLEDapi_keystring ""AIS data-provider API key HEIMDALL__AIS__API_KEYradius_kmf64 10.0Search radius around GPS position (km) HEIMDALL__AIS__RADIUS_KMignore_mmsiu32[] []MMSI numbers to exclude (e.g. own vessel) HEIMDALL__AIS__IGNORE_MMSI
Next.js App Environment Variables
The web apps (platform/backend, platform/id, platform/policies) are configured
entirely via .env (see each app's .env.example). NEXT_PUBLIC_* vars are exposed to
the browser; all others are server-only.
Common to all three apps
Variable Example Description NEXTAUTH_URLhttp://localhost:3001NextAuth base URL (per app port) NEXTAUTH_SECRET— NextAuth session secret HEIMDALL_CLIENT_IDheimdall-backend-clientOAuth client ID for this app HEIMDALL_CLIENT_SECRET— OAuth client secret NEXT_PUBLIC_API_URLhttp://localhost:3000Heimdall API base URL NEXT_PUBLIC_GRAPHQL_URLhttp://localhost:3000/v1/gqlGraphQL endpoint NEXT_PUBLIC_WS_URLws://localhost:3000/v1/wsWebSocket endpoint NEXT_PUBLIC_ID_URLhttp://localhost:3002ID app URL (app switcher) NEXT_PUBLIC_POLICIES_URLhttp://localhost:3004Policies app URL NEXT_PUBLIC_CONSOLE_URLhttp://localhost:3001Console/backend app URL NEXT_PUBLIC_DOCS_URLhttp://localhost:3003Docs app URL NEXT_PUBLIC_MAIN_URLhttp://localhost:3005Main site URL SYSTEM_API_KEY— Server-only system API key (*:*) for NextAuth adapter calls SOURCE_SERVICEbackend / id / policiesIdentifies the app in audit logs (X-Source-Service) NEXT_PUBLIC_TURNSTILE_SITE_KEY— Cloudflare Turnstile site key (empty disables) NEXT_PUBLIC_GA_MEASUREMENT_ID— Google Analytics ID (statistics-consent gated) NEXT_PUBLIC_FB_PIXEL_ID— Facebook Pixel ID (optional) NEXT_PUBLIC_HOTJAR_SITE_ID— Hotjar site ID (optional) NEXT_PUBLIC_PLAUSIBLE_DOMAIN— Plausible domain (optional) NEXT_PUBLIC_SENTRY_DSN— Sentry DSN (functional-consent gated) SENTRY_ORG— Sentry org (build/source maps) SENTRY_PROJECT— Sentry project SENTRY_AUTH_TOKEN— Sentry auth token NEXT_PUBLIC_SENTRY_DEBUGfalseEnable Sentry in development
App-specific defaults
App NEXTAUTH_URL portHEIMDALL_CLIENT_IDSOURCE_SERVICEbackend3001heimdall-backend-clientbackendid3002heimdall-id-clientidpolicies3004heimdall-policies-clientpolicies
Backend-only
Variable Example Description NEXT_PUBLIC_HEIMDALL_CLIENT_IDheimdall-backend-clientPublic client ID for WebSocket consent-revocation detection
Policies-only
Variable Example Description NEXT_PUBLIC_HEIMDALL_CLIENT_IDheimdall-policies-clientPublic client ID for WebSocket consent-revocation detection
ID-only — user-auth OAuth providers
The ID app brokers social login. Each provider needs a client ID/secret (Steam also
needs an API key):
Provider Variables Twitch TWITCH_CLIENT_ID, TWITCH_CLIENT_SECRETGoogle GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRETDiscord DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRETGitHub GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRETKick KICK_CLIENT_ID, KICK_CLIENT_SECRETSteam STEAM_CLIENT_ID, STEAM_CLIENT_SECRET, STEAM_API_KEYTrovo TROVO_CLIENT_ID, TROVO_CLIENT_SECRET
These ID-app provider OAuth credentials are separate from the API's
[integrations.*] platform OAuth, which connects channel/bot features.
Webapp Runtime Configuration (Next.js apps)
The Next.js apps (platform/backend, platform/id, platform/policies) resolve their
service URLs at request time , not at build time. This lets a single Docker image be
built once and re-pointed per environment via runtime env vars — no rebuild required.
Single source: src/lib/config.ts
Each app has a src/lib/config.ts that is the single source of env resolution . It
exposes getter functions; each getter resolves its value at call time with this priority:
HEIMDALL_* — runtime override, set at container start, server-only (wins)
NEXT_PUBLIC_* — build-time fallback, baked into the bundle (client fallback)
localhost default — local dev
HEIMDALL_* → NEXT_PUBLIC_* → localhost default
(runtime) (build-time) (dev)
The server evaluates HEIMDALL_* at request time (runtime config), while the
browser has no access to server-only HEIMDALL_* vars and therefore falls back to the
build-time NEXT_PUBLIC_* value. This split removes any SSR requirement for client code.
Getter Resolves Example value getApiUrl()API base host (no /v1) http://localhost:3000getGraphqlUrl()GraphQL endpoint http://localhost:3000/v1/gqlgetWsUrl()WebSocket endpoint ws://localhost:3000/v1/wsgetIdUrl()ID app URL http://localhost:3002getConsoleUrl()Console/backend app URL http://localhost:3001getDocsUrl()Docs app URL http://localhost:3003getPoliciesUrl()Policies app URL http://localhost:3004getMainUrl()Main site URL http://localhost:3005getSystemApiKey()System API key (server-only , no public fallback → "" on client) — getSourceService()App identifier for audit (SOURCE_SERVICE) idgetCookieDomain()Shared cookie domain (HEIMDALL_COOKIE_DOMAIN → COOKIE_DOMAIN) none getSelfUrl()This app's own URL (NEXTAUTH_URL) http://localhost:3002
getApiUrl() returns the base host only — the app appends paths (/v1/...). Do not
point HEIMDALL_API_URL at a /v1 path.
Config-first @elcto/api
The shared @elcto/api library is env-agnostic : it never reads process.env itself.
Every transport (apiRequest, graphqlRequest, createWebSocket) and route helper takes
an ApiClientConfig as its first argument:
interface ApiClientConfig {
baseUrl : string ;
wsUrl ? : string ;
systemApiKey ? : string ;
}
Each app builds this from config.ts via getApiConfig() in src/lib/api/index.ts and
passes it to every API call:
export function getApiConfig ( ) : ApiClientConfig {
return { baseUrl : getApiUrl ( ) , wsUrl : getWsUrl ( ) , systemApiKey : getSystemApiKey ( ) || undefined } ;
}
On the client, getSystemApiKey() returns "" (no public fallback) → undefined, so the
system key never reaches the browser. See the API Library docs for
the transport APIs.
Runtime override vars (HEIMDALL_*)
All optional and server-only . Each app's .env.example documents the same set. When
set, each takes precedence over the matching NEXT_PUBLIC_* build-time value.
Variable Overrides Maps to getter HEIMDALL_API_URLNEXT_PUBLIC_API_URLgetApiUrl()HEIMDALL_GRAPHQL_URLNEXT_PUBLIC_GRAPHQL_URLgetGraphqlUrl()HEIMDALL_WS_URLNEXT_PUBLIC_WS_URLgetWsUrl()HEIMDALL_ID_URLNEXT_PUBLIC_ID_URLgetIdUrl()HEIMDALL_CONSOLE_URLNEXT_PUBLIC_CONSOLE_URLgetConsoleUrl()HEIMDALL_DOCS_URLNEXT_PUBLIC_DOCS_URLgetDocsUrl()HEIMDALL_POLICIES_URLNEXT_PUBLIC_POLICIES_URLgetPoliciesUrl()HEIMDALL_MAIN_URLNEXT_PUBLIC_MAIN_URLgetMainUrl()HEIMDALL_COOKIE_DOMAINCOOKIE_DOMAINgetCookieDomain()
Note the distinct prefixes: the API (Rust) uses HEIMDALL__* (double underscore,
nested config keys); the webapp runtime overrides use HEIMDALL_* (single
underscore, flat URL vars). They are different mechanisms.
Bot Configuration
The bots use the same layered-TOML approach but with their own env prefix and a single
underscore (_) separator (not __). Each bot has config/default.toml and an
optional config/local.toml.
Bot Env prefix Run-mode env Separator Discord DISCORD_BOT_DISCORD_BOT_RUN_MODE_Twitch TWITCH_BOT_— _YouTube YOUTUBE_BOT_— _
The Discord bot (platform/discord_bot/src/config/settings.rs) loads
config/default.toml → config/{RUN_MODE}.toml → DISCORD_BOT_* env vars. Because the
separator is a single _, a nested key like bot.token maps to DISCORD_BOT_BOT_TOKEN:
TOML key Env var bot.tokenDISCORD_BOT_BOT_TOKENapi.keyDISCORD_BOT_API_KEYsentry.dsnDISCORD_BOT_SENTRY_DSN
Discord bot config sections (default.toml): environment, [bot] (prefix,
default_locale, token), [api] (graphql_endpoint, rest_endpoint,
websocket_endpoint, key, bind_address, bind_port), [apps], [sentry]
(dsn, traces_sample_rate), [logging] (level).
The Twitch and YouTube bots ship the same TOML layout (referencing TWITCH_BOT_* /
YOUTUBE_BOT_* env vars in their config/default.toml) but are currently scaffolds —
their main.rs is a stub.
Next Steps