# 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` (container `jellyfin-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 1. **Edit** files locally under `/home/admin/projects/media-acquisition/`. 2. **Push to Forgejo** (this repo's authoritative remote is `git.s8n.ru/s8n/media-acquisition.git`). 3. **On nullstone**: `cd /opt/docker/media-acquisition && git pull && docker compose up -d`. 4. **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 `. - Bot/automation commits (e.g. catalog service, scripted edits): `obsidian-ai `. 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 1. Read this file. 2. Read `docs/architecture.md` if working on compose or catalog code. 3. Check `git status` and `git remote -v` (must show `git.s8n.ru/s8n/media-acquisition.git`). 4. Owner says what they want; ship + verify kill-switch + commit to Forgejo. 5. 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 `; 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. |