Skip to content

Architecture

Browser (PWA) ──► web (SvelteKit) ──► api (NestJS) ──► PostgreSQL
│
├──► TMDB, AniList, IGDB, Open Library, MusicBrainz
└──► SMTP, Web Push
  • apps/api: a NestJS API on Fastify, with Prisma and PostgreSQL. It serves the web app’s internal routes and the versioned public API under /api/v1, and runs the scheduled jobs.
  • apps/web: a SvelteKit app, installable as a PWA. The public pages are prerendered; everything under /app runs in the browser and talks to the API.
  • packages/shared: the types and enums both sides share, so a change in the API’s shapes breaks the build rather than the app.
  • apps/docs: this site, Astro Starlight for the guides and Scalar for the API reference, rendered from the API’s own OpenAPI document.

Loomkeep doesn’t mirror any catalogue. Search queries them live; a title is copied into the database, with its seasons, episodes and ids in other catalogues, only once someone tracks it. It is refreshed when it gets old, and its episodes are never deleted, so a viewing always keeps its target. Each domain has one catalogue: TMDB for films and series, AniList for anime, IGDB for games, Open Library for books, MusicBrainz for music.

Every viewing is a row: watching an episode twice is two rows, a film’s rewatch is one more. Games and books group dated sessions into playthroughs and readings. That is what lets history, stats and rewatches be exact rather than estimated.

Sessions live in encrypted, HttpOnly, SameSite=Strict cookies, never in browser storage, with short-lived access tokens and rotating refresh tokens, one per device. Two-factor authentication (authenticator app, email codes, security keys and passkeys) sits on top. The public API instead takes personal API keys, only on its own routes. See Account security and Authentication.

The API answers errors with stable codes (auth.invalid_api_key), never with sentences: the web app turns each code into text in the reader’s language. The interface is English first, with French and Italian, all through translation catalogs.

Permanent choices for an instance (social features, registration, the public API) are instance settings, stored in the database. Temporary switches (maintenance of a domain, a news banner) are feature flags in Unleash, read live without a reload.

A few premium features live in ee/ directories under their own license; the core never depends on them. See License.

The detailed guide, with every convention, is CLAUDE.md at the repository’s root.