Skip to main content

Environment Configuration

Environment Configuration

Project: Drop Version: 0.1.0 Date: 2026-02-23 Author: Platform Architect (AI) Status: In Review Reviewers: Alem Bašić (CEO)

Document History

Version Date Author Changes
0.1 2026-02-23 Platform Architect (AI) Initial draft from source code analysis

1. Environment Overview

Environment Purpose URL Access Managed By
Local Developer workstation http://localhost:3000 Developer Individual
Staging Pre-production validation drop-staging.fly.dev (Fly.io Stockholm) Team + CI Alem Bašić
Production Live system https://9ef3szvvsb.eu-west-1.awsapprunner.com (future: getdrop.no) Alem Bašić only Alem Bašić

2. Per-Environment Configuration

2.1 Development Environment

Parameter Value Notes
NODE_ENV development Auto-seeds demo user ([email protected] / demo1234)
Database SQLite at ./drop.db (project root) Auto-detected when no DATABASE_URL set
Auth Email/password login (legacy, deprecated in prod) OR BANKID_MOCK=true BankID mock skips OIDC flow
Service mode NEXT_PUBLIC_SERVICE_MODE=mock No real external API calls
Rate limiting Enabled (in-memory fallback when no DB table) Lower limits in dev are OK
JWT_SECRET Dev fallback (hash of process.cwd()) Fatal error if missing in production
Demo user Auto-seeded Login at http://localhost:3000 with demo credentials
Security headers CSP with unsafe-eval + unsafe-inline Required for Next.js HMR
Cookies secure: false No HTTPS in local dev

2.2 Staging Environment

Parameter Value Notes
Platform Fly.io, region arn (Stockholm) App name: drop-staging
Database SQLite ephemeral volume at /app/data/drop.db No automated backup
NODE_ENV production No demo seed data
NEXT_PUBLIC_SERVICE_MODE mock External services mocked
BANKID_MOCK true Mock BankID OIDC flow
Auto-scaling Scale to 0 idle, auto-start on request
SEED_DEMO Optional — set to seed test data
Storage Persistent Fly.io volume drop_data at /app/data
Secrets Fly.io secrets (fly secrets set KEY=VALUE)

Intentional staging/production differences:

  • External payment/banking services: mocked (not real BankID or bank connections)
  • Scaling: scales to zero (production does not)
  • Database: SQLite (production is PostgreSQL RDS)

2.3 Production Environment

Parameter Value Notes
Platform AWS App Runner (eu-west-1) Service ARN: arn:aws:apprunner:eu-west-1:324480209768:service/drop-web/8e45b0d335304487a1880f4e32d6aeec
NODE_ENV production No demo seed, demo credentials disabled
Database RDS PostgreSQL 16 at drop-db.czu2qe4quy4v.eu-west-1.rds.amazonaws.com:5432 DB: dropapp, user: dropuser
DATABASE_URL postgresql://dropuser:<password>@drop-db.czu2qe4quy4v.eu-west-1.rds.amazonaws.com:5432/dropapp From AWS Secrets Manager
JWT_SECRET Min 32 chars, stored in AWS Secrets Manager Fatal startup error if missing
NEXT_PUBLIC_SERVICE_MODE production Real external service calls
BANKID_MOCK Not set (false) Real BankID OIDC
SLACK_WEBHOOK_URL From AWS Secrets Manager Operational alerts to #drop-ops
Security headers Full CSP (no unsafe-eval), HSTS max-age=63072000; includeSubDomains; preload
Cookies secure: true, httpOnly: true, sameSite: strict
Rate limiting Persistent (DB-backed via rate_limits table) 10/min auth, 120/min public
Auto-scaling AWS App Runner managed Minimum 1 instance

3. Environment Variables Reference

Variable Description Required Default Sensitive Environments
NODE_ENV Runtime environment Yes development No All
PORT HTTP server port No 3000 No All
HOSTNAME Server bind address No 0.0.0.0 No All
DATABASE_URL PostgreSQL connection string No (SQLite if absent) Yes Staging, Prod
JWT_SECRET JWT signing key (min 32 chars) Yes (prod) Dev: process.cwd() hash Yes All
JWT_ALGORITHM HS256 or RS256 No HS256 No All
JWT_EXPIRY Token expiry (web) No 24h No All
NEXT_PUBLIC_SERVICE_MODE mock or production No mock No All
NEXT_PUBLIC_APP_URL App URL for CSRF origin check No No Staging, Prod
SLACK_WEBHOOK_URL Slack incoming webhook for alerts Yes (prod) Yes Staging, Prod
BANKID_CLIENT_ID BankID OIDC client ID Yes (prod) No Staging, Prod
BANKID_CLIENT_SECRET BankID OIDC client secret Yes (prod) Yes Staging, Prod
BANKID_CALLBACK_URL BankID web callback URL Yes (prod) No Staging, Prod
BANKID_CALLBACK_URL_MOBILE BankID mobile deep link Yes (mobile) No Staging, Prod
BANKID_MOCK Skip real BankID OIDC (true) No false No Local, Staging
SUMSUB_API_URL Sumsub KYC API endpoint No https://api.sumsub.com No All
SUMSUB_APP_TOKEN Sumsub API token Yes (prod) Yes Staging, Prod
SUMSUB_SECRET_KEY Sumsub webhook secret Yes (prod) Yes Staging, Prod
DOPPLER_TOKEN Doppler secrets provider token No Yes Optional
AWS_SECRET_ARN AWS Secrets Manager ARN No Yes Prod (optional)
SEED_DEMO Seed demo data in staging No No Staging
NEXT_PUBLIC_FF_NOTIFICATIONS Enable notifications feature No true No All
NEXT_PUBLIC_FF_MERCHANT_DASHBOARD Enable merchant dashboard No true No All
NEXT_PUBLIC_FF_VIRTUAL_CARDS Enable virtual cards (future) No false No All
NEXT_PUBLIC_FF_PHYSICAL_CARDS Enable physical cards (future) No false No All
NEXT_PUBLIC_FF_CARD_DETAILS Enable card details (future) No false No All
NEXT_PUBLIC_FF_CARD_FREEZE Enable card freeze (future) No false No All
NEXT_PUBLIC_FF_CARD_PIN Enable card PIN (future) No false No All
NEXT_PUBLIC_FF_SPENDING_LIMITS Enable spending limits (future) No false No All

Rules:

  • Sensitive variables MUST be sourced from AWS Secrets Manager (production) or Fly.io secrets (staging)
  • Never commit sensitive values to source control
  • Use .env.local for local development (never committed — in .gitignore)
  • Rotate all secrets on team member offboarding

4. Secrets Management

4.1 Secret Storage Solution

Drop uses an abstracted secrets management system (src/lib/secrets.ts) with auto-detected provider:

Priority: DOPPLER_TOKEN set → Doppler | AWS_SECRET_ARN set → AWS Secrets Manager | default → process.env

Environment Secret Store Access Method
Local .env.local (never committed) Developer managed
Staging Fly.io secrets (fly secrets set) Alem Bašić / CI
Production AWS Secrets Manager + App Runner env App Runner IAM role

Cache TTL: 5 minutes (in-memory, resets on restart)

4.2 Secret Rotation Schedule

Secret Type Rotation Schedule Automated Owner
JWT_SECRET 90 days No (manual) Alem Bašić
DATABASE_URL / PostgreSQL credentials 90 days No (manual) Alem Bašić
BANKID_CLIENT_SECRET Per BankID policy / on compromise No Alem Bašić
SUMSUB_APP_TOKEN 180 days No Alem Bašić
SLACK_WEBHOOK_URL On compromise only No Alem Bašić
TLS certificates Auto-renewed by App Runner / Fly.io Yes AWS / Fly.io

Rotation impact of JWT_SECRET: Invalidates all active user sessions — users must re-authenticate. Generate new JWT secret: openssl rand -base64 48

4.3 Access Controls

Role Local Secrets Staging Secrets Production Secrets
Alem Bašić (CEO) Read/Write Read/Write Read/Write
AI Director (John) No access Via tooling only Via tooling only
CI/CD (GitHub Actions) No access Read (via OIDC) Read (deploy role)
Application runtime Read (process.env) Read (Fly.io secrets) Read (App Runner env / Secrets Manager)

5. Feature Flags Per Environment

Tool: Environment variables (NEXT_PUBLIC_FF_* baked at build time)

Flag Local Staging Production Notes
notifications true true true Enabled
merchantDashboard true true true Enabled
virtualCards false false false Future — requires card issuing partner
physicalCards false false false Future
cardDetails false false false Future
cardFreeze false false false Future
cardPin false false false Future
spendingLimits false false false Future

6. Database Configuration Per Environment

Parameter Local Staging Production
Engine SQLite (better-sqlite3) SQLite (better-sqlite3) PostgreSQL 16
Host ./drop.db (project root) /app/data/drop.db (Fly volume) drop-db.czu2qe4quy4v.eu-west-1.rds.amazonaws.com
Port 5432
Database name dropapp
Username dropuser
SSL required No No Yes (RDS default)
Max connections 10 (SQLite) 10 (SQLite) ~85 (db.t4g.micro default)
Connection pool No No TBD (PgBouncer planned)
Backup No Manual sqlite3 export Automated RDS snapshot, daily at 23:24 UTC, 7-day retention
Point-in-Time Recovery No No Yes (5-min granularity)

7. External Service Configuration Per Environment

Service Local Staging Production Notes
BankID OIDC Mock (BANKID_MOCK=true) Mock Live (Norwegian BankID) Mock skips full OIDC flow
Sumsub KYC Mock (90% approval, 3s delay) Mock Live (api.sumsub.com) Only production-ready external service
Open Banking (AISP/PISP) Mock (Swan mock deprecated) Mock TBD provider Provider selection pending
Slack alerts Console log only (no webhook) SLACK_WEBHOOK_URL required SLACK_WEBHOOK_URL required Alert channel: #drop-ops
BetterStack uptime N/A N/A 3 monitors (free tier) Production uptime monitoring

8. Environment Provisioning Process

Staging (Fly.io)

  1. fly launch — creates new Fly.io app
  2. fly volumes create drop_data --region arn --size 1 — persistent SQLite volume
  3. fly secrets set JWT_SECRET="$(openssl rand -base64 48)" — set secrets
  4. fly secrets set NEXT_PUBLIC_SERVICE_MODE=mock
  5. fly deploy — first deployment
  6. curl https://drop-staging.fly.dev/api/health — verify health

Estimated time: 10 minutes

Production (AWS App Runner)

  1. Create ECR repository drop-web in eu-west-1
  2. Build and push Docker image to ECR
  3. Create App Runner service from ECR image
  4. Configure environment variables (or link to Secrets Manager)
  5. Configure health check: GET /api/health, 30s interval
  6. Verify service at App Runner URL
  7. Configure BetterStack monitors

Estimated time: 30 minutes


9. Environment Teardown Process

Staging teardown:

  1. fly apps destroy drop-staging — removes Fly.io app and volumes
  2. Clear Fly.io secrets
  3. Archive staging config

Production (never fully torn down — use maintenance mode instead):

  1. Scale App Runner service to 0 (pause)
  2. Take final RDS snapshot before any destructive action
  3. Any full teardown requires explicit written approval from Alem Bašić

10. Parity Policy (Staging ↔ Production Drift)

Area Policy
Application version Staging is always ahead by ≤ 1 release
Database engine Intentional difference: SQLite (staging) vs PostgreSQL (production) — tracked for upgrade
OS & runtime version Node.js 22 Alpine — same
Security controls Same security headers and rate limiting
Feature flags Same — all flags match
External services Intentional: mock in staging, live in production

Drift detection: Manual review before each production deployment Drift resolution owner: Alem Bašić



Approval

Role Name Date Signature
Author Platform Architect (AI) 2026-02-23
Reviewer
Approver Alem Bašić