Architecture
This page describes how the Tune server is built: its components, its startup, its modules and its extensions. For a shorter overview, see the Technical Guide.
Overview
- A single program:
tune-server, written in Rust. It runs on the Tokio async runtime and the Axum HTTP framework. - A single port (8888 by default). It serves the web interface, the
/api/v1API, the event WebSocket and the audio streams sent to devices (/stream/…). - Web interface: a Svelte application served by the server. It talks to the server through the API and the WebSocket.
- Database: SQLite, built into the program, or PostgreSQL as an option. See Database Architecture.
- Audio: decoding, resampling, processing and delivery to devices all happen inside the server, without ffmpeg. See Audio Pipeline.
The repository and its crates
The server is a Cargo workspace, a set of Rust libraries ("crates") built together. The main ones:
| Crate | Role |
|---|---|
tune-core | The core: music library, database, playback, zones, audio outputs, device discovery, streaming services |
tune-server | The HTTP server (API routes, WebSocket, startup). Its entry point holds no logic and calls tune_server::run, so another program can reuse the same startup with its own extensions. |
tune-http-types | HTTP types shared by the server and its route crates |
tune-stream-http | The routes that serve audio streams to devices |
tune-streaming-http | The streaming service routes |
tune-smart-http | Smart playlists, smart collections and rule-based recommendations |
tune-output-api | The stable contract between Tune and its audio outputs: the OutputTarget trait, for writing an output outside the repository |
tune-plugin-runtime-wasm | The sandboxed runtime for WebAssembly extensions |
tune-cli | The tune command-line tool |
tune-ffi | A C interface for embedding the server in an application (start, stop, read status) |
plugins/… | The extensions shipped with Tune (see below) |
sdk/ | The audio extension development kit (a separate workspace) |
A desktop application, tune-desktop, lives in the same repository but outside the server workspace. It is a thin client of the same API.
Startup
When it starts, the server goes through these steps in order:
- Reads the configuration (
tune.toml, environment variables) and sets up logging. - Reserves the HTTP port. If another instance already holds it, the server stops before touching the database. When started from the Windows shortcut, it first opens the browser on the running instance, if that instance is the same version.
- While the database opens and migrations run, a holding response is served on the port.
- Opens the database and applies migrations.
- Initialises its state (zones, settings, service tokens), then starts the automatic scan and the music folder watcher.
- Remounts network shares, then registers local audio outputs.
- Loads extensions.
- Starts network discovery: SSDP (DLNA/UPnP, OpenHome) and mDNS (AirPlay, Chromecast).
- Starts background tasks, builds the routes, then opens the HTTP service.
Core modules
tune-core has more than eighty modules. The main ones:
| Module | Contents |
|---|---|
audio | Decoders, resampling, DSD (DoP and PCM conversion), processing (ReplayGain, equaliser, convolution, crossfeed, dither) |
scanner | Folder walking, change watching, CUE sheets |
library | Library: full-text search, artwork, import, collections |
metadata | Metadata: MusicBrainz, Last.fm, fingerprints, tag writing |
playback, zones | Playback, queue, track chaining, zones and groups |
outputs | Outputs: DLNA/UPnP, OpenHome, Chromecast, AirPlay, Squeezebox, BluOS, HQPlayer, OAAT, local outputs |
discovery | Device discovery (SSDP, mDNS) |
slimproto | SlimProto server for Squeezelite and Squeezebox players, without LMS |
streaming | Streaming services, radio, podcasts |
upnp_server, upnp_renderer | Tune seen from the network as a UPnP media server and, zone by zone, as a UPnP renderer |
db | Database access: SQLite, PostgreSQL, migrations |
Events
Components talk to each other through an internal event bus: any module can emit an event, and several consumers can receive it. The /ws WebSocket subscribes to this bus and to playback events, then relays everything to clients. Event names such as playback.track_changed or library.scan.progress are part of the contract with clients. See API Reference.
Extensions
Tune has two extension systems. Neither replaces the other: the first processes sound, the second adds features.
1. Native audio extensions
- They process the signal: equaliser, crossfeed, audio file converter, Dé-ploc.
- They are
.tunepluginpackages built for one specific platform and signed. A valid signature is required to install one. - An installed extension becomes active at the server's next startup.
- The equaliser is free. Crossfeed, the converter and Dé-ploc require a Tune Premium licence. Crossfeed Pro is a separate Premium extension.
- They are written with the kit in the
sdk/folder and thecargo tune-plugintool (new,check,test,dev,pack).
2. WebAssembly extensions
- They add features, for example transferring playlists between services.
- An extension is a folder containing a
manifest.json(identifier, version, permissions, routes, subscribed events, minimum server version, Premium flag) and amain.wasmmodule. - It runs in a sandbox with memory and execution-time limits. It has no direct access to files or the network: it can only use the functions Tune opens to it, according to the permissions it declares.
- Its routes are served under
/api/v1/plugins/{name}/….
Built-in extensions
Some extensions are written in Rust and compiled with the server. They register with Tune at startup and can add outputs, zones or routes, served under /api/v1/ext/{name}/…. Those in the repository:
- Bandcamp: search and discovery;
- CD: plays an audio CD without ripping it;
- Audio input: captures a USB input (turntable, S/PDIF, optical) and streams it live to a zone;
- Concerts (Premium): concerts by artists in your library;
- Pont Roon (Premium): imports a Roon export archive;
- DJ and Karaoke (synced lyrics).
A built-in extension can stay inactive until the user installs it from the extension manager.