Skip to content

fix(calibre-web): grant Guest download rights so OPDS readers can fetch books - #157

Merged
pofallon merged 1 commit into
mainfrom
fix/calibre-web-guest-download
Sep 19, 2026
Merged

pofallon merged 1 commit into
mainfrom
fix/calibre-web-guest-download

Conversation

@pofallon

Copy link
Copy Markdown
Contributor

What

The first-run seeder now grants the Guest user ROLE_DOWNLOAD and ROLE_VIEWER on top of ROLE_ANONYMOUS, and the README's per-user auth table is corrected. Bundle 1.2.0 → 1.2.1.

Why

Anonymous browsing (shipped in #155) makes every reader the Guest user, and Calibre-Web creates Guest with ROLE_ANONYMOUS (32) and nothing else. Guest can therefore list the library over OPDS while every book download answers 401 — an OPDS feed you cannot download from.

Evidence

Disposable VM, hola 0.11.0-rc.3, Authentik mode, this package installed unmodified at 1.2.0:

Request Before After
/opds (Traefik credential accepted) 200, catalog feed 200, catalog feed
/opds/download/2/epub/ 401, realm="calibre-web" 200, valid EPUB (PK…, 6 entries)
same download straight at the container, no header 401 200

That last row is the isolation: no gate involved, so it is the Guest role and not the Traefik credential. select name, role from user showed admin 479, Guest 32.

Guarding

The grant fires on every boot but only while the role is still Calibre-Web's untouched default, so an operator who edits Guest's permissions in the UI keeps their choice. Exercised in the real image (lscr.io/linuxserver/calibre-web:0.6.26-ls393) against a stand-in app.db:

first boot:                                    Guest 32 -> 290
second boot (must be a no-op):                 Guest 290
operator narrows Guest to 34, then reboots:    Guest 34   (left alone)

Unlike the settings write, this is not behind the first-run guard — existing installs are already past first run, and they have the same broken downloads.

README correction

The "what's still per-user underneath that gate" table said a reader could send its own Calibre-Web account credentials on /opds for a personalized view. It cannot: a request carries one Authorization header and on this path it belongs to Hola's gate, which strips it after verifying (try-hola/hola#455 — before that fix Calibre-Web received the platform credential, looked for a user named hola, and 401'd every reader that had just passed the gate). /opds is a single shared Guest view; /kobo/<token>/ stays per-user because its token rides in the URL path.

Depends on

try-hola/hola#455. Without it /opds 401s before the Guest role is ever consulted, so this change is necessary but not sufficient on its own.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh

…ch books

Anonymous browsing makes every reader the Guest user, and Calibre-Web creates
Guest with ROLE_ANONYMOUS (32) and nothing else. Guest can therefore LIST the
library over OPDS while every book download answers 401 — an OPDS feed you
cannot download from, which is the entire point of the feed.

Verified on a disposable VM (hola 0.11.0-rc.3, this package at 1.2.0,
Authentik mode): with the Traefik credential accepted, `/opds` returned the
catalog but `/opds/download/2/epub/` returned 401 with Calibre-Web's own
realm, and the same request straight at the container (no header at all) also
returned 401 — so it is the Guest role, not the gate. Granting ROLE_DOWNLOAD
and ROLE_VIEWER made the pushed EPUB download as a valid file.

The grant runs on every boot but only while the role is still Calibre-Web's
untouched default, so an operator who edits Guest in the UI keeps their
choice. Exercised in the real image: first boot grants (32 -> 290), a second
boot is a no-op, and a role an operator narrowed to 34 is left alone.

README: the "what's still per-user" table claimed a reader could send its own
Calibre-Web account credentials on /opds. It cannot — a request carries one
Authorization header and on this path it belongs to Hola's gate, which strips
it after verifying (try-hola/hola#455). /opds is a single shared Guest view;
Kobo stays per-user because its token rides in the URL path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh
@pofallon
pofallon merged commit a665fa5 into main Sep 19, 2026
5 checks passed
@pofallon
pofallon deleted the fix/calibre-web-guest-download branch September 19, 2026 02:04
pofallon added a commit to try-hola/hola that referenced this pull request Sep 19, 2026
Carries the protected-bypass fix (#455): Traefik now strips the credential it
verified on `protectedBypassPaths` instead of forwarding it to the app, so an
app that runs its own per-request auth on an exempted path — Calibre-Web's
OPDS and Kobo endpoints, the only catalog user of the feature — no longer
rejects every reader that just passed the gate.

Pairs with catalog calibre-web 1.2.1 (try-hola/apps#157), which grants the
Guest role the download right an OPDS reader needs once past the gate. Both
halves are required; neither works alone.


Claude-Session: https://claude.ai/code/session_01Vck5KSX2CLxhohx14nb5Sh

Co-authored-by: Paul O'Fallon <paulofallon@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant