Tune by Mozaiklabs

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/v1 API, 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:

CrateRole
tune-coreThe core: music library, database, playback, zones, audio outputs, device discovery, streaming services
tune-serverThe 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-typesHTTP types shared by the server and its route crates
tune-stream-httpThe routes that serve audio streams to devices
tune-streaming-httpThe streaming service routes
tune-smart-httpSmart playlists, smart collections and rule-based recommendations
tune-output-apiThe stable contract between Tune and its audio outputs: the OutputTarget trait, for writing an output outside the repository
tune-plugin-runtime-wasmThe sandboxed runtime for WebAssembly extensions
tune-cliThe tune command-line tool
tune-ffiA 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:

ModuleContents
audioDecoders, resampling, DSD (DoP and PCM conversion), processing (ReplayGain, equaliser, convolution, crossfeed, dither)
scannerFolder walking, change watching, CUE sheets
libraryLibrary: full-text search, artwork, import, collections
metadataMetadata: MusicBrainz, Last.fm, fingerprints, tag writing
playback, zonesPlayback, queue, track chaining, zones and groups
outputsOutputs: DLNA/UPnP, OpenHome, Chromecast, AirPlay, Squeezebox, BluOS, HQPlayer, OAAT, local outputs
discoveryDevice discovery (SSDP, mDNS)
slimprotoSlimProto server for Squeezelite and Squeezebox players, without LMS
streamingStreaming services, radio, podcasts
upnp_server, upnp_rendererTune seen from the network as a UPnP media server and, zone by zone, as a UPnP renderer
dbDatabase 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 .tuneplugin packages 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 the cargo tune-plugin tool (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 a main.wasm module.
  • 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.

See also