-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
For contributors. Conventions and the full list of invariants live in
CLAUDE.md; the
environment reference is
docs/deploy.md.
backend/ FastAPI · SQLAlchemy 2.0 async · Alembic · arq worker
frontend/ React 18 · Vite · TypeScript · TanStack Query · react-virtual · PWA
connector/ standalone agent for HDHomeRun tuners the server can't reach
docker/ entrypoint.sh — bootstraps secrets, runs migrations, dispatches web|worker
Dockerfile multi-stage: node builds the SPA → python image (API + worker + SPA)
One image runs three roles: entrypoint web (uvicorn, after
alembic upgrade head), entrypoint worker (arq app.worker.WorkerSettings),
and the built SPA served by the API at /. Postgres is the only stateful store;
Redis backs the arq queue and rate limiting.
The API serves the SPA from / (mounted after the /api router), so there
is no CORS in production and the refresh cookie is first-party. Dev uses a Vite
proxy to localhost:8000.
-
Guide timezone — every surface (grid, agenda, cards, sheet, exports)
renders times in
channel.timezone= per-source override → tenant default → UTC, plus a per-channel clock shift. Never viewer-local. - Channel refresh — sources reconcile channels in place by a stable dedupe key, so unchanged channels keep their id and their programmes.
- EPG fan-out — one XMLTV key can map to several channel ids (East/West feeds sharing a tvg-id); a programme row is written per channel.
-
Watchlist items are snapshots — a
programme-kind item stores channel + start + title, not an FK toprogramme(an EPG refresh recreates those rows). Awatchlist_notificationledger makes reminders send-once. -
Export token —
Tenant.export_token_hashis the sha256 of a shown-once token; it gates the unauthenticated/api/exports/*routes via?token=. - Email is fail-open — a provider error is logged, never raised.
-
SourceKindisnative_enum=False(VARCHAR) — new kinds need no migration.
Feature branch → PR → squash-merge with --delete-branch. CI runs the
backend / connector / frontend / image jobs. Releases are cut by pushing a tag:
v* (app image) or connector-v* (connector image + wheel).