Getting started
Getting started
Technology stack
- Server: Hono on Node; web app: React, Vite and TanStack Router
- Language: TypeScript (strict mode)
- Styling: Tailwind CSS
- Database: SQLite (better-sqlite3) with Drizzle ORM
- Testing: Vitest and Playwright
- Package manager: Bun
Where things live:
- Frontend: the single-page app in
apps/web; see The web app - Database layer:
db/—schema.ts,container.ts, repositories indb/repositories/sqlite/ - Server: use cases in
server/modules/, the HTTP surface inserver/http/ - Utils: Shared utilities in
utils/ - Domain and contracts: workspace packages —
packages/domain(vocabulary, constants, pure rules) andpackages/contracts(zod schemas); see ADR 0010 - Migrations: Drizzle-managed SQL migrations in
drizzle/
The architecture is written up separately in Architecture; the rules the build enforces today are in Architecture rules.
Prerequisites
- Bun (package manager and script runner)
- Node.js 22 (or higher), installed and on your
PATH. Although Bun runs the app, the tooling shells out to a realnode—bun x tsxfor migrations/scripts, and Vitest’s test workers — sonodemust be directly on yourPATH. Check withnode -v.
Setup
Clone the repo and install dependencies:
make install(Optional) Create
.env.dev.localto customize environment variables:DATABASE_URL=file:./data.db SITE_PASSWORD=your-password AUTH_SECRET=<generated via openssl rand -base64 32>See Environment variables below for all options. Note:
AUTH_SECRETis required only whenSITE_PASSWORDis set or guests protect their name (it signs their session cookies). Omitting this file uses sensible defaults.(Optional) Seed the database with test data:
make dev-db-seedThis seeds the
largeprofile: the hand-curated fixtures the E2E suite uses plus a few hundred generated guests, proposals and sessions for realistic manual testing. Conference Gamma also gets volunteer shifts (locations “Volunteers 1” and “Volunteers 2”, hosted by an organizer account), attendee reports and 1-on-1 requests in every status, to try the admin statistics (move the dev clock past Gamma to see it as an ended event).SEED_PROFILE=small make dev-db-seedseeds only the curated fixtures (what the E2E suite always runs against).Start the dev server:
make devOpen http://localhost:3000 in your browser. That is the Vite dev server for the SPA; it proxies the API to the server, which runs on the next port (3001). When it is ready, it prints the test credentials: the site password, the seeded site admin and guests, and Mailpit.
Development commands
Run make to see all available commands:
make # List all commands
make dev # Start dev server
make test # Run tests
make lint # Lint code
make format # Format code
Before committing or pushing, run:
make precommit # Format, lint, type check, and run all tests (incl. e2e)
Admin UI
A web admin UI is available at /admin for managing all core records: events (basic info, phases,
days), the global pools of users and locations, event↔guest and event↔location assignments,
moderation of proposals, sessions, and RSVPs, and site settings (title, description, and the
optional venue map). It is for site admins, who sign in at /admin; make dev-db-seed seeds one
(username site-admin, password seed-password), and make bootstrap-link prints a link that
makes another. It has its own layout, and the sign-in asks for SITE_PASSWORD first when one is
set.
Dev fake clock
Set SB_ENABLE_DEV_TOOLS=1 and visit any event page with ?dev=1 to get a toolbar for
time-traveling the app (real time / +1h / +1d / +7d / pick a date), so you can walk an event
through its proposal → voting → scheduling phases without editing dates in the database. It’s a
request-scoped cookie, inert unless the env var is set — safe to enable on a shared staging/demo
instance. See ADR 0004 for details.
Environment variables
See Configuration for the full list and descriptions.
For local development, DATABASE_URL is the only required variable — unlike Docker, no default is
provided (e.g. file:./data.db). AUTH_SECRET is additionally required when SITE_PASSWORD is set
or guests protect their name (it signs their session cookies); generate one with:
openssl rand -base64 32
A new variable a self-hoster can set has to reach three files: the reference table above,
docker-compose.yml (which forwards it into the container) and .env.docker.example (where they
fill it in). Nothing runs docker-compose.yml — it is the one file here whose only user is a
stranger — so tests/unit/docker-compose-env.test.ts compares the three and names whichever
variable fell out of step. If a variable deliberately doesn’t go through compose, record the reason
in that test instead.
Running multiple instances
Several clones or jj workspaces on one machine compete for the same ports. Give each clone
explicit, distinct ports in .env.dev.local and .env.test.local — see
Running multiple instances for which variable does what and a worked
two-clone example.
Database migrations
make dev-migrate-create (drizzle-kit generate) diffs db/schema.ts against the latest snapshot
in drizzle/meta/ and writes a new migration plus updated meta files. When two branches each add
one, the meta files conflict — don’t hand-edit them, regenerate as described in
Database migrations.