OpenSubsonic Compatibility (Current Fork Status)
Last updated: 2026-02-14
Scope
This document defines the current /rest compatibility contract implemented in this fork and captures readiness evidence used to close the current OpenSubsonic phase gate.
Milestone Status
- Current OpenSubsonic compatibility milestone:
Complete(for soundspan's in-scope music client surface) - Remaining missing domains are intentionally out-of-scope for this milestone unless real client demand requires promotion.
Client Connection URL
- Split frontend/backend deployments: use the frontend base URL for clients (for example
http://host:3030in deployment,http://host:3031in local dev). Frontend proxies/restto backend. - Backend-direct deployments: clients may target backend directly (
http://host:3006).
Supported Auth Modes
u+p(plain andenc:hex password forms)u+t+stoken mode (t = md5(subsonicPassword + salt))apiKeyquery auth (OpenSubsonic extension), with optionaluusername consistency check- Required protocol params: one auth mode,
v,c(urequired for password/token auth, optional forapiKey) - Response formats: JSON, XML, JSONP (
f+ optionalcallback)
Implemented Endpoint Surface
Tier A foundation and browse/search/media:
ping,getLicense,getOpenSubsonicExtensions,tokenInfo,getMusicFolders,getIndexesgetArtists,getArtist,getArtistInfo2,getAlbum,getAlbumInfo2,getSong,getTopSongs,getSimilarSongs,getSimilarSongs2,getMusicDirectory,getAlbumList,getAlbumList2,getGenres,getSongsByGenre,getRandomSongs,search,search2,search3stream,download,getCoverArt,getLyrics,getLyricsBySongId
Tier B mutation/readiness:
getUser,getAvatargetPlaylists,getPlaylist,createPlaylist,updatePlaylist,deletePlaylistgetPlayQueue,savePlayQueue,getPlayQueueByIndex,savePlayQueueByIndexgetBookmarks,createBookmark,deleteBookmark(compatibility no-op surface)getScanStatus,startScanscrobble,getNowPlayinggetStarred,getStarred2,star,unstar,setRating
Alias support:
- Both bare and
.viewforms are mounted (for example/rest/pingand/rest/ping.view) - Mutation routes accept both
GETandPOSTwhere compatibility clients commonly vary
ID Policy
Protocol-facing IDs are deterministic and typed:
- Artists:
ar-<id> - Albums:
al-<id> - Tracks:
tr-<id> - Playlists:
pl-<id>
Legacy/raw ID fallback remains supported where endpoint type context is sufficient.
Readiness Evidence (2026-02-14 Manual Validation)
Environment:
- Backend service:
backend(/healthOK onhttp://127.0.0.1:3006) - Database user under test:
baseline-artist-skewed - Validation client identifier:
c=manual-check
Key outcomes:
- Auth handshake
- Password mode (
u/p) succeeded for/rest/ping.view. - Token mode (
u/t/s) succeeded for/rest/ping.view.
- Stream seek/range behavior
Range: bytes=0-9onstream.viewreturned206 Partial ContentwithContent-Range: bytes 0-9/33.- Subsequent seek
Range: bytes=10-20returned206 Partial ContentwithContent-Range: bytes 10-20/33. - Full fetch without range returned
200 OKand full payload length.
- Cover-art retrieval
getCoverArt.viewreturned200 OK,Content-Type: image/jpeg, and cache headers.sizeparameter path executed successfully for the same entity.
- Tier B mutation/readiness flows
createPlaylistcreated a playlist with two tracks.updatePlaylistrenamed playlist, removed one index, and re-added a track (result remained valid with expected song count).getPlaylistafter delete returned protocol failure witherror.code=70(not found) as expected.starthengetStarred2showed track present;unstarremoved it.scrobblerespected mixedsubmissionflags; DB delta confirmed only submitted entry persisted.getNowPlayingreturned active entry after writing recent playback state.
DB side-effect checkpoints:
Playrow delta for validated submitted track:+1LikedTrackfinal count for tested track after unstar:0
Third-Party Client Profile Matrix (2026-02-14)
Environment:
- Updated code path validated on local backend runtime (
http://127.0.0.1:3007) - Test user:
opensubsonic-matrix - Client profile IDs (
c):symfonium,dsub,ultrasonic,amperfy,substreamer
Matrix outcomes:
symfoniumprofile
ping.viewreturnedstatus=ok.search3.viewwithquery=\"\",artistCount=0,albumCount=0,songCount=0returnedstatus=okwith bounded full-sync payload (artist=80,album=80,song=1200).
dsubprofile
search2.viewwithquery=\"\"+ zero-count full-sync pattern returnedstatus=okwith populated payload (artist=80,album=80,song=1200).search2.viewwith unsupportedmusicFolderId=99returnedstatus=okwith empty payload arrays.
ultrasonicprofile
getIndexes.viewreturnedstatus=okwithindexgroups and a stablelastModifiedvalue.getIndexes.viewwithifModifiedSince=<lastModified>returnedstatus=okandindex=[](no-change semantics).getIndexes.viewwith unsupportedmusicFolderId=99returnedstatus=okandindex=[].startScan.viewandgetScanStatus.viewboth returnedstatus=ok; scan-state contract was observable (scanning=truesnapshot during active run).
amperfyprofile
getArtists.viewwith unsupportedmusicFolderId=99returnedstatus=okandindex=[].
substreamerprofile
savePlayQueueByIndex.viewpersisted indexed queue state (index=2,position=1234) for a real track.getPlayQueueByIndex.viewreturned matching indexed state (entries=1, expected track id, expectedcurrentandposition).
Automation support:
cd backend && npm run test:smokeexecutes baseline health checks and Subsonic profile checks whenSMOKE_SUBSONIC_USER+SMOKE_SUBSONIC_PASSWORDare provided.SMOKE_MODE=fullenables extended non-GUI client emulation (browse/search/list, profile/avatar, playlist lifecycle, queue baseline/indexed read, starred baseline, now-playing baseline, scan contract checks).SMOKE_REQUIRE_TRACKS=trueenforces track-dependent coverage (song/album/artist metadata calls, lyrics-by-song, rating/star/unstar, scrobble, queue mutations with track IDs, stream/cover-art probes) and fails if no library songs are available.SMOKE_ENABLE_FIXTURES=trueprovisions a temporary DB-backed artist/album/track fixture when full-mode strict checks require tracks but the library has none; cleanup is automatic by default (SMOKE_FIXTURE_CLEANUP=falsedisables cleanup for debugging).SUBSONIC_TRACE_LOGS=trueenables backend/resttrace lines for real-client troubleshooting (endpoint,c,v,f, HTTP status, Subsonic protocol status/error code) without logging auth secrets.
Automated run evidence (2026-02-14):
- Command:
cd backend && SOUNDSPAN_BASE_URL=http://127.0.0.1:3007 SMOKE_SUBSONIC_USER=opensubsonic-matrix SMOKE_SUBSONIC_PASSWORD=*** SMOKE_MODE=full SMOKE_REQUIRE_TRACKS=true SMOKE_ENABLE_FIXTURES=true DATABASE_URL=postgresql://... npm run test:smoke - Result:
health+matrix+ strict full emulation all passed; a temporary fixture track was created and cleaned automatically to execute track-dependent checks on an empty-library dataset.
Known Gaps / Non-Goals for Current Milestone
- This is not a full OpenSubsonic superset; unimplemented endpoints remain out of scope for the current phase.
- Validation now includes a third-party client-profile matrix (curl-driven request emulation of real client patterns), but full GUI-client certification runs are still pending.
Known-Gap Backlog (Carry Forward)
High-value gaps to revisit first if future compatibility demand appears:
- Streaming-profile parity gaps:
hls- OpenSubsonic transcoding extensions (
getTranscodeDecision,getTranscodeStream)
- Sharing feature gaps:
getShares,createShare,updateShare,deleteShare
- Certification gap:
- Full GUI-client validation passes (current matrix is non-GUI request-profile validation)
Current non-goal domains unless product scope changes:
- Video endpoints (
getVideos,getVideoInfo) - Podcast endpoints (Subsonic-format podcast domain)
- Chat domain
- Jukebox / internet-radio management domain
- Subsonic user-administration endpoints (
getUsers,createUser,updateUser,deleteUser,changePassword)
Revisit Triggers
Promote a deferred gap to in-scope when at least one of these is true:
- A target real client fails a core workflow due to a specific missing endpoint/domain.
- soundspan product scope expands into the corresponding domain (for example podcasts/video/share).
- A deployment/operator requirement explicitly depends on a missing Subsonic/OpenSubsonic contract surface.
Implementation Gaps Inside Already-Implemented Endpoints
star/unstarsupportalbumIdandartistIdvia track-like projection (all matching library tracks are starred/unstarred), not separate album/artist favorite tables.getStarred/getStarred2artistandalbumarrays are derived from liked-track projection state.getNowPlayingcurrently reports only the authenticated user's active playback state, not global multi-user now-playing.getPlayQueue/savePlayQueuecurrently use the legacy playback-state device bucket (deviceId=legacy) for compatibility clients.getPlayQueueByIndex/savePlayQueueByIndexmap index0todeviceId=legacyand indexNtodeviceId=legacy-N.getBookmarkscurrently returns an empty bookmark list andcreateBookmark/deleteBookmarkare protocol-success no-ops (bookmark persistence is not modeled yet).startScanis compatibility-throttled with a cooldown window; repeated requests during cooldown return current scan status and do not enqueue new scan jobs.getIndexesnow honorsmusicFolderIdfiltering andifModifiedSinceno-change semantics.getArtistsnow honorsmusicFolderIdfiltering.search/search2/search3now honormusicFolderIdfiltering, normalize quoted-empty full-sync queries (query=\"\"), and treat zero counts as bounded full-sync requests.search/search2/search3now pass through large offset values without a hard10000clamp so client pagination can complete on very large libraries.- Song/album protocol payloads now project
genrefrom library metadata (preferuserGenres, thengenres), and genre-filtered song responses (getSongsByGenre,getRandomSongswithgenre) force explicitgenrevalues in each returned song item. getTopSongsnow deterministically falls back to case-insensitive artist-name lookup when ID-path lookup misses, including artist names containing hyphens.getSimilarSongsuses artist-to-similar-artist graph data;getSimilarSongs2merges similar-artist tracks with genre and same-artist fallback sources to avoid empty responses when similarity metadata is sparse.getLyricscurrently resolves by best-match library track (artist/title query), then returns plain lyrics or synced lyrics flattened to plain text lines.- Auth middleware now supports
u/p,u/t/s, andapiKey; bearer-token style OpenSubsonic auth variants remain unsupported. getAlbumInfo2notescurrently use mapped library metadata (album title fallback) because soundspan does not maintain dedicated album notes fields.- Some optional query keys outside the validated client matrix may still be ignored.
Missing Subsonic/OpenSubsonic API Surface (Exhaustive)
Spec coverage snapshot (current):
- Implemented endpoints:
52 - Catalog endpoints tracked for compatibility:
84 - Missing endpoints:
32
System (missing)
- None
Browsing (missing)
getVideosgetVideoInfogetArtistInfogetAlbumInfo
Lists (missing)
- None
Searching (missing)
- None
Media Retrieval (missing)
hlsgetCaptions
Media Annotation (missing)
- None
Sharing (missing)
getSharescreateShareupdateSharedeleteShare
Podcast (missing)
getPodcastsgetNewestPodcastsrefreshPodcastscreatePodcastChanneldeletePodcastChanneldeletePodcastEpisodedownloadPodcastEpisodegetPodcastEpisode
Jukebox (missing)
jukeboxControl
Internet Radio (missing)
getInternetRadioStationscreateInternetRadioStationupdateInternetRadioStationdeleteInternetRadioStation
Chat (missing)
getChatMessagesaddChatMessage
User Management (missing)
getUserscreateUserupdateUserdeleteUserchangePassword
Bookmarks and Play Queue (missing)
- None
Library Scan (missing)
- None
OpenSubsonic Transcoding Extensions (missing)
getTranscodeDecisiongetTranscodeStream
Which Missing Endpoints Are Required For This Project?
Decision basis:
- We are currently staying on Subsonic/OpenSubsonic feature implementation.
- Project objective is broad third-party client compatibility for soundspan's music-server use case.
- This does not require implementing every historical Subsonic domain (chat, jukebox, podcasts, videos, admin user provisioning) to deliver project value.
Required (P0) to move toward practical client completeness
None in this category remain from the current P0 list.
Recommended (P1) for broader ecosystem compatibility
- None
Not required for soundspan's current Subsonic-track scope (non-goal unless strategy changes)
- Video domain:
getVideos,getVideoInfo - Podcast domain endpoints
- Jukebox and internet-radio management endpoints
- Chat endpoints
- Subsonic user-admin endpoints (
createUser,deleteUser, etc.) where soundspan native auth/admin UI already owns this - Share-link endpoints (optional; product decision dependent)
Reference Catalogs Used For Gap Audit
- OpenSubsonic endpoint catalog: https://opensubsonic.netlify.app/docs/endpoints/
- Subsonic API endpoint catalog: https://www.subsonic.org/pages/api.jsp