Every new engineer first task: pnpm dev and see something. But this repo wraps Postgres, auth, and LLM keys behind env vars. By the end you will know what each one does, why db/run.js exists, and what "merge to main auto-deploys" means.
Everything else is automatic. Understand these two and you control the local stack.
# Required
DATABASE_URL=postgres://user:pass@localhost:5432/maturity_se
BETTER_AUTH_SECRET=run: openssl rand -hex 32
# Optional - admin access
ADMIN_EMAILS=you@company.com
# Optional - fallback LLM
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
# Optional - web research
TAVILY_API_KEY=
vercel link then vercel env pull .env.development.local.
Two consumers: runtime Pool for queries, dbmate wrapper for migrations. Wrong value = dev starts but queries crash.
Signing session cookies AND deriving AES key for BYOK encryption. Change it = sessions invalidate + keys unreadable.
Platform LLM via Bedrock. Only needed if template gen should work without user BYOK.
Web search for research pipeline. Without it, research gen falls back to pure LLM.
Every migration goes through a 5-step chain:
db/run.js:10 reads .env.development.local via dotenv. dbmate alone does not know about this file.
db/run.js:14 - if USE_AWS_AURORA=true, generates IAM auth token. Local dev skips this.
db/run.js:34 - Aurora mode constructs URL with IAM token. Locally uses your .env value.
db/run.js:37 - runs dbmate up with merged env. dbmate reads DATABASE_URL from env object.
dbmate reads db/migrations/*.sql in order, tracks in dbmate_migrations table. Idempotent - safe to re-run.
Look at package.json line 7:
"build": "next build && node db/run.js dbmate up"
Two things happen on deploy:
TypeScript compiles, Next.js bundles. Note: ignoreBuildErrors: true in next.config.mjs means type errors do NOT block deploy. pnpm lint is your real gate.
Runs unapplied migrations from db/migrations/*.sql. Every merge to main auto-deploys code AND schema changes. No separate migration step in CI.
0000_create_user_table.sql user, session, account, verification
+ templates, assessments, responses, llm_keys
0001_promo_codes.sql promo_codes, promo_code_redemptions
0002_llm_usage_log.sql llm_usage_log, user.defaultLlmMode
0003_promo_codes_add_enabled.sql adds enabled column to promo_codes
0004_templates_add_research_brief.sql adds researchBrief to templates
Numbering: YYYYMMDDHHMMSS_NNNN_name.sql. New migrations always go last. Run pnpm db:new my_name to generate a timestamped file.
You now know more about this repo infrastructure than most engineers who only use the UI. The wrapper pattern, the dual-purpose secret, the migration chain - these are the real walls of the system. Everything else is UI on top.
Effortful recall builds storage strength. All choices same length - no formatting clues.
Why does every db script go through db/run.js instead of calling dbmate directly?
What happens if you change BETTER_AUTH_SECRET in production?
In pnpm build, what does the chain next build && node db/run.js dbmate up guarantee?
Scores not stored - this is for your memory, not grading.
It is 40 lines. Read db/run.js and you understand the entire migration infrastructure. Then skim package.json scripts to see how every command flows through it.
Citations: env from .env.example, wrapper from db/run.js:1-40, build chain from package.json:7, migrations from db/migrations/.
Deep dive into lib/db/schema.ts. You will learn what templates, assessments, responses, and llm_keys actually store, and how Drizzle queries map to SQL.