Loomis
Getting started

Local setup

Every command in the order it is needed, what a fresh copy of the folder does, and the three gates that must stay clean.

This page is for the person who would rather type than click. Everything the installer does is available as a command, and running the app in development is four commands from a fresh copy.

From nothing to a running app

node --version                 # 22.6 or newer, please
pnpm install                   # the packages
pnpm configure                 # the same walk the installer does
pnpm dev                       # http://localhost:3000

pnpm configure needs no packages: it is a script that Node runs directly, so it works on a copy that has never been installed. It writes .env.local, keeps a dated backup of the previous one, and prints a masked hint for each value rather than the value itself.

The commands that exist

CommandWhat it does
pnpm installInstalls the packages
pnpm configureThe guided walk over the nineteen names
pnpm configure --dry-runSays what it would write, writes nothing
pnpm configure --only=deploymentOnly the twelve names the deployment reads
pnpm devDevelopment server on port 3000, with hot reload
pnpm buildA production build
pnpm startServes the production build
pnpm testThe test suite
pnpm typecheckTypeScript, no emit
pnpm lintESLint
npx convex devWaits on the schema and functions of a development deployment and pushes changes
npx convex dev --oncePushes once and exits, for a script
npx convex deployPushes to the production deployment
npx convex env set NAME valueSets one deployment variable
npx convex env listLists the names the deployment has (not their values)
npx convex run admin:bootstrapCreates the first administrator

`pnpm start` serves what was built, not what is in the editor

If the app's behaviour disagrees with the code you just changed, you are looking at the previous build. Run pnpm build again, then pnpm start.

A fresh copy of the folder, step by step

What actually happens the first time, in order, with the points where people get stuck:

Node is checked, by version rather than by command name

The scripts are TypeScript files that Node runs by stripping the types. That works from 22.6 and refuses earlier versions. Two Node installations on one machine is the usual cause of a complaint, and node --version in the same terminal tells you which one is being used.

Convex is created or connected

npx convex dev

This signs in, creates a development deployment, and prints the address. That address is NEXT_PUBLIC_CONVEX_URL, and without it the app serves nothing at all.

The nineteen names

pnpm configure walks them. Seven go into .env.local, which the Next.js process reads. Twelve are set on the deployment with npx convex env set, because that is where the functions that read them run.

The first administrator

npx convex env set ADMIN_EMAIL "you@your-domain.com"
npx convex env set ADMIN_PASSWORD "a long one"
npx convex run admin:bootstrap

The app

pnpm dev

A blank page for a few seconds is normal: Next.js serves nothing until the first compile finishes.

Two environments, side by side

This is the single most common source of confusion, so it is worth saying twice. Development and production are two deployments with two environments, and neither one reads the other's file.

# development: the one pnpm dev talks to
npx convex dev                 # pushes and watches
npx convex env set NAME value  # sets a name there

# production: the one the deployed app talks to
npx convex deploy
npx convex env set NAME value --prod

The app's own half is a file, so it is per machine: .env.local on your laptop and a different set of values on the server, or the same ones if that is what you want. Everything in .env.local is written with restrictive permissions and ignored by version control.

The three gates

The project is shipped with three checks and all three are clean. They are the ones to run before you change anything of your own, because a change that breaks one of them is a change you will have to find later:

pnpm typecheck   # TypeScript
pnpm lint        # ESLint
pnpm test        # Vitest

`allowImportingTsExtensions` is deliberate

The configuration scripts in scripts/ and the installer server in installer/ import their helpers with a .ts extension, because Node's type stripping does not guess extensions the way a bundler does. Both the app's tsconfig.json and the one under convex/ allow it on purpose. Removing the flag breaks the installer, not the app.

Where to look when you want to change something

LayerFolderWhat lives there
Routesapp/Marketing, product, admin, auth, API routes, and content/ for the docs
Interfacecomponents/One folder per area, plus ui/ for the primitives
Ruleslib/Pure functions with tests beside them: plans, credits, limits, flags
Backendconvex/Schema, queries, mutations, actions, migrations, schedules
Documentationcontent/docs/The manual you are reading, in MDX

The panel is the answer to most "where do I change" questions, and the last section of the README is a table of them. Code is the answer for everything else.