Skip to content

Contributing

Campfire is open source under the MIT license. Issues and pull requests are welcome at github.com/AwsomeFox/campfire.

Repository layout

apps/server      NestJS API — REST + OpenAPI, SQLite via Drizzle, MCP server
apps/web         React (Vite) frontend
packages/schema  @campfire/schema — the Zod domain contract (single source of truth)
website/         This documentation + marketing site

Local development

Prereqs: Node 22.14+, 23.6+, or 24+ and just.

That version list is the real requirement, not a rounded-up minimum: better-sqlite3 ships a Node-API 10 binary, and Node added Node-API 10 in 22.14.0 and 23.6.0. On an older Node the install still succeeds and npm only warns, then the server dies with a bare segfault the first time it opens the database. node -p process.versions.napi should print 10 or higher, and npm run check:native-addons confirms your runtime can actually load the native dependencies.

just setup     # install all workspaces
just dev       # API on :8080 + web on :5173, hot reload
just test      # server suite (unit + API e2e)
just build     # production build of everything

The schema package is the contract — API validation, OpenAPI shapes, and MCP tool schemas all derive from the Zod definitions in packages/schema. Don't redefine domain types elsewhere.

Mobile nav contract (apps/web/src/app/Layout.tsx, issue #637): the bottom tab bar shows five primary targets — Home, Quests, Party, Notes, and either More or a temporary Live shortcut to the running encounter. Overflow destinations stay in the More sheet (reachable from the role chip in the mobile top bar when Live occupies the tab-bar slot). Do not add a sixth tab without condensing or replacing an existing one.

Testing — the regression safety net

Five layers run together so a change that breaks combat turn order, leaks a DM secret, or regresses a permission check fails CI before it merges — not at someone's table.

Layer Where Run it Guards
Server unit apps/server/test/unit/*.spec.ts just test Pure logic — dice parsing, redactSecrets, token scope-capping, combatant sort / turn math, ability & initiative derivation. No app bootstrap.
API e2e apps/server/test/*.e2e-spec.ts just test Full-app HTTP against a fresh temp SQLite per suite (test/test-app.ts) — auth, roles, every route.
Integration apps/server/test/integration/* just test Real-DB concerns — migration idempotency, delete cascades (no orphan rows), concurrent HP writes / WAL, shutdown checkpoint.
Web unit apps/web/e2e/tests/*.unit.spec.ts just test-unit-web Pure front-end logic and source assertions — nav IA, i18n / locale / time formatting, CSS-token and design-system drift, storage helpers. Playwright's runner, but no browser and no backend.
Browser E2E apps/web/e2e/tests/*.spec.ts just test-e2e Playwright across roles (admin / DM / player / viewer) against the real server serving the built SPA — combat tracker, dmSecret visibility, role gating.
just test          # server: unit + API e2e + integration (Jest, one config)
just test-unit-web # web unit tier (no browser needed)
just e2e-install   # one-time: fetch the Playwright chromium browser
just test-e2e      # browser E2E (builds the app, seeds a per-role backend)
just test-all      # the whole net: lint + server + web unit + web build + Playwright

Which config runs which web tier. Both web tiers live under apps/web/e2e/tests/ and are told apart only by filename, so it matters which you are writing:

File suffix Config CI job Browser?
*.unit.spec.ts / *.unit.spec.mts apps/web/playwright.unit.config.ts unit-web (required) No
any other *.spec.ts apps/web/playwright.config.ts e2e-web Yes

The default config testIgnores *.unit.spec.* and the unit config testMatches only those, so every file belongs to exactly one tier. Name a new pure-logic spec *.unit.spec.ts and both just test-unit-web and the required unit-web check will run it. Type-checking is not enough on its own to know a spec runs — these files compiled cleanly for a long time while no script or workflow executed them (issue #1574).

just test-e2e / just test-all need a Chromium browser — run just e2e-install once first (CI installs it per run). Everything else is pure Node.

CI (.github/workflows/ci.yml) enforces all of it on every PR: a lint job, a build-test job (npm run build + the full server suite), a coverage job (re-runs with instrumentation and uploads an lcov/HTML artifact), a unit-web job (the web unit tier — browserless, so it is in the required set), and an e2e-web Playwright job. The aggregate required check is ci; e2e-web and pwa-web run for signal but are deliberately not required, because a browser check in the required set would block every PR on a timing race. Add tests alongside behaviour changes — the safety net only holds if it grows with the code.

What's most wanted

See the roadmap for the honest list of what's missing and half-built. With account lifecycle, between-session engagement, and multi-system rule packs now shipped, the highest-leverage areas are combat mechanical depth, smoother mobile live-combat nav, published-adventure import, and hardening the still-experimental AI Dungeon Master seat.