Add the initial project blueprint, Go backend skeleton, frontend app shell, database schema draft, and local development/deployment files.
15 KiB
15 KiB
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:
viperor a smaller env-based config layer - Database: SQLite for MVP
- Migrations:
gooseoratlas - Tag reading: a mature tag library for MP3/FLAC/M4A/OGG metadata
- Background jobs: in-process worker loops
- File watching:
fsnotifyor polling fallback - Auth: session + token support
- Logging:
slogorlogrus
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-composeor separate dev servers
High-Level Architecture
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
/
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/meGET /api/homeGET /api/artistsGET /api/artists/:idGET /api/albums/:idGET /api/tracks/:idGET /api/playlistsGET /api/playlists/:idPOST /api/queue/scrobbleGET /api/search?q=...GET /api/browse/recentGET /api/browse/randomPOST /api/favorites/:idDELETE /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/weband 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
- Choose repository structure and naming
- Initialize Go module
- Initialize frontend app in
apps/web - Add root
.editorconfig - Add root
.gitignore - Add backend formatter/linter commands
- Add frontend formatter/linter commands
- Add shared
Makefileor task runner - Add
.env.example - Add
docker-compose.yml - Add backend
Dockerfile - Add frontend
Dockerfileif needed - Add CI workflow for lint/build/test
Backend Bootstrap
- Create
cmd/server/main.go - Add config loading
- Add HTTP server bootstrap
- Add graceful shutdown
- Add structured logging
- Add health endpoint
- Add request logging middleware
- Add panic recovery middleware
- Add CORS strategy for local dev
Database
- Choose migration tool
- Create initial schema migration
- Add DB connection setup
- Add migration runner at startup
- Add indexes for artist/album/track lookup
- Add indexes for search
- Add repository helpers or query layer
Auth and Users
- Create users table
- Implement password hashing
- Implement login endpoint
- Implement session or bearer token issuance
- Implement auth middleware
- Implement current user endpoint
- Implement admin bootstrap user creation
- Add logout endpoint
Library Scanning
- Add library roots table
- Add config for one or more music paths
- Recursively discover supported audio files
- Ignore unsupported file types
- Read tags from files
- Map tags into normalized artist/album/track model
- Extract embedded artwork when present
- Load folder sidecar artwork when present
- Persist scan results transactionally
- Track deleted files and remove stale DB rows
- Add initial full scan command
- Add rescan endpoint or admin action
- Add filesystem watch or scheduled scan
- Record scan job progress
- Expose scan status endpoint
Browse API
- List artists
- Artist detail with albums
- Album detail with tracks
- Track detail
- Recent albums
- Random albums or songs
- Favorites listing
- Search endpoint
- Pagination support
- Sorting support
Streaming
- Implement authenticated stream endpoint
- Support range requests
- Support HEAD where appropriate
- Return correct content type
- Handle missing files gracefully
- Add cover art endpoint
- Add basic download endpoint
Playlists and History
- Create playlists table
- Create playlist tracks table
- Add create playlist endpoint
- Add rename playlist endpoint
- Add delete playlist endpoint
- Add reorder tracks endpoint
- Add add/remove track endpoints
- Add listening history table
- Record play/scrobble events
- Add recently played endpoint
Favorites
- Add favorites table
- Star track
- Unstar track
- Star album if desired
- Unstar album if desired
- Star artist if desired
- Unstar artist if desired
Subsonic Compatibility
- Implement request auth parsing
- Support username/password auth where needed
- Support token/salt auth
- Add common Subsonic response builder
- Implement
ping - Implement
getLicense - Implement
getArtists - Implement
getArtist - Implement
getAlbum - Implement
getSong - Implement
stream - Implement
getCoverArt - Implement
search3 - Implement
getRandomSongs - Implement
getStarred2 - Implement
star - Implement
unstar - Implement playlist endpoints
- Implement
scrobble - Test against at least one existing Subsonic client
Frontend Bootstrap
- Create Vite React TypeScript app
- Configure routing
- Configure Tailwind
- Add Radix UI primitives
- Add TanStack Query client
- Add Zustand stores
- Add API client layer
- Add auth persistence strategy
- Add theme tokens and CSS variables
Frontend App Shell
- Login page
- App layout with sidebar/topbar/player area
- Responsive navigation
- Toast/notification system
- Error boundary
- Query loading/error patterns
Frontend Music Views
- Home page
- Artists grid/list page
- Artist detail page
- Album detail page
- Playlist page
- Search results page
- Favorites page
- Recently played page
Frontend Player
- Global player store
- Queue model
- Play/pause
- Next/previous
- Seek bar
- Volume control
- Repeat modes
- Shuffle
- Track switching
- Keyboard shortcuts
- Mini player
- 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
- Test on Windows paths
- Test on Linux paths
- Test large album art and missing metadata edge cases
Deployment
- Build production frontend assets
- Serve frontend assets from backend or reverse proxy
- Docker image for all-in-one deployment
- Persistent DB volume
- Persistent cache volume
- Music folder mount strategy
- Reverse proxy example
- HTTPS deployment notes
- Backup/restore notes
Nice-to-Have After MVP
- On-the-fly transcoding
- Multi-bitrate streaming profiles
- 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
- Backend bootstrap and DB
- Scanner and normalized schema
- Browse endpoints
- Stream and cover art endpoints
- Web login and app shell
- Artist/album pages
- Queue and player
- Search and favorites
- Playlists
- Subsonic compatibility layer
- 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