Skip to content

Releases: Routarr/Routarr

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 29 Oct 18:15
Immutable release. Only release title and notes can be modified.
f0d788e

Routarr 0.3.0 routes on what Radarr and Sonarr already know about a title (its quality profile,
runtime, studio, network, collection and scores), starts a library from ready-made rule
templates, and turns down a proposal in one click. Moves with files go to each Arr one title at a
time, automatic application is switched per instance, each Arr's webhook is checked, backups are
copied to a second folder, people of an OIDC group can sign in to read only, and a full disk, a
reused Arr id or a source refusing everything no longer loses or misroutes work. Everyone signs
in again after the upgrade and some API answers change, so read Before you upgrade first.

Before you upgrade

Upgrading

  1. From 0.1.0 to 0.1.4, start 0.1.5 once first. 0.3.0 refuses an older database at start:
    "the database was left by a Routarr older than v0.1.5. Start v0.1.5 on it once, which brings it
    to the schema this v0.3.0 starts from". 0.3.0 opens a 0.1.5 database directly, and the
    Do this first steps of the 0.2.0 notes then apply too.
  2. Name the minor line in image:, ghcr.io/routarr/routarr:0.3, as the install snippets
    now do. 0.2 stays on 0.2.x. latest, which the 0.2.0 snippets named, moves to 0.3.0 once
    these notes are published and keeps following every new release, minor ones included.
  3. Change the environment where it says so:
    • ROUTARR_JIKAN_BASE_URL is no longer read. MyAnimeList is now read through Tenrai, and
      ROUTARR_TENRAI_BASE_URL points it at a mirror or a self-hosted Jikan v4 API.
    • The start now stops, naming the variable, on ROUTARR_PORT=0, on an entry of
      ROUTARR_ALLOWED_HOSTS holding a path such as nas.lan/routarr (a URL such as
      https://nas.lan reads as nas.lan), and on a line of .env it cannot read. A .env is
      read from the working directory only, no longer from a folder above it.
    • PUID and PGID, which the linuxserver.io and hotio images read, are not read here, and the
      start now says so. To run Routarr as the data folder's owner, set user: in the compose
      file, or --user with docker run.
    • The sample compose file now rotates Docker's log, five files of 10 MB at most: copy its
      logging block, or its journald variant for the fail2ban jail of SECURITY.md.
  4. Update the scripts that name the jikan source. It is tenrai everywhere. The upgrade
    renames it in the database, but PUT /settings refuses jikan in metadata_providers, and a
    configuration exported before the upgrade that lists it is refused on import until it names
    tenrai. Scripts and applications calling the API lists the other changes.
  5. docker compose pull && docker compose up -d. The first start archives the database as
    backups/routarr-backup-<date>-pre-0.3.0.zip in the data folder (.zip.enc with a backup
    passphrase), then applies 003_release_0_3_0.sql. That archive is kept until you delete it,
    and a start retried after a failed migration reuses it and names the command that restores it.
    The same start ends every session, renames the second of two instance names that differ only
    by a case outside ASCII, Émissions and émissions, to émissions (2), and keeps automatic
    application on for every instance.
  6. Sign in again. In apikey mode, paste the key again:
    docker exec routarr cat /config/routarr.api_key. Application keys keep working.
  7. Watch the first passes. TMDB, OMDb, Tenrai and AniList are asked again for every title they
    described, for its scores, each at its own pace. The Last webhook column of Instances says
    whether each Arr's webhook reaches Routarr, and "Needs a fix" or "Not set up" leads to what to
    change. A failing rule test now holds automatic application (see Rules and moves). Run a
    simulation and read what it proposes before applying anything.

Going back to 0.2.0. 0.2.0 refuses a database 0.3.0 has opened and names the archive it can
open. With Routarr stopped, set the image to ghcr.io/routarr/routarr:0.2.0, run
docker compose run --rm routarr /app/routarr restore with that archive's name,
routarr-backup-<date>-pre-0.3.0.zip, or .zip.enc with a backup passphrase, then start it:
0.2.0 applies the archive as it starts. What changed under 0.3.0 is lost.

Behind a reverse proxy

  • Forward the public host and scheme, X-Forwarded-Host and X-Forwarded-Proto, and
    X-Forwarded-Port when the port is not 80 or 443. Without the host, every save from the
    browser answers 403. Caddy and Traefik forward them unasked. nginx does not, and the README's
    nginx block sets them.
  • Let a 64 MiB body through for Import a file of the anime identifier table, where nginx
    refuses one over 1 MB by default (client_max_body_size 64m).
  • List the proxy in ROUTARR_TRUSTED_PROXIES, by its own address or a network nothing else
    joins. Sign-in waits then count the client it forwards rather than the proxy, the proxy is
    exempt from the share of connections one address may hold (32 once half of the 512 are taken),
    and an X-Request-Id it sets is kept, where any other is replaced.
  • The session cookie is __Host-routarr_session over HTTPS at the root of a host, and stays
    routarr_session under ROUTARR_BASE_PATH or over plain HTTP. A proxy rule that names the
    cookie needs the new name.

You will notice

  • The screens lead with the answer. Simulation opens on To move, beside Skipped and
    All, with the apply bar at the foot once a move is ticked. Why? beside each title opens
    the explanation, which starts with what decided. The Library says whether each title moves and
    where, the Rules screen reads each rule as one sentence, and Categories and folders saves the
    folder mappings together from a bar.
  • Filters stay in the address on Simulation, History, the Library, the Move log, the Security
    log, Tasks and Rule tests: Back, a reload and a shared link keep them.
  • Settings saves only what you changed, so a screen left open no longer writes back a field
    changed elsewhere since, the global dry run included.
  • A backup download asks for the password or the API key again, or in oidc mode a sign-in
    under ten minutes old (see Security).
  • The MyAnimeList source reads "Tenrai (MyAnimeList)" on Metadata sources, and Diagnostics
    badges a source its pace or its daily quota holds back "Held back", not "Error".
  • Certification regions: UK is saved as GB, and a code naming no country is refused.
  • The start log says more: the .env it read, and a TZ naming no zone the system knows,
    the log then being in UTC.

Rules and moves

  • A failing rule test holds automatic application. Rule tests run after each sync. While one
    fails, automatic application waits on every instance, the dashboard warns, and
    rule_tests_failing is sent when the first one starts failing. A manual apply is not held.
  • Automatic application is switched per instance, in its form on Instances, under the global
    switch. An instance switched off keeps its moves as proposals, and its row says "Proposals
    only".
  • The batch limit counts each instance alone: one instance's backlog over batch_limit, 50 by
    default, no longer holds another's moves.
  • Moves with files go to each Arr one title at a time, each followed to its end before the
    next is sent, so a finished download imports between two copies instead of after the last.
    The instances of one run move at once. A run stops unreachable when an Arr's commands cannot be
    read for five minutes, and stalled when one move runs past six hours. An unattended apply waits
    up to five minutes for one already running, then leaves its decisions pending.
  • A move the Arr cannot see is refused. The Arrs end a move whose source folder they cannot
    see as completed and drop the file records at their next scan, so the Arr is now asked before
    the write. A source folder that does not answer, a sleeping disk, is asked about as a sleeping
    target already was.
  • A sync keeps the titles an Arr suddenly stops reporting. One that would drop more than ten
    titles and more than half of an instance keeps them, says how many on Instances, and Remove
    the titles it no longer reports
    removes them. The exceptions of a removed title come back
    when the Arr reports it again.
  • A category can lead to several folders of one instance, when you choose it: Categories and
    folders asks before adding a second one. A title already in one of them stays, and a title
    moving in goes to the one with the most free space.
  • Tenrai's explicit genres (Ecchi, Hentai, Erotica) read as genres, so a genre rule can match
    anime it did not.
  • Metadata sources: a source refusing every request with 400, 404, 410 or 422 is set aside for
    the pass after five refusals and no answer, instead of recording the whole library as not found.
    A title the Arr names with its year, Dororo (2019), is searched without it.
  • Imported rules go after the rules in place, in the bundle's order, and a rule already
    present is not added again. A priority stays between -1,000,000 and 1,000,000.
  • The rule check says more: a value that folds to nothing and an exclusion that vetoes every
    title are refused, and a language or country written as a name is warned of, with its code.
  • Names compare as they are stored: a category typed with a decomposed accent names the stored
    one, and instance names compare with Unicode case folding.

Notifications

  • Moves name their titles. apply_completed and revert_completed carry task_id,
    moves_total, truncated and moves, the first 50 titles, each with its type, instance,
    TMDB, TVDB and IMDb ids, old and new path, and status. Discord, ntfy, Gotify and Apprise name the
    first five. GET /move-log?task_id= lists every title of the task.
  • New events: `moves...
Read more

v0.3.0-rc.1

v0.3.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Oct 22:46
Immutable release. Only release title and notes can be modified.
387eb46

What's Changed

  • chore: drop the API breaks list of the release after v0.1.5 by @Njal57 in #174
  • test(e2e): measure a width only once the style has taken it by @Njal57 in #175
  • refactor(api)!: remove what 0.2.0 deprecated by @Njal57 in #176
  • chore(devcontainer): stop installing RTK by @Njal57 in #177
  • fix(site): word the detail page's heading as a whole case by @Njal57 in #178
  • fix(deps): move zip to 9 past two symlink advisories by @Njal57 in #180
  • feat(rules): offer ready-made rule templates by @Njal57 in #179
  • feat: keep a title where it is and hold auto-apply on a failing test by @Njal57 in #181
  • feat: filter what a simulation applies and revert by filter by @Njal57 in #182
  • feat: route on the quality profile, runtime, studio and network by @Njal57 in #183
  • feat: route on a title's score, by scale and votes by @Njal57 in #184
  • fix: read MyAnimeList through Tenrai and refresh the Sources screen by @Njal57 in #185
  • feat: switch automatic application per instance by @Njal57 in #186
  • feat: show each instance's last webhook delivery by @Njal57 in #187
  • feat: name the moved titles in move notifications by @Njal57 in #188
  • feat: ship a Grafana dashboard and Prometheus alert rules by @Njal57 in #189
  • feat: check each Arr's webhook connection to Routarr by @Njal57 in #190
  • feat: let application keys expire by @Njal57 in #192
  • fix: report a sliced run whose moves are still under way by @Njal57 in #193
  • feat: send moves with files to each Arr one at a time by @Njal57 in #194
  • feat: let a category lead to several folders of an instance by @Njal57 in #195
  • ci: run cargo-mutants over the rule engine, the guards and paths weekly by @Njal57 in #197
  • chore: run locally what CI runs, through one check script by @Njal57 in #196
  • fix: keep a title moved under a declared destination in place by @Njal57 in #198
  • ci: drive a real Radarr and Sonarr through Routarr every week by @Njal57 in #199
  • feat: copy each backup archive into a second folder by @Njal57 in #205
  • test: catch every mutant the weekly run reported, then gate on them by @Njal57 in #206
  • fix(deps): take the certificate fixes of rustls-webpki and its roots by @Njal57 in #208
  • feat: let people of an OIDC group in to read only by @Njal57 in #207
  • feat: add MyAnimeList's official API as a keyed source by @Njal57 in #209
  • feat(api): name the paths as the interface does, old ones deprecated by @Njal57 in #210
  • feat(api): tell the operator who still calls a deprecated path by @Njal57 in #211
  • feat(ui): start the empty Rules screen from the templates by @Njal57 in #212
  • feat(categories): suggest categories from folder names by @Njal57 in #213
  • fix(ui): keep the Categories load from restarting once access is known by @Njal57 in #215
  • docs: trust a homelab CA through SSL_CERT_DIR, date the stored key by @Njal57 in #214
  • fix(ui): reread the warnings after a probe without reloading the guide by @Njal57 in #217
  • feat(sources): find anime by their identifiers before any search by @Njal57 in #216
  • fix(db): keep acknowledged writes and stop reads waiting on writers by @Njal57 in #218
  • fix(moves): keep history with its title and refuse impossible moves by @Njal57 in #219
  • fix(backup): keep the copies, restores and start a person can trust by @Njal57 in #220
  • fix(security): bind sessions to what opened them and harden the edges by @Njal57 in #222
  • fix(rules): order runs without the clock and keep rules where they go by @Njal57 in #223
  • fix(sources): read a source refusing everything as down, bound waits by @Njal57 in #224
  • fix(api): warn of failing syncs, carry rule tests, answer as documented by @Njal57 in #226
  • fix(interface): keep what a person did and say what could not load by @Njal57 in #227
  • feat(interface): fit the shell to a phone and say where to settle by @Njal57 in #228
  • feat(interface): answer first on the screens that do the work by @Njal57 in #229
  • feat(interface): read a rule as a sentence and edit it in that order by @Njal57 in #230
  • feat(interface): say what matters first on each configuration screen by @Njal57 in #231
  • feat(interface): copy, filter and set the same way on every screen by @Njal57 in #232
  • test: make each claim fail when it breaks, and hold it once by @Njal57 in #233
  • ci: guard the release, cache the browsers, check what ships by @Njal57 in #235
  • fix(site): describe the release it serves, and check what it claims by @Njal57 in #242
  • docs: reorder the README and hold the documents to the code by @Njal57 in #243
  • feat(i18n): translate every string added or reworded since 0.2.0 by @Njal57 in #244
  • chore(release): prepare 0.3.0 by @Njal57 in #245
  • fix: make the backup copy, the restore and move records hold up by @Njal57 in #246
  • feat: features as a datasheet on the site, calmer dashboard figures by @Njal57 in #247
  • chore(release): 0.3.0-rc.1 by @Njal57 in #248
  • fix(release): find the tag the publish and promote checks look for by @Njal57 in #249

Full Changelog: v0.2.0...v0.3.0-rc.1

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 16 Oct 19:08
Immutable release. Only release title and notes can be modified.
858f067

Routarr 0.2.0 opens the whole configuration to application keys, writes notifications in each
receiver's own format, runs on Radarr and Sonarr hosted on Windows, encrypts backups, and
explains every screen in the interface. Some of it changes what a rule decides, what a script
receives and what the compose file needs, so read Before you upgrade first.

To upgrade:

  1. Go through Do this first below and change your compose file where it says so.
  2. docker compose pull && docker compose up -d. The first start archives the database before
    it migrates it.
  3. Run a simulation and read what it proposes before applying anything: see Rules may route
    differently
    .

Before you upgrade

Do this first

  • From 0.1.0 to 0.1.4, install 0.1.5 first. 0.2.0 opens a database 0.1.5 has migrated, and
    refuses an older one with a message saying so.
  • The data folder is /config. The image and the sample compose file mount ./config:/config
    and set ROUTARR_DB_PATH=/config/routarr.db. A compose file that still sets
    ROUTARR_DB_PATH=/data/routarr.db, as the 0.1.5 sample did, keeps working. One that mounts
    /data without setting the path keeps working too, and the start log asks you to remount it at
    /config.
  • OpenID Connect needs an allow-list. With ROUTARR_AUTH=oidc, set
    ROUTARR_OIDC_ALLOWED_SUBJECTS, ROUTARR_OIDC_ALLOWED_GROUPS (read from the claim named by
    ROUTARR_OIDC_GROUPS_CLAIM, groups by default), or ROUTARR_OIDC_ALLOW_ANYONE=true.
    Without one, Routarr does not start.
  • A pinned ROUTARR_API_KEY needs 32 characters or more, or Routarr does not start.
    openssl rand -hex 32 makes one.
  • Keep routarr.key beside the database. When it is missing and the database holds sealed
    credentials, the start stops and names the file, instead of making a new key that opens
    nothing. ROUTARR_ALLOW_NEW_MASTER_KEY=true makes a new one, and every credential is then
    entered again.
  • A generated API key is no longer printed in the log. Read it with
    docker exec routarr cat /config/routarr.api_key.
  • Add stop_grace_period: 30s to your compose file. A stop waits up to 20 seconds for a move
    in progress, and Docker's default of 10 cuts it. read_only: true with a small tmpfs on
    /tmp, as in the sample, is recommended too.
  • The way back is an archive. The first start of 0.2.0 archives the database before it
    migrates it, and 0.1.5 refuses a database 0.2.0 has opened. To return to 0.1.5, with
    Routarr stopped and the 0.2.0 image still in place, stage that archive:
    docker compose run --rm routarr /app/routarr restore routarr-backup-<date>.zip. Then set
    the image to 0.1.5 and start it: 0.1.5 applies the archive at start. What changed under
    0.2.0 is lost.

You will notice

  • You may be signed out. Sessions now end after about a week unused and at the latest 30 days
    after sign-in (a day through OpenID Connect), so older ones end at the upgrade. In apikey mode
    the browser trades the key for a session once and forgets it.

  • Changing a key asks for proof. Rotating or removing the API key, and making an application
    key, ask for the current password or key, or a sign-in at the provider under ten minutes old.

  • Screens moved, and old addresses are not redirected:

    Before Now
    /media /library
    /overrides /exceptions
    /rules/tests /rule-tests
    /jobs /tasks
    /logs /move-log
    /health /diagnostics
    /root-folders /categories
    Settings #metadata /sources
    Settings #routing Settings #guardrails

    Notifications and Security have tabs of their own in Settings, and the API key, the account
    and the sessions are under Security.

  • "Rescan after a move" is gone. Moves go title by title through the Arr, which rescans by
    itself.

  • Instance names are unique, whatever their case and spacing: a duplicate is renamed with
    (2) at the upgrade.

  • A burst of metadata requests follows the upgrade. Answers holding a rating are asked again
    once, from TMDB, TheTVDB and OMDb. OMDb now has a daily budget, omdb_daily_requests, 1000 by
    default: raise it with a paid key.

  • Log lines follow TZ and carry their offset, and RUST_LOG, when set, replaces
    ROUTARR_LOG_LEVEL and says so at start.

  • Supported browsers: Chrome and Edge 130, Firefox 140 ESR and Safari 18, and later.

Rules may route differently

Run a simulation after the upgrade and look at what it proposes before applying.

  • Ratings follow your regions, not the source order. A title takes the rating of the first
    region in certification_regions that any source answers.
  • Former countries have their own codes. East Germany is XG, no longer DE, so a rule on
    DE no longer takes East German films. Czechoslovakia is XC, Yugoslavia YU, Serbia and
    Montenegro CS, the Netherlands Antilles AN, the Soviet Union SU.
  • Every ISO language and country is read from OMDb and TheTVDB, so a language or country rule
    can match more titles once they are asked again.
  • A rating is compared whole, case and separators aside: MyAnimeList's R+ no longer matches
    a rule on R.
  • Accents: Latin letters lose theirs however a source wrote them (Shōgun reads shogun),
    other scripts keep theirs.
  • Anime sources are asked for animated titles only. AniList and MyAnimeList are searched for
    titles filed under Animation or Anime, anime series, and titles with no genre.
    anime_search = all asks them for everything, as before.
  • TheTVDB answers for series only, and a new title arriving by webhook is enriched from every
    source before it is routed, not TMDB alone.
  • "Added within N days" counts N times 24 hours, and 0 is stored as 1.
  • Rules of equal priority are tried by name, then by id. A rule created through the API
    without a priority goes after every other.
  • Limits: 1,000 rules, 50 conditions and 50 exclusions each, 200 values in a condition.
  • Rule bundles carry instances by name. A bundle exported by 0.1.5 brings an enabled rule with
    no instance switched off, and the import report says so.

Notifications

  • Each receiver gets its own format. notification_format is auto by default: a Discord
    webhook address receives an embed, and a plain ntfy.sh/<topic> address a plain message with
    its title, priority and tags. Every other address receives JSON as before. Set
    notification_format to json to keep JSON everywhere.
  • The title and message follow the interface language. event, severity, type and data
    do not.
  • New events: backup_failed, sent for every failed scheduled backup, and auto_apply_held,
    when automatic moves are held over their cap. apply_completed and revert_completed carry
    moving.

Scripts and applications calling the API

The contract is GET /api/v1/openapi.json. Before 1.0, a minor release may break a v1 client,
and these do:

  • GET /overrides answers a page, {"data": [...], "pagination": {...}}, 50 per page by
    default, and so does GET /rule-tests.
  • Creates answer 201 with Location (exceptions, rules, rule tests, categories, instances,
    declared folders, application keys, a rule's duplicate). Removals answer 200
    {"deleted": true}
    , where some answered 204 or the id.
  • GET /ping no longer carries version. GET /status does.
  • A field the request does not define is refused with a 400 naming it, in a body or a query.
    The Arr webhook and the OpenID Connect callback stay lenient.
  • Timestamps are RFC 3339 in UTC (2026-10-05T21:26:14Z) in every answer.
  • A refusal carries a code to branch on. A 409 names its reason (a refusal caused by a
    running task also sends Retry-After), and the global dry run answers 409 dry_run where it
    answered 400. Messages and decision reasons are written in the interface language: branch on
    error, reason and error_key, never on the text.
  • /health reads degraded only while a warning of severity error stands, and failing
    with a 503 when the database does not answer.
  • GET /jobs leaves result out unless asked with ?include=result. A simulation that
    stores nothing is a task of kind preview, and backups and purges are tasks too.
  • GET /logs/export takes no page or per_page, and stops at 50,000 rows with
    X-Routarr-Truncated: true.
  • PUT /root-folders/{id}/category requires category, and null unmaps the folder.
  • Application keys are limited to 10 requests a second after a burst of 50 (429 with
    Retry-After). A backup asked with one is named -app.zip and waits ten minutes after the
    newest archive.
  • The contract's proposals tag is decisions, which matters to code generated from it.

Deprecated, and removed in the next release

  • The error: <sentence> form of instances[].status in /health: read detail.
  • The metrics routarr_media_total and routarr_jobs_total: read routarr_media_items and
    routarr_jobs.
  • MediaMetadata.poster_path, which is always null.

What changed

New

  • Radarr and Sonarr on Windows: drive letters and \\server\share paths are compared without
    case or separator, and declared destinations accept them.
  • Moves you can follow and stop: titles move one by one through the Arr, with their files,
    and an apply or a revert can be stopped.
  • Application keys reach the whole configuration: they read everything, and the configure
    scope edits rules, categories, folder mappings, declared destinations and rule tests.
  • A menu regrouped around what each screen is for, with a screen for the metadata sources.
  • Simulations run as tasks, with a progress bar.
  • Every condition reads as a sentence, in every language.
  • Notifications in each receiver's format, Discord, ntfy, Gotify, A...
Read more

v0.1.5

Choose a tag to compare

@github-actions github-actions released this 04 Oct 14:42
07e8896

Before you upgrade

  • You will be signed out. Sessions are now stored by a digest of their id, so every session
    open before the upgrade ends. Sign in again.
  • ROUTARR_AUTH=none answers only to known names. Routarr then answers an IP address,
    localhost, or a name listed in ROUTARR_ALLOWED_HOSTS. If you open it by a host name in that
    mode, add the name to ROUTARR_ALLOWED_HOSTS.
  • Client addresses come only from trusted proxies. X-Forwarded-For is read only from the
    addresses in ROUTARR_TRUSTED_PROXIES. Behind a reverse proxy, list it there.
  • Back up before upgrading if you may go back. From this release on, Routarr records the
    release that opened a database, and an older release refuses a database a newer one opened.
  • MyAnimeList ratings are stored as their code (G, PG, PG-13, R, R+, Rx). A rule
    written against the full label (PG-13 - Teens 13 or older) needs the code instead.
  • Notifications: type now carries the outcome Apprise reads (failure, warning,
    success or info), and Apprise accepts every notification again. The event is named by
    event, as before. For ntfy, use
    https://ntfy.sh/your-topic?template=yes&title={{.title}}&message={{.message}}.

What changed

  • Stronger isolation, optional: the same image runs under gVisor with runtime: runsc in
    the compose file. CI runs the whole start-up check under it.
  • Long writes no longer time out: applying, reverting and syncing are followed as tasks to
    their report, and syncing every instance can be followed too.
  • Safer moves: an apply states every guardrail it crosses and asks about each one, a revert
    skips a title moved since, and a sync no longer drops a title added while it reads.
  • Restores keep today's credentials: the API key, the password, signing secrets and every
    key revoked since the backup stay as they are, and everyone is signed out.
  • Locked out? docker exec routarr /app/routarr reset-account sets a new password.
  • Category names take letters of any script.
  • Settings and the rule editor ask before dropping unsaved changes.
  • The configuration export carries the rules, and the import replaces or joins them as asked.
  • Every language is complete, in all 26.
  • Sign-in: OpenID Connect sends the client secret as providers expect by default, Authelia
    included.
  • Metadata: a refused key or a spent quota stops the enrichment instead of caching misses,
    and an unknown year is no year.

The list of every change follows.

What's Changed

  • fix(site): say which notifications need switching on by @Njal57 in #88
  • feat(site): put the API beside the repository in the header by @Njal57 in #89
  • feat: run the image under gVisor by @Njal57 in #90
  • fix: hold keys, guardrails, restores and secrets to what was granted by @Njal57 in #91
  • fix: keep the library, its rules and its moves right across syncs by @Njal57 in #92
  • fix: settle the edge cases of syncs, moves, rules and restores by @Njal57 in #93
  • fix: harden sources, notifications and sign-in by @Njal57 in #94
  • fix: settle the edge cases of sources, notifications and sign-in by @Njal57 in #95
  • chore(devcontainer): size the ramdisk from the machine's memory by @Njal57 in #96
  • fix: harden the frontend screens and the tests that guard them by @Njal57 in #97
  • fix: settle the edge cases of the frontend screens by @Njal57 in #98
  • chore(devcontainer): drop the build caches one at a time, within budget by @Njal57 in #99
  • fix: name categories in any script and keep confirmations apart by @Njal57 in #100
  • fix: settle the wording of every language by @Njal57 in #101
  • ci: start each released image before pushing it by @Njal57 in #102
  • fix: hold the checks and documents to what the code does by @Njal57 in #103
  • feat(site): cut the API page to a first call by @Njal57 in #104
  • fix(site): say what the application does, and check more of it by @Njal57 in #105
  • feat(notify): tell Apprise each notification's outcome by @Njal57 in #106
  • chore: release 0.1.5 by @Njal57 in #107
  • test: give the full-screen sweeps a bound that fits a sweep by @Njal57 in #108

Full Changelog: v0.1.4...v0.1.5

v0.1.4

Choose a tag to compare

@github-actions github-actions released this 30 Sep 19:08
206395c

Routarr 0.1.4 opens Routarr to other applications. A script, n8n, Home Assistant, a dashboard or a request bot can now drive it, each with a key of its own, against a contract that later releases keep.

Before upgrading

Nothing to change. The database gains three tables on first start. Every existing call, setting and notification field works as before. As before any upgrade, back up data/.

Keys for other applications

  • A new Applications screen makes one key per application. The key is shown once and only its hash is kept. Revoking it cuts the application off at once.
  • Every key reads. Operating (sync, simulate, apply, revert) and writing (setting and removing exceptions) are each granted on their own.
  • A key answers only the guardrails you list for it. It moves no file on disk unless you allow it.
  • Every task, move and exception an application causes names it, in Tasks, History, Logs and Exceptions.
  • Under ROUTARR_AUTH=external or none, a key that is presented is still held to its scopes.

A contract that holds

  • What an application may call is described at /api/v1/openapi.json (OpenAPI 3.1). The new API reference screen shows it inside Routarr.
  • Within /api/v1, nothing documented is removed, renamed or given another type. CI refuses such a change against the last release.
  • The same reference is on https://routarr.app/api/, with recipes for cron, Jellyseerr, Homepage, Prometheus and webhook signatures.

New in the API

  • GET /api/v1/route says where a title would go, by its TMDb, TheTVDB or IMDb id, even when your Arrs do not hold it yet. Nothing is stored, and enrich=true asks the metadata sources first.
  • PUT and DELETE /api/v1/overrides/external pin a title to a category, or unpin it, by the same ids.
  • GET /api/v1/media filters by tmdb_id, tvdb_id and imdb_id.
  • A simulation, an apply, a revert or the sync of one instance can answer 202 at once, when asked with Prefer: respond-async. The task keeps its report for GET /api/v1/jobs/{id}.
  • Each warning from /api/v1/status carries a stable code.

Signed notifications

  • Generate a signing secret in Settings and every notification is signed as Standard Webhooks specifies (webhook-id, webhook-timestamp, webhook-signature). A replaced secret keeps signing beside the new one for a day.
  • A delivery refused or left unanswered is tried again after 10 s, 60 s and 5 min.
  • Failed syncs, finished simulations and finished moves can each be sent too, from their own switch in Settings.
  • Each notification also carries type, timestamp and data (the ids and counts behind the message).

Fixes

  • An enrichment pass that identifies nothing no longer leaves its progress short of the end.
  • Progress bars have square ends, which read better when they are short.

What's Changed

  • feat: give other applications keys held to their own scopes by @Njal57 in #78
  • feat: publish the API contract other applications may rely on by @Njal57 in #79
  • feat: let other applications follow long work and pin by external id by @Njal57 in #80
  • feat: say where a title would go, by the id another service gives it by @Njal57 in #81
  • feat: sign the notification webhook and send completions on request by @Njal57 in #82
  • style: square the ends of the progress bars by @Njal57 in #83
  • fix: end an enrichment task's progress when it finds nothing to fetch by @Njal57 in #84
  • feat: add a reference of the API other applications call by @Njal57 in #85
  • feat(site): add an API page with recipes and the reference by @Njal57 in #86
  • chore: set the version to 0.1.4 by @Njal57 in #87

Full Changelog: v0.1.3...v0.1.4

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 30 Sep 11:23
1f501de

Routarr 0.1.3 is a hardening release: no new screen, a great many corrections, and a few changes to check before you upgrade.

Check before upgrading

  • Metrics labels. routarr_media_total, routarr_instance_up and routarr_instance_enabled now name an Arr instance with arr_instance (its name) and arr_instance_id. The old instance label was renamed exported_instance by Prometheus, and two instances sharing a name were merged into one series. Update any dashboard or alert that filters on instance or exported_instance.
  • ROUTARR_AUTH. Only apikey, forms, oidc, external and none are read. The undocumented spellings disabled and required now fall back to the API key: an installation that set ROUTARR_AUTH=disabled asks for its key until the value reads none.
  • move_files_default is gone. Nothing read it: whether to move the files is asked at each apply and each revert. A backup that carries it restores everything else and lists it as not restored.

Safer moves

  • The apply, revert and automatic-apply guards are tightened, and a revert asks the questions an apply asks.
  • A manual sync runs to its end even when the browser or a proxy lets go of the request.
  • A movie moved with its files is recorded at the folder Radarr actually chose.
  • A title moved twice can only have its latest move undone, never skipping a folder on the way back.
  • The explanation of a title is built by the same function as the simulation, so the two always name the same folder.

Sign-in

  • The forms and oidc modes are hardened: attempts are throttled per client, and a session renews itself while in use.
  • A provider sign-in keeps its session cookie. It was dropped on the way back from the provider.

Your language, everywhere

  • Task details, log entries, sync results and every status now read in the interface language, and the browser tab names the screen.
  • The 26 dictionaries say each core term (dry-run, simulation, move, apply, revert, exception, root folder...) one way.
  • Counts read as a label and a number, so no language has to agree a noun with it.
  • The Logs screen and its CSV export name what set each write off, and who asked.

Credentials and data

  • The notification webhook address is sealed like an API key.
  • An Arr address carrying a user and password is masked wherever it is shown.
  • Repeated values in a rule or a source list are refused, typed input survives an error, and mappings, rule tests and exceptions are checked more strictly.

Operations

  • A panic is logged with the request id the response carries, and as JSON under ROUTARR_LOG_FORMAT=json.
  • A malformed query string gets the same JSON error as any other.

What's Changed

  • Keep a rule from widening or vanishing by accident by @Njal57 in #49
  • Tighten the apply, revert and auto-apply guards by @Njal57 in #50
  • Seal the notification webhook address like an API key by @Njal57 in #51
  • Mask the credentials an Arr address carries by @Njal57 in #52
  • Ask before a revert what an apply asks by @Njal57 in #53
  • Keep a category on its path, and retry a dead Arr at its own pace by @Njal57 in #54
  • Read Jikan, AniList, TMDb and TheTVDB as they answer by @Njal57 in #55
  • Refuse a source without its key, and keep tags when the tag endpoint fails by @Njal57 in #56
  • Close four quiet backend failures by @Njal57 in #57
  • Keep what is typed, and act on a pick only when asked by @Njal57 in #58
  • Keep mappings, rule tests and overrides honest by @Njal57 in #59
  • fix(frontend): keep typed input and say plainly what failed by @Njal57 in #60
  • fix: refuse repeated values, and tidy small interface faults by @Njal57 in #61
  • fix(auth): harden sign-in and keep sessions alive by @Njal57 in #62
  • fix(auth): hand a provider sign-in its session cookie by @Njal57 in #63
  • ci: test what every merge left, and guard what a release publishes by @Njal57 in #64
  • fix(site): hold contrast, focus and copy to what a reader gets by @Njal57 in #65
  • feat(site): answer a missing page in the reader's language by @Njal57 in #66
  • fix(site): use the application's terms and stop clipping on phones by @Njal57 in #67
  • fix: speak the interface language on every screen and in the tab by @Njal57 in #68
  • fix(i18n): say each core term one way in every language by @Njal57 in #69
  • fix(i18n): name the Arr as the Arr, and undo apart from restore by @Njal57 in #70
  • fix: log panics in their request, and label metrics and errors plainly by @Njal57 in #71
  • docs: state each comment as the constraint it keeps by @Njal57 in #72
  • fix: finish a sync the caller left, and say who wrote each log entry by @Njal57 in #74
  • build(deps-dev): Bump the frontend group across 1 directory with 3 updates by @dependabot[bot] in #73
  • refactor: one spelling of each decision, and tests that can fail by @Njal57 in #75
  • fix: record the folder Radarr chose, and revert only a title's last move by @Njal57 in #76
  • chore: set the version to 0.1.3 by @Njal57 in #77

Full Changelog: v0.1.2...v0.1.3

v0.1.2

Choose a tag to compare

@github-actions github-actions released this 28 Sep 22:03
467be65

What's Changed

  • chore(devcontainer): stop forwarding the ports test runs open by @Njal57 in #31
  • fix(scripts): write merged translations in the English key order by @Njal57 in #32
  • fix(frontend): frame the empty Tests page like every other screen by @Njal57 in #33
  • feat: guide a first setup from the first instance to live mode by @Njal57 in #34
  • Remove duplicates and obsolete code, and fix what they hid by @Njal57 in #35
  • build(deps): Bump thiserror from 2.0.20 to 2.0.21 in /backend in the rust group by @dependabot[bot] in #41
  • build(deps): Bump the frontend group in /frontend with 9 updates by @dependabot[bot] in #42
  • build(deps-dev): Bump astro from 7.3.3 to 7.3.5 in /site in the site group by @dependabot[bot] in #39
  • build(deps): Bump actions/download-artifact from 7.0.0 to 8.0.1 in the actions group by @dependabot[bot] in #43
  • build(deps): Bump rust from 462a9af to a8a5f0a in /.devcontainer in the devcontainer-images group by @dependabot[bot] in #36
  • build(deps): Bump alpine from 28bd5fe to 294b683 by @dependabot[bot] in #37
  • build(deps): Bump rust from 1716b3a to 7cc1c22 by @dependabot[bot] in #38
  • build(deps): Bump node from 50c8e8c to ebfe2f9 by @dependabot[bot] in #40
  • Let the server ask Apply all's question by @Njal57 in #44
  • Bring every control within keyboard reach by @Njal57 in #45
  • Hold the route walk, the Cancel answer and the master key in tests by @Njal57 in #46
  • Refuse cross-site writes in none and external modes by @Njal57 in #47
  • Release 0.1.2 by @Njal57 in #48

Full Changelog: v0.1.1...v0.1.2

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 26 Sep 07:07
7ed844c

Routarr 0.1.1 fixes what the first release got wrong. Upgrading is a pull of the new image: the database, the settings and the keys carry over as they are.

  • A restore is whole or it is nothing. An archive is checked before it is staged (integrity, a known schema), a restore that fails halfway is discarded instead of being applied at the next start, and a backup that fails no longer leaves a file that looks complete.
  • A move is decided again when it is applied. A proposal made before a rule, a category mapping or a destination changed is skipped and withdrawn, rather than sending files where no rule sends them any more.
  • A declared destination is checked as typed. A folder that exists is no longer refused, and a misspelt last folder no longer reads as verified.
  • Signing in with an OpenID Connect provider works from the sign-in screen, and a sign-in that cannot start brings you back with a message.
  • Each screen says once how an action went. Part done and part failed shows in amber with the items that failed, a complete failure is never shown as a success, and a refusal no longer disappears on the next reload. A configuration import names the instances waiting for their API key instead of listing them as not restored.
  • The interface reads in plain punctuation in all 26 languages, and its translations were reviewed: one form of address per language, one word per concept, and grammar errors and mistranslations fixed. The rule import no longer tells you to press Cancel to append.
  • A screen you leave stops its requests, so a dashboard left while it waits on an unreachable Arr no longer holds the browser's connections.

What's Changed

  • Give each block on /how/ the form its content has, and one figure between sections by @Njal57 in #16
  • Say that the bundle size is gated, because it is by @Njal57 in #17
  • Drive the language offer in a browser by @Njal57 in #18
  • Cancel a load the page no longer needs by @Njal57 in #19
  • Make restores and archives whole or absent by @Njal57 in #20
  • Revalidate a proposal against the rules at apply time by @Njal57 in #21
  • Let the provider sign-in leave the page by @Njal57 in #22
  • Ask the Arr about the declared folder itself by @Njal57 in #23
  • Make request cancellation safe on older browsers by @Njal57 in #25
  • Split CLAUDE.md into a short root and path-scoped rules by @Njal57 in #24
  • Keep the pusher's address out of build records and provenance by @Njal57 in #26
  • feat(site): describe Routarr for language models in llms.txt by @Njal57 in #27
  • fix: report each action through one outcome per screen by @Njal57 in #28
  • fix: write the interface text in plain punctuation by @Njal57 in #29
  • chore(release): set the version to 0.1.1 by @Njal57 in #30

Full Changelog: v0.1.0...v0.1.1

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 22 Sep 21:31
Immutable release. Only release title and notes can be modified.
d5fbeab

The 0.1.0 image is no longer on the registry: run 0.1.5 or later.

What's Changed

  • fix: the two failures of the first CI run by @Njal57 in #6
  • build(site): declare the site as a Worker serving static assets by @Njal57 in #7
  • docs(site): publish a security.txt by @Njal57 in #8
  • build(deps): Bump the rust group across 1 directory with 2 updates by @dependabot[bot] in #1
  • build(deps): Bump the actions group across 1 directory with 3 updates by @dependabot[bot] in #3
  • build(deps-dev): Bump astro from 7.2.10 to 7.3.3 in /site in the site group across 1 directory by @dependabot[bot] in #2
  • build(deps-dev): Bump the frontend-breaking group across 1 directory with 2 updates by @dependabot[bot] in #5
  • refactor(site): shorten the page and give each section its width by @Njal57 in #9
  • docs: describe the Worker deployment and restore the README screenshot by @Njal57 in #10
  • build(deps): Bump the frontend group across 1 directory with 9 updates by @dependabot[bot] in #4
  • refactor(site): split the page in two and lighten what is left by @Njal57 in #11
  • fix(site): name the second page by the path that is served by @Njal57 in #12
  • ci(release): build each architecture on its own native runner by @Njal57 in #13
  • fix(release): give the release job the repository gh needs by @Njal57 in #14
  • Prepare the v0.1.0 reissue: showcase rebuild, schema squash, README logo by @Njal57 in #15

New Contributors

Full Changelog: https://github.com/Routarr/Routarr/commits/v0.1.0