All notable changes to this project are documented here.
The format is based on Keep a Changelog, and this project follows Semantic Versioning.
0.2.0 (2026-09-26)
- Install the Windows build, then use the loopback WebUI for first-run sign-in, server discovery and management, Spotify Client ID or hosted GitHub sign-in, card and Discord settings, status and logs. The tray opens Settings. Older native and browser setup wizards were replaced before this release.
- Windows installer and portable ZIP are the distributed builds; check the matching
SHA256SUMSbefore installing. The hosted card never uploads artwork or private server details.
The changes below describe what ships in 0.2.0; use the current Windows guide for setup steps.
- accept same-origin JSON bodies on the localhost server (#195) (25bbb37)
- add bounded privacy-safe app logs (#185) (e584479)
- add Discord artwork proxy setting (#191) (788af42)
- add Discord preview publish clear controller (#169) (674a117)
- add Discord restart convergence core (#186) (3f20c1b)
- add Open log folder tray action (#192) (d5b253a)
- add OS-backed credential store contract (#168) (a46ee7c)
- add privacy-safe diagnostics core (#176) (9a0f61c)
- add privacy-safe Discord artwork resolver (#187) (d3a350d)
- add privacy-safe hosted-card stale diagnostics headers (#189) (39e1d54)
- add privacy-safe tray health state (#184) (d8cac95)
- add startup recovery state core (#181) (b7b23d6)
- add versioned config migration core (#182) (1175ff7)
- app: first launch opens setup in the default browser (#271) (#324) (ff09c13)
- app: local status page with live server, Discord and card state (#253) (#257) (7e5cdcf)
- app: Logs page with recent events and a level filter (#253) (#327) (3328ab4)
- apply privacy-safe artwork when publishing to Discord (#188) (05af104)
- app: make safe mode offline - no sign-in read or server polling (#122) (#329) (fe302aa)
- app: persist crash-loop state and guard app starts for safe mode (#122) (#317) (cccc45c)
- app: poll every signed-in server, keep the first one's presence for now (#252) (#384) (19793ed)
- app: run config migrations at start-up, document backups (#307) (b26760f)
- app: start in safe mode after repeated failed starts (#122) (#322) (883bfca)
- back up config before migration (#183) (2bbb1ec)
- card: artwork placement and size in the config.json card section (#94) (#400) (3a1d55e)
- card: field order and text alignment in the renderer (#94) (#402) (219e4e6)
- card: progress bar position and width in the renderer (#94) (#408) (d0bd81f)
- card: right-to-left layout in the renderer (#94) (#410) (f433547)
- config: field order and text alignment in the card section (#94) (#404) (7edd1ed)
- config: saved card appearance in config.json, used by the local card (#94) (#363) (205e920)
- config: top-level spotify block in config.json (#135) (#389) (e6423bd)
- config: v2 config with a list of servers, migrate v1 (#252) (#364) (a642d7f)
- create secret-free setup config (#170) (08ac47b)
- define provider connection validation (#177) (16ce573)
- discord: artwork source and fallback reason in app status (#154) (#335) (2875319)
- discord: clear sessions stuck at the same position (#153) (#339) (988e0a1)
- discord: episode defaults show series and episode code (#143) (#334) (dbf1edf)
- discord: in-house Discord IPC client (#227) (cdad8b9), closes #226
- discord: jitter on IPC reconnect backoff (#153) (#338) (39c7b56)
- discord: look up album art by default for new setups (#268) (#282) (e4f9332), closes #154
- discord: reconnect backoff and a privacy-safe connection status (#123) (#240) (fb8821d)
- discord: refresh artwork hook that drops cached covers (#154) (#325) (006b759)
- discord: run Discord presence from the saved setup (#226) (#232) (c2a6b9e)
- discord: send safe artwork with the Discord status (#124) (#237) (3075506)
- discord: show as Listening with a progress bar by default (#266, #267) (#273) (6ea172d)
- discord: show films and TV as Watching, music as Listening (#143) (#308) (fd5376f)
- discover media servers running on this PC during setup (#201) (ea1d912)
- hosted: card layout options in the card URL (#94) (#414) (957dabb)
- hosted: credential store and polling loop for hosted uploads (#140) (#297) (dc20ce3)
- hosted: desktop uploader client for hosted cards (#140) (#295) (04ac1b4)
- hosted: GitHub sign-in, one card per user updated from several PCs (#140) (#471) (1121bba)
- hosted: per-day aggregate counters for the usage dashboard (#218) (#460) (9d33eb4)
- hosted: privacy projection for hosted card uploads (#140) (#289) (2ae4723)
- hosted: push-based ingest and SVG card service (#140) (#241) (0db2922)
- hosted: rate limit ingest per device (#140) (#313) (f63e93d)
- hosted: run hosted upload from the app config (#140) (#304) (6a69821)
- hosted: setup API for the card hosting step, upload preview and self-hosted check (#140) (#468) (b7167bf)
- hosted: Sign in with GitHub from setup, app side of one card per user (#140) (#473) (d89e87b)
- jellyfin, emby: map series, season, episode and year (#143) (#333) (026973a)
- log Windows startup lifecycle (#193) (a621e1e)
- opt-in MusicBrainz artwork lookup for Discord (#202) (f51c75f)
- opt-in page CSP and 404 fallback on the localhost server (#197) (a30d205)
- plex: map series, season, episode and year (#143) (#332) (e5222d6)
- presence: optional series, season, episode and year (#143) (#326) (e39d35b)
- privacy: privacy settings in config, applied to Discord, hosted and card (#253) (#344) (0939f12)
- provider sign-in flows for setup (#211) (b044363)
- providers: back off from a failing media server (#153) (#340) (c0994b9)
- providers: declare which media kinds each provider reports (#143) (#361) (6c000c8)
- providers: one-off 127.0.0.1 listener for Spotify sign-in (#135) (#387) (2585be1)
- providers: provider backoff honours Retry-After (#135) (#386) (6db14b7)
- providers: Spotify now-playing mapping, first slice (#135) (#366) (bf01a1b)
- providers: Spotify PKCE sign-in and token refresh (#135) (#379) (22b8f01)
- providers: Spotify runtime source and card source picker (#135) (#393) (afb52d4)
- providers: Spotify sign-in resolves the refresh token and who signed in (#135) (#397) (503b73a)
- providers: YouTube bridge and provider for the browser extension (#136) (#395) (9c1b702)
- resolve stable provider identities (#167) (b26b2c3)
- save and reset setup config atomically (#171) (45f7cac)
- server: opt-in open write paths for self-authenticating handlers (#136) (#405) (d24768e)
- server: optional per-install session secret for state-changing requests (#173) (#281) (b6ee265)
- settings: add bounded cancellable LAN discovery (#550) (1b81e5a)
- settings: artwork side and size controls on the Card section (#94) (#401) (c1a745d)
- settings: card appearance in the settings API, with a draft preview (#94) (#365) (51ac230)
- settings: Card section with live preview on the settings page (#94) (#368) (0b2a1f6)
- settings: choose what Discord shows when nothing is playing (#253) (#359) (6b97fcf)
- settings: Discord section in the local settings page (#272) (#318) (2d0b64d)
- settings: hosted card link carries the card layout options (#421) (#423) (cd24872)
- settings: hosted card link uses the saved card style and width (#94) (#370) (efad5a7)
- settings: hosted card section with link, README snippet and disconnect (#290) (#330) (1eba172)
- settings: line order and text alignment controls on the Card section (#94) (#406) (8802e47)
- settings: move onboarding and servers into the WebUI (3d07921)
- settings: Privacy section on the settings page (#253) (#345) (96cc673)
- settings: progress bar position and width in config.json and on the Card section (#94) (#409) (d5e2664)
- settings: Refresh album art button in the Discord section (#154) (#331) (1f68dbf)
- settings: Start with Windows on the settings page (#253) (#337) (389c5d9)
- settings: text direction in config.json and on the Card section (#94) (#411) (d4ce2d4)
- settings: YouTube pairing code on the settings page (#136) (#428) (f77c4f5)
- setup API for provider sign-in that saves secrets to Credential Manager (#221) (95cfbb2)
- setup: add, cancel and remove servers on the browser setup page (#252) (#381) (c832dd1)
- setup: find Jellyfin and Emby on the LAN with UDP discovery (#269) (#286) (ecac482)
- setup: Finish writes the app config (#224) (909661e), closes #223
- setup: optional Spotify sign-in API for setup, writes config.spotify (#135) (#396) (1b559b8)
- setup: probe the LAN gateway for Navidrome and Jellyfin/Emby (#270) (#291) (124c966)
- setup: rate-limit Test connection, test websocket refusal (#173) (#254) (d74d407)
- setup: sign in to more than one server during setup (#252) (#378) (73c9a42)
- setup: Spotify Client ID and sign-in on the browser setup page (#135) (#399) (c6cdd36)
- setup: test the signed-in provider connection before finishing (#116) (#242) (064c59f)
- start: run from the setup config (#225) (8f8f1d2), closes #223
- status: copy or download a privacy-safe diagnostics report (#115) (#279) (31f5016)
- status: list every server on the status page, settings helpers to view and remove servers (#252) (#388) (d5090f9)
- status: show build commit, channel and signing in status and diagnostics (#119) (#292) (7703695)
- tray: app health in the tray tooltip and menu (#314) (a5e0f3f)
- tray: Settings item opens the settings page (#253) (#341) (14f3f2a)
- validate packaged build provenance (#180) (8a503dd)
- Windows Credential Manager adapter for provider secrets (#200) (0961cbe)
- youtube: browser extension skeleton for YouTube and YouTube Music (#136) (#407) (4e8bc8f)
- align card width validation (d5a6cb8)
- allow fresh setup drafts to advance and resume (#194) (7675c47)
- analytics: recover the write queue after a failure (8e37cec)
- app: move the local app off port 3000, serve a home page at / (#255) (#256) (551d21a)
- app: status colours blue/orange instead of green/red (#263) (#264) (389f372)
- artwork: cap streamed artwork bodies without Content-Length (#508) (#514) (9fdf0cb)
- artwork: decode simple lossy WebP dimensions (#505) (#512) (e2592e4)
- artwork: follow same-host redirects for artwork (#542) (f33a88b)
- build: write build-info.json in a way Windows PowerShell 5.1 supports (#119) (#299) (8b0fd43)
- card: clamp progress bar and clock to the track length (#504) (#510) (781bfa1)
- card: drop XML-invalid characters from card text (#502) (#513) (13ce7a4)
- ci: only create the dev release when the lookup returns 404 (#321) (cae875e)
- ci: update the dev release by ID so a moved tag doesn't fail the edit (#319) (3ead31f)
- clean shutdown no longer counts as a crash toward safe mode (#497) (#507) (8821540)
- config: accept a config.json that starts with a UTF-8 BOM (#522) (#523) (dda7ca9)
- diagnostics: redact JSON-style and camelCase credential keys (#528) (#529) (768c42c)
- discord: bring the status back after Discord restarts (#123) (#235) (127bba5)
- discord: never look up a cover from a title alone (#154) (#305) (d108a85)
- discord: never send video or motion artwork to Discord (#154) (#316) (6c6ffd6)
- discord: only look up covers for music, key the cache by media kind (#154) (#303) (dc9fc81)
- discord: real fallback image instead of the ? placeholder (#268) (#283) (eaffe58), closes #154
- entries report a missing data dir plainly instead of a TypeError stack (#500) (#515) (8fb06dd)
- harden localhost server boundary (#175) (fb668c8)
- hosted: clear the hosted card on provider failure and stuck sessions (#343) (#349) (ac98927)
- hosted: landing page at /, stop serving hosted/lib (#245) (#246) (33910a0)
- http: a response that fails after headers are set no longer crashes the app (#525) (#526) (f716945)
- include current dev commit in release notes (#139) (b0df93b)
- key resilient card renders per variant and add diagnostics (#190) (09b285c)
- migrate configs saved with a UTF-8 BOM instead of rejecting them (#532) (#536) (6d2aaa2)
- pass explicit updater channel (ea02aff)
- preserve updater prerelease ordering (#178) (bedb205)
- providers: follow Jellyfin and Emby sessions by stable user ID (#125) (#294) (e0c31d1)
- providers: follow Plex sessions by plex.tv ID, owner via /accounts (#125) (#301) (e145e83)
- reject non-loopback request hosts (#179) (21b20f6)
- reject untrusted updater redirects (#174) (31858d0)
- release: dedupe repeated changelog entries on the release PR (#258) (#259) (b88972a)
- release: tag releases as vX.Y.Z without the component prefix (#348) (9f37cfb)
- release: write dev SHA256SUMS with LF line endings (d2336bf)
- settings: distinguish cancelled server scans from empty results (#555) (b2dd890)
- settings: retry the config.json replace while Windows has it locked (#530) (#531) (f92d4c7)
- startup: explain a port the OS refuses (EACCES) (#533) (#534) (e606259)
- updater: cap update response sizes and time out stalled requests (#172) (#280) (94596fc)
- updater: install the Windows bundle and test check -> download -> swap end to end (#373) (#375) (6e4a38e)
- updater: linear-time version parsing, skip malformed release tags (#172) (#310) (8ac1ed2)
- updater: require an explicit update channel, no silent beta default (#172) (#323) (9cb3ea6)
- updater: Windows installs update from the Windows bundle, not the source tarball (#373) (#374) (fc69e70)
- upgrade keeps Start with Windows when it was enabled from the app (#520) (#521) (6ffde05)
- use release metadata for update channels (3fc6333)
- windows: detect moved portable startup shortcuts (5d9c1b9)
- Follow-on updates remain in progress; see the current install and Settings guides for what ships in 0.2.0.
0.1.0 - Unreleased
- Provider-neutral presence model for Plex, Jellyfin, Navidrome and Emby.
- Self-contained SVG cards with themes, privacy controls and validated embedded artwork.
- Discord Rich Presence formatting, lifecycle, IPC mapping, safe buttons and update controls.
- Provider artwork request, cache, fetch and pixel-bound validation pipeline.
- Documentation for deployment, providers, privacy, customization, architecture and releases.
- Multi-version CI, build smoke artifacts and tag-driven release automation.