Avatarr
One Rust core behind a desktop app, a web UI, and a CLI. A single-binary Sonarr and Radarr replacement.
Sonarr and Radarr are the standard tools for automating a media library, and running both means running two services that do the same kind of work on two halves of the problem. I wanted one program that did both, shipped as a single binary, and met the bar a real homelab tool has to meet.
A single Rust core, avatarr-core, with three ways to drive it: a Slint desktop app for power users, a React web UI for a homelab Docker deployment, and a headless CLI as the permanent fallback. All three sit on the same core, the same SQLite database, and the same scheduler, so behaviour does not fork between them.
One core, three surfaces
All the real work lives in avatarr-core: parsing, matching, database access, scheduling. The desktop app, the web UI, and the CLI are thin surfaces over it. A feature lands in the core once and every surface gets it, which is the only way one person keeps three front ends honest.
Single-binary distribution
The whole thing compiles to one binary with no runtime to install and no container required for the desktop or CLI path. Configuration lives in a TOML file and data in a local SQLite database. Installing it is copying a file.
SQLite with pinned migrations
Schema migrations are content-addressed: each one is pinned by its sha256 so a migration cannot silently change under a shipped release. When a migration needs a fix it is a forward fix with a new hash, never an edit to history. For a tool that owns someone's library database, that guarantee matters.
A real scheduler
Background tasks (library scans, metadata refreshes, RSS checks) run on a scheduler inside the core rather than as external cron jobs. The schedule is part of the program, so every surface sees the same task state.
The parser is its own crate
The release-name parser was the fiddliest part, so I pulled it into its own crate and published it to crates.io. It has one job, a clean interface, and its own test suite, and anything that needs to parse a scene release name can depend on it directly.
Shared-core, multi-surface
One core behind several front ends is a pattern that applies far beyond media tools. The discipline is keeping every decision in the core and letting the surfaces stay thin.
Single-binary distribution
Shipping one file with no runtime removes a whole class of support problems. It changes what installation and upgrade feel like for the person on the other end.
Migration pinning as safety
Content-addressing your migrations is cheap to add and turns a scary class of production bug, a migration that changed after it already ran somewhere, into something the tool refuses to do.
See the rest of the work.