Prerequisites
Clone the workspace
Each repository is a separate clone inside one workspace folder, on thedev branch.
dev script look for ../backend and ../tools. Ask a teammate for the root package.json and the tools folder, since they are not inside any of the three repositories.
Backend
1
Install dependencies
2
Create the env file
The backend README tells you to copy Set
.env.example, but that file is not in the repository at the time of writing. Create backend/.env by hand using Backend environment variables. The minimum set that passes validation is:PORT=3031 explicitly. The schema default is 8000, but every script in the workspace expects 3031.packages/config loads the env with dotenv from the current working directory. pnpm dev:api runs inside apps/core-api, and Prisma commands run inside packages/db or packages/database. Existing checkouts keep a copy of the same .env in backend/, backend/apps/core-api/, backend/packages/db/ and backend/packages/database/. Copy your file to those four places, or symlink them.3
Start Postgres and Redis
Any local Postgres 16 and Redis will do. The web repository ships a
docker-compose.yml with a Postgres 16 container (and MinIO) left over from the starter kit. The backend has no compose file. A minimal setup with Docker:4
Generate the Prisma clients
prisma generate in both @perform/db and @repo/database. It does not need a database connection.5
Create the schema
db:migrate is prisma migrate deploy against packages/db/prisma/schema.prisma. It applies the hand-written migrations in packages/db/prisma/migrations.On a completely empty database this command is not enough on its own. The migration history was written for the public schema, and at least one migration (20260823120000_user_phone_number) alters auth.user, a better-auth table that no migration creates. The auth schema is described only in packages/database/prisma/schema.prisma.The repository does not document a bootstrap path for an empty database, so treat the following as the practical route, not an official one. The manual deploy workflow creates and syncs tables with a schema push from @repo/database, whose schema contains both auth and public:6
Build the packages once
@repo/* packages through their dist output, so they must be built before the API can start or type-check.7
Run the API
nodemon, which watches apps/core-api/src and restarts tsx src/server.ts. Check it with:pnpm db:seed runs packages/db/prisma/seed.ts.
Web app
1
Install dependencies
2
Create the env file
Create
frontend/.env. There is no .env.example in this repository either. The scripts load it with dotenv -c, so .env.local overrides also work.SERVICE_AUTH_SECRET must be identical in both repositories, or every /v1/web call fails with 401.3
Run it
pnpm dev first runs ../tools/ensure-core-api.sh --start. If nothing answers on http://127.0.0.1:3031/healthz, it starts the backend for you in the background. Then Turborepo starts two dev servers: saas on port 3000 and marketing on port 3001.Use pnpm dev:app to skip the API check./ is rewritten to the marketing dev server on port 3001 by apps/saas/proxy.ts. Go to /login to reach the app.
Run the API and the web app together
From the workspace root:/healthz, then starts the web app. Stopping one stops both.
Mobile app
1
Install dependencies
npm install, but the repository pins pnpm in packageManager and CI installs with pnpm install --frozen-lockfile. Use pnpm so the patches in patchedDependencies are applied.2
Create the env file
src/lib/api/config.ts reads the Metro host and calls http://HOST:3031. On the Android emulator it falls back to 10.0.2.2, on the iOS simulator to localhost.To force a URL, set EXPO_PUBLIC_API_URL. pnpm use-lan-ip writes your machine’s LAN address into .env for you. It reads the en0 interface by default. Set IFACE to use another one.3
Start Metro
4
Sign in
The trainee signs in with a phone number and a code sent over WhatsApp. With
TRAINEE_OTP_DEV_MODE=true on the backend the code is not sent through SmartSend. The API logs it with the message trainee OTP dev mode: skipping WhatsApp send and returns it as devCode in the response, so you can test without WhatsApp credentials. The same flag makes the coach phone login log its code instead of sending it. The trainee’s phone must exist as a client in a studio first. Create one in the web app.Ports
First sign-in on a fresh database
Sign up in the web app athttp://localhost:3000. Creating an organization creates the matching Studio row the first time the web app calls the /v1/web lane. The owner is provisioned as a head coach at the same moment.