Aède exposes a compatibility adapter at /rest/<method> and /rest/<method>.view, using the existing catalog, accounts and personal store. It follows the Subsonic API and the OpenSubsonic API-key extension. The supported browsing, original audio, sidecar artwork and private personal operations are listed below. Local authentication, artist/album browsing, original FLAC playback, ratings and artist/album favourites have been confirmed with Submariner 3.4 on macOS. Supersonic's request shape has been checked in its official source; its real-client playback and broader client interoperability remain to be validated.
In the route reference, /rest/{method} means replacing {method} with a supported method name, optionally followed by .view.
Create a client key#
Create an application key before configuring a Subsonic/OpenSubsonic client. The Aède account password cannot connect to this adapter. Initialize accounts through accounts, then create a separate key for each client using the server's data folder:
aede accounts keys alice create "Phone"
Replace alice with your existing account name. Commands in this guide use aede when it is on your PATH. From the repository root, use ./target/release/aede; from the directory containing an extracted aede folder, use ./aede/aede (or ./aede once inside that folder). If you use a custom data folder, add the same --data "/path/to/data" to account commands and serve.
Copy only the complete value after API key (shown once):: id.secret, 129 characters including the dot (64 hexadecimal characters, a dot, then 64 hexadecimal characters). The ID shown in the table cannot authenticate on its own. The secret is revealed only once, after successful saving; key listing cannot recover it. Keep the complete key privately, or create a replacement if it is lost. The Submariner steps below show exactly where to paste it.
Creation and revocation take effect without restarting the server. Keys do not expire automatically, survive server restart and are independently revocable. See key listing and revocation. Listing exposes only id,label,created_at; --json creation adds token to those metadata fields, while list/revoke produce an array of metadata. A password/name/role/status change, accounts revoke or restoration of credentials invalidates affected keys; restoring accounts rotates the epoch and removes all restored keys. A legacy backup without accounts preserves current keys. Create replacement keys after credential recovery. Each account has at most eight keys and the installation at most 512; labels need non-whitespace text within 128 UTF-8 bytes.
The private account store contains only salted Argon2 verifiers for key secrets. A client may supply apiKey=<id.secret>, or use the legacy fields u=<account-name>&p=<complete-id.secret> when it lacks an API-key field. The password field carries the revocable client key, not the account password. Hex encoding the complete key as p=enc:<hex> is also accepted; it does not encrypt the request. The username must match the key's current account. Account-password login remains unavailable (code 42), as does salted-MD5 u+t+s (41). Combining apiKey with any old credential parameter returns 43; an invalid/revoked key or mismatched account returns 44. Native bearer sessions remain exclusive to /api; they cannot authenticate this adapter, and these keys cannot authenticate native routes.
Use the server's HTTPS URL for remote clients, as explained in remote access. Loopback HTTP remains available for local clients. Host/Origin checks, connection admission and remote work limits still apply. The protocol supplies apiKey in its query or form parameters, so keep complete URLs and form bodies out of logs/shared screenshots. The server does not log these requests or secrets. A separate key per client makes revocation practical.
Requests and responses#
Both GET and form POST are supported; POST requires application/x-www-form-urlencoded. HEAD is refused with HTTP 405, including on methods that change personal data. For authenticated calls supply apiKey (or the legacy key transport above), v=1.16.1 and a nonempty client name c. f=json selects JSON; the default is UTF-8 XML with the Subsonic namespace. Replies use subsonic-response, protocol version 1.16.1, server type Aede, the actual serverVersion, and openSubsonic=true. Protocol failures use status=failed and error.code/message, generally with HTTP 200. Media success uses its own binary content type and HTTP 200/206; an unsatisfiable byte range gives HTTP 416. Parser failures before a format can be selected use XML. JSONP is refused. Every response is no-store.
Parameters require valid UTF-8 and percent encoding. Unknown options and duplicate scalar fields, including duplicates across query/body, are refused. Repetition is accepted only for the ID/time lists specified below and preserves order. Query plus form body is limited to 16 KiB; each decoded value to 2048 bytes; at most 32 distinct fields and 256 total field occurrences; body completion to one second. Large playlists therefore need bounded append batches. Browsing and personal work run outside HTTP runtime workers using the two shared catalog slots. Cold key verification uses two bounded password workers and the existing authentication rate limits; a bounded 128-entry fingerprint cache avoids repeated Argon2 work for valid keys. Every call rechecks the current account/key store; media rechecks during transfer. Personal writes additionally recheck permissions under the shared writer lock before an atomic save.
getOpenSubsonicExtensions is public, including when accounts are unavailable, and advertises only apiKeyAuthentication and formPost version 1. It accepts the common protocol parameters but does not require credentials. tokenInfo returns the authenticated key's username.
Client protocol versions must use major version 1 and a minor version no newer than 16. The third component must be numeric but does not affect compatibility, following the Subsonic version rules.
Supported methods#
| Method | Extra parameters and behavior |
|---|---|
ping, getLicense, tokenInfo | None. License is valid; no paid license is required. |
getUser | Required username, restricted to yourself. Users/admins can stream, download, scrobble and manage their own playlists; auditors have metadata/artwork read access only. Installation administration remains unavailable. |
getMusicFolders | None. One logical library, ID 1, named Aède; this does not expose filesystem roots. |
getArtists | Optional musicFolderId=1; sorted artist indexes limited to artists associated with a navigable album. Submariner uses the album-artist view described below. |
getArtist, getAlbum, getSong | Required opaque id from an earlier response. |
getAlbumList2 | Required type, one of alphabeticalByName, alphabeticalByArtist, byYear, byGenre, random; optional size (default 10, maximum 500), offset, musicFolderId=1. byYear requires fromYear,toYear; reversed bounds give descending years. byGenre requires genre. Those filters are refused for other types. Supersonic's limit is a bounded alias for size; supplying both is an error. Random pages are reshuffled per request. Other types are refused. |
search3 | Required query, which may be empty for library synchronization. Optional artistCount,artistOffset,albumCount,albumOffset,songCount,songOffset and musicFolderId=1. Each count defaults to 20, maximum 1000; offsets default to 0. Counts of zero return an empty category. Search text is at most 1024 UTF-8 bytes. |
getGenres | None; genre names with distinct song/album counts and inherited album genres for otherwise untagged tracks. |
getSongsByGenre | Required genre; optional count (default 10, maximum 500), offset, musicFolderId=1. Sorted, independently paged songs; an unknown genre returns an empty list. |
getRandomSongs | Optional size (default 10, maximum 500), genre, fromYear, toYear, musicFolderId=1. Distinct songs matching all supplied filters; chronological lower bound must not exceed upper bound. |
getScanStatus | None. scanning reflects synchronous, HTTP-job and delegated-CLI scan workers, through snapshot publication. count is the track count of the last published catalog, not a progress estimate. It does not start a scan. |
getCoverArt | Required dedicated cover id from an album/song response; optional size=1..2048. Catalogued JPEG/PNG sidecars only. Without size, return the verified original bytes. A requested thumbnail is response-only; see artwork below. |
stream, download | Required song id; exact original binary file. Optional format=raw, maxBitRate=0, timeOffset=0, converted=false, estimateContentLength set to true or false. Other conversion/video settings are refused. |
getStarred2 | Optional musicFolderId=1; your favourite artists/albums/songs only. |
star, unstar | At least one repeatable id, albumId or artistId, with the corresponding opaque IDs; update your existing annotations. |
setRating | Required id,rating; artist, album or song rating from 0 to 5, with 0 removing your rating. |
getPlaylists | Optional username, restricted to yourself; private static playlists only. |
getPlaylist | Required playlist id; entries keep order and repetitions. A missing current track fails explicitly and its saved reference remains intact. |
createPlaylist | Repeatable songId; required name for creation, or playlistId to replace an existing private playlist's entries. An empty list is allowed. |
updatePlaylist | Required playlistId; optional name,comment,public=false, repeatable songIdToAdd,songIndexToRemove. Removal indexes address the original list; duplicate/out-of-range indexes are refused. |
deletePlaylist | Required playlist id; deletes only your playlist. |
scrobble | Repeatable song id, matching optional Unix-millisecond time values, and submission=true by default. Stores client declarations and increments your private counts. submission=false accepts exactly one song and optional time as a temporary now-playing report. |
getNowPlaying | None; current, unexpired reports from your own clients only, checked against still-valid keys. |
Albums are local release editions and songs are track placements, including tracks without albums. By default, an artist's albums include editions naming them as an album artist or as a main artist on one of that edition's tracks or recordings. A main-track credit links only its own edition; composer, performer or featured-only credits do not create an album association. getArtists and the artist results of search3 include only artists with such an album association. This view reflects your local catalog, not a complete artist discography.
Album-artist view for Submariner. For the exact client name c=submariner (ASCII case-insensitive), getArtists and the artist results of search3 list only artists named by at least one album's canonical artistId. getArtist returns only that artist's canonical albums, and albumCount follows the same rule. Explicitly starred artists remain visible in favourites through getStarred2, including track-only artists; their albumCount follows this canonical rule and may be zero. This matches Submariner 3.4's album parser, which assigns an album to its reported artist. It is a distinction between album and track credits, not between bands and individual people. A compilation remains under its album artist, often Various Artists; tracks by its individual artists remain accessible through song search and the compilation. Other clients keep the broader artist view above. The native graph and the album's canonical artist are unchanged.
After updating and restarting Aède, select its server in Submariner and choose View → Reload Server (⌘R), or right-click the server and choose Reload Server, to request fresh indexes. Submariner 3.4 can also create artist entries in its own cache from song searches, playlists, folder navigation or now-playing information. Those names can therefore reappear without canonical albums, despite Aède's filtered artist index. Ordinary album browsing does not create those performer entries. This is client cache behavior; Aède retains the true track-artist references rather than relabeling compilations.
An artist whose only tracks have no album remains available through song search and the native API; Aède does not fabricate an album for artist navigation. IDs hash stable Aède references with SHA-256; they survive dense catalog renumbering when the underlying references stay unchanged. Paths, cover paths, raw tags and source prose are omitted. Required album created is a documented UTC proxy for the catalog's scan timestamp, since Aède has no album-added timestamp; it is not used to promise a newest-album sort. Browse responses include only your own favourite/rating fields and song play counts, when available.
Audio is passed through byte-for-byte, without DSP, normalization, resampling or tag changes. At most four native/audio/artwork transfers run together; reads use bounded chunks and queues and stop on revocation, shutdown or failed source checks. Audio sources must remain regular catalogued files with the precise size/timestamp captured by a current scan; a changed/legacy-imprecise source requires a rescan. A single Range: bytes=… selects original audio bytes. Without a recognized strong validator, If-Range causes a full response, protecting resumptions from combining different files. Auditors may browse metadata and artwork but cannot stream or download audio.
Original transfers retain shutdown from admission through catalogue preparation, source opening and streaming. An intervening shutdown refuses the pending response rather than starting a new transfer after a catalogue wait. Native bearer-session exports share this same transport guard.
When confirmation waits for account-policy work, the client key is checked again afterwards, with an unchanged publication marker through that final key read. Revoking a key or publishing a changed policy during those waits refuses the pending result rather than prolonging its access. Credential and policy reads retain their bounded worker admission after a cancelled wait.
This transport does not decode FLAC and therefore does not check its decoded-audio MD5. That check belongs to the selected client's decoder, if supported. A successful byte transfer or client scrobble does not establish decoded-content verification. Aède's native PCM playback separately verifies a stored FLAC digest when its complete decode reaches EOF.
Artwork must be an ordinary JPEG/PNG sidecar confined to the release's native folder; source links, escaping paths, corrupt files and inputs beyond the existing image budgets are refused. Validation reads every pixel, under the existing 32 MiB input, 8192-pixel-axis, 16-million-pixel and decoded-memory limits. size bounds the longest axis while keeping aspect ratio and never enlarging an image. If reduction is needed, the response contains an in-memory PNG thumbnail; otherwise the original bytes are returned. Source artwork, downloaded 4K images and audio files are never rewritten. Embedded covers and other artwork families are not advertised by this adapter.
Personal data and listening declarations#
The adapter maps setRating directly to the authenticated owner’s existing annotation: 1–5 stores that rating and 0 removes it. star/unstar separately set or clear the favourite flag. These requests update the existing Aède annotation model without launching CLI commands. Subsonic and the native API read the authenticated account’s personal data; ordinary CLI commands read the local owner, retained by the first administrator. The storage is shared, while other accounts’ annotations remain isolated.
Favourites and ratings use the existing annotations. The starred date is their last annotation-change time, an explicit proxy because there is no separate favourite timestamp; changing a rating can change that displayed date. Static playlists are separate from query collections. They use stable track references in user.json, preserve repeated entries and keep missing references until an explicit edit. Names contain non-whitespace text within 256 UTF-8 bytes; comments are at most 2048 bytes. The store permits 128 playlists per owner, 512 in total, and 2000 ordered entries per playlist. Sharing and selecting another owner are refused, including for administrators. Invalid arguments or unavailable stores never cause a partial save.
Streaming alone creates no listen. A submitted scrobble records a client declaration with its timestamp; it has no fabricated played duration, completion, decoded-audio or acknowledgement evidence. Its recent log retains the newest 500 declarations per owner, including repetitions; another account cannot evict them. All-time counts remain private to that owner, outlive the log and also include native acknowledged listens. The personal-store budgets refuse global capacity growth without deleting other owners’ records. Timestamps default to receipt time and cannot be more than one day in the future. Submission is not idempotent: replaying a successful request can increment the count again. A now-playing report does not create history or increment counts. One report per key is retained in process memory, with 128 total reports and expiry based on the known track duration (30 seconds to 24 hours; five minutes if unknown). Revocation/account changes hide a report immediately; restart clears all reports.
First macOS test with Supersonic#
Install a macOS build from the official Supersonic releases, following its macOS installation instructions; the project documents an extra launch step for its non-notarized application. Scan your music, initialize accounts if needed, create a separate client key, and restart the rebuilt server using the same data folder:
aede accounts keys alice create "Supersonic Mac"
aede serve --port 8787
Replace alice with your existing account name. From this repository, the verification build produces ./target/release/aede: use that path instead of aede to test the current executable. If your catalog uses a custom data folder, append the same --data "/path/to/data" to both commands. Aède itself provides the compatible server; no separate Subsonic server installation is required.
In Supersonic's server dialog choose Subsonic, use http://127.0.0.1:8787 on this Mac (without /rest), enter your Aède account name under Username, and paste the entire id.secret key under Password. Enable Use legacy authentication. This exact client setting sends the key in p instead of hashing it into an unsupported MD5 token. Keep transcoding disabled so the client requests original audio.
On the Albums page select Title (A-Z) in the sort menu. Supersonic's default Recently Added uses newest, which Aède explicitly refuses until a real addition timestamp exists; the supported Title (A-Z) mapping uses alphabeticalByName. Year, genre and random browsing are also supported. Start with an album and a format that the client can decode, then verify artwork, seeking, favourites, playlists and declared play counts. A phone or another computer needs the configured HTTPS address, not this Mac's loopback URL.
Local macOS setup with Submariner#
For Submariner 3.4, use the same client-key transport. Its authentication implementation sends u+t+s when Use Token-Based Authentication is checked, and u+p=enc:<hex> when unchecked. Only the latter can carry an Aède application key.
The key-creation command is required before entering the client settings. Follow these steps in order from the directory containing the aede executable. From the repository root, replace ./aede with ./target/release/aede; if Aède is on your PATH, use aede instead.
Check that accounts are initialized and choose an enabled administrator or user account. If needed, first follow account setup.
./aede accounts list
Create the Submariner key, replacing the example name
alicewith the account you chose:./aede accounts keys alice create "Submariner Mac"
- Copy the complete 129-character
id.secretvalue afterAPI key (shown once):. Keep the dot and both halves; copy neither the table's ID alone nor theAPI key (shown once):label. It is shown only once. Do not use the account password.
Before the first client playback, refresh the existing catalog with a normal scan:
./aede scanThis reuses the watched music folders and refreshes older file identities that lack precise modification timestamps. No
--fullor--replaceis needed. Music files and tags remain intact; watched folders that are temporarily offline retain their catalog entries. Their audio becomes available only when those folders are accessible again. See scan.
Start the server on this Mac if it is not already running:
./aede serve --port 8787Use the same
--datadirectory for all four commands if customized. An already-running server sees the new key and published scan without a restart. If it uses another port, use the address it prints instead of the example port8787.
Add the server in Submariner with these settings:
Field Value Server Name Aède, or another label you chooseURL http://127.0.0.1:8787Username The account used in step 2, for example alicePassword The complete 129-character id.secretkey from step 3Use Token-Based Authentication Unchecked
The URL has no /api/v1/status or /rest suffix. Leave Use Token-Based Authentication unchecked: Submariner encodes the key itself; do not add enc: or hex-encode it manually. Aède already supplies the server; no separate Subsonic installation is required.
If Aède returns enc: must encode a complete API key, the password field did not contain a complete key: an account password, the ID alone or a truncated paste cannot work. Create a key with step 2 if you have not done so, then replace the client's password with the entire value from step 3 and check that token-based authentication is disabled. A lost secret must be replaced; accounts keys alice lists IDs, not usable complete keys.
If Play stays at 00:00 and Aède reports Media source is unavailable or changed; refresh the catalog, check that the music folder is accessible and repeat the normal ./aede scan from step 4 with the server's data folder. An old catalog without precise file timestamps needs this refresh before streaming; changed or unavailable sources remain refused until their current identity can be checked. This keeps source validation in place and does not rewrite the audio.
Local authentication, artist/album browsing, original FLAC playback, ratings and artist/album favourites with Submariner 3.4 on macOS have been confirmed in a real-client check. Other audio formats, optional client panels, remote access and target NAS capacity still need validation; folder navigation and some album sorts may call unsupported methods. This check does not establish complete Submariner interoperability.
Remaining compatibility work#
Unimplemented surfaces include newest/highest/frequent/recent/starred album-list types, legacy folder browsing and getStarred, queue persistence, rich artist/album-info endpoints, OpenSubsonic playback reports, embedded covers, transcoding, podcasts, radio, video and remote administration. Unsupported calls/options fail explicitly. Supersonic's relevant initial/browse/playback paths were checked against its official source; optional panels that need these missing endpoints can still fail. Full real-client navigation/playback, target NAS capacity and compatibility with individual clients remain to be tested. Original audio must be supported by the selected client. Android Symfonium supports API keys according to its official release note; its provider guide describes original-file and fast-sync settings, but it has not yet been tested against Aède.