Tocata

An OpenSubsonic music server, written in Rust.

One binary with its administration panel inside it, statically linked and bringing its own libc. There is nothing to install beside it, no runtime to provide and no web server to put in front of it.

Early days, and now being run in earnest to find out what is still wrong. The first release is 0.1.0. master is where the work happens — built and tested on every push, and published as an image of its own, for anybody who would rather see what is coming than what is finished.

1

binary, panel included

0

runtimes to provide

5

OpenSubsonic extensions

GPL-3

free software

The server side

There are already plenty of good players that speak OpenSubsonic, so building another one is not the goal. What Tocata ships is the server, and the only interface it carries is a small administration panel: what the collection holds, who may reach which part of it, and the housekeeping that keeps both true.

What it leaves alone

Audio is the whole of what this server is for, and the API covers more than audio. Video, transcoding, podcasts, internet radio, chat and sharing are other programs' work. A client is told so where it asks — five extensions declared, an empty listing rather than a 404 — and the reasons, endpoint by endpoint, are in the README.

The panel

The overview screen: counts of songs, albums, artists and genres, the
                 collection's size and length, what the process is costing, and what the
                 last scan found.
What the collection holds, what the process is costing and what the last scan found, on one screen. Eleven thousand eight hundred songs indexed, and the server sitting in twenty-one and a half megabytes while it plays one.
A grid of album covers filtered by a search for Genesis, each with its
                 artist, year, number of tracks and length.
Records, filed and searched. Cover art comes off the files themselves, read when a screen asks for it rather than copied into a second collection beside the first.
A panel over the album grid showing one record: its tracks, its length,
                 its label and where it is on disk.
One record: what it is, what it weighs, where on disk it lives and the tracks in the order the files give them.
A track's panel showing timed lyrics, with the line at the playhead lit
                 in the accent colour.
Lyrics, timed, read out of the file where they were found. The line at the playhead is lit and the reading follows it — as long as that is the line you are reading: wander off and the words stay where you put them.
A track's panel showing what the scan recorded: title, artist, album,
                 year and genre, then the file's length, format, bitrate and path.
One track as the last scan read it. The tabs beside it hold the words and every tag the file carries, both taken off the disk when they are asked for, so a file edited since the scan can be seen disagreeing with it.
An artist's panel: five albums, fifty-eight tracks, total length, plays,
                 the tracks played most, and every record they are credited on.
An artist, and every record they are credited on rather than only the ones filed under their name. What gets played most is counted across everybody who listens on the server, not only you.

Running it

The container image

podman run -d --name tocata \
  -p 4224:4224 \
  -v tocata-data:/data \
  -v /srv/music:/media:ro \
  ghcr.io/ogarcia/tocata:latest

Three kinds of tag are published. latest is the newest release and moves when one is cut; a version — 0.1.0 — names one release and never moves again, which is the tag to write down somewhere that has to keep working; and master is the development branch, rebuilt on every push to it.

docker in place of podman works the same way. Two directories matter: /data is Tocata's own — the database lives there — and /media is where the music is. Mounting the music read only is not a precaution against Tocata, which never writes to a collection; it is simply true, so the filesystem may as well say so.

Nothing is scanned for having been mounted: /media, or a directory inside it, is added as a collection in the panel afterwards. Somebody who keeps their music beside their audiobooks wants two collections with different people reaching each, and one mount cannot say that.

The first run

Tocata creates its data directory and its database on the way up, and makes one administrator to get in with. That account's password is generated and written to the log exactly once:

WARN tocata::user: initial password for 'admin': …

Then the panel is at http://localhost:4224/. Collections are added there, one row each, and adding one reads nothing by itself: the scan is started from the panel's first screen, which is also where it reports what it found while it runs.

The other two ways

Every release carries a prebuilt binary per architecture, statically linked against musl and bringing its own libc, so it runs on any Linux of the right architecture whatever the distribution — with the checksums beside it on the release page, and nothing needed to fetch it. And Tocata builds from a checkout with Rust, the WebAssembly target and trunk, which builds the panel the server carries inside itself. Both, with the commands, are in the README — along with every environment variable, since everything about the deployment is read from the environment and everything about the music belongs to the panel.

Logging in

There are two doors and they work differently, because a browser and a music player are not in the same situation. The panel takes a username or an email address and a password, and hands back a session it can list and close. A player under /rest proves who it is the way the protocol says, with u and p — or with an API key from the apiKeyAuthentication extension, made in the panel, one per client and each withdrawn on its own.

What is worth knowing before pointing a client at it: token authentication — s and t=md5(password + salt) — is refused with error 42, and cannot be anything else. Verifying that token means having the password to hand, in clear or beside the key that decrypts it, and what Tocata keeps is an Argon2id hash whose whole purpose is that the password cannot be got back out of it. Between the two, the passwords win. Every client we have tried has a setting for the other way, worded as plain, clear text or legacy password authentication depending on the client, and that is what Tocata answers. The long version is in the README.