Environment Setup
Create .env from the root example file:
cp .env.example .envThe API reads runtime configuration from the monorepo root .env first, then app-local environment where applicable.
Required server-side values
| Variable | Purpose |
|---|---|
DATABASE_URL | Supabase PostgreSQL pooler URL for API runtime queries |
DATABASE_URL_DIRECT | Direct PostgreSQL URL for migrations and verification |
SUPABASE_URL | Supabase project URL |
SUPABASE_ANON_KEY | Supabase anonymous key |
SUPABASE_SERVICE_ROLE_KEY | Server-only Supabase service-role key |
SUPABASE_JWT_SECRET | JWT verification secret |
REDIS_URL | Redis connection for BullMQ, OTP, sessions, and rate limits |
ENCRYPTION_KEY | 64-character hex AES-256-GCM key for regulated data |
ENCRYPTION_KEY_PREVIOUS | Optional. The old key during a rotation — keeps rows written under it readable until they are re-encrypted or aged out by retention |
eSIM feature lanes
The complete eSIM activation surface and the global eSIM store are controlled independently:
| Variable | Unset/default resolution | Purpose |
|---|---|---|
FEATURE_ESIM | true | Complete eSIM activation: eSIM validation, activation creation, and QR/profile delivery. Set false to disable eSIM activation while physical-SIM activation remains available. |
FEATURE_ESIM_STORE | true outside production; false in production | Global eSIM store: catalog sync, browse, purchase, admin store operations, and FX refresh. Set explicitly to true or false when deployment behavior must not depend on NODE_ENV. |
These are separate lanes: disabling FEATURE_ESIM does not disable the store, and disabling FEATURE_ESIM_STORE does not disable physical or complete-lane activation. In Docker Compose, both variables are passed to the API without a Compose-side default, so an unset value keeps the API resolution above.
| Variable | Default | Purpose |
|---|---|---|
ESIM_STORE_PROVIDERS | mock-esim outside production, empty in production | Comma-separated eSIM provider plugin keys the registry registers at boot. mock-esim is refused in production. An unknown key fails the boot. When the store flag is on and the resolved list registers nothing, the boot fails — an enabled store must never run without a provider. |
MOBIMATTER_CALLBACK_URL | none | Required HTTPS callback when ESIM_STORE_PROVIDERS includes mobimatter. Credentials are saved encrypted through Admin → Plugins, not environment variables. |
MOBIMATTER_MAX_CONCURRENCY | 2 | Bounds concurrent MobiMatter calls (1-10). It does not add retries. |
ESIM_SYNC_INTERVAL_MS | 300000 | Scheduled eSIM provider catalog refresh interval. Browse routes never call a provider catalog directly. |
See MobiMatter eSIM Provider Operations for the safe deployment and live-test sequence.
FX rate values (defaults exist — required only conditionally)
| Variable | Purpose |
|---|---|
FX_PROVIDER | FX rate provider — mock (default) or exchangerate-api. mock is refused in production when the eSIM store is enabled. |
EXCHANGE_RATE_API_KEY | Optional, but required when FX_PROVIDER=exchangerate-api |
FX_REFRESH_INTERVAL_MS | FX rate refresh interval in milliseconds (default 86400000) |
Local app ports
| Workspace | URL |
|---|---|
apps/admin | http://localhost:3000 |
apps/api | http://localhost:3001 |
apps/web-b2c | http://localhost:3002 |
apps/site | http://localhost:3003 |
Set CORS_ORIGINS to include every frontend origin that calls the API.
Public frontend values
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_API_BASE_URL | API URL exposed to Next.js browser bundles |
NEXT_PUBLIC_SITE_URL | Public landing site URL for Next.js links |
VITE_API_BASE_URL | API URL exposed to Vite browser bundles |
VITE_B2C_WEB_URL | B2C web URL used by the landing site CTA |
VITE_SITE_URL | Public landing site URL for Vite deployments |
Never place secrets in NEXT_PUBLIC_* or VITE_* variables.