Repository navigation
Releases: Routarr/Routarr
Release list
v0.3.0
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
- 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. - Name the minor line in
image:,ghcr.io/routarr/routarr:0.3, as the install snippets
now do.0.2stays 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. - Change the environment where it says so:
ROUTARR_JIKAN_BASE_URLis no longer read. MyAnimeList is now read through Tenrai, and
ROUTARR_TENRAI_BASE_URLpoints 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_HOSTSholding a path such asnas.lan/routarr(a URL such as
https://nas.lanreads asnas.lan), and on a line of.envit cannot read. A.envis
read from the working directory only, no longer from a folder above it. PUIDandPGID, 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, setuser:in the compose
file, or--userwithdocker run.- The sample compose file now rotates Docker's log, five files of 10 MB at most: copy its
loggingblock, or itsjournaldvariant for the fail2ban jail ofSECURITY.md.
- Update the scripts that name the
jikansource. It istenraieverywhere. The upgrade
renames it in the database, butPUT /settingsrefusesjikaninmetadata_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. docker compose pull && docker compose up -d. The first start archives the database as
backups/routarr-backup-<date>-pre-0.3.0.zipin the data folder (.zip.encwith a backup
passphrase), then applies003_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,Émissionsandémissions, toémissions (2), and keeps automatic
application on for every instance.- Sign in again. In
apikeymode, paste the key again:
docker exec routarr cat /config/routarr.api_key. Application keys keep working. - 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-HostandX-Forwarded-Proto, and
X-Forwarded-Portwhen 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 anX-Request-Idit sets is kept, where any other is replaced. - The session cookie is
__Host-routarr_sessionover HTTPS at the root of a host, and stays
routarr_sessionunderROUTARR_BASE_PATHor 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
oidcmode 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:
UKis saved asGB, and a code naming no country is refused. - The start log says more: the
.envit read, and aTZnaming 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_failingis 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 stopsunreachablewhen an Arr's commands cannot be
read for five minutes, andstalledwhen 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_completedandrevert_completedcarrytask_id,
moves_total,truncatedandmoves, 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...
v0.3.0-rc.1
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
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:
- Go through Do this first below and change your compose file where it says so.
docker compose pull && docker compose up -d. The first start archives the database before
it migrates it.- 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 setROUTARR_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
/datawithout 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,groupsby default), orROUTARR_OIDC_ALLOW_ANYONE=true.
Without one, Routarr does not start. - A pinned
ROUTARR_API_KEYneeds 32 characters or more, or Routarr does not start.
openssl rand -hex 32makes one. - Keep
routarr.keybeside 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=truemakes 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: 30sto 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: truewith a smalltmpfson
/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 to0.1.5and 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. Inapikeymode
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/categoriesSettings #metadata/sourcesSettings #routingSettings #guardrailsNotifications 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
TZand carry their offset, andRUST_LOG, when set, replaces
ROUTARR_LOG_LEVELand 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 incertification_regionsthat any source answers. - Former countries have their own codes. East Germany is
XG, no longerDE, so a rule on
DEno longer takes East German films. Czechoslovakia isXC, YugoslaviaYU, Serbia and
MontenegroCS, the Netherlands AntillesAN, the Soviet UnionSU. - 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 onR. - Accents: Latin letters lose theirs however a source wrote them (
Shōgunreadsshogun),
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 = allasks 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_formatisautoby default: a Discord
webhook address receives an embed, and a plainntfy.sh/<topic>address a plain message with
its title, priority and tags. Every other address receives JSON as before. Set
notification_formattojsonto keep JSON everywhere. - The title and message follow the interface language.
event,severity,typeanddata
do not. - New events:
backup_failed, sent for every failed scheduled backup, andauto_apply_held,
when automatic moves are held over their cap.apply_completedandrevert_completedcarry
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 /overridesanswers a page,{"data": [...], "pagination": {...}}, 50 per page by
default, and so doesGET /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 /pingno longer carriesversion.GET /statusdoes.- 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 sendsRetry-After), and the global dry run answers 409dry_runwhere it
answered 400. Messages and decision reasons are written in the interface language: branch on
error,reasonanderror_key, never on the text. /healthreadsdegradedonly while a warning of severityerrorstands, andfailing
with a 503 when the database does not answer.GET /jobsleavesresultout unless asked with?include=result. A simulation that
stores nothing is a task of kindpreview, and backups and purges are tasks too.GET /logs/exporttakes nopageorper_page, and stops at 50,000 rows with
X-Routarr-Truncated: true.PUT /root-folders/{id}/categoryrequirescategory, andnullunmaps 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.zipand waits ten minutes after the
newest archive. - The contract's
proposalstag isdecisions, which matters to code generated from it.
Deprecated, and removed in the next release
- The
error: <sentence>form ofinstances[].statusin/health: readdetail. - The metrics
routarr_media_totalandroutarr_jobs_total: readroutarr_media_itemsand
routarr_jobs. MediaMetadata.poster_path, which is always null.
What changed
New
- Radarr and Sonarr on Windows: drive letters and
\\server\sharepaths 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...
v0.1.5
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=noneanswers only to known names. Routarr then answers an IP address,
localhost, or a name listed inROUTARR_ALLOWED_HOSTS. If you open it by a host name in that
mode, add the name toROUTARR_ALLOWED_HOSTS.- Client addresses come only from trusted proxies.
X-Forwarded-Foris read only from the
addresses inROUTARR_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:
typenow carries the outcome Apprise reads (failure,warning,
successorinfo), 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: runscin
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-accountsets 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
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=externalornone, 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/routesays where a title would go, by its TMDb, TheTVDB or IMDb id, even when your Arrs do not hold it yet. Nothing is stored, andenrich=trueasks the metadata sources first.PUTandDELETE /api/v1/overrides/externalpin a title to a category, or unpin it, by the same ids.GET /api/v1/mediafilters bytmdb_id,tvdb_idandimdb_id.- A simulation, an apply, a revert or the sync of one instance can answer
202at once, when asked withPrefer: respond-async. The task keeps its report forGET /api/v1/jobs/{id}. - Each warning from
/api/v1/statuscarries a stablecode.
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,timestampanddata(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
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_upandroutarr_instance_enablednow name an Arr instance witharr_instance(its name) andarr_instance_id. The oldinstancelabel was renamedexported_instanceby Prometheus, and two instances sharing a name were merged into one series. Update any dashboard or alert that filters oninstanceorexported_instance. ROUTARR_AUTH. Onlyapikey,forms,oidc,externalandnoneare read. The undocumented spellingsdisabledandrequirednow fall back to the API key: an installation that setROUTARR_AUTH=disabledasks for its key until the value readsnone.move_files_defaultis 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
formsandoidcmodes 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
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
462a9aftoa8a5f0ain /.devcontainer in the devcontainer-images group by @dependabot[bot] in #36 - build(deps): Bump alpine from
28bd5feto294b683by @dependabot[bot] in #37 - build(deps): Bump rust from
1716b3ato7cc1c22by @dependabot[bot] in #38 - build(deps): Bump node from
50c8e8ctoebfe2f9by @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
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
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
- @Njal57 made their first contribution in #6
- @dependabot[bot] made their first contribution in #1
Full Changelog: https://github.com/Routarr/Routarr/commits/v0.1.0