bookshelf: its config dir is placed, its image is the one ace runs #163

Closed
mesh-admin wants to merge 2 commits from feat/bookshelf-for-ace into main
2 Commits
Author SHA1 Message Date
jschoubben bd0c6b3dc9 bookshelf: reach nzbget, qbittorrent and jackett through the mesh
bookshelf reached its download clients as nzbget:6789 and qbittorrent:8112 and its indexers
through jackett:9117 or indexers.zurag.be - HAL container names and a public route,
typed into its database by hand. Nothing on the mesh answers those names.

It now requires nzbget-api, qbittorrent-api and jackett-api. A run-once step
(downloads/, declared last, restart-on its bindings, credentials and settings) writes
host, port, TLS, base path, user and credential into bookshelf through bookshelf's own API:

- A download client is the mesh's when its name is downloads.<provision>.name and its
  kind the provider's; it is registered when missing. A jackett feed is the mesh's when
  its host is the bound one, one the step bound before, or one in
  downloads.jackett-api.adopt-hosts; the jackett indexers in
  downloads.jackett-api.indexers are registered when missing. Every other entry is left
  alone, and nothing is ever deleted.
- Only connection fields, only when they differ. The stored password is masked, so the
  app tests the entry with the credential it holds; only if that fails is the delivered
  one written. Categories, priorities and "enabled" are never touched.
- Each credential is tried against its provider first. A minted value (nothing accepted
  yet) is never written; the step exits 1 naming the exact secret accept.

The step is byte-identical in sonarr, radarr, lidarr and bookshelf (the same Servarr
API, v3 or v1): each module builds from its own directory, so each carries a copy, and
test/downloads.test.ts fails if a sibling's copy differs.

Verified: strict typecheck, the Dockerfile build, 14 unit tests; and the compiled step
against fresh pinned sonarr/radarr/lidarr/bookshelf with throwaway nzbget, qBittorrent
and jackett - minted credentials refused with nothing written, accepted ones registered
and tested by the app, a migration-shaped radarr repointed, reruns unchanged.
2026-09-30 13:07:10 +02:00
jschoubben 287e5e6d3f bookshelf: its config dir is placed, its image is the one ace runs
The config directory was the stated path /services/bookshelf/config; it
is now a pathless directory the mesh places (0700, 1000:1000), and the
route binding lives in a placed state dir, as in jackett and searxng.
/var/lib/mesh/bookshelf stays for the broker secret.

The image is pinned to the digest ace runs (hardcover, 0.4.21.182,
ls36). The old pin was a February build: older than the database it
would open, which Readarr-family apps migrate forward only.

The sidecar dials ${port:8787}, the port the mesh assigned, not the
software's own (the same one-line change as #154).

Not carried from HAL: its install hook seeded config.xml with an API key
(the image writes its own on first start, and the data keeps it) and
registered nzbget, qbittorrent and jackett through the API with wrong
hosts and ports, so ace's bookshelf has no download client and no
indexer today. Wiring them is provisioning work that depends on the
download-client and jackett providers, and on where ace's books and
downloads live (hq 153).

Verified: catalogue tests with MESH_CATALOGUE; a scratch resolution with
ace's assignment composes books.zurag.be and keeps the port to the
mesh; a throwaway container at the pinned digest on a fresh 0700
1000:1000 dir answered /ping and /api/v1/system/status (401 on a wrong
key); client.ts typechecks strict, found the key in config.xml and read
the queue.
2026-09-30 11:58:37 +02:00