Contributing
Prerequisites
- mise, then
mise installin the repo: it installs the Bun and Go versions pinned inmise.toml(preinstallblocks npm/yarn/pnpm). prek comes fromnode_modules, nothing to install. - ffmpeg built with the
libwebpencoder, ffprobe and pdftoppm (poppler), which the Go worker execs. mise can't install those; its postinstall hook runsmise run doctor, which checks them and prints the install command for what's missing. - Docker, only for the end-to-end test stacks.
Setup
git clone https://github.com/orochibraru/penombre.git
cd penombre
bun install
bun run devbun run dev starts the Vite dev server on http://localhost:5173. It runs on
SQLite by default (./data/penombre.sqlite), so no database server is needed;
point DATABASE_URL at a Postgres instance to develop against Postgres instead
— it stays supported, it's just no longer the default.
Commands
# App
bun run dev # Vite dev server
bun run build # Build for production
bun run preview # Preview the production build
bun run db:generate # Generate Drizzle migrations (Postgres + SQLite)
bun run db:studio # Open Drizzle Studio
# Quality
bun run lint # oxlint + biome + markdownlint + tailwint
bun run lint:fix # ...and fix what is fixable
bun run format # biome format --write
bun run check # check:app && check:scripts && check:go, sequentially
bun run check:app # svelte-check on the app alone
bun run circular # Report circular imports
# Tests
bun test # Unit tests (fully mocked, no services needed)
bun test path/to.test.ts # A single file
bun test -t "some test name" # Filter by test name
bun run test:docker # Unit tests in Docker (mirrors CI, adds real Redis)
bun run test:e2e # E2E on SQLite (the default stack)
bun run test:e2e:pg # E2E on PostgreSQL
bun run test:e2e:ui # Playwright UI mode
# Codegen
bun run gen:api # OpenAPI spec + typed API client
bun run gen:env # Regenerate .example.envUnit tests preload test.setup.ts (see bunfig.toml), which mocks
$app/*/#lib/server/* and the Drizzle db object; they need no database or
Redis. bunfig.toml also sets rerunEach = 3 to catch flaky tests, and
enforces coverage thresholds.
Adding an API endpoint
Every /api/v1/... endpoint lives in two places:
- Contract — a
defineRoute()call insrc/lib/server/openapi/v1/<resource>.tsdeclaring method, path, Zod schemas and auth requirements. Import the module fromsrc/lib/server/openapi/routes.tsso it registers with the OpenAPI registry. - Handler —
src/routes/api/v1/.../+server.tsimports the contract and calls.handler(...). Auth and validation already ran; the callback just returnsHttp.Ok(...)and friends.
Skipping the contract means no validation, no OpenAPI spec entry, and no
generated client. Run bun run gen:api after changing either half.
Database changes
Edit src/lib/server/db/schema.pg.ts and schema.sqlite.ts (kept
structurally in sync by hand; schema.ts is a dialect-resolving shim that
re-exports whichever is active, and is what the rest of the app imports from),
then run bun run db:generate; it emits migrations for both dialects
(drizzle/pg/ and drizzle/sqlite/). Migrations run automatically on boot.
Git hooks
bun install runs prek install, which wires .pre-commit-config.yaml into
.git/hooks; prek itself comes from node_modules (@j178/prek), nothing
extra to install. The pre-commit stage (fast linters) runs on every commit and
also runs in CI over the whole repo; check and test-unit are pre-push hooks,
so a green commit does not mean green CI; those two still run on git push and
in CI's own type-check/test job.
prek run --all-files # run every hook over the whole repo
prek run biome # run a single hook
SKIP=test-unit git push ... # skip a hook for one pushCommit messages follow
Conventional Commits — the commit-msg
hook enforces it, and releases are cut from it by
releaser.
Linting gotchas
console.*is an oxlint error in app code; useLoggerfrom#lib/logger. Biome only formats and sorts imports here; its linter is off.- Floating and misused promises are errors —
awaitthem or handle them. - Don't hand-edit generated output:
src/lib/paraglide,src/lib/api/v1.d.ts,drizzle/,.example.env.
This guide lives in the project repo: edit it there, and this page follows within a day.