# Subsonic Server Blueprint ## Goal Build a self-hosted music server with: - a Subsonic-compatible API for third-party clients - a modern web interface inspired by Aonsoku - an architecture that is simpler to build than a full Navidrome fork - a codebase that can grow into a serious long-term project This project should not try to clone Navidrome feature-for-feature on day one. The right approach is: - build a focused backend with strong library and streaming fundamentals - build a separate SPA frontend with a polished listening experience - support the Subsonic API where it matters - keep a separate internal web API for faster frontend development ## Reference Direction ### What to take from Navidrome - overall product shape: self-hosted music server - robust scanning and indexing mindset - low-overhead backend architecture - Subsonic compatibility strategy - cover art, streaming, playlists, favorites, search, multi-user ### What to take from Aonsoku - modern visual language - frontend stack direction - player UX patterns - layout ideas for home, album, artist, playlist, queue, lyrics - separation between data fetching, UI state, and playback state ### What not to copy directly - do not embed the frontend tightly into the backend from day one - do not aim for full Subsonic coverage immediately - do not duplicate Navidrome's whole surface area before the core works well ## Recommended Stack ## Backend - Language: Go - HTTP router: `chi` - Config: `viper` or a smaller env-based config layer - Database: SQLite for MVP - Migrations: `goose` or `atlas` - Tag reading: a mature tag library for MP3/FLAC/M4A/OGG metadata - Background jobs: in-process worker loops - File watching: `fsnotify` or polling fallback - Auth: session + token support - Logging: `slog` or `logrus` ## Frontend - Framework: React - Build tool: Vite - Language: TypeScript - Routing: React Router - Server state: TanStack Query - Client state: Zustand - UI primitives: Radix UI - Styling: Tailwind CSS - Forms: React Hook Form + Zod - Audio playback: HTMLAudioElement first, optional HLS/transcoding later ## Infra - Single binary backend for local/self-hosted use - Separate frontend app built into static assets - Docker support early - Local dev via `docker-compose` or separate dev servers ## High-Level Architecture ```text music files -> scanner -> metadata extractor -> database index -> artwork cache clients -> web SPA -> third-party Subsonic clients backend -> auth -> library API -> Subsonic compatibility API -> streaming/transcoding -> playlist/favorites/history ``` ## Recommended Repository Layout ```text / apps/ web/ cmd/ server/ internal/ auth/ config/ db/ httpapi/ library/ media/ playlist/ scanner/ subsonic/ users/ migrations/ assets/ scripts/ deploy/ docs/ ``` ## API Strategy Use two APIs. ### 1. Subsonic-compatible API Purpose: - support existing clients - preserve compatibility expectations - allow easy migration from other Subsonic servers Examples: - `/rest/ping` - `/rest/getLicense` - `/rest/getArtists` - `/rest/getArtist` - `/rest/getAlbum` - `/rest/getSong` - `/rest/stream` - `/rest/getCoverArt` - `/rest/search3` - `/rest/getRandomSongs` - `/rest/getStarred2` - `/rest/star` - `/rest/unstar` - `/rest/createPlaylist` - `/rest/updatePlaylist` - `/rest/getPlaylists` - `/rest/getPlaylist` - `/rest/scrobble` ### 2. Internal web API Purpose: - reduce frontend complexity - return cleaner payloads than Subsonic XML/legacy JSON - aggregate data for fast UI screens Examples: - `GET /api/me` - `GET /api/home` - `GET /api/artists` - `GET /api/artists/:id` - `GET /api/albums/:id` - `GET /api/tracks/:id` - `GET /api/playlists` - `GET /api/playlists/:id` - `POST /api/queue/scrobble` - `GET /api/search?q=...` - `GET /api/browse/recent` - `GET /api/browse/random` - `POST /api/favorites/:id` - `DELETE /api/favorites/:id` ## Data Model Core entities: - users - artists - albums - tracks - genres - album_art - playlists - playlist_tracks - favorites - play_history - scan_jobs - library_roots Important fields: ### users - id - username - password_hash - is_admin - last_login_at - created_at ### artists - id - name - sort_name - album_count - song_count - biography - cover_art_id ### albums - id - artist_id - title - sort_title - year - genre - cover_art_id - track_count - duration_seconds - album_artist - release_type ### tracks - id - album_id - artist_id - title - track_number - disc_number - year - genre - duration_seconds - bitrate - sample_rate - file_path - file_size - suffix - content_type - lyrics_embedded - cover_art_id - created_at - updated_at ### playlists - id - user_id - name - comment - public - created_at - updated_at ## Phased Delivery Plan ## Phase 0: Foundation Outcome: - repo initialized - dev tooling set up - backend and frontend boot independently Deliverables: - monorepo or single repo with `apps/web` and Go backend - linting and formatting - basic CI - environment examples - Dockerfile and local compose ## Phase 1: Library Core Outcome: - server can scan a music folder and build a usable index Deliverables: - config for music folder paths - scanner for recursive file discovery - metadata extraction - SQLite schema - initial scan endpoint/job - rescan changed files - artist/album/track records - cover art extraction or sidecar loading ## Phase 2: Playback Core Outcome: - music can be streamed and played in the web app Deliverables: - authenticated stream endpoint - range requests - MIME/content-type handling - artwork endpoint - queue and now-playing state in frontend - album/artist pages - playable track lists ## Phase 3: Subsonic Compatibility MVP Outcome: - major Subsonic clients can connect Deliverables: - auth handshake support - required response envelope - core artist/album/song endpoints - stream endpoint compatibility - cover art compatibility - search support - favorites support - playlists support ## Phase 4: Product UX Outcome: - the web app feels like a polished daily driver Deliverables: - home page - recent albums - random picks - favorites - search experience - playlists - mini player + full player - keyboard shortcuts - responsive layout ## Phase 5: Power Features Outcome: - project becomes meaningfully competitive Deliverables: - on-the-fly transcoding - lyrics - listening history - scrobble - share links - radio/mixes - folder browsing - multi-library - admin dashboard ## Frontend Product Shape ## Core Screens - login - home - artists list - artist detail - album detail - playlist detail - search - queue - settings - full-screen player or expanded player panel ## UX Goals - art-forward layout - fast navigation - minimal friction to start playback - stable queue behavior - good empty/loading/error states - pleasant desktop and mobile behavior ## Visual Direction - dark-first listening UI is acceptable, but keep theme system flexible - strong album art presence - large typography for hero sections - compact dense lists where appropriate - smooth transitions for queue and player states - do not overbuild visual effects before playback and browsing feel solid ## Backend Module Breakdown ## `internal/config` Responsibilities: - env/file config loading - defaults - path validation ## `internal/db` Responsibilities: - DB connection - migrations - query helpers - transactional boundaries ## `internal/scanner` Responsibilities: - directory crawl - changed file detection - deleted file cleanup - scheduling rescans ## `internal/library` Responsibilities: - domain services for artists/albums/tracks - browse/search logic - cover art association ## `internal/media` Responsibilities: - file streaming - range requests - transcoding hooks - content-type detection - artwork serving ## `internal/subsonic` Responsibilities: - request parsing - auth compatibility - response shaping - endpoint mapping from internal domain services ## `internal/auth` Responsibilities: - password hashing - sessions/tokens - permissions ## `internal/httpapi` Responsibilities: - REST endpoints for the web app - JSON response contracts - middleware ## Detailed MVP Checklist ## Project Setup - [x] Choose repository structure and naming - [x] Initialize Go module - [x] Initialize frontend app in `apps/web` - [x] Add root `.editorconfig` - [x] Add root `.gitignore` - [ ] Add backend formatter/linter commands - [ ] Add frontend formatter/linter commands - [x] Add shared `Makefile` or task runner - [x] Add `.env.example` - [x] Add `docker-compose.yml` - [x] Add backend `Dockerfile` - [ ] Add frontend `Dockerfile` if needed - [ ] Add CI workflow for lint/build/test ## Backend Bootstrap - [x] Create `cmd/server/main.go` - [x] Add config loading - [x] Add HTTP server bootstrap - [x] Add graceful shutdown - [ ] Add structured logging - [x] Add health endpoint - [x] Add request logging middleware - [x] Add panic recovery middleware - [x] Add CORS strategy for local dev ## Database - [x] Choose migration tool - [x] Create initial schema migration - [x] Add DB connection setup - [x] Add migration runner at startup - [x] Add indexes for artist/album/track lookup - [ ] Add indexes for search - [x] Add repository helpers or query layer ## Auth and Users - [x] Create users table - [x] Implement password hashing - [x] Implement login endpoint - [x] Implement session or bearer token issuance - [x] Implement auth middleware - [x] Implement current user endpoint - [x] Implement admin bootstrap user creation - [x] Add logout endpoint ## Library Scanning - [x] Add library roots table - [ ] Add config for one or more music paths - [x] Recursively discover supported audio files - [x] Ignore unsupported file types - [x] Read tags from files - [x] Map tags into normalized artist/album/track model - [x] Extract embedded artwork when present - [x] Load folder sidecar artwork when present - [x] Persist scan results transactionally - [x] Track deleted files and remove stale DB rows - [x] Add initial full scan command - [x] Add rescan endpoint or admin action - [ ] Add filesystem watch or scheduled scan - [ ] Record scan job progress - [x] Expose scan status endpoint ## Browse API - [x] List artists - [x] Artist detail with albums - [x] Album detail with tracks - [x] Track detail - [x] Recent albums - [x] Random albums or songs - [x] Favorites listing - [x] Search endpoint - [ ] Pagination support - [ ] Sorting support ## Streaming - [x] Implement authenticated stream endpoint - [x] Support range requests - [x] Support HEAD where appropriate - [x] Return correct content type - [x] Handle missing files gracefully - [x] Add cover art endpoint - [ ] Add basic download endpoint ## Playlists and History - [x] Create playlists table - [x] Create playlist tracks table - [x] Add create playlist endpoint - [x] Add rename playlist endpoint - [x] Add delete playlist endpoint - [ ] Add reorder tracks endpoint - [x] Add add/remove track endpoints - [x] Add listening history table - [x] Record play/scrobble events - [x] Add recently played endpoint ## Favorites - [x] Add favorites table - [x] Star track - [x] Unstar track - [x] Star album if desired - [x] Unstar album if desired - [x] Star artist if desired - [x] Unstar artist if desired ## Subsonic Compatibility - [x] Implement request auth parsing - [x] Support username/password auth where needed - [x] Support token/salt auth - [x] Add common Subsonic response builder - [x] Implement `ping` - [x] Implement `getLicense` - [x] Implement `getArtists` - [x] Implement `getArtist` - [x] Implement `getAlbum` - [x] Implement `getSong` - [x] Implement `stream` - [x] Implement `getCoverArt` - [x] Implement `search3` - [x] Implement `getRandomSongs` - [x] Implement `getStarred2` - [x] Implement `star` - [x] Implement `unstar` - [x] Implement playlist endpoints - [x] Implement `scrobble` - [ ] Test against at least one existing Subsonic client ## Frontend Bootstrap - [x] Create Vite React TypeScript app - [x] Configure routing - [x] Configure Tailwind - [ ] Add Radix UI primitives - [x] Add TanStack Query client - [x] Add Zustand stores - [x] Add API client layer - [x] Add auth persistence strategy - [ ] Add theme tokens and CSS variables ## Frontend App Shell - [x] Login page - [x] App layout with sidebar/topbar/player area - [ ] Responsive navigation - [ ] Toast/notification system - [ ] Error boundary - [x] Query loading/error patterns ## Frontend Music Views - [x] Home page - [x] Artists grid/list page - [x] Artist detail page - [x] Album detail page - [x] Playlist page - [x] Search results page - [x] Favorites page - [ ] Recently played page ## Frontend Player - [x] Global player store - [x] Queue model - [x] Play/pause - [x] Next/previous - [x] Seek bar - [x] Volume control - [x] Repeat modes - [x] Shuffle - [x] Track switching - [x] Keyboard shortcuts - [x] Mini player - [x] Expanded player ## Quality and Testing - [ ] Unit tests for scanner parsing - [ ] Unit tests for auth - [ ] Unit tests for Subsonic envelope formatting - [ ] Backend integration tests for browse endpoints - [ ] Backend integration tests for stream endpoint - [ ] Frontend component tests for player and key pages - [ ] Frontend E2E smoke tests - [ ] Test with a realistic sample library - [x] Test on Windows paths - [ ] Test on Linux paths - [ ] Test large album art and missing metadata edge cases ## Deployment - [x] Build production frontend assets - [x] Serve frontend assets from backend or reverse proxy - [x] Docker image for all-in-one deployment - [x] Persistent DB volume - [x] Persistent cache volume - [x] Music folder mount strategy - [x] Single app port for web UI and Subsonic clients - [x] Reverse proxy example - [x] HTTP/reverse proxy deployment notes - [x] Backup/restore notes ## Nice-to-Have After MVP - [ ] On-the-fly transcoding - [ ] Multi-bitrate streaming profiles - [x] Lyrics support - [ ] Last.fm or ListenBrainz scrobbling - [ ] Shareable public links - [ ] Smart playlists - [ ] Radio or generated mixes - [ ] Folder view - [ ] Multi-library roots with permissions - [ ] Podcast support - [ ] Admin analytics page - [ ] PWA support ## Suggested MVP Cut Line If we want the fastest realistic first release, include only: - auth - scanner - artists/albums/tracks browse - cover art - streaming - search - favorites - playlists - recent albums - Subsonic core endpoints - polished web player Delay until later: - transcoding - lyrics - podcasts - radio - advanced admin tools - recommendations and smart mixes ## Recommended First Build Order 1. Backend bootstrap and DB 2. Scanner and normalized schema 3. Browse endpoints 4. Stream and cover art endpoints 5. Web login and app shell 6. Artist/album pages 7. Queue and player 8. Search and favorites 9. Playlists 10. Subsonic compatibility layer 11. Docker and deployment polish ## Practical Recommendation The best implementation strategy is: - build your own backend - use Navidrome as a behavior reference, not a codebase to fork - use Aonsoku as a frontend inspiration and partial architectural reference - prioritize a strong MVP over broad feature parity If you want, the next step should be scaffolding the actual repository: - Go backend skeleton - React frontend skeleton - initial DB schema - first endpoints - first app shell ```