Published by KnownBetter Studios → This page is the engineering case study.
Catlg
In developmentA cross-media catalog with shareable, forkable collections and recommendations that identify their source.
At a glance
- Outcome: Unifies books, games, music, film and TV in one catalog with shareable, forkable collections and recommendations that name their source. Product page and beta plans at KnownBetter Studios; this page covers how it is built.
- Status: Runs pre-release at catlg.app on its own domain since May 31, 2026; targets a 2027 open beta. First guarded production deployment landed September 2, 2026. Reached 1,351 commits over 40 working days from March 14 through September 11, 2026.
- Role: Solo: product, data model, API, web app, native app and deployment. Implementation is AI-assisted under a documented node-by-node workflow and reviewed.
- Stack & libraries: Uses pnpm/Turborepo monorepo, Vite/React/TanStack Router/Query, Fastify/generated OpenAPI (136 paths, 167 operations), PostgreSQL/Drizzle/plain-SQL migrations, pg-boss worker, Expo/React Native, Dokku/VPS and Nix dev shell.
- Source: Private while the product takes shape; the demo is public.
- Validation: Counts 342 test files: 214 unit, 94 integration, 8 contract and 26
Playwright specs with 78 cases across mobile/tablet/desktop and Firefox smoke. Covers 81
migrations; serializes real-PostgreSQL integration on one fork. Applies fourteen
verify:*production-config/release-plan/deploy-image/backup-posture/schema-parity gates; 39 ADRs record decisions. - Limitations: Not release-ready: 60 of 78 v1 checklist items are done, and production sign-in email delivery is still open. The relay for self-hosted libraries has its API side built but not its daemon. The native app builds on EAS but has not reached TestFlight or a Play track.
Data and recommendations
Every library item is one row with one of five statuses: saved, in progress, completed, paused or dropped. Medium-specific data hangs off that row: playtime and achievements for games, episodes for TV, pages for books, listen state for music. Films need no extension.
Collections belong to a content identity of kind user or anonymous, not to
an account. Deleting an account flips the identity to anonymous and erases the
personal data; the collection, its forks and its attribution chain stay. The cost
is one extra join per collection read. Collections have groups and S-to-F tiers;
forks keep their type and store source, fork root and depth.
Separates canonical items, sources and user progress. Tracks pending, canonical and folded items; keys raw views by provider/ID. Ingests TMDB, IGDB, Last.fm, Google Books, MusicBrainz, Open Library, TVmaze, OMDb and ListenBrainz; Wikidata and iTunes bridge IDs. Matches external IDs first, never titles alone. Separates people/credits; refreshes cannot overwrite user records.
Precomputes franchise, genre, Collection-affinity, social, trending, exploration, popularity-fallback and item-similarity candidates. Since September, PostgreSQL trains co-occurrence nightly without a vector store. Stores typed primary/supporting reasons and causal references; deleted references resolve to nothing on read.
An evaluation harness holds out 10% of each library in a 2,000-user fixture and reports hit-rate@20 and recall@100 against a bar recorded in the repo.
Authentication and sync
Owns auth in the API. Uses emailed codes and signed HTTP-only SameSite=Strict
cookies. Added recovery codes, Google/Apple/Discord/GitHub OAuth and WebAuthn passkeys in
September through an amending ADR. Stores personal-access, app-session,
device-code-enrolled and relay bearer tokens in one table. Contract-tests every route boundary.
Syncs progress through 18 connectors: Plex, Jellyfin, Emby, Steam, Spotify, Trakt, Navidrome, Audiobookshelf, Sonarr, Radarr, Kavita, Komga, calibre and others, with four CSV importers. Guards self-hosted hosts against SSRF; queues ambiguous matches per service. Specifies the relay protocol; builds API routes, data and command producers; leaves its daemon unbuilt.
Architecture and operations
Shares queries/design tokens across web/Expo/React Native. Native has 42
unit-test files, code/OAuth/passkey sign-in, deep links and Sentry-compatible crash reporting.
Added a September Dokku predeploy migration hook with advisory lock and statement timeout. Stores nightly encrypted, append-only off-host backups; rehearsed restore takes six minutes. Exposes API/worker Prometheus metrics. Reduced 50k-item fixture search from about 182 ms to 8.6 ms with a trigram index.
Runs CI secret scans, lint, quick/full verification, browser tests, deploy-image proof against internal details and separate Trivy supply-chain scans.