# This file is a safe config template for .env. # Keep placeholders like ID_IN_ENV_LOCAL and SECRET_IN_ENV_LOCAL here. # Store real secrets in .env.local (see .env.local_template). # # Sections are grouped per @luckystack/* package so you can see which keys # belong to which install. Keys inside a section marked (Optional) can stay # empty if you don't use that package. # ============================================ # === @luckystack/core + @luckystack/server (required) === # ============================================ # Base runtime: process mode, host, public URL, CORS, Redis. # # NOTE: the listen port and the preset bundle are positional argv, NOT env-vars: # npm run server (default preset, port 80) # npm run server -- billing (one bundle, port 80) # npm run server -- billing,vehicles 4001 (merge bundles, listen on 4001) # Maps merge across the listed presets; key collisions throw at boot. NODE_ENV=development # development | production SECURE=false # when true we use https else http, make sure your nginx server is configured to use https if this is set to true PROJECT_NAME=YOUR_PROJECT_NAME # used for the redis cache so that when using the same template other session data will not collide with this one SERVER_IP=localhost # this should either be localhost if using a reverse proxy or 0.0.0.0 if you want other devices on your network to access the server to test mobile support for example or networking between devices. have it set to the ip when calling the node server directly # DNS is used to redirect the user to the server when they login with oauth # so when NODE_ENV is set to development it should be someting like http://localhost:5173 # and when NODE_ENV is set to production it should be someting like https://app.server.com DNS=http://localhost:5173 REDIS_HOST=127.0.0.1 REDIS_USER=default REDIS_PASSWORD= REDIS_PORT=6379 # Extra origins allowed to call the server (comma-separated). DNS is always allowed. EXTERNAL_ORIGINS=https://accounts.google.com,https://github.com,https://www.facebook.com,https://discord.com # Override which env files the framework loads, in order ("later overrides earlier"). # Default is ".env,.env.local". This must be a REAL environment variable (set in your # shell / process manager), NOT a key here — these files are only read AFTER the list # is resolved. Leave unset for the default. # LUCKYSTACK_ENV_FILES=.env,.env.local # ============================================ # === @luckystack/router (Optional) === # ============================================ # Only set these when you actually run the router service (multi-service / split # deploys). Plain `npm run server` does NOT read any of these. # Override of NODE_ENV when looking up the current `deploy.config.ts -> environments` key. # Useful for staging / preview deploys that keep NODE_ENV=production but live in a separate Redis namespace. LUCKYSTACK_ENV= # Preset name that the locally running backend bundle owns. The router treats other # services as remote and uses fallback routing. Leave empty for single-bundle setups. LUCKYSTACK_PRESET= # Port the router process listens on. Defaults to deploy.config.ts -> routing.defaultRouterPort (4000). ROUTER_PORT= # ============================================ # === @luckystack/login (Optional OAuth providers) === # ============================================ # Credentials-based login works without any of these. Add a block ONLY for the # OAuth providers you actually want to expose on /login. Each provider is # independent — you do not need to fill in all five. # # Real client secrets belong in .env.local. The keys below stay on # ID_IN_ENV_LOCAL / SECRET_IN_ENV_LOCAL so that the dotenv loader doesn't crash. # --- Google --- # https://console.cloud.google.com/apis/credentials # Create a new project -> Credentials -> Create OAuth client ID. # Authorized redirect URI for dev: http://localhost:80/auth/callback/google GOOGLE_CLIENT_ID=ID_IN_ENV_LOCAL GOOGLE_CLIENT_SECRET=SECRET_IN_ENV_LOCAL DEV_GOOGLE_CLIENT_ID=ID_IN_ENV_LOCAL DEV_GOOGLE_CLIENT_SECRET=SECRET_IN_ENV_LOCAL # --- GitHub --- # https://github.com/settings/developers # Homepage: http://localhost:80, callback: http://localhost:80/auth/callback/github GITHUB_CLIENT_ID=ID_IN_ENV_LOCAL GITHUB_CLIENT_SECRET=SECRET_IN_ENV_LOCAL DEV_GITHUB_CLIENT_ID=ID_IN_ENV_LOCAL DEV_GITHUB_CLIENT_SECRET=SECRET_IN_ENV_LOCAL # --- Facebook --- # https://developers.facebook.com/async/registration/dialog/?src=default # Use Cases -> "Authenticate and request data from users with Facebook Login" -> Quickstart. # For production set callback under Use Cases -> Settings -> Valid OAuth redirect URIs. FACEBOOK_CLIENT_ID=ID_IN_ENV_LOCAL FACEBOOK_CLIENT_SECRET=SECRET_IN_ENV_LOCAL DEV_FACEBOOK_CLIENT_ID=ID_IN_ENV_LOCAL DEV_FACEBOOK_CLIENT_SECRET=SECRET_IN_ENV_LOCAL # --- Discord --- # https://discord.com/developers/applications # Create app -> OAuth2 -> add redirect http://localhost:80/auth/callback/discord DISCORD_CLIENT_ID=ID_IN_ENV_LOCAL DISCORD_CLIENT_SECRET=SECRET_IN_ENV_LOCAL DEV_DISCORD_CLIENT_ID=ID_IN_ENV_LOCAL DEV_DISCORD_CLIENT_SECRET=SECRET_IN_ENV_LOCAL # --- Microsoft (Azure AD) --- MICROSOFT_CLIENT_ID=ID_IN_ENV_LOCAL MICROSOFT_CLIENT_SECRET=SECRET_IN_ENV_LOCAL DEV_MICROSOFT_CLIENT_ID=ID_IN_ENV_LOCAL DEV_MICROSOFT_CLIENT_SECRET=SECRET_IN_ENV_LOCAL # 'common' (default) accepts any tenant; set to a UUID to restrict to a single # Azure AD tenant. Auto-forwarded to microsoftProvider by @luckystack/login's # env-driven provider scan when MICROSOFT_CLIENT_ID/SECRET are set (no oauthProviders.ts file needed). MICROSOFT_TENANT_ID=common # ============================================ # === Database (Prisma, required) === # ============================================ # DATABASE_URL must match the datasource in prisma/schema.prisma. # # After changing providers: # 1. Update prisma/schema.prisma datasource # 2. Update DATABASE_URL below # 3. Run: npm run prisma:generate && npm run prisma:db:push # --- MYSQL --- # DATABASE_URL="mysql://root:SECRET_IN_ENV_LOCAL@localhost:3306/databaseName" # --- POSTGRESQL --- # DATABASE_URL="postgresql://root:SECRET_IN_ENV_LOCAL@localhost:5432/databaseName" # --- MONGODB (default) --- # DATABASE_URL="mongodb://root:SECRET_IN_ENV_LOCAL@localhost:27017/databaseName" # With some query parameters: DATABASE_URL="mongodb://root:SECRET_IN_ENV_LOCAL@localhost:27017/databaseName?authSource=admin&replicaSet=rs0" # --- SQLITE (no server needed - great for development) --- # DATABASE_URL="file:./dev.db" # ============================================ # === @luckystack/email (Optional) === # ============================================ # Used by the framework's password-reset flow and any project code that calls # sendEmail(). autoSelectEmailSender() in server/server.ts picks an adapter # based on which env vars are set, in this order of priority: # 1. RESEND_API_KEY set -> Resend # 2. SMTP_HOST set -> SMTP (nodemailer) # 3. neither set -> ConsoleSender (logs the rendered email to the # terminal; nothing is actually sent) # # Pick ONE of the two blocks below; you do NOT need to fill in both. With # ConsoleSender you can still test the reset flow end-to-end by copying the # reset link out of the terminal log. # Shared by both adapters. Falls back to onboarding@resend.dev (Resend sandbox). EMAIL_FROM= # --- OPTION A: Resend --- # Sign up at https://resend.com (free tier: 100 emails/day). API Keys -> Create. # Use onboarding@resend.dev as EMAIL_FROM until you verify your own domain. RESEND_API_KEY= # --- OPTION B: SMTP (Mailtrap, Gmail SMTP, your own server, etc.) --- # Leave RESEND_API_KEY empty if you want this block to win. # SMTP_SECURE=true for port 465 (implicit TLS); false for 587 (STARTTLS). SMTP_HOST= SMTP_PORT=587 SMTP_SECURE=false SMTP_USER= SMTP_PASS= # ============================================ # === @luckystack/error-tracking (Optional) === # ============================================ # Get your DSN from https://sentry.io — create one project for Node.js (server) # and one for React (client). The VITE_ prefix is required for client-side # values so Vite exposes them to the browser bundle. # Server-side SENTRY_DSN= SENTRY_ENABLED=false # 'true' to enable Sentry in development # Client-side VITE_SENTRY_DSN= VITE_SENTRY_ENABLED=false # 'true' to enable Sentry in development # ============================================ # === @luckystack/secret-manager (Optional) === # ============================================ # Centralizes secrets behind an external append-only secret-manager server. # Instead of real secrets, .env holds POINTERS of the shape _V: # # OPENAI_KEY=OPENAI_AUTHORIZATION_KEY_V5 # # When LUCKYSTACK_SECRET_MANAGER_URL is set AND @luckystack/secret-manager is # installed, server boot resolves every pointer against the server and overwrites # process.env with the real value (remote mode: an unresolved pointer or an # unreachable server is a HARD boot failure). A value that is NOT pointer-shaped # is left untouched, so plain local secrets keep working. # # Leave LUCKYSTACK_SECRET_MANAGER_URL empty (or don't install the package) and # boot simply falls through to the plain local env files — no resolution, no crash. # # Wiring: config.ts exposes a `secretManager` slot ({ url, token }); server.ts # resolves it through server/bootstrap/initSecrets.ts before any secret is read. # The shared bearer token is NOT an env var — it lives in a gitignored single-line # file `.secret-manager-token` referenced via token: { fromFile: ... }. See # docs/ARCHITECTURE_SECRET_MANAGER.md. # Base URL of the external secret-manager server. Empty = feature off (local env). LUCKYSTACK_SECRET_MANAGER_URL= # Example committed pointer (uncomment + point at a real server to use): # OPENAI_KEY=OPENAI_AUTHORIZATION_KEY_V1 # ============================================ # === Test scripts (Optional, scripts/test*.ts) === # ============================================ # Only consumed by the ad-hoc test scripts (testAuth, testContract, testFuzz, # testRateLimit). Plain `npm run server` does NOT read these. Leave everything # blank if you don't run those scripts. # Base URL the scripts hit. Defaults to http://localhost:80. TEST_BASE_URL= # Comma-separated `/` pairs to skip (e.g. `playground/spam,settings/deleteAccount`). TEST_SKIP= # Rate-limit ceiling — endpoints declared with a rateLimit above this are skipped (default 50). TEST_MAX_RATE_LIMIT= # 'true' (default) to call /_test/reset between endpoints to clear rate-limit counters. TEST_RESET_BETWEEN= # X-Test-Reset-Token header — required only when /_test/reset is locked down (staging). TEST_RESET_TOKEN= # Cookie name used to inject a pre-built session for contract/fuzz tests. Defaults to the framework cookie. TEST_SESSION_COOKIE_NAME= # Bearer token used by contract/fuzz tests when running in header-token mode instead of cookies. TEST_AUTH_TOKEN= # ============================================ # === Framework debugging (Optional) === # ============================================ # Print a warn + stacktrace each time a session is deleted (logout / # revokeSession / signOutEverywhere / deleteAccount). OFF by default — enable # only when hunting a "why was I logged out?" bug to identify the originating # caller. Leave blank for normal dev/test/prod (no log noise). LUCKYSTACK_TRACE_SESSION_DELETES= # Auto-pick the next free port when SERVER_PORT is already taken (dev convenience). # Defaults ON in dev, OFF in prod. Set to 0 to force-fail on a busy port instead. # SERVER_PORT_AUTO_INCREMENT=1 # Grace window (ms) the dev supervisor waits for a clean child exit before SIGKILL # on restart. Default 1500 — raise it for a slow-shutting-down server. # LUCKYSTACK_SUPERVISOR_GRACE_MS=1500 # Log, at boot, every key defined in MORE THAN ONE loaded env file (and which wins). # Use when a value "won't change" — it's usually still set in another loaded file. # LUCKYSTACK_ENV_DEBUG=1