Self-hosted BitTorrent + arr-stack + catalog-update pipeline targeting
nullstone (Debian 13). Replaces the legacy onyx -> rsync -> import
round-trip.
Contents:
- README.md headline + ASCII architecture diagram + quickstart
- CLAUDE.md project rules (mirrors beta-flix style)
- .gitignore secrets dirs (.env, gluetun, qbt config, ssh keys)
- .gitleaksignore allowlist nullstone LAN addr + Tailscale CGNAT
- docs/architecture.md the plan in detail (gluetun + qbt + arr + catalog)
- docs/migration.md onyx-qbt -> nullstone-qbt runbook (3 phases)
- docs/trackers.md tracker schema + IP-pinning + ratio notes (user-curated)
- compose/docker-compose.yml gluetun v3.40 + qbt 5.0.5 (netns=gluetun) +
sonarr/radarr/prowlarr (hotio) + betaflix-catalog
- compose/.env.example documented env-var template (no secrets)
- compose/traefik/arr.yml file-provider for qbt/sonarr/radarr/prowlarr
.s8n.ru subdomains, LAN+TS only via
trusted-only@file + authentik-forwardauth@file
- catalog/catalog.py Flask service, ~340 LoC, /sonarr + /radarr +
/healthz; pulls beta-flix, inserts alphabetic
row into MEDIA-LIST.md, writes run log, commits
+ pushes as obsidian-ai. Idempotent via
payload-hash cache.
- catalog/Dockerfile python:3.12-slim + git + tini
- catalog/requirements.txt flask + jinja2 + requests + gitpython + pyyaml (pinned)
- catalog/templates/*.j2 run log + catalog row Jinja templates
- catalog/README.md service docs
- scripts/migrate-onyx.sh phase-2 helper (rsync + .torrent ship, dry-run by default)
- scripts/add-tracker.sh Prowlarr API helper
- scripts/killswitch-test.sh gluetun kill-switch verification (3 steps)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
107 lines
4.3 KiB
Markdown
107 lines
4.3 KiB
Markdown
# media-acquisition
|
|
|
|
Self-hosted BitTorrent + arr-stack + canonical-import pipeline that lands torrents
|
|
directly on **nullstone**, through a Proton WireGuard VPN with verified kill-switch,
|
|
hardlinks files into the existing ARRFLIX library, and auto-commits catalog rows
|
|
to `git.s8n.ru/s8n/beta-flix`.
|
|
|
|
Replaces the legacy `onyx → rsync → nullstone` round-trip.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
+-----------------+
|
|
| Proton VPN |
|
|
| (WireGuard) |
|
|
+--------+--------+
|
|
| wg0
|
|
v
|
|
+------------+ indexer queries +-------------------+ torrent traffic
|
|
| Prowlarr |-------------------->| gluetun |<------------------+
|
|
+-----+------+ (via netns) | kill-switch fw | |
|
|
| +-------------------+ |
|
|
| search ^ ^ ^ |
|
|
v | | | |
|
|
+------------+ grabs +----------+ | +----------+ |
|
|
| Sonarr/ |----------->| qBittorrent (netns=gluetun) |
|
|
| Radarr | | /home/user/media/_downloads/{incomplete,complete}
|
|
+-----+------+ +-------------------------------------------------+
|
|
|
|
|
| OnImport webhook (POST /sonarr or /radarr)
|
|
v
|
|
+--------------------+
|
|
| betaflix-catalog |--+ XFS reflink/hardlink into /home/user/media/{movies,tv}
|
|
| (Flask, Python) | |
|
|
+--------+-----------+ +--> Jellyfin (tv.s8n.ru) picks up new items
|
|
|
|
|
| git commit + push (obsidian-ai)
|
|
v
|
|
+-----------------------------+
|
|
| git.s8n.ru/s8n/beta-flix |
|
|
| playbooks/import-media/ |
|
|
| MEDIA-LIST.md (updated) |
|
|
| runs/<slug>.md (new) |
|
|
+-----------------------------+
|
|
```
|
|
|
|
Single XFS filesystem at `/home/user/media` → hardlinks / reflinks are free.
|
|
|
|
## Quickstart
|
|
|
|
```bash
|
|
# Clone on nullstone
|
|
ssh user@nullstone
|
|
git clone https://git.s8n.ru/s8n/media-acquisition.git /opt/docker/media-acquisition
|
|
cd /opt/docker/media-acquisition/compose
|
|
|
|
# Configure
|
|
cp .env.example .env
|
|
${EDITOR:-vi} .env # fill in PVPN_WG_PRIVKEY, PVPN_WG_ADDRESSES, FORGEJO_PUSH_TOKEN, etc.
|
|
|
|
# Bring up
|
|
docker compose up -d
|
|
|
|
# Verify VPN kill-switch (CRITICAL — do not skip)
|
|
bash ../scripts/killswitch-test.sh
|
|
|
|
# Sanity: pick a sacrificial legal torrent in qbt UI, confirm it lands in
|
|
# /home/user/media/_downloads/complete/ and arr stack hardlinks it.
|
|
```
|
|
|
|
## Layout
|
|
|
|
```
|
|
README.md This file.
|
|
CLAUDE.md Project rules for Claude Code.
|
|
docs/
|
|
architecture.md The plan in detail. Decision log + reasoning.
|
|
migration.md onyx-qbt → nullstone-qbt migration runbook.
|
|
trackers.md Tracker schema + IP-pinning notes (user fills in).
|
|
compose/
|
|
docker-compose.yml Full stack: gluetun + qbt + sonarr + radarr + prowlarr + catalog.
|
|
.env.example All env vars documented.
|
|
traefik/arr.yml Traefik file-provider for *.s8n.ru subdomains (LAN+TS only).
|
|
catalog/
|
|
catalog.py Flask webhook receiver → beta-flix catalog updater.
|
|
Dockerfile python:3.12-slim base.
|
|
requirements.txt Pinned versions.
|
|
templates/ Jinja2 templates for run logs and catalog rows.
|
|
README.md Catalog service docs.
|
|
scripts/
|
|
migrate-onyx.sh Phase-2 migration: rsync + .torrent mass-add.
|
|
add-tracker.sh Helper: add tracker to Prowlarr via API.
|
|
killswitch-test.sh Verify gluetun blocks traffic when VPN drops.
|
|
```
|
|
|
|
## Related
|
|
|
|
- Plan: `docs/architecture.md`
|
|
- Catalog target: `git.s8n.ru/s8n/beta-flix` (`playbooks/import-media/MEDIA-LIST.md`)
|
|
- Jellyfin (consumer): `tv.s8n.ru` (`jellyfin-stock` container on nullstone)
|
|
- Host docs: `ai-lab/SYSTEM.md`
|
|
|
|
## Status
|
|
|
|
Scaffold. Live deploy pending VPN slot allocation + tracker IP-pinning review.
|
|
Next step: fill in `compose/.env` and bring up gluetun + qbt only (no arr yet)
|
|
to validate kill-switch.
|