docs: OKF v0.1 compliance pass + fact-check + 3 research briefs
- Migrate all 127 non-reserved docs to OKF v0.1 frontmatter (type/description/tags/timestamp); fix 13 YAML issues, 2 duplicate-FM blocks, strip reserved-page FM - Restructure log.md to OKF (## YYYY-MM-DD, newest-first) - Fact-check against scripts/AGENTS.md ground truth: fix 6 false 'Active' banners on retired runbooks, 96->114 GiB GTT, deployment.md vs deploy-plan.py/deploy-remote.sh, Deployment Flow frank-medium=Quadlet, Calibre 9.9.0->9.15.0 + CVE-2025-7404 conflation, Copyparty v1.20.24, RomM 5.3.0 breaking change - Move *arr docs to new FrankBook-Pro/ section (git mv, link-safe) - Write active runbook bodies from ground truth (frank-large, frank-small) - Add 3 research briefs + HTML report: Local TTS and Voice Cloning, AI Job Search and Resume Tools, Most Popular Self-Hosted AI Apps (Jun-Sep 2026) - Add upstream-version rows to ComfyUI/OpenWebUI/Hermes app docs - All 336 tests pass; 136 pages, 0 unresolved wikilinks
This commit is contained in:
1 parent
24f7a7772d
commit
93d1181fee
103 files changed
+2047
-4329
No files matched your search
@@ -0,0 +1,117 @@
|
||||
---
|
||||
type: reference
|
||||
title: qBittorrent
|
||||
description: qBittorrent download client for the *arr stack — pinned 4.6.7 on FrankBook-Pro, shares the Gluetun WireGuard netns, PBKDF2 WebUI credential recipe.
|
||||
tags: [qbittorrent, arr-stack, frankbook-pro, gluetun]
|
||||
timestamp: 2026-09-03T00:00:00Z
|
||||
---
|
||||
|
||||
# qBittorrent
|
||||
|
||||
qBittorrent is the torrent download client for the *arr stack. The stack runs on
|
||||
**FrankBook-Pro** (the Mac mini) as quadlet units inside its podman machine —
|
||||
not on Dankserver. It receives download requests from Sonarr, Radarr, Lidarr,
|
||||
and Readarr via category tagging.
|
||||
|
||||
**Host:** FrankBook-Pro (podman machine) — **Access:** WebUI on port `8080`,
|
||||
published by the [[Gluetun]] unit because qBittorrent shares that container's
|
||||
network namespace (a netns-sharer publishes no ports of its own).
|
||||
|
||||
## Facts
|
||||
|
||||
| Item | Value |
|
||||
|------|-------|
|
||||
| Image | `lscr.io/linuxserver/qbittorrent:4.6.7` — **pinned**, see the pin rationale below |
|
||||
| Network | `Network=gluetun.container` (torrent egress exits via the WireGuard/ProtonVPN tunnel) |
|
||||
| Ports | 8080 (WebUI) and 6881 TCP+UDP published on the gluetun unit |
|
||||
| Env | `PUID=0`, `PGID=0`, `TZ=America/Los_Angeles`, `UMASK_SET=022` |
|
||||
| Volumes | `/Volumes/COMPLETE/qbittorrent-data:/config`, plus `/Volumes/COMPLETE` and `/Volumes/INCOMPLETE` mounted at identical paths |
|
||||
| Auth | Web UI username/password (`admin` + generated password); LAN bypass not used |
|
||||
|
||||
## Why the 4.6.7 pin exists (2026-09-01)
|
||||
|
||||
Readarr's abandoned qBittorrent client rejects qB ≥ 5's **204 empty-body login
|
||||
success** as `Authentication failure` — a runtime break, not just an API-create
|
||||
issue. The fleet pins `lscr.io/linuxserver/qbittorrent:4.6.7` while Readarr is
|
||||
in the stack. **Never unpin to `latest` while Readarr is in the fleet.**
|
||||
|
||||
Second trap on any version flip: qB 5.x's shutdown conf rewrite leaves a
|
||||
`WebUI\Password_PBKDF2=@ByteArray(...)` line that 4.6.7 rejects — every login
|
||||
then returns "invalid credentials" (the arrs included). Fix = rewrite the same
|
||||
conf line with the same password while qB is stopped.
|
||||
|
||||
## Persistent WebUI credentials (verified 2026-08-30)
|
||||
|
||||
qBittorrent 5 mints a **new temporary password at every start** unless a
|
||||
password is stored in `qBittorrent.conf`. That rotation silently breaks every
|
||||
arr download client after each reboot. The Web-API has no password endpoint
|
||||
any more (`/api/v2/app/setPassword` was removed), and the linuxserver image
|
||||
has no `WEBUI_AUTH` handling — so persistence must be written to the conf
|
||||
directly, in qb's own `salt:hash` format (`src/base/utils/password.cpp`:
|
||||
PBKDF2-HMAC-SHA512, 100 000 iterations, 16-byte salt = 4×uint32, then
|
||||
`base64(salt) + ":" + base64(hash)` as the raw value):
|
||||
|
||||
```python
|
||||
import base64, hashlib, os
|
||||
salt = os.urandom(16)
|
||||
h = hashlib.pbkdf2_hmac("sha512", password.encode(), salt, 100000, 64)
|
||||
val = (base64.b64encode(salt) + b":" + base64.b64encode(h)).decode()
|
||||
# with the container STOPPED, in [Preferences]:
|
||||
# WebUI\Username=admin
|
||||
# WebUI\Password_PBKDF2=<val>
|
||||
```
|
||||
|
||||
Then `podman start qbittorrent` and confirm login survives a
|
||||
`podman restart` — the temp-password rotation stops the moment stored
|
||||
credentials verify. The chosen value lives host-local in `/home/dank/.qbpass`
|
||||
(mode 600, owner `dank`) — never in git; every arr download client and
|
||||
[[Prowlarr]] reference it. (The classic `@ByteArray(salt+hash)` form is WRONG
|
||||
for qb5: login 401s on it; verify() base64-decodes the two halves separately.)
|
||||
|
||||
## Deployment (current)
|
||||
|
||||
Quadlet unit `qbittorrent.container` from this repo
|
||||
(`frankbook-pro/qbittorrent/quadlet/`), installed inside the podman machine at
|
||||
`/home/core/.config/containers/systemd/qbittorrent.service` by the
|
||||
frankbook-pro handler in `scripts/deploy-remote.sh`:
|
||||
|
||||
```ini
|
||||
[Container]
|
||||
ContainerName=qbittorrent
|
||||
Image=lscr.io/linuxserver/qbittorrent:4.6.7
|
||||
Network=gluetun.container
|
||||
Environment=PUID=0
|
||||
Environment=PGID=0
|
||||
Environment=TZ=America/Los_Angeles
|
||||
Environment=UMASK_SET=022
|
||||
Volume=/Volumes/COMPLETE/qbittorrent-data:/config
|
||||
Volume=/Volumes/COMPLETE:/Volumes/COMPLETE
|
||||
Volume=/Volumes/INCOMPLETE:/Volumes/INCOMPLETE
|
||||
|
||||
[Service]
|
||||
TimeoutStartSec=900
|
||||
Restart=always
|
||||
```
|
||||
|
||||
## Gotchas (verified live)
|
||||
|
||||
- Temporary-password rotation on every boot is the default state of an
|
||||
untouched qb5 — see the persistence recipe above; without it, all arr
|
||||
download clients silently fail queue adds ("Couldn't add release … to
|
||||
download queue" in the arr logs) after any restart.
|
||||
- API login from `curl` needs a `Referer: http://<host>:<port>/` header
|
||||
(CSRF protection) or POSTs get rejected even with a valid SID.
|
||||
- Cookie-name quirk by version: qB 5.x names its WebUI session cookie
|
||||
`QBT_SID_<port>`; 4.6.7 uses plain `SID`. Any automation must parse the
|
||||
cookie generically (e.g. `grep -oE '[A-Za-z_]*SID[A-Za-z0-9_]*=[^;[:space:]]*'`)
|
||||
instead of hard-coding a port-suffixed name, or it fails with 403 cascades
|
||||
that never reach qB as failed logins.
|
||||
- Egress verification for this stack must run **inside the gluetun container**
|
||||
(`podman exec gluetun wget -qO- https://ipinfo.io/json`); running the same
|
||||
probe one ssh-context out returns the residential IP — a false leak alarm.
|
||||
- Ensure umask is set to `022` so downloaded files are readable by arr apps.
|
||||
|
||||
## See Also
|
||||
|
||||
- [[ARR-Stack]] — full *arr stack documentation
|
||||
- [[Gluetun]] — the WireGuard tunnel this container shares
|
||||
Reference in new issue
Block a user