# Architecture How STTracker is built today. Update in the same commit as any structural change. ## Overview Status 2026-10-30: only the workspace skeleton exists (root `package.json`, `tsconfig.json`). The target design is in `docs/specs/001-sttracker-v1-design.md` §11. Replace each "planned" marker below as the code lands. One Bun process serves the REST API, the React web app and plugin ingest endpoints, and runs a database-backed job worker. Source plugins pull data from platforms and return normalised `SourceRecord`s. The core snapshots them, diffs against the previous run, matches them to works, and either applies them to the user layer or files a Changes-inbox event. ## Map | Path | Responsibility | State | |---|---|---| | `apps/server` | `Bun.serve`: `/api/v1`, `/ingest/`, job worker, static web | planned | | `apps/web` | React app (HTML imports) | planned | | `packages/core` | domain services: entries, matching, change engine, history, export | planned | | `packages/db` | Kysely, SQLite/Postgres dialects, migrations | planned | | `packages/plugin-sdk` | plugin contract, `SourceRecord`, http/rate-limit helpers, fixture test harness | planned | | `plugins/` | one source integration each (hardcover, trakt, mangabaka, listenbrainz, lastfm, csv) | planned | | `docs/design/mockups` | approved UI mockups (reference only) | exists | ## Data flow Planned sync lifecycle: 1. The scheduler enqueues a job. 2. The worker loads the plugin and calls `pull` or `pullChanges` with an SDK context. 3. The plugin returns `SourceRecord[]`. 4. The core stores a new `source_record_versions` row for each changed hash and diffs the records into `change_events`. 5. Each event is either auto-applied (new record, or a field never edited by the user) or sent to the inbox. 6. Every user-layer write appends to `user_history`. ## External dependencies - Database: SQLite file under `DATA_DIR` (default) or Postgres via `DATABASE_URL`. If unavailable, nothing works. - Platform APIs (Hardcover, Trakt, MangaBaka, ListenBrainz, Last.fm). If unavailable, only that source's sync fails; the run is logged and retried. ## Invariants - A sync never overwrites or hard-deletes user-entered data (spec §8.3). - Plugins never touch the database. - All domain behaviour is identical on SQLite and Postgres. ## Known rough edges None yet. See `docs/TODO.md`.