gates / gates (push) Successful in 6m27s
The catalog published qBittorrent and Transmission today (app-catalog 9081a47). Here: - two cards in the media category on both language sets, each with the project's own logo, each saying what a closed peer port costs (slower swarms, no incoming peers) - the published count 58 -> 60 on every page that states it, FAQ JSON-LD with its visible twin - documentation/audits/qbittorrent-2026-10-12/: both bench runs, both box walks and verdicts, verify-fixes.json, teardown-newapps.json, and the README brought to its final state - the 2026-09-30 walk harness: a `type: path` deploy field is filled with the drive ROOT — the controller refuses a path inside a drive (409), so the walker could not install any app with a drive field at all - R-988: the Usenet pair (SABnzbd, NZBGet), operator request, not started - STATUS.md and REPORT-newapps-2026-10-12.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0117kRTSgs3rJWUFGxyoWN6Y
89 lines
6.8 KiB
Markdown
89 lines
6.8 KiB
Markdown
# Download clients for the catalog — qBittorrent and Transmission (2026-10-12)
|
|
|
|
**Why.** Radarr, Sonarr and Seerr have been in the catalog for months and **none of them downloads
|
|
anything** — they decide what to fetch and hand it to a download client. There was no download
|
|
client. Radarr's own template said so in its prerequisites („Letöltő kliens szükséges (pl.
|
|
qBittorrent — külön telepítendő)") while the website promised „Automatikus film letöltő… a filmek
|
|
automatikusan a könyvtáradba kerülnek". Operator ruling, 2026-10-12: add them.
|
|
|
|
**Where this run got to. Both are PUBLISHED** — `app-catalog-felhom.eu` `9081a47`, the two templates
|
|
and their two complete onboarding records in one commit, with the README and FIRST-ADMIN rows, the
|
|
fixtures for both venues and the website cards. Each was measured twice before that: on the bench
|
|
(LXC 9401 on demo-hp) and on scratch guest 9202 **through the product**.
|
|
|
|
## What is in here
|
|
|
|
| | |
|
|
|---|---|
|
|
| `bench/` | qBittorrent on LXC 9401: image, telemetry, entrypoint, first admin, storage, ownership, health, memory, a real download, the config as the app rewrote it, and `MV-qbittorrent/` — the harness verdict for the ladder step |
|
|
| `transmission-bench/` | the same twelve questions asked of Transmission, plus `MV-transmission/` |
|
|
| `box/` | guest 9202 through the product: the two box walks, the per-app box verdicts under `qbittorrent/` and `transmission/`, the app logs, `verify-fixes.json` (the proof the defects below are gone) and `teardown-newapps.json` |
|
|
| `tools/` | `boxwalk_newapps.py` (deploy + stranger + sign-in + lock-out through traefik) and `verify_fixes.py` (the re-measure). Both reuse the 2026-09-30 harness for the session, CSRF and setup gate |
|
|
|
|
## Six defects, each found by measuring and fixed before publication
|
|
|
|
1. **qBittorrent was unreachable on its own public name.** `WebUI\ServerDomains` with a comma list
|
|
makes the app answer 401 „Invalid Host header" to everything, its own name included; semicolons
|
|
work **through the API** (the four-way test: comma 401/401, single 200/401, semicolon 200/200, `*`
|
|
200/200). Nothing upstream documents the separator.
|
|
2. **…and the config file's own dialect drops the extra entries anyway.** A semicolon list seeded
|
|
into `qBittorrent.conf` comes back from the app's first read-write cycle as the public name
|
|
**alone**, after which the controller's health probe — which dials by container name — is refused
|
|
(„Invalid Host header … 'qbittorrent:8080'"). The list moved into `after_install`, where the API
|
|
honours it, and the marker is printed only after both the list and the UPnP flag are read back.
|
|
3. **Both clients asked the household's router to open a port.** qBittorrent logs
|
|
`UPnP/NAT-PMP support: ON` and tries a mapping; Transmission ships `"port-forwarding-enabled": true`.
|
|
The box opens no port at home (`../architecture/01-topology-and-trust.md` §7), so both are off now.
|
|
qBittorrent's seeded `Session\UseUPnP=false` **is written and then ignored at first start** — the
|
|
app reported `"upnp": true` with that very line in its config — so it goes through `after_install`,
|
|
which the app does honour: `ON` at 17:21:51, **`OFF` at 17:21:56** (`box/verify-fixes.json`).
|
|
4. **Transmission could never become healthy.** It answers 401 on every path once authentication is
|
|
on, so `curl -f` always failed (`exit 22`). The probe accepts 200 or 401 now, with a negative
|
|
control that still fails.
|
|
5. **Transmission's host whitelist refused its own healthcheck** (400 to `Host: 127.0.0.1`). The
|
|
local names are admitted beside the public one.
|
|
6. **Two fixtures were reading the wrong thing.** The box fixture looked for a cookie named `SID`;
|
|
qBittorrent 5.x names it `QBT_SID_<port>`. The Transmission fixture read the session id with an
|
|
`awk` that matched the header NAME on a second line and so got 400 on every RPC; it uses an
|
|
anchored `sed` now, with the payload on stdin.
|
|
|
|
**And one finding that was not a defect.** The first box walk looked like a household lock-out.
|
|
Stranger and household ran from the same machine, so a shared ban could not be told from a
|
|
per-visitor one. Re-run from two addresses: the stranger is banned (`401 401 401 401 401 403`, and
|
|
403 on the right password) while **the household signs in at the same instant (204)**. qBittorrent
|
|
reads the real visitor address through the proxy headers; its log names `192.168.0.180`, not traefik.
|
|
Transmission does not ban at all.
|
|
|
|
## Does a download work with no inbound port? Measured, yes
|
|
|
|
The operator's question. With port mapping off and neither peer port published, each client was given
|
|
one real torrent (Big Buck Bunny, Blender Foundation, CC BY) through its **own** interface:
|
|
|
|
| | finished | where the files landed |
|
|
|---|---|---|
|
|
| qBittorrent | yes, inside a minute — `progress: 1`, `downloaded: 276610512` | `…/media/torrents/Big Buck Bunny/` (`bench/09-download.txt`) |
|
|
| Transmission | yes — `percentDone: 1.0`, `status: 6` (seeding) | `…/media/transmission/complete/Big Buck Bunny/` (`transmission-bench/05-download.txt`) |
|
|
|
|
Both files are the completed-state snapshots, which is why their peer counts read 0: the swarm work
|
|
was over. The tunnel is not involved either way — Cloudflare carries the app's **web page** inbound,
|
|
while peer traffic goes out through the household's own router by NAT. What a closed port costs is
|
|
incoming peers (slower on sparse swarms, less upload), and the app text now says so.
|
|
|
|
## State left behind — three layers, verified
|
|
|
|
- **Machine (guest 9202):** both apps removed **through the product** (`POST …/stop` then
|
|
`…/remove` with the drive data), `deployed: false` for both, the two config volumes gone, no
|
|
container left, and the two download folders deleted from the scratch drive by name. The guest's
|
|
standing apps (filebrowser, paperless) are untouched and running. `box/teardown-newapps.json`.
|
|
- **Catalog pointer:** the controller is back on the **live** catalog from this run's own saved copy
|
|
(`controller.yaml.pre-newapps1012`), the cache removed with it (R-615 — `repo_url` alone is inert),
|
|
and the restoration is proven from a **different channel than the controller's own verdict**
|
|
(R-286): the sha256 of `qbittorrent`, `transmission` and `grocy`'s `.felhom.yml` on the box equals
|
|
the sha256 of the same files at the published `origin/main`, with a decoy pair that must differ and
|
|
does. The config names the drill repo zero times.
|
|
- **Host (demo-hp):** bench LXC 9401 has no containers and no volumes and is **stopped**, as it was
|
|
found. The drill catalog `admin/app-catalog-drill` is reset to the live `main` (`9081a47`), so it
|
|
carries the published templates and no work in progress.
|
|
- **Hub:** nothing to undo — 9202 reports to no hub by its own configuration (the config header says
|
|
hub reporting OFF, no tunnel, no agent local API).
|