Run ingest service python workers as abc, not root#1290
Conversation
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.
|
Backported into Calibre-Web-NextGen v4.0.17 at This release ships the patch alongside a one-time chown migration that aligns Thanks @haraldpdl. |
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>
Problem
With the documented
PUID=103 PGID=112configuration,cps.py(the web app) correctly runs as theabcuser vias6-setuidgid abcinsvc-calibre-web-automated/run. Insidecwa-ingest-service/run, theinotifywaitinvocation is also prefixed withs6-setuidgid abc.Three other python3 invocations in the same script run as root:
python3 watch_fallback.pyon 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-libraryasroot:root. The web UI, running asabc, then fails to delete these books:Fix
Prefix each of those three python3 invocations with
s6-setuidgid abc, matching the pattern already used forinotifywaitin the same file.