By the end of this page the app is running and the home screen shows your own email address. Every command here was run on a clean clone and the timings are real.
What you need first
- Node.js 22.14 or newer. Check with
node --version. Get it from nodejs.org. - pnpm 10.19 or newer. Install with
npm install -g pnpm, then checkpnpm --version. - Git, or GitHub Desktop. Check with
git --version.
You do not need Xcode, Android Studio, or a Mac to get through this page. The app runs in your browser first.
1. Install the dependencies
From inside the project folder:
pnpm installThis pulls the app, the backend, and the shared packages in one go. It took 20 seconds here with a warm package cache. The first time on a new machine it downloads the whole dependency tree, so expect two to four minutes.
2. Create your env files
node scripts/init-env.mjsThis writes apps/api/.env and apps/mobile/.env, and generates a real random JWT_SECRET for you. It never overwrites a file that already exists, so it is safe to run again.
Do not skip this and copy the example files by hand
apps/api/.env.example ships a placeholder secret that reads replace-me-with-a-long-random-string-at-least-32-bytes. It is 54 characters long, so it passes the server's length check and the backend starts without complaining. But that value is published in every copy of this repository, and anyone who has it can mint a valid login token for any account on your server. We tested exactly that: a token signed with the placeholder was accepted as a real session.
init-env.mjs generates a random secret instead. The backend now refuses to start with the placeholder when NODE_ENV=production, and prints a loud warning in development, so you get told rather than silently exposed. If you ever do copy the example by hand, open apps/api/.env and replace that line before you go further.
3. Start the backend
pnpm --filter api db:migrate
pnpm --filter api devThe migration takes under three seconds and creates a SQLite file. The server starts on port 3000 in roughly six seconds.
Check it is alive from a second terminal:
curl http://127.0.0.1:3000/healthzYou should see {"ok":true,"ts":1785481847}.
It is /healthz, not /health
/health returns a 404. The z is a convention borrowed from Kubernetes.
4. Start the app
Leave the backend running. In a second terminal:
pnpm --filter mobile startPress w for the browser, i for the iOS simulator (Mac only), or a for an Android emulator. The browser is the fastest way to see something.
The first browser bundle takes about 90 seconds. Later reloads are a few seconds. Open http://localhost:8081 if it does not open by itself.

If expo start exits immediately
Expo's startup check that compares your package versions against its registry can crash with TypeError: Body is unusable: Body has already been read. It is a bug in the check, not in your install. Skip it:
EXPO_NO_DEPENDENCY_VALIDATION=1 pnpm --filter mobile startOn Windows PowerShell: $env:EXPO_NO_DEPENDENCY_VALIDATION=1 first, then the normal command.
5. Create your account
The app opens on a sign-in screen. Choose Sign up, fill in a name, any email, and a password of at least 8 characters, then press Create account.
You land in a three-step welcome flow. Step through it, and the home screen greets you with Signed in as [email protected]. That is the full stack working: the app talked to your backend, the backend created a user, and the session came back.

Check your install
Two commands, both should come back green:
pnpm test
pnpm turbo run typecheckpnpm test gives 85 passed, 0 failed across 12 suites and 5 packages. typecheck
reports 7 successful, 7 total and covers both apps and all six shared packages.
If either comes back red on a clean clone, that is a bug in the template and not in your setup. Tell me and I will fix it.
Common stumbles
- Port 3000 or 8081 already in use. Another dev server has it. On Windows,
netstat -ano | findstr :3000shows which process; stop it, or setPORT=3001inapps/api/.env. - The app loads but sign-up fails. The backend is not running, or
EXPO_PUBLIC_API_URLinapps/mobile/.envdoes not point at it. - Testing on a real phone. Set
EXPO_PUBLIC_API_URLto your computer's network address, for examplehttp://192.168.1.20:3000, and restart the app.localhoston a phone means the phone.
Next: Open It in Claude Code, which is the one-time setup that makes every prompt after it land.