# Setup — Phase 1 (Foundation) ## Prerequisites - Node.js 18+ (tested on v22) - A reachable PostgreSQL 15+ database (this project does not use Docker) ## Login This app has no local login screen — see `../manager/`. Sign-in happens once at the central Account Manager (2FA, Google, Microsoft), which hands this app a short-lived signed token (`POST /api/auth/handoff`) instead of a password. `../manager/keys/handoff-public.pem` must be copied into `keys/handoff-public.pem` here (see step 4) before that will work. ## First-time setup 1. Copy `.env.example` to `.env` and fill in a real `DATABASE_URL`, two random JWT secrets (e.g. `node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"`), and `COMPANY_SLUG` (must match the `slug` of this company's `Company` row in the Manager). `.env` is gitignored — never put real secrets in `.env.example`. 2. `npm install` 3. `npm run prisma:migrate` — applies all migrations, including the hand-written `prisma/migrations/*_add_journal_balance_trigger/migration.sql` (the double-entry DB backstop; source copy also kept at `prisma/manual-sql/add_journal_balance_trigger.sql` for reference). 4. `mkdir keys && cp ../manager/keys/handoff-public.pem keys/` (after running `npm run generate-handoff-keys` in `../manager/` at least once). 5. `npm run prisma:seed` — creates the default 4-segment account structure, default Chart of Accounts across two example branches (HQ, AMA), the GJ general journal, the 2026 fiscal year, default roles/permissions, and an admin user (`joosth2002@icloud.com`, no local password — this email must match the Manager `Person` who signs in). 6. `npm run dev` — starts the API on `http://localhost:4000` (see `PORT` in `.env`). ## Verifying it works - `npm test` — unit tests (no DB required; validates PostingService's pre-write balance checks). - `npm run test:integration` — integration tests against the real DB: posts balanced/unbalanced entries through the full stack (including a direct raw-SQL attempt that proves the DB trigger backstop works even if `PostingService` is bypassed), and exchanges real handoff tokens (minted with the Manager's real private key) to prove sign-in, replay protection, and cross-company rejection all work end-to-end. - `npm run smoke` — seeds two sample balanced entries (HQ and AMA), confirms an unbalanced one is rejected, and prints a Main-level trial balance check. - `GET /health` — basic liveness check. ## Adding a new migration Prisma's schema DSL can't express multi-row CHECK constraints or triggers, so any future raw-SQL addition (e.g. more constraints) follows the same pattern used for the balance trigger: 1. `npx prisma migrate dev --create-only --name ` 2. Hand-edit the generated `migration.sql` 3. `npx prisma migrate dev` to apply it