Maturity-SE Onboarding Lesson 2 of 5
You know the loop - now run it on your machine

Run it locally without fear

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.

10 min One win: env, migrate, dev - and explain why Mission
The two files that matter

Env file and db runner

Everything else is automatic. Understand these two and you control the local stack.

.env.development.local - what lives here

# 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=
Pro tip: Copy from Vercel. Run vercel link then vercel env pull .env.development.local.

What each var powers

DATABASE_URL
lib/db/index.ts + db/run.js

Two consumers: runtime Pool for queries, dbmate wrapper for migrations. Wrong value = dev starts but queries crash.

BETTER_AUTH_SECRET
lib/auth.ts + lib/crypto.ts

Signing session cookies AND deriving AES key for BYOK encryption. Change it = sessions invalidate + keys unreadable.

AWS keys
lib/llm.ts

Platform LLM via Bedrock. Only needed if template gen should work without user BYOK.

TAVILY_API_KEY
app/actions/research.ts

Web search for research pipeline. Without it, research gen falls back to pure LLM.

The wrapper

Why db/run.js exists

Every migration goes through a 5-step chain:

1

Load env

db/run.js:10 reads .env.development.local via dotenv. dbmate alone does not know about this file.

down
2

Check Aurora

db/run.js:14 - if USE_AWS_AURORA=true, generates IAM auth token. Local dev skips this.

down
3

Build DATABASE_URL

db/run.js:34 - Aurora mode constructs URL with IAM token. Locally uses your .env value.

down
4

Execute dbmate

db/run.js:37 - runs dbmate up with merged env. dbmate reads DATABASE_URL from env object.

down
5

Migrations run

dbmate reads db/migrations/*.sql in order, tracks in dbmate_migrations table. Idempotent - safe to re-run.

Key insight: dbmate is a standalone Go binary that does NOT read .env files. Every script goes through node db/run.js instead of calling dbmate directly. You cannot just run dbmate up from your shell.
The build chain

What pnpm build actually does

Look at package.json line 7:

"build": "next build && node db/run.js dbmate up"

Two things happen on deploy:

A

next build

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.

then
B

dbmate up

Runs unapplied migrations from db/migrations/*.sql. Every merge to main auto-deploys code AND schema changes. No separate migration step in CI.

The five migrations in order

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.

Field note

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.

Retrieval - make it stick

Check yourself

Effortful recall builds storage strength. All choices same length - no formatting clues.

Q1 - The wrapper1 / 3

Why does every db script go through db/run.js instead of calling dbmate directly?

Q2 - The secret2 / 3

What happens if you change BETTER_AUTH_SECRET in production?

Q3 - The deploy3 / 3

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.

Go deeper - one primary source

Read db/run.js line by line

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/.

What is next

Lesson 3 - DB Schema: the four tables you care about

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.

Stuck? Ask me anything - "why does my migration fail?", "what is the dbmate_migrations table?", "how do I seed data?" - I am your teacher for this repo.

Maturity-SE Onboarding - Glossary - Mission - Resources