Skip to content

v1.34.0

Latest

Choose a tag to compare

@github-actions github-actions released this 06 Sep 00:16
· 3 commits to main since this release
f7ad3d6

A tidier interface, the second half of the audit, and the two things Discord asked for.

Twenty nine entries below, from thirty seven merged pull requests. The visible
change is the first pass at decluttering the interface: the Authors and Books
filter rows collapse into two menus, the rare per row actions move into an
overflow menu, and the app stops flashing light before it turns dark. Nothing
was taken away to achieve it; every option that was on those pill rows is still
there, one click further in, and the Filters button says how many are applied.

The rest is mostly the remainder of the tree scan that produced v1.33.4: four
more security fixes, a set of hot paths that were redoing work on every call,
and two controls that had never done anything being taken out of the interface
rather than left there promising behaviour that did not exist. The two features
both came from reports: a daily query cap for private trackers, and a log line
at the end of every automatic search so a silent sweep can be told apart from
one that never ran.

Two removals change what the API accepts. If you drive Bindery from a script,
read the Removed section before upgrading.

Added

  • Per-indexer daily query limit (#2312). Set a cap on Settings → Indexers and Bindery stops searching that indexer once it has been sent that many requests in a rolling 24 hours, rather than finding out the hard way when the tracker starts refusing. This is for private trackers with a daily API allowance: one scheduled sweep over a large library searches every wanted book against every indexer back to back, and on a big enough library that single burst could spend a whole day's allowance and lock the account out for everything else pointed at the same tracker. Raising the search interval never helped, because it changes how often the burst happens and not how big one burst is. The unit is requests rather than books, since one book costs between one and eight depending on how far the search has to fall back, and the Indexers tab shows how much of the limit is spent. Leave it blank for no limit, which is what every existing indexer gets. The count survives a restart, a capped indexer says so in the interactive search panel instead of just going quiet, and the Test button is exempt so it still works when you are trying to work out why an indexer has gone quiet. Thanks to DrCrypt for the report.
  • Every automatic search now logs when it finishes (#2154). A scheduled sweep, a bulk search or a series fill that found nothing used to log nothing at all, so "the search never ran" and "the search ran and found nothing" looked identical from the log. Each one now emits a single INFO line naming what started it, the book and format, how many indexers were queried, how many results came back, how many survived filtering, and how long it took. Thanks to SturmB for the report.
  • GET /series can be paginated (#2345). The endpoint returned every series with every linked book, so the response grew with the whole catalogue and a large library paid for all of it on every load. Pass limit and/or offset and you get the same {items, total, limit, offset} envelope authors and books already use. Pass neither and the response stays the bare array it has always been, so nothing that consumes it today has to change.
  • A startup warning when a second account exists without tenancy (#2358). BINDERY_ENFORCE_TENANCY is off by default, which is right for the single user install nearly everyone runs, but an operator who adds a second account through Settings had no way to learn that the two accounts share one library. Boot now logs one warning naming the variable when there is more than one user and the gate is off. It is a warning and not a gate: several accounts sharing a library is a supported setup, and changing the default would change what every existing multi user install does.

Changed

  • The Authors and Books filter rows are two menus instead of two rows of pills (#2427). Every option survives, including the monitored filter and the choice it remembers between visits. An applied filter shows twice over, as a count on the Filters button and as a chip beside it that clears it, so nothing is hidden by being one click in. In table view the Sort menu is gone entirely, because the column headers already sort.
  • Refresh and Delete on an author row moved into a menu (#2427). The monitored toggle stays where it was, since that is the control people actually use. The Discover card's own hand rolled menu now uses the same component and gains the keyboard handling it never had.
  • The setup checklist is a single strip (#2427), naming the next step rather than boxing all five, and it stops showing once only one step is left.
  • The Authors rating column is hidden when nothing on the page has a rating (#2427). Only OpenLibrary supplies author level ratings, so for a Hardcover or DNB sourced library it was a column of dashes. It still appears, and still sorts, wherever there is data.
  • Confirmation prompts are in app instead of browser dialogs (#2359). Twenty two actions asked for confirmation through the browser's own dialog, which is unstyled, shows the origin in its title bar and cannot be translated. Four of them were hardcoded English that never reached the translation files at all. Every prompt now uses the in app dialog that book deletion already used, and the "I understand" checkbox is kept for the three that genuinely earn it rather than applied to all of them.
  • Queue, Wanted and the Logs tab stop polling while the tab is hidden (#2360). Each polled every five seconds whether or not anyone was looking, so a queue left open in a background window made roughly 1,400 requests an hour with nothing to show for them. Polling now pauses when the tab goes away and does one immediate refresh when it comes back, so returning to the tab shows current data rather than data up to an interval stale.
  • Faster status reads, searches and wanted sweeps (#2340, #2341, #2344, #2346, #2370, #2407). Several hot paths were redoing work on every call. /system/status walked the entire image cache with one stat per file, which a 10,000 book library pays twice per cover, on a request the web UI makes from the root shell and four separate pages; the total is now memoised and adjusted by the exact delta after each cache write. The search filter compiled the same regular expression once per release and threw all but the first away, so a 500 result search compiled it 500 times. The wanted sweep reloaded the indexer list, the whole blocklist, the delay profiles and the preferred language once per book instead of once per sweep, and made seven separate status queries where one does. And the two startup backfills, which exist so that a change to the name normaliser re canonicalises existing rows, scanned the whole books and authors tables on every boot rather than only the boot after such a change. None of this changes what Bindery does, only how much it costs. Installs upgrading to this run each backfill once more and then settle.
  • File sizes render the same way everywhere (#2350). Seven near copies of a byte formatter had drifted into four behaviours: the same release read "800 MB" on Search and History and "800.0 MB" on Queue and Wanted, Queue and Wanted showed "0 KB" for a zero size where the others showed nothing, and Settings → Root Folders had no unit ceiling, so a root folder with a petabyte or more free rendered "1.1 undefined". There is now one implementation. Two small things change on screen: exactly 1 GiB reads "1.0 GB" on Queue and Wanted rather than "1024.0 MB", and Search, History and the book detail page gain the decimal on MB.
  • Dependency updates (#2176, #2291). go-chi to v5.3.2 and modernc.org/sqlite to v1.57.0 on the Go side; i18next, react-i18next, react-router and the build and test tooling on the frontend. Every package keeps the licence it had.

Fixed

  • The app no longer flashes light before turning dark (#2427). The theme class was applied from a React effect, which runs after the browser has already painted, so every route showed its light background for a frame first. It is now set before the first paint.
  • A mistyped or shared URL says so instead of showing an empty page (#2427). Any path the app did not recognise rendered the header and nav around nothing at all. /settings/indexers also works now: it redirects to the tab it names.
  • A malformed id in a request no longer reports success for work that did not happen (#2364). Five download client and root folder handlers parsed {id} and threw the error away, and a failed parse yields 0, so GET /downloadclient/abc looked up id 0 and answered "download client not found". A client that sent a bad id was told the resource does not exist. DELETE was worse: it ran the delete, the downloader eviction and the health drop against id 0 and then answered 204, so a typo in a script reported a successful delete that deleted nothing. All five now answer 400.
  • A redeploy no longer cuts off an author sync or a manual import mid flight (#2371, #2372). The author catalogue sync, the manual batch import and the reassign move ran on a context nothing cancelled or waited for, so a restart while one was running closed the database under it. The visible result was a half synced catalogue plus "database is closed" in the log, and on the import paths, files that had been moved with nothing in the database describing them. All three are now tracked and drained on shutdown the way a library scan already was.
  • Grab history records which indexer the release came from (#2368). It was recording the wrong id, so the history for a grab pointed at an indexer that had not supplied it.
  • Re-binding a book to an OpenLibrary work now refreshes its release date and language (#2306). An OpenLibrary work record carries neither field, so the single-work fetch behind Re-bind returned a book with no date and no language, and the stale values from the record being replaced survived the re-bind. The work is now enriched from the search index the way author sync already enriches every work it returns, so a re-bound book agrees with what a sync would have produced. The lookup is best-effort: if the search index is unreachable the re-bind still completes. Thanks to yarafie for the report.
  • Author aliases (#2268). A latin-script author (a pen name, or a two-author collaboration credit) no longer has other real authors' OpenLibrary alternate names saved as its aliases. Alternate-name aliases are kept only for authors whose primary name is non-latin, which is the case they exist for. This is the root of the alias collapse edsilberberg reported in #1684.
  • make test finishes again outside CI (#2293). The race detector cannot get internal/api (1,000 tests) through a single timeout budget, so the target ran for half an hour and then died in a goroutine dump instead of a test result. make test now mirrors the gating CI check exactly, and the race detector moved to make test-race, which runs CI's same six shards. Thanks to magrhino for the report.
  • The rTorrent deletion test passes on macOS (#2294). t.TempDir() there hands back /var/..., an alias for /private/var/..., and Bindery's delete guard refuses any path reached through a symlinked parent. The test now resolves its temp root the way the guard's own unit test already did. Production behaviour is unchanged. Thanks to magrhino for the report.

Removed

  • The "Downloading" book status filter, which always came back empty (#2374). The downloading and downloaded book statuses were never written by anything in Bindery, so filtering the Books page or an author's catalogue by either returned no books no matter how many downloads were in flight. Both statuses are gone, along with the filter controls, the status legend entries and the API validation that accepted them. The Queue page remains the place to see downloads in progress. Any book row that picked up one of these values from a script or a third party client is rewritten to "wanted" on upgrade, and the API now rejects them.
  • Quality profile Cutoff and "Allow upgrading to a better format" (#2373). Both controls are gone from Settings → Quality. Neither has ever done anything: no code path read either value, and Bindery has no upgrade re-grab sweep for a cutoff to gate, so an imported book is never searched again at any quality. The form was promising behaviour that did not exist, and nobody loses an upgrade they were getting. Your profiles are otherwise untouched: the stored values stay in the database and are still accepted over the API, so no migration runs and third party clients that send them keep working.

Security

  • Server filesystem paths in Settings are now admin only (#2361). GET /setting and GET /setting/{key} handed the Calibre library and binary paths, the import drop folder, the CWA ingest path, the ABS and Calibre path remaps and the last library scan summary to every authenticated account, including OPDS only readers, while GET /system/storage has always been admin gated for revealing exactly that. Reads of those keys now match the admin gate that already guarded writing them; admins see the real values and Settings is unchanged for them.
  • The Readarr database import now validates the URLs it imports (#2349). It was the one door into the indexer and download client tables that never ran the outbound URL check every other path uses, and its input is an uploaded database file rather than a form. A row pointing at a link local or internal address was created and then polled on a schedule, with whatever credentials came with it. Both loops now validate under the same policy the create and update handlers use, and a rejected row is named in the migration report with the reason instead of being silently created.
  • Prowlarr and OIDC discovery can no longer be redirected after the URL check (#2353). Both validated the address up front and then dialled whatever the name resolved to a moment later, which is a window a DNS answer can be changed inside. Both now install the same dial time guard the image proxy and the notifier already use, and hand back the shared proxy transport untouched when an outbound proxy is configured.
  • The image proxy refuses SVG (#2355). SVG is a scripting document, not an image, and it was being served same origin with the app. It is now rejected rather than proxied.
  • The container scan is meaningful again (#2425). golang.org/x/crypto moves to v0.56.0, closing two high severity advisories that had made the image scan red on every pull request for long enough that people had started treating it as noise. Nothing in Bindery reaches the vulnerable code (it uses the library only for password hashing), so there is no exposure to act on, but a check that is always red hides the one that matters. The runtime image also moves off Debian 12, which reached end of life: the scanner's own data for an EOL release is incomplete, so a clean result there was not evidence of a clean image.