# Castarr Native Kotlin app for Google TV that lets every user of a Dispatcharr backend watch live IPTV comfortably on their own TV: full D-pad UI, login via OIDC Device Flow (Authentik), plus an optional phone Remote paired via QR code. ## Language **TV-App (Castarr)**: The native Kotlin app running on Google TV; full TV UI, plays streams, serves the Remote. _Avoid_: receiver, player app, NodeCast (old name) **Device Flow**: OIDC device authorization grant: TV shows QR/code, user confirms on the phone at Authentik, app receives Bearer tokens for the Backend API. _Avoid_: pairing (that word is reserved for the Remote) **Backend (Dispatcharr)**: The self-hosted stream manager owning sources, aggregation, failover, EPG and stream profiles; forked at be-nj with native OIDC. Admin UI internal (), API external for TV clients (api.). _Avoid_: server (ambiguous) **Viewer (nodecast)**: The browser frontend for the family on — OIDC login, plays via its own transcode; consumes exactly one source, the Backend's M3U/EPG output (ADR-0006). _Avoid_: treating it as a second source of truth **Quelle (Source)**: A configured backend the TV-App reads channels from — primary type Dispatcharr fork (Bearer API), fallback type generic M3U+XMLTV without login (Threadfin, Tunarr, ErsatzTV). **Remote**: The phone web UI served by the TV-App over HTTP; talks to the TV-App via WebSocket. Never talks to the Backend directly. _Avoid_: app (it is not an installed app), controller **Pairing**: Trusting a Remote via the QR token (128-bit) or the rate-limited 4-digit code. **Channel**: A playable live entry the Backend exposes via Xtream API or M3U. _Avoid_: station, sender (in code/docs) **Onboarding**: First-run flow: the TV shows one QR (its own pairing server); server URL and the IdP login then happen entirely on the phone via the Remote. Nothing is ever typed on the TV. **Ten-Foot-Regel**: In the family path the TV UI has no text fields and no scrolling except the channel list itself; every screen fits the screen. Text-heavy configuration lives in an "Erweitert" sub-screen. **Now/Next**: The EPG scope of the Remote in phase 1: current + upcoming programme per Channel, no full guide timeline. ## Relationships - The **TV-App** is a client of the **Backend**; the **Remote** only ever talks to the TV-App. - One **User** logs into one **TV-App** via **Device Flow**; favorites belong to the User on the Backend, not to the device. - A **Remote** controls exactly one **TV-App**; a TV-App accepts multiple paired Remotes. - Aggregation, failover and stream shaping happen in the **Backend**, not in the TV-App. ## Example dialogue > **Dev:** "Does the **Remote** need Backend credentials?" > **Domain expert:** "No — the Remote is where **Onboarding** happens, not > where credentials live: the user types the server URL there and finishes the > IdP login on the phone. The **TV-App** holds the tokens and is the only one > talking to the **Backend**. A generic M3U **Quelle** needs no login at all." ## Flagged ambiguities - "nativ" means: no Flutter/WebView wrapper — Kotlin app. It does not mean "no embedded web content" (the Remote is deliberately a web page). - "Passthrough" means the fork's `raw` stream profile (ffmpeg copy), selected per request via profile parameter — not a bypass of the Backend proxy. - Favorites are per **User** in the Dispatcharr fork (resolved; app-local and channel-group approaches were rejected).