qbittorrent: placed config, the build ace runs, a login 5.2 accepts, its ports as announced #168

Closed
mesh-admin wants to merge 4 commits from feat/qbittorrent-for-ace into main
Contributor

Prepares qbittorrent for ace (conversion only — nothing assigned).

Changes

  • config dir pathless ${dir:config}; runtime config + route binding in placed state dir
  • image pinned to the digest ace runs, 5.2.3_v2.0.14-ls477 (sha256:457e4eec…); the old pin ls474 was older than running
  • client.ts: login works against qBittorrent 5.2 (204 with no body; cookie QBT_SID_<port> instead of SID) and still against older builds; user read from qBittorrent.conf (WebUI\Username), not assumed "admin"
  • web listens on 8112 (WEBUI_PORT) published as 8112:8112: qBittorrent refuses a Host header naming a port other than its own (verified: "Invalid Host header, port mismatch" → 401), so a caller dialling a different machine port never gets in. 8112 is ace's and HAL's number and clear of unifi's 8080
  • torrent port declared: peers 6881/tcp and peers-udp 6881/udp, long-form, since the client announces the number. (Note: the short form "N/udp" is passed through unmapped by publishedOn — #148 uses it for unifi)
  • provides qbittorrent-api (node scope) and serves scheme/port/url-base/username; password = operator-accepted pair credential (same model as #156)
  • route contribution qbittorrent → web; runtime dials ${port:8112} (same shape as #154)

Verified: catalogue tests with MESH_CATALOGUE (not skipped); render for ace with and without pins; throwaway of the pinned image on an ace-shaped conf: port mismatch refused, then on a same-number port the compiled client logged in, read the user from the conf, listed torrents, and was refused a wrong password and the default user; strict tsc + Dockerfile build.

Blocked for ace by hq 153: downloads access path (ace: /storage/downloads) and run-as uid:gid (ace: 1001:2000). Do not assign on ace before 153.

Prepares qbittorrent for ace (conversion only — nothing assigned). **Changes** - config dir pathless `${dir:config}`; runtime config + route binding in placed `state` dir - image pinned to the digest ace runs, 5.2.3_v2.0.14-ls477 (`sha256:457e4eec…`); the old pin ls474 was older than running - `client.ts`: login works against qBittorrent 5.2 (204 with no body; cookie `QBT_SID_<port>` instead of `SID`) and still against older builds; user read from `qBittorrent.conf` (`WebUI\Username`), not assumed "admin" - web listens on **8112** (`WEBUI_PORT`) published as `8112:8112`: qBittorrent refuses a Host header naming a port other than its own (verified: "Invalid Host header, port mismatch" → 401), so a caller dialling a different machine port never gets in. 8112 is ace's and HAL's number and clear of unifi's 8080 - torrent port declared: `peers` 6881/tcp and `peers-udp` 6881/udp, long-form, since the client announces the number. (Note: the short form `"N/udp"` is passed through unmapped by `publishedOn` — #148 uses it for unifi) - provides `qbittorrent-api` (node scope) and serves `scheme/port/url-base/username`; password = operator-accepted pair credential (same model as #156) - route contribution `qbittorrent` → `web`; runtime dials `${port:8112}` (same shape as #154) **Verified**: catalogue tests with MESH_CATALOGUE (not skipped); render for ace with and without pins; throwaway of the pinned image on an ace-shaped conf: port mismatch refused, then on a same-number port the compiled client logged in, read the user from the conf, listed torrents, and was refused a wrong password and the default user; strict tsc + Dockerfile build. **Blocked for ace by hq 153**: downloads access path (ace: `/storage/downloads`) and run-as uid:gid (ace: 1001:2000). Do not assign on ace before 153.
mesh-admin added 1 commit 2026-09-30 10:01:09 +00:00
The manifest named /services/qbittorrent/config and /var/lib/mesh/qbittorrent/
config.json, host paths ADR 0112 takes out of definitions. The config dir is now
pathless (${dir:config}); the runtime's config and route binding live in a
placed state dir.

The image is pinned to 5.2.3_v2.0.14-ls477, the digest ace runs; the old pin
(ls474) was older than the running build.

The tools could not log in to qBittorrent 5.2: it answers a good login with 204
and no body (not 200 "Ok.") and names its cookie QBT_SID_<port> (not SID). The
client now accepts both shapes and sends the cookie back under the name it was
set. It also read the user as "admin" always; it now reads WebUI\Username from
qBittorrent.conf on the read-only config mount.

qBittorrent refuses a request whose Host header names a port other than the one
it listens on. With the mesh publishing 8080 on another machine port, the tools
and every consumer dialling that port were refused. The WebUI now listens on
8112 (WEBUI_PORT, ace's and HAL's number, and clear of unifi's 8080) and is
published on the same number. The torrent port 6881 tcp+udp was not declared at
all; it is now, long-form, because the client announces it to peers.

sonarr, radarr and lidarr reached it by container name on HAL's shared network.
qbittorrent now provides qbittorrent-api (node scope: a download client must share
the consumer's spool) and serves scheme, port, url-base and username; the
password is the operator-accepted pair credential, as for #156. The web endpoint
is routed (label qbittorrent). The runtime dials ${port:8112}, as #154 does.

Verified: catalogue tests with MESH_CATALOGUE set (not skipped); rendered for ace
with pins and without (8112:8112, 6881:6881, 6881:6881/udp either way); a
throwaway of the pinned image on an ace-shaped conf showed the port-mismatch
refusal, then on a same-number port the compiled client logged in, read the user
from qBittorrent.conf, listed torrents and was refused a wrong password and the
default user; strict typecheck and the Dockerfile build pass.
jschoubben added 1 commit 2026-09-30 10:32:18 +00:00
The manifest published 8112:8112 and 6881:6881 because qBittorrent refuses
a Host naming another port and announces its torrent port to peers — so the
machine port must equal the software's. Fixing the number in the manifest
states a machine port in a definition. Instead the server runs on the host
network and listens where the mesh put it (WEBUI_PORT=${port:8112},
TORRENTING_PORT=${port:6881}): the two are equal by construction on every
machine, and an assignment still pins 8112/6881 where clients know them.

Verified: the pinned image on the host network with WEBUI_PORT=18710 and
TORRENTING_PORT=18711 served the web UI on 18710 (200, no Host mismatch)
and listened for peers on 18711 tcp+udp.
Author
Contributor

Review: the fixed 8112:8112 / 6881:6881 publishes stated machine ports in the manifest. Replaced (commit above) with host networking and WEBUI_PORT=${port:8112}, TORRENTING_PORT=${port:6881} — the software listens on the machine port the mesh assigned, so they are equal on every machine by construction; the ace assignment still pins 8112/6881. Tested with a throwaway container.

Review: the fixed `8112:8112` / `6881:6881` publishes stated machine ports in the manifest. Replaced (commit above) with host networking and `WEBUI_PORT=${port:8112}`, `TORRENTING_PORT=${port:6881}` — the software listens on the machine port the mesh assigned, so they are equal on every machine by construction; the ace assignment still pins 8112/6881. Tested with a throwaway container.
jschoubben added 2 commits 2026-09-30 11:18:03 +00:00
A host-network sidecar reaches its service over the machine's loopback, and
the mesh publishes that service on a machine port it assigns (ADR 0038) —
so dialling the software's port reaches whatever else holds it. On ace,
searxng's sidecar dialled 127.0.0.1:8080 and got unifi's inform port. The
same shape in bazarr, bookshelf, lidarr, nzbget, qbittorrent, radarr and
sonarr; each now asks with ${port:N} (hq 088). Found in review of ace's
module preparation.
# Conflicts:
#	modules/qbittorrent/module.json
Author
Contributor

Superseded: this module now lives in novox/mesh-media-catalog (the media chain, consolidated from #145–#168 in stack order; its non-media parts merged via #195). Closing.

Superseded: this module now lives in novox/mesh-media-catalog (the media chain, consolidated from #145–#168 in stack order; its non-media parts merged via #195). Closing.
mesh-admin closed this pull request 2026-09-30 19:45:28 +00:00

Pull request closed

Please reopen this pull request to perform a merge.
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#168