Local set-up
Requirements
- Node 22 or later. Nothing else: locally, the API runs on an embedded Postgres (PGlite) with development sign-in, and AI is optional.
- Git, and a terminal. On Windows the scripts work in PowerShell and Git Bash.
First run
npm install
npm run demo:data # optional: fictional staff lists for three clients in demo-data/
npm run dev # API on :8787, web on :5173
Open http://localhost:5173. We're signed in as dev@davies-group.com, an administrator. To try the whole flow: create an engagement, choose Upload staff list, and pick demo-data/northwind-staff.csv.
To start with realistic data instead, run npm run demo:seed against a fresh database while npm run dev is running (set PW_CHANNEL=msedge or chrome to use an installed browser). It uploads fictional staff lists for three clients through the real upload path and adds a team (Meera Singh, admin; Priya Shah, lead; Tom Evans, analyst; Aisha Khan, viewer; Sam Jones, deactivated), overrides, Copilot rollout plans, reference data version 2, a closed engagement and a purged one. We switch between those people with Sign in as in the account menu.
npm run dev runs two processes with concurrently:
| Process | Command | Port | What it is |
|---|---|---|---|
api | tsx watch src/node.ts in apps/api | 8787 (PORT) | The Hono API on Node, reloaded on change. |
web | vite in apps/web | 5173 | The React app with hot reload. Proxies /api to localhost:8787, or to API_PORT when that's set in the shell. |
On start, the API prints something like:
Applied migrations: 0001_init.sql, 0002_reference_and_prompts.sql, 0003_ai_choice_and_pii_grants.sql
Workforce Workbench API on http://localhost:8787 (PGlite, dev sign-in, AI off)
The local database
With no DATABASE_URL, the API uses PGlite persisted under .data/pglite at the repository root (DATA_DIR changes the folder, relative to the repository root). On every start it:
- applies any pending migrations from
apps/api/migrations; - seeds version 1 of the reference data and of every prompt (idempotent);
- generates a development identity key into
.data/dev-identity-kekthe first time, ifIDENTITY_KEKisn't set, so name-and-email storage works locally.
It also runs the retention purge every hour.
To start again from nothing, stop the dev server and delete .data/.
Stop npm run dev with Ctrl+C. The server releases its port and closes PGlite on SIGINT/SIGTERM; an embedded database killed mid-write can leave .data/pglite unreadable, and then the only fix is deleting it.
To use a real Postgres instead, set DATABASE_URL (the role needs CREATEROLE, because the first migration creates workbench_app). See Configuration.
Development sign-in
Locally, AUTH_MODE=dev: the API accepts an x-dev-user header naming the caller, defaulting to DEV_USER_EMAIL (dev@davies-group.com). In the app, the account menu shows Sign in as (development): pick any active user, or type an email and choose Go. The choice is kept in localStorage (workbench.devUser) and sent as x-dev-user on every request.
New emails from an allowed domain (davies-group.com locally) are provisioned as members on first use. This is how we try roles and PII access without editing code:
- as the default admin, create an engagement;
- sign in as
analyst@davies-group.com(which provisions them), then back as the admin; - add the analyst to the engagement's team, with or without PII access;
- sign in as the analyst to see what they see.
curl works the same way:
curl -H "x-dev-user: dev@davies-group.com" http://localhost:8787/api/me
Development sign-in is refused by configuration outside ENVIRONMENT=development or test. See Authentication and authorisation.
AI locally
Three options, set in .env at the repository root (copy .env.example):
| Setting | Behaviour |
|---|---|
| Nothing | AI_PROVIDER defaults to none: AI off. Uploads classify with overrides, packs, the cache and rules; the executive summary is rule-based; Ask this workforce is hidden; the Prompt Lab can preview but not run. |
AI_PROVIDER=fake | A deterministic development stand-in model (workbench-dev-stand-in). It answers from the request's structured hints, so it keeps working while prompts are edited, and its answers are obviously synthetic. Allowed only in development and test. Useful for exercising every AI path without a key or spend. |
GEMINI_API_KEY=… | Real Gemini (AI_PROVIDER defaults to gemini when a key is set). Uses GEMINI_MODEL (default gemini-3.1-flash-lite) and the budget settings. |
Real calls count against AI_MONTHLY_BUDGET_USD (default 50) using the local database's ai_usage table. Deleting .data/ resets the month's spend along with everything else.
Commands
| Command | What it does |
|---|---|
npm run dev | API and web together, with reload. |
npm run dev:api / npm run dev:web | One of them. |
npm test | Vitest in every workspace: core, API (on real embedded Postgres with RLS) and web. Must pass before every commit. |
npm run typecheck | Strict TypeScript across the monorepo. Must pass before every commit. |
npm run e2e | The Playwright journey on a throwaway database. See Testing. |
npm run build | Production build of the web app (apps/web/dist). |
npm run db:migrate | Apply migrations (and seed) to DATABASE_URL. |
npm run demo:data | Write the fictional staff lists (Northwind insurance, Fenwick Bank risk and compliance, Harbour facilities) to demo-data/. |
npm run demo:seed | Seed a fresh development database through the UI and API (needs npm run dev). |
npm run docs:screenshots | Retake the user guide's screenshots (docusaurus/static/img/screens/) from the seeded demo data (needs npm run dev and npm run demo:seed). Run it whenever the UI changes. |
npm run format | Prettier over the repository. |
npm run deploy --workspace @workbench/api | Build the SPA and deploy the Worker. See Deployment and operations. |
Run a single workspace's tests with --workspace, for example npm test --workspace @workbench/core, or npx vitest run --root packages/core.
Running the Worker locally
npm run dev:worker --workspace @workbench/api runs wrangler dev against wrangler.jsonc. It needs a DATABASE_URL (Neon) and Cloudflare Access settings in .dev.vars, so for everyday work the Node dev server is simpler. Use wrangler dev to check Worker-specific behaviour (the Neon driver, the scheduled handler, static asset routing).
The documentation site
cd docusaurus
npm install # once; it isn't an npm workspace
npm run start # live preview on :3000
npm run build # static site in docusaurus/build