Skip to content

Run ingest service python workers as abc, not root#1290

Open
haraldpdl wants to merge 1 commit into
crocodilestick:mainfrom
haraldpdl:fix-ingest-run-as-abc
Open

Run ingest service python workers as abc, not root#1290
haraldpdl wants to merge 1 commit into
crocodilestick:mainfrom
haraldpdl:fix-ingest-run-as-abc

Conversation

@haraldpdl
Copy link
Copy Markdown

Problem

With the documented PUID=103 PGID=112 configuration, cps.py (the web app) correctly runs as the abc user via s6-setuidgid abc in svc-calibre-web-automated/run. Inside cwa-ingest-service/run, the inotifywait invocation is also prefixed with s6-setuidgid abc.

Three other python3 invocations in the same script run as root:

  • python3 watch_fallback.py on the polling path (line 114)
  • timeout $safety_timeout python3 ingest_processor.py "$queued_file" in the retry loop (line 158)
  • timeout $safety_timeout python3 ingest_processor.py "$filepath" on the primary event path (line 216)

Because the actual ingest work (ingest_processor.py) runs as root, every auto-imported book lands in /calibre-library as root:root. The web UI, running as abc, then fails to delete these books:

Deleting book 66 failed: [Errno 13] Permission denied: '/calibre-library/Author Name/Book Title (66)/metadata.opf'

Fix

Prefix each of those three python3 invocations with s6-setuidgid abc, matching the pattern already used for inotifywait in the same file.

With PUID/PGID set (the documented configuration), cps.py already
drops to abc via s6-setuidgid in svc-calibre-web-automated/run,
and the inotifywait call in cwa-ingest-service/run is also
prefixed with s6-setuidgid abc. The three other python invocations
in the same run script (watch_fallback.py on the polling path and
the two ingest_processor.py calls) run as root, so every
auto-imported book lands in /calibre-library as root:root. The web
UI, running as abc, then fails to delete these books with
Permission denied on metadata.opf.

Prefix each of those python3 invocations with s6-setuidgid abc so
the polling watcher and the ingest processor run with the same
uid and gid as the rest of the stack, producing abc-owned files
that the web UI can manage.
@new-usemame
Copy link
Copy Markdown

Backported into Calibre-Web-NextGen v4.0.17 at 3d6c034. Drop-in image for users hitting this: ghcr.io/new-usemame/calibre-web-nextgen:latest — same compose, swap the image, restart.

This release ships the patch alongside a one-time chown migration that aligns /calibre-library and /cwa-book-ingest to abc:abc on first boot — without it, users whose libraries had pre-existing root-owned author directories would have hit Permission denied the first time the abc-uid ingest worker tried to land a new book in those dirs. The migration is gated by a sentinel marker (idempotent across reboots), and skipped under NETWORK_SHARE_MODE=true with logged guidance for the manual run. Eight integration tests in tests/integration/test_ingest_setuidgid_permission.py cover the regression scenario, the idempotent sentinel, the NFS-skip path, and end-to-end ingest after migration.

Thanks @haraldpdl.

lsimoneau added a commit to lsimoneau/monotrope that referenced this pull request May 9, 2026
Move Kobo→calibre ingestion off the LLM-driven Hermes skill onto a
deterministic Python cron in the calibre-web container, fired daily by a
systemd timer on the apps LXC. A rules-based title+author-surname matcher
(normalize → drop subtitle after `: ` → exact-wins-substring) resolves
the actual library 99/102 cleanly with no false positives, so the LLM
fuzzy-matching path was never doing real work.

State (`/var/lib/kobo-ingest/state.json`) is keyed by Kobo RevisionId
with five statuses: imported, pinned, removed_at_source, downloaded
(awaiting CWA pickup), multi_match. `pinned.json` covers the two calibre
records with corrupted metadata that would otherwise re-download forever.
Refunded/DNF-removed entitlements are filtered on
`BookEntitlement.IsRemoved` — kobodl surfaces them despite Kobo's UI
hiding them.

Also patches CWA's `cwa-ingest-service` to wrap the python workers with
`s6-setuidgid abc`. Upstream wraps only `inotifywait`; the pipe consumer
runs as root, which makes `calibredb add` fail with EPERM on
root_squash NFS and (off-NFS) lands books as `root:root` so the web UI
can't delete them. Same fix as crocodilestick/Calibre-Web-Automated#1290;
the build-time grep asserts exactly 4 wrap sites, so the build fails
loudly when that PR merges and the manual patch becomes redundant.

Drops the Hermes `kobo-ingest` seed skill and the `mcp_servers.calibre`
config block. Also moves the kobodl config volume from
`/root/.config/kobodl` to `/var/lib/kobodl` so it's reachable by the
runtime PUID (root's home is mode 0700).

Co-Authored-By: Claude Opus 4.7 <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.

2 participants