All pages

Install and run

You are about to get both apps running against your own accounts. This page covers your machine. The following pages explain each provider integration before deployment.

Everything here is reported rather than enforced. Skip Stripe and you still reach a running app, with the checkout route answering 501.

What you need

Tool Version Why
Node 22.12.0 or newer engines in the root package.json
pnpm 11.25.0 pinned by packageManager
git any
Careful

Do not run this repo through corepack. pnpm refuses to self-switch under it and the packageManager check then fails inside nested turbo tasks. Install pnpm directly and invoke pnpm directly.

The short version

  1. Clone and install

    git clone git@github.com:<owner>/<repo>.git
    cd <repo>
    pnpm install

    One lockfile covers all four packages. @ffd/shared has no build step, so there is nothing to compile before the apps can import it.

  2. Fill in the two env files

    apps/api/.env and apps/web/.env.development are committed encrypted by dotenvx (docs/adr/0050-env-files-are-committed-encrypted.md). If you have the repository's .env.keys, put it at the repository root and skip the copy below: pnpm dev decrypts both files. Otherwise start from the examples:

    cp apps/api/.env.example apps/api/.env
    cp apps/web/.env.example apps/web/.env.development

    Both example files ship with local defaults. Run pnpm env:encrypt after you fill them in. It encrypts every plaintext value and creates .env.keys on the first run. Keep that file out of git and share it only with people who run the app. SETUP.md directs an agent to obtain the account-backed values through each provider's supported tools and write them directly without exposing credentials in chat or terminal output. The steps only you can take, such as choosing an account or copying a dashboard-only key, run in your terminal through pnpm setup:wizard, which opens each page, takes secrets through hidden input, and checks them against the provider. The provider pages explain those values and the human handoffs. The full list, with which app reads each one, is Environment variables.

  3. Create the database and run the migrations

    pnpm db:migrate

    This creates the target database if it is not there, then applies every migration under packages/shared/drizzle/. It resolves to the development database unless you say otherwise, so migrating production is NODE_ENV=production pnpm db:migrate, typed on purpose.

    Nothing migrates at boot, so this is a step you run rather than something a deploy does for you.

  4. Run both apps

    pnpm dev

    Turbo runs vite dev for the web app on port 3000 and tsx watch for the api on port 5001. Both reload on save, including changes inside packages/shared.

  5. Check that the api agrees with you

    curl http://localhost:5001/health
    {
      "status": "ok",
      "service": "api",
      "uptimeSeconds": 12,
      "database": "ffd_dev",
      "stripeEnv": "test"
    }

    database is the resolved database name, never the URL, which carries a password. stripeEnv is null when no Stripe key is configured, which is a supported state. Those two fields are the two environment axes, and this endpoint is where you find out they disagree.

Let an agent do it

SETUP.md at the repository root is the agent's end-to-end guide. It covers repository work and external provisioning, using CLIs and APIs first and authenticated browser control where needed. It pauses only for account control, consent, approval, or an ambiguous resource choice, then continues the same sequence.

It is a guide, not a gate. Nothing in it is required, and a buyer who skips a section still reaches a running app.

The commands that gate a change

pnpm check      # Biome, formatting and lint
pnpm typecheck  # tsc --noEmit across every package
pnpm test       # Vitest, the api's webhook and Entitlement tests

Run pnpm test as well. The api's Vitest suite checks webhook signatures and the Entitlements each payment state grants. The Entitlement tests need Postgres and are skipped unless TEST_DATABASE_URL names a bootstrap database; the suite creates and migrates its own ffd_test database there. A change that has not passed all three commands is not finished.

.github/workflows/ci.yml runs the same gates on every pull request against a throwaway Postgres, together with the web build and pnpm audit --prod --audit-level high. .github/dependabot.yml opens weekly update pull requests for versions at least 14 days old.

pnpm check:fix writes the formatting fixes rather than reporting them.

What runs where

Command What it does
pnpm dev both apps, watching
pnpm build vite build for web, nothing for the api
pnpm db:generate writes the next migration from the schema diff
pnpm db:migrate creates the database if needed, then migrates
pnpm check Biome
pnpm typecheck tsc --noEmit

The api has no build step. It runs under tsx in production too, started by pnpm --filter @ffd/api start.

  • docs/adr/0009-drizzle-v1-on-neon-postgres.md, why nothing migrates at boot
  • docs/adr/0010-typescript-7-and-biome.md, the toolchain

Updated