Skip to content

harmonia

A unified self-hosted media platform: one Rust server across 21 crates, replacing a stack of separate *arr-pattern services with one coherent media lifecycle manager.

SHIPPED CORE · 2 STUBS AT HTTP LAYER Repo → Rust · Tokio/Axum/SQLite · 21 workspace crates Shipped and wired to routes: auth, library import, feed scheduling, torrent download, queue orchestration, the streaming API · harmonia/README.md, capability-status table
harmonia · server boot, health check, library scan · seeded sample media only 1:15 · .tape

RECORDING FORTHCOMING: harmonia serve boots → a health check answers → a library scan triggers → its import queue populates, against a small seeded sample-media directory

What this shows: A real server boot, a health check answering, and a library scan populating an import queue — the most user-facing recording on this site. What it doesn’t show: The two HTTP-layer resolvers that are still null placeholders (metadata resolution and curation) — those are named directly in the solid/open list below, not hidden behind the demo.

What it is

harmonia is a unified self-hosted media platform: a single Tokio/Axum/SQLite server spanning 21 workspace crates, aimed at replacing the pattern where you run five or six separate *arr-style applications (one for movies, one for TV, one for music, one for indexing, one for requests) and wire them together yourself. Instead it’s one coherent server covering the full media lifecycle: import and rename, library scanning, metadata enrichment, quality verification, torrent acquisition, download-queue orchestration, household request handling, HTTP streaming, and a native audio pipeline with bit-perfect decode and DSP.

Decisions and trade-offs

One server instead of five federated services

The *arr-stack pattern (a separate app per media type, each with its own database, its own auth, its own web UI, wired together by the user) is well-established and has a large plugin ecosystem behind it. harmonia rejects that shape on purpose: one server, one auth layer, one database, 21 crates organized by concern (core, auth, media ops, acquisition, serving, audio, UI) rather than by deployable unit. The cost is losing the *arr ecosystem’s existing plugin surface; the benefit is a media manager that doesn’t require reasoning about five services’ worth of drift between each other.

DecisionChoseRejectedCost accepted
Status reportingA capability table naming shipped-and-wired vs. stubbed capabilities, down to the exact stub countA blanket “it works” status claimHarder to keep honest than a vague claim — the count has to stay current
Audio decodeNative bit-perfect decode and DSP (akouo-core), in-processPiping through an external decoder toolALSA development headers required at build time on Linux, a real stated prerequisite

What’s solid / what’s open

Solid, shipped and wired to live routes: auth, library scan/import, the feed scheduler, the torrent download engine, queue orchestration, the HTTP/OpenSubsonic streaming API, external integrations (Plex, Last.fm, Tidal), QUIC renderer transport, the native audio pipeline, and post-download import — a completed download lands directly in the library for music, movie, and book wants.

Open: two null placeholders remain at the HTTP layer, for metadata resolution and curation, on the live serve path — named directly, not folded into a vaguer “still improving” claim. Audiobook, comic, podcast, and TV-series wants have no library type yet and are deferred. A fallback/test path (AppState::with_stubs) defines nine additional Null* service implementations used for testing, separate from the two live-path placeholders above. The README documents the build and test commands but not yet how to run the server day to day: no quick-start section, no example config. Reaching a running instance from a fresh clone means reading the archon CLI’s own --help output rather than following a documented path; the recording above shows the same binary running, not a shortcut around that gap.

Numbers, and how they were measured

ClaimMethodWhere to check
100,460 lines Rust (code-only), 117,575 including commentstokei against a local clone, 2026-07-20reproducible: tokei on a fresh clone
21 workspace cratescrate count in the workspace Cargo.tomlreproducible on a fresh clone
~2,290 test-attribute occurrencesrg -c '#\[(tokio::)?test', 2026-07-20reproducible: same rg command on a fresh clone
2 null placeholders remain at the live HTTP layer (metadata resolution, curation)stated directly in the repo’s own capability-status tableREADME.md, Capability status section

Where to look