Environment Variables
Every environment variable thunder-core reads, plus database-backed env vars resolved at runtime.
thunder-core reads the variables below. Set them in .env, or store them at runtime in the envs collection (see Database-backed Env Vars).
The authoritative list is whatever the code reads. If in doubt, grep for Env.get / Env.list / Env.number / Env.enabled / Env.params across lib/, utils/, routes/, and hooks/.
Required
BASE_URL=http://localhost:8000 # Public base URL; used by better-auth & the OAuth2 issuer
SECRET_KEY=<random-strong-secret> # Signing secret for sessions and OAuth2 JWTs
BETTER_AUTH_CAPABILITIES=email,signup # Comma list enabling auth features (see below)BETTER_AUTH_CAPABILITIES toggles auth features (see lib/auth.ts):
| Capability | Effect |
|---|---|
signup | Allow new user creation |
email | Enable email + password auth |
magicLink | Enable magic-link sign up / sign in |
github | Enable the GitHub social provider (requires GITHUB_* vars) |
multiSession | Allow multiple concurrent sessions per user |
Auth / Session (optional)
BETTER_AUTH_TRUSTED_ORIGIN=http://localhost:3000 # Comma list of trusted origins
BETTER_AUTH_TRUST_DEVICE_DAYS=30 # 2FA trust-device window (default 30)
BETTER_AUTH_MAX_MULTI_SESSIONS=5 # Max sessions when multiSession is enabled (default 5)
GITHUB_CLIENT_ID=... # Required only if the "github" capability is on
GITHUB_CLIENT_SECRET=...Branding
Served by the auth branding endpoint:
APP_DISPLAY_NAME=My App # Required for the branding endpoint
APP_DESCRIPTION=... # Optional
APP_LOGO=https://.../logo.png # Optional
APP_HOMEPAGE_URL=https://... # Optional
APP_THEME=primary:#000&bg:#fff # Parsed via Env.params() into a key/value recordEmail (SMTP)
Used by the Mailer:
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USERNAME=...
SMTP_PASSWORD=...
SMTP_SENDER=no-reply@example.com
SMTP_SENDER_NAME=My AppImageKit
IMAGEKIT_PRIVATE_KEY=private_...
IMAGEKIT_PUBLIC_KEY=public_...RBAC Behavior
RBAC_NO_CASCADE=false # If true, a tenant member's role does NOT cascade from the parent inviterDatabase-backed Env Vars
The essentials hook (hooks/essentials.ts) installs Env.onGetFailed, so any env var not found in the process environment is looked up in the envs MongoDB collection ({ key, value }).
This lets you manage secrets/config at runtime through the envs CRUD route instead of redeploying. The same hook also handles CORS (preflight + response headers, and allows the X-TENANT-ID header).
Because of this fallback, you can ship with only DATABASE_URL and SECRET_KEY in .env and store the rest in the database - useful for rotating secrets without a redeploy.