Getting started
From clone to three products running on one dev server, in five commands.
Millroom runs three products from one codebase on your machine: studio (this site), cutlist (todos, with billing), and planer (streaming AI). Those are the three worked products the kit ships, and they come up on one dev server, against one database, out of one repo, which is the arrangement the kit is for. This page is the honest path from clone to all three rendering locally.
Prerequisites
- Node 22 or later
- pnpm (
corepack enableis the easy way) - Docker (the local Supabase stack runs in it)
- The Supabase CLI (
brew install supabase/tap/supabaseor see supabase.com/docs)
Stripe CLI and any API keys are optional. The kit is built to run with zero third-party keys; see the table at the bottom.
The fast path
pnpm install
pnpm onboard
The wizard checks your tools, starts the local database, fills .env.local for you, and prints the three local URLs. It is resumable; run it again if it stops.
The manual path (five commands)
The wizard runs these same commands. Nothing is hidden.
pnpm install
pnpm supabase:start # local stack on ports 57xxx (first run downloads images)
cp .env.example .env.local # then paste the two keys, next step
pnpm db:reset # applies migrations + dev seed
pnpm dev # Next.js on port 3100
For step 3, run pnpm supabase:status and copy two values into .env.local:
- the publishable key into
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY - the secret key into
SUPABASE_SECRET_KEY
NEXT_PUBLIC_SUPABASE_URL is already set to http://127.0.0.1:57321 in the example file.
Open the three hosts
Browsers resolve *.localhost natively. No /etc/hosts edits, no sudo.
| URL | Product |
|---|---|
| http://studio.localhost:3100 | studio (this site) |
| http://cutlist.localhost:3100 | cutlist (worked product: todos, with billing) |
| http://planer.localhost:3100 | planer (worked product: streaming AI) |
| http://localhost:3100 | studio (the bare default) |
Same dev server, three skins. Each host resolves to its tenant through core.domains, exactly as production does.
Make a test account
- Open http://cutlist.localhost:3100 and sign up with any email and password.
- Confirmation emails land in the local mail catcher (Mailpit) at http://127.0.0.1:57324. Nothing leaves your machine.
- Open the confirmation link from there and you are in. An org is provisioned for you on first sign-in.
The link comes back to the host you signed up on, not the bare default. Supabase only honours a redirect URL it recognises and silently rewrites anything else to site_url, so supabase/config.toml allowlists http://*.localhost:3100/**. The wildcard covers any product you add later. A cloud project needs the same list under Authentication, URL configuration.
Verify
pnpm doctor
Read-only. It checks your tools, the database, the env file, and that each local host resolves to the right tenant, then prints the single next command if anything is missing.
What works with zero keys
With only the local Supabase keys set (the required-local block in .env.example), everything degrades on purpose to a documented no-op:
| Area | With zero keys |
|---|---|
| Tenants and theming | Fully working. Three hosts, three brands. |
| Auth (password, MFA, orgs, RLS) | Fully working. Auth emails arrive in the local mail catcher. |
| Cutlist free tier | Fully working. |
| Docs, changelog, content | Fully working. |
| Rate limiting | Working, via an in-memory fallback (Upstash keys make it durable). |
| Stripe billing | Pages render; checkout and the customer portal are disabled until STRIPE_SECRET_KEY (test mode) is set. See billing. |
| AI (planer) | Returns a friendly not-configured error until AI_GATEWAY_API_KEY is set. See ai. |
| Transactional email | Logged locally instead of sent until RESEND_API_KEY is set. |
| Analytics (PostHog) | Silent no-op until NEXT_PUBLIC_POSTHOG_KEY is set. |
| Cron routes | Refuse to run (fail-closed) until CRON_SECRET is set. |
| Sign-up CAPTCHA | Not rendered until Turnstile keys are set. |
Every variable in .env.example carries a comment naming exactly what turns off without it.
Next
- Architecture: how one codebase carries many products, and where each shared capability lives.
- Adding a product: the checklist, or just run
pnpm new:product <slug>. - Running many products: the business side of the same idea, one account per vendor, and what that does to the bill.
- Working with an agent? Open the repo in Claude Code and say: set up millroom.