Architecture
Four layers: apps that people hold, one Go server in the middle, a set of media servers it drives on their behalf, and five plain folders at the bottom. This page is the map; the pages under it go down into the decisions, a single request, where data lives and how the repository is laid out.
The four layers
/api/... ↓Apps
The web app (internal/webui/assets) is the product: one page, app.js,
covering every kind of media. The Android and iPhone apps are native shells around that same
page, adding only what a web page cannot do - native audio on Android, the lock screen, hiding
system bars, photo backup. The Apple TV has no web view, so it is the one app written natively,
against the same API the page uses.
The server
One Go program with no third-party Go dependencies, shipped as a container image. It holds the accounts, answers every API call, merges search across backends, and carries every byte of media from a backend to a device. It also provisions the backends - creating their admin accounts on first start - so nobody ever logs into one.
Backends
Each kind of media belongs to exactly one mature open-source server, running in its own container on a private compose network. Only SoundStorm's port is published. Ebooks and documents are the exception: they need no transcoding and describe themselves, so SoundStorm reads those folders itself.
The library
Five folders (with films and TV split, and documents beside ebooks) are the one part of the system a person can touch without SoundStorm. Media can be copied in with a file manager or dropped onto the app's window, which files it in the same place. Nothing scans the same folder twice.
How the pieces talk
- One API. Every app speaks the same HTTP API under
/api/, signed in with a session cookie. There is no second protocol for the TV or the phones. - Admin credentials SoundStorm made itself. Each backend is provisioned
with a generated account (192 random bits per password) kept in
state.json. Members get their own backend accounts only where a backend keeps per-person data that would otherwise collide: Audiobookshelf (listening positions) and Immich (each member's own photos). - Bytes are proxied. A song, a film's HLS segments, a cover, a photo -
everything travels through
internal/stream. The backends are never reachable from a device, which is what makes "one login" true. - Normalised at the edge. Each backend has an adapter under
internal/source/that turns its vocabulary intomedia.Item. Past the adapter, nothing knows which backend an item came from.
Three rules the design rests on
- A dead backend must never take the search down. Each source gets its own
deadline (5 seconds by default) and its own error slot; the answer is always 200, with
degradedsaying what is missing. Tests ininternal/federateandinternal/httpapiprotect this. - Normalisation happens at the edge. Only an adapter knows its backend's
quirks; everything past it speaks
media.Item. - A backend is two halves: search and provisioning. A backend a human must configure by hand defeats the point, so both halves or it is not done.