# angular-boilerplate environment. Copy to .env (gitignored) and fill in. # Empty values are treated as unset. Nothing is required — every value below # is optional and degrades gracefully. # RECOMMENDED — iron-session cookie encryption (32+ char random string). # Without it a random per-instance secret is used (warning logged) and # sessions reset on every restart / new serverless instance. # Generate: openssl rand -base64 32 SESSION_PASSWORD= # Demo mode — lets /login mint a demo admin session without configuring # OAuth or a DB. Auto: on only when NODE_ENV=development (npm run dev # sets it), off otherwise — including when NODE_ENV is unset. # true = always on — set explicitly on any deployment that should offer # a public demo (grants an ADMIN session to anyone) # false = always off (recommended for real production) # unset = auto (see server/utils/env.ts → isDemoMode) DEMO_MODE= # GitHub OAuth App credentials (via arctic) # Create at: https://github.com/settings/applications/new # Authorization callback URL: http://localhost:4201/auth/github/callback GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= # Postgres (Neon, Supabase pooler, or local) DATABASE_URL= # Public site URL — used for OAuth redirects + emails SITE_URL=http://localhost:4201 # Bootstrap admin logins (comma-separated GitHub usernames). # Matched users are CREATED as role='admin' on their first OAuth sign-in. # This only affects the initial row — once a user exists, the DB is the # source of truth and this env is ignored (so demotions stick). # Leave empty in production unless you actually want a new env-driven # admin to land on next first-sign-in. INITIAL_ADMIN_LOGINS= # Axiom — structured log shipping (free tier: 500GB/mo). # Sign up at https://app.axiom.co. Create a dataset, then an API token # scoped to that dataset. # Both TOKEN + DATASET must be set together (or both left empty). # Leave empty in dev; the logger falls back to console-only stdout. AXIOM_TOKEN= AXIOM_DATASET= AXIOM_ORG_ID= # Sentry — error monitoring + session replay. # Sign up at https://sentry.io. Create a project, copy the DSN (public). # DSN unset = Sentry never inits. Leave empty in dev unless you want # errors to flow. SENTRY_DSN= # PostHog — product analytics (free tier: 1M events/mo). # Both values are public by design — PostHog's client SDK is the one # sending events, so the project key lives in the browser. Empty key = # analytics no-op + posthog-js never enters the client bundle. # HOST: 'https://us.i.posthog.com' (default) or 'https://eu.i.posthog.com'. POSTHOG_KEY= POSTHOG_HOST= # Resend — transactional email (free tier: 100 emails/day, 3000/mo). # Sign up at https://resend.com. Verify your sending domain first. # Without RESEND_API_KEY the mailer console-prints emails to the dev # server log instead of sending — fine for development. # EMAIL_FROM must be a verified sender on your Resend account; # 'onboarding@resend.dev' is allowed for local testing only. # EMAIL_OPS is where in-app forms (feedback, alerts) deliver to. If # unset, those messages route to EMAIL_FROM so they don't go to a # random address. RESEND_API_KEY= EMAIL_FROM= EMAIL_OPS= # Polar — billing / checkout / subscriptions. # Sign up at https://polar.sh. Create an organization, then a product # for each plan tier you want to offer. Mint an access token under # Settings → Developers → New token (scope: products:read, checkouts: # write, customer_sessions:write, customers:read). # Configure a webhook pointing to /api/webhooks/polar and # copy the signing secret. # Both ACCESS_TOKEN and WEBHOOK_SECRET must be set together. # Without them all /api/billing/* endpoints return an instructive # INTERNAL error. # POLAR_SERVER toggles between Polar's sandbox and production # environments (defaults to production when unset). Use sandbox while # testing — sandbox checkouts don't charge real money and have a separate # token + product catalog. POLAR_ACCESS_TOKEN= POLAR_WEBHOOK_SECRET= POLAR_SERVER=sandbox POLAR_PRO_PRODUCT_ID= POLAR_TEAM_PRODUCT_ID= POLAR_ENTERPRISE_PRODUCT_ID= # Ports (defaults shown). The browser only ever talks to the web port — # /api + /auth are proxied to the API port in dev (proxy.conf.json) and # served by the same Express app in prod (src/server.ts). PORT=4000 API_PORT=4202 # Angular SSR host allowlist (comma-separated, `*.example.com` wildcards OK). # The build bakes in localhost only (angular.json → security.allowedHosts); # set this in production so SSR accepts your real domain(s) — no rebuild # needed, the env var overrides the baked list. Include EVERY hostname # clients use (apex + www + any LB health-check host); other hosts get a # 400 Bad Request from Angular SSR. # Leave empty for local dev. On Vercel, *.vercel.app is added automatically # (api/index.mjs); set this only for custom domains. NG_ALLOWED_HOSTS= # Set by tooling / the shell, not read from .env — listed for completeness. # NODE_ENV development | production | test. `npm run dev` sets development; # unset counts as production for demo mode and cookies. Also the # Sentry environment tag. # BASE_URL Playwright: test an already-running app instead of starting # dev servers (e.g. BASE_URL=https://staging.example.com npm run test:e2e). # CI Playwright: forbids test.only and enables 2 retries. # NODE_ENV= # BASE_URL= # CI=