Skip to main content

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:

ProcessCommandPortWhat it is
apitsx watch src/node.ts in apps/api8787 (PORT)The Hono API on Node, reloaded on change.
webvite in apps/web5173The 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:

  1. applies any pending migrations from apps/api/migrations;
  2. seeds version 1 of the reference data and of every prompt (idempotent);
  3. generates a development identity key into .data/dev-identity-kek the first time, if IDENTITY_KEK isn'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 the server cleanly

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:

  1. as the default admin, create an engagement;
  2. sign in as analyst@davies-group.com (which provisions them), then back as the admin;
  3. add the analyst to the engagement's team, with or without PII access;
  4. 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):

SettingBehaviour
NothingAI_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=fakeA 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.
Budget locally

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​

CommandWhat it does
npm run devAPI and web together, with reload.
npm run dev:api / npm run dev:webOne of them.
npm testVitest in every workspace: core, API (on real embedded Postgres with RLS) and web. Must pass before every commit.
npm run typecheckStrict TypeScript across the monorepo. Must pass before every commit.
npm run e2eThe Playwright journey on a throwaway database. See Testing.
npm run buildProduction build of the web app (apps/web/dist).
npm run db:migrateApply migrations (and seed) to DATABASE_URL.
npm run demo:dataWrite the fictional staff lists (Northwind insurance, Fenwick Bank risk and compliance, Harbour facilities) to demo-data/.
npm run demo:seedSeed a fresh development database through the UI and API (needs npm run dev).
npm run docs:screenshotsRetake 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 formatPrettier over the repository.
npm run deploy --workspace @workbench/apiBuild 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