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>
5.2 KiB
CLAUDE.md — media-acquisition
Read this at session start. Rules for managing the nullstone media-acquisition pipeline.
What this repo is
The BitTorrent + arr-stack + catalog-update pipeline that feeds the ARRFLIX
library on nullstone (Debian 13, 192.168.0.100). Consumed by:
- Jellyfin at
tv.s8n.ru(containerjellyfin-stock). - Catalog at
git.s8n.ru/s8n/beta-flix→playbooks/import-media/MEDIA-LIST.md.
Source map
docs/architecture.md Plan + reasoning. Read this BEFORE editing compose.
docs/migration.md onyx-qbt → nullstone-qbt migration runbook.
docs/trackers.md Tracker schema + IP-pinning risks (user-curated).
compose/docker-compose.yml gluetun + qbt + sonarr + radarr + prowlarr + catalog.
compose/.env.example Env template — secrets live in .env (gitignored).
compose/traefik/arr.yml File-provider routing for arr stack.
catalog/ betaflix-catalog Python service (Flask + webhooks).
scripts/ migrate-onyx.sh, add-tracker.sh, killswitch-test.sh.
Deploy lifecycle
- Edit files locally under
/home/admin/projects/media-acquisition/. - Push to Forgejo (this repo's authoritative remote is
git.s8n.ru/s8n/media-acquisition.git). - On nullstone:
cd /opt/docker/media-acquisition && git pull && docker compose up -d. - CRITICAL — verify kill-switch after every gluetun change:
bash scripts/killswitch-test.sh. If the second curl succeeds, you have a leak; tear down before re-trying.
Rules paid for in blood (mirrored from beta-flix where applicable)
Rule 1 — SSH user
user@nullstone. NOT admin@nullstone. AllowUsers was tightened
2026-05-03; uid 1000 only. Memory: [[feedback_nullstone_ssh_user]].
Rule 2 — Commit + push to my git
Authoritative remote is git.s8n.ru/s8n/media-acquisition.git (Forgejo).
No GitHub mirror. Always git remote -v before push. Memory:
[[feedback_always_commit_to_my_git]], [[feedback_check_remote_before_push]],
[[feedback_my_git_is_forgejo]].
Rule 3 — Commit identity
- Human commits:
s8n <admin@s8n.ru>. - Bot/automation commits (e.g. catalog service, scripted edits):
obsidian-ai <obsidian-ai@s8n.ru>.
Memory: [[user_git_identity]].
Rule 4 — Kill-switch is non-negotiable
Every change to gluetun service or VPN env vars MUST be followed by
scripts/killswitch-test.sh. A torrent client leaking outside the VPN is the
single failure mode that defines this project — do not "trust" the firewall
based on config inspection. Run the test.
Rule 5 — No secrets in repo
.env, WireGuard keys, Forgejo PATs, deploy keys: all gitignored. Use
.env.example to document variable names with placeholders. If you commit a
secret by accident, rotate it (Proton WG: regenerate key, update gluetun;
Forgejo PAT: revoke at git.s8n.ru/-/user/settings/applications).
Rule 6 — Tracker IP pinning
Private trackers may pin sessions to a single source IP. Switching from
onyx public IP → Proton exit IP will trip them. Before adding a new tracker
or migrating an old torrent, check docs/trackers.md for the per-tracker
policy. Update docs/trackers.md whenever a new tracker is on-boarded.
Rule 7 — XFS reflinks / hardlinks
/home/user/media is XFS, single device. Sonarr/Radarr "Use Hardlinks
instead of Copy" = ON. Catalog service may use cp --reflink=always for
divergent-perm scenarios (free inodes, zero block cost). Never cp plain;
that doubles disk usage and breaks seeding atomicity.
Canonical naming
Catalog rows pushed to beta-flix/playbooks/import-media/MEDIA-LIST.md follow
the ARRFLIX house style:
- TV:
Series Title (Year)— alphabetic by title, year tiebreaker. - Movies:
Movie Title (Year)— alphabetic by title. - "Source / Version" column = raw Sonarr/Radarr
sourceTitle(release name). Human edits to "Why on arrflix" stay; bot never overwrites that column.
The catalog service is append + merge only — never overwrites human-authored notes.
How to start a session
- Read this file.
- Read
docs/architecture.mdif working on compose or catalog code. - Check
git statusandgit remote -v(must showgit.s8n.ru/s8n/media-acquisition.git). - Owner says what they want; ship + verify kill-switch + commit to Forgejo.
- End every turn: commit + push to
git.s8n.ru/s8n/media-acquisition.git.
Glossary
| Term | Means |
|---|---|
| ship / deploy | git push to Forgejo → on nullstone, git pull && docker compose up -d. Kill-switch test on any gluetun change. |
| migrate | Phase-2 onyx→nullstone runbook in docs/migration.md. Read scripts/migrate-onyx.sh first; dry-run mode mandatory. |
| add tracker | scripts/add-tracker.sh <name> <url>; then update docs/trackers.md with IP-pinning policy + ratio requirements. |
| killswitch test | bash scripts/killswitch-test.sh. NEVER claim "VPN works" without running this. |
| owner | P (xynki.dev@gmail.com). Final say. Executive-override pattern from [[feedback_s8n_executive_override]] applies. |