Skip to content

Running the stack

Drive the dev environment through the justfile at the root of the openbooks superproject. It sequences things that need sequencing, waiting for Postgres to report healthy before migrations run, installing web dependencies on first run, and tracking pids so the servers can be stopped cleanly. Hand-rolling docker compose, cargo run, and npm run dev gets those wrong.

just run start Postgres, the API, and the web dev server in the background
just desktop run the desktop app in the foreground, starting Postgres and the API first if they’re down
just cli [args] run the terminal UI in the foreground; arguments pass through to the scriptable mode
just docs serve this documentation site
just stop stop the API, the web dev server, and Postgres
just restart stop, then start; recorded data survives
just seed load a club’s worth of sample data, 2025 through mid-2026
just reset rebuild from scratch, come back up with the sample data loaded
just reset-clean rebuild from scratch, stay down, empty database
just status what’s up right now
just logs follow the API and web logs
just smoke check the ledger and the reports end to end, needs jq
just test run the API’s test suite against a running Postgres
just user-add <email> create a user and print a generated password once
just users list who can sign in, with passkey count and last seen
just mint-token <email> print a bearer token for scripts and for openbooks-cli/openbooks-desktop before they log in

just on its own lists them. user-add, users, and mint-token wrap the API binary’s own admin verbs, which need DATABASE_URL — see Authentication and API overview.

These recipes are a development convenience: they shell out to cargo run -- user add … against whatever DATABASE_URL is in scope locally. A deployed instance has no just and no local checkout — the same admin verbs run against the built binary inside the cluster instead, with kubectl exec standing in for cargo run. See Deploying for the command.

Web UI http://localhost:38080
API http://localhost:38081
Postgres localhost:38083 on the host

The API answers GET /health, which is what just status and the startup waits poll.

Variable Default Purpose
WEB_ORIGIN http://localhost:38080 the only origin the API’s CORS layer allows
API_ORIGIN http://localhost:38081 the API’s own URL, used as the token audience and in the 401 challenge
SESSION_TTL_DAYS 30 how long a signed-in web session lasts
OPENBOOKS_TOKEN none the bearer token openbooks-cli, openbooks-desktop, and /mcp read for authentication

See Authentication for what each one governs.

The API and web dev server run in the background, so nothing prints to your terminal. Output goes to:

  • .dev/api.log
  • .dev/web.log

just logs follows both. .dev/ also holds api.pid and web.pid, and it’s gitignored.

The desktop app, the CLI, and the docs site run in the foreground on purpose. They own your terminal, ctrl-C stops them, and compile errors land where you can see them.

just reset and just reset-clean both delete:

  • the Postgres volume, which means every recorded transaction
  • openbooks-web/node_modules, .nuxt, and .output
  • openbooks-api/target

They prompt first. Add -y to skip the prompt. A full reset costs about a minute because everything rebuilds, so reach for just restart when all you want is fresh processes.

If you’d rather not use just:

Terminal window
cd openbooks-api && docker compose -f compose.yaml up -d && cargo run # migrations run on startup
cd openbooks-web && npm install && npm run dev
cd openbooks-cli && cargo run
cd openbooks-desktop && cargo run

The API runs its migrations on startup, so there’s no separate migrate step.

Terminal window
just smoke

smoke.sh posts to a running API and asserts real numbers: quarterly totals, a balance sheet that balances, and rejection of unbalanced and single-legged posts. It works in the year 2099 and deletes everything it finds there, so leave 2099 alone in the sample data.

Run it after touching the ledger or the reports.