# Status & Roadmap Where the project is and the de-risked path to the full vision. Architecture is in [architecture.md](./architecture.md); the mesh design origin is [`../.project/history/20260608_fleet-manager-mesh-design.md`](../.project/history/20260608_fleet-manager-mesh-design.md). ## Status at a glance | Area | Status | Note | |---|---|---| | App — playback (VLC / mpv / QuickTime) | ✅ Shipped | `blacktv` retired → mpv; no NFS — local players play downloads, else route to black | | App — library browser | ✅ Shipped | black index fast-path + local `MEDIA_ROOTS` walk + registry fallback (no NFS) | | App — downloads (search + transmission) | ✅ Shipped | via `mcp` CLI; search needs FlareSolverr | | App — metadata enrichment + artwork | ✅ Shipped | regex parse + TMDB/IMDb/keyless + ffmpeg frame-grab | | App — all UI (Home/Player/Library/Search/Downloads/Metadata/Adult/Devices/Logs/Settings) | ✅ Shipped | wired, no TODO/FIXME/`fatalError` debt | | App — device registry (Devices tab) | ✅ Shipped (registry only) | `DeviceConfig`: type→services presets mapping to fleet classes, per-device load badge; duty *engine* still unbuilt (see fleet below) | | App — adult content tab | ✅ Shipped | `porn-rotation.py` ported to `PornCollectionService`; `ENABLE_ADULT` compile flag + runtime `pornFeature` setting, concealed by default | | App — VPN subsystem | ✅ Shipped | OVPN profile/credential stores (Keychain), controller, settings UI | | App — offline cache + Now Playing/media keys | ✅ Shipped | `OfflineCacheController`, `NowPlayingController`, bandwidth policy | | iOS app (`TVAnarchyiOS`) | ✅ Shipped (companion) | VLCKit player, library, downloads, remote control via HTTP bridge (default `:8787`); the bridge *server* is not in this repo's `mcp/` tree | | Distribution (release/update) | ✅ Shipped | `tools/release.sh` → Forgejo on forge.black; `tools/update.sh` installs/updates any node without a toolchain | | `governor` (`portable-net-tv`) | ✅ Shipped (single-host) | watch tracking + prefetch buffer | | `mcp` (`plum-control-mcp`) | ✅ Shipped | VLC / black-tv / transmission / display tools | | `recommender` | ✅ Shipped | enrichment + local recs | | MLX title refiner | ✅ Shipped | `LocalLLMTitleRefiner` → `media_rec/title_refiner.py` (MLX Qwen); cached, self-disabling, wired at app startup | | `governor` → fleet orchestrator (stage 1) | ✅ Shipped | `governor/src/fleet/`: registry ingest, duty assignment, custody floor-check, zombie reaper, **re-pin actuation** (`fleet repin --apply`: rsync holder→target + recorded holdings), capacity probes (`fleet probe`: ssh df + EWMA uptime), periodic daemon (`fleet daemon`), research feed into `mcp` search | | `peers_for` / `custodians_of` (stage 3 core) | ✅ Shipped (single-fleet) | source model + both policy gates enforced; unions fleet ∪ seedbox ∪ live DHT, provenance-tagged; served over HTTP by `fleet serve` (`/peers_for/`, bearer-token) — runnable on any node until a real broadcast host exists | | Fleet WireGuard fabric (plane 1) | ❌ Designed | blocked on the `10.9.0.4` open question + root on each node | | Seedbox source + off-home face | ❌ Blocked external | engine supports the class/duty today; needs an actual provisioned seedbox | | Friend-mesh / F2F relay (stage 4) | ❌ Designed | needs other fleets to exist | | Private-tracker source (stage 5) | ❌ Designed | gates already enforced in `peers.ts`; ships last, after F2F is battle-tested | | Discord planes | ❌ Designed | control/QA/announce + availability bot; needs bot tokens/servers | | Multi-identity / cross-fleet | ❌ Designed | single-fleet foundation must land first | **Verdict:** the media-client layer is complete and production-grade. The fleet/mesh layer now has its single-fleet core (registry → duties → custody → reaper → `peers_for`) implemented and tested in the governor; what remains is actuation (cross-host copies), the WG fabric, and every stage that requires infrastructure outside this repo (seedbox, friends, Discord). ### Repo-state note The `PlumTV → TVAnarchy` rename and the helper subsystems (`governor/`, `mcp/`, `recommender/`, `search/`, `fleet/`, `tools/`) were committed to `main` on 2026-06-09 as a series of atomic commits (`41afc1c`…`b44b5a2`). Stage 0 (hygiene) below is **done**. ## Remaining work — detail ### ~~MLX title refiner~~ — ✅ done 2026-06-09 `LocalLLMTitleRefiner` (Swift) shells into `media_rec/title_refiner.py` (MLX Qwen 1.5B, same model as the show grouper), consulted only for degenerate (<2-char) regex titles. Results are disk-cached (`~/.local/state/tv-anarchy/title-refinements.json`); two consecutive subprocess failures disable it for the session so a scan never pays repeated timeouts when MLX is absent. Wired at app startup. (The seam itself had a wiring bug — the raw-name fallback ran *before* the refiner check, making it unreachable — fixed in the same change.) ### governor → fleet orchestrator — ✅ engine + read-only actuation done 2026-06-09 `governor/src/fleet/` implements stage 1: registry ingest (the app-side `fleet.json` array is authoritative, `devices.json` the fallback), deterministic duty assignment with the spec's invariants, the N-copy custody floor-check with rolling-baton custodianship + re-pin planning, and the zombie reaper (`healthy | stalled | dead`, mesh-first recovery, public re-search fallback). CLI: `portable-net-tv fleet status|duties|custody|repin|reaper|peers|probe|daemon|serve` (all `--json`). `reaper --apply` performs only safe idempotent transmission nudges (reannounce/verify). **Completed 2026-06-09 (later, parallel agent team):** the actuation layer — `fleet repin --apply` executes re-pin plans (rsync on the target pulling from the holder, recorded holdings feed back into the floor-check), `fleet probe` measures disk/uptime over ssh (EWMA score; feeds custody disk-eligibility), `fleet daemon` runs the duties→floor→reaper tick periodically for launchd, and `fleet serve` exposes `/registry /custody /reaper /peers_for/` over HTTP with optional bearer auth (the broadcast-host service, runnable on any node). Research actions build real `mcp` search invocations (`fleet repin`/reaper plans name them; execution behind apply flags). Remaining inside stage 1: nothing — what's left is infrastructure (targets need `ssh` + `mediaRoot` in `fleet.json` for repin to have somewhere to copy to) and the launchd plist itself. ### fleet WireGuard fabric (plane 1) — blocked on a decision The spec promotes a scoped fleet WG mesh (`tva0`, fleet-subnet-only `AllowedIPs`, collision-probe) to a foundational stage, but its design is explicitly blocked on the open question of what `10.9.0.4` is today (general overlay vs ad-hoc tv-anarchy overlay), and bring-up needs root on every node. Decide, then build. ### stages 2/4/5 + Discord — blocked on external infrastructure The engine already models seedboxes (class, duty preference, source defaults) and enforces the private-tracker gates; what doesn't exist is the infrastructure: a provisioned seedbox, friends' fleets for F2F, private-tracker credentials, Discord bots/servers. None of this is code in this repo until those exist. ### `blackd` — black as a real service (no ssh on the control path) — planned milestone ssh-as-transport is the app's load-bearing design debt: player control is a fresh `ssh → sudo socat → /tmp/mpv.sock` pipeline per command (`MpvTarget`), the library index is `ssh cat`, launch/stats/releases/restart are ssh-invoked `black-tv` verbs (whose deploy drift required the `helper_sha` badge), and the offline cache rsyncs over ssh. Consequences: every keyless client needs a proxy (the iOS app exists only via the plum `:8787` bridge; a Roku/web client can't ssh at all), every capable client reimplements ssh plumbing, and the `sudo socat` hack exists purely because the far socket is root-owned. The counter-examples are already in the stack: transmission (HTTP RPC :9091) and the Roku's ECP are the robust integrations. **Design:** one daemon on black — HTTP on LAN + WG overlay, bearer token: - `/player/*` + WebSocket events — local unix-socket access to mpv (root problem dissolves; no per-command process spawn) - `/library/index` — replaces `ssh cat` - `/media/` (HTTP range now, on-demand HLS remux later) — the SAME media plane the Roku channel / web client milestone needs; one investment - `/launch`, `/stats`, `/releases`, `/restart` — the `black-tv` verbs - `/version` — replaces helper_sha drift detection **Migration (each phase shippable):** 1) daemon wraps the existing pieces (systemd unit; deploy via the existing helper mechanism); 2) `BlackdTarget` (PlayerTarget/MediaLaunchable over HTTP, like VLC's) preferred with ssh fallback; 3) retire ssh paths + the plum iOS bridge (iOS talks to black directly). ssh remains for deploys/admin only. ### Roku dev channel — planned milestone The living-room display has a Roku Streaming Stick 4K (`10.0.0.233`, ECP) alongside black's HDMI input. ECP **transport control is done** (HostKind `.roku` / `RokuTarget` — pause/jump-back/exit/now-playing from the Devices tab). The channel itself — a couch-native TVAnarchy UI *on the Roku* — needs: 1. **HTTP media plane on black** — Roku only plays HTTP(S) streams; today the media is NFS/ssh. A file server plus on-demand ffmpeg remux/transcode→HLS for the incompatible tail (mkv with FLAC/Opus audio, PGS subs). This is the heavy piece and is useful beyond Roku (any future web/TV client). 2. **BrightScript/SceneGraph client** — library browse (black index over HTTP) + Video node playback. New codebase, no Swift reuse. 3. **Release pipeline** — `TVAnarchy--roku.zip` asset + a push-deploy step (dev channels are uploaded TO the stick: `curl --digest -u rokudev: -F archive=@channel.zip http://10.0.0.233/plugin_install`). One dev-channel slot, persists indefinitely; dev mode enable = ECP keypress sequence. Store publishing is out: Roku killed non-certified channels (2024) and certification would reject the torrent surfaces regardless of `ENABLE_ADULT`. Sideload-only, which the forge model already fits. ## Build order (de-risked; each stage independently shippable) From the design spec. Each stage ships on its own; later stages depend on earlier. 0. ~~**Hygiene (prerequisite)**~~ — ✅ done 2026-06-09: the `PlumTV → TVAnarchy` rename and the helper subsystems are committed on `main`. 1. ~~**Host registry + duty assignment, single fleet**~~ — ✅ done 2026-06-09 (engine): app-side registry (Devices tab, `fleet.json`) + governor-side duty engine, custody floor-check, and reaper (`governor/src/fleet/`). Remaining inside stage 1: re-pin *actuation* (cross-host copy execution) and running `fleet duties`/`reaper` under launchd instead of on demand. 2. **Seedbox source + `public_swarm_face` duty.** Adds an always-on custodian; proves the source model on the zero-risk source first. ("Add a seedbox" is the single most important onboarding step for non-power-users — most multi-device fleets have zero always-on nodes otherwise.) 3. ~~**`peers_for()` over local sources**~~ — ✅ core done 2026-06-09: `fleet peers ` unions fleet holders ∪ seedbox ∪ live DHT peers, provenance-tagged, with both private-tracker policy gates enforced (`search_only` default-closed; `f2f_only` forced un-overridably). Remaining: a broadcast host to *serve* `peers_for` to other devices (today it's a local CLI query). 4. **Friend-mesh source + F2F relay.** Federates; the custody floor goes cross-fleet. Multi-identity lands here. 5. **Private-tracker source, default-closed.** LAST and highest-consequence: `swarm_isolation` forced to `f2f_only`, un-overridable; `content` share is an account-killer for the source user. Ship only once F2F isolation is battle-tested on safe sources. **Parallel, any time:** ~~MLX `TitleRefiner`~~ — ✅ done 2026-06-09. **After a working mesh:** the Discord planes — control/ops + QA/community + release announcements (project home server) and content/availability (private mesh + a bot inside users' *own* servers, never the public home server). Discord is a surface you notify, not a backbone you route through: custody/reaper/F2F run over WireGuard regardless, and a Discord ban must not take the mesh down. ## Non-goals / explicitly deferred (not defects) - **`BlackTVTarget` removed.** Intentional — auto-migrated at runtime to `MpvTarget` with `CommandsConfig.blackTVDefaults(bin:)`. Black-TV control still works. - **No ratio enforcement.** Resolved decision (2026-06-08): ship gift-economy, build the `custody_capacity` knob, leave it off. First lever is visibility, not enforcement; hard ratio is a per-group last resort. Ratio anxiety is the pain being sold against — "minus the ratio police." - **No fingerprint-stripping on private shares.** Resolved decision: **warn hard, strip never** by default. Stripping re-encodes and destroys the quality that makes a private release valuable; the friend is protected structurally by `f2f_only`, the source by the explicit warning. Strip is a per-release opt-in only, never auto. - **App doesn't link `governor` or call the MCP server directly.** By design — it shells out to the `mcp` CLI and `recommender` as subprocesses; `governor` runs independently under launchd. - **Black-TV watch state not on plum.** Intentional — black-local, read over SSH, never written across NFS. - **Watch parties** (if built) = synchronized *local* playback; Discord carries only voice + timestamps, never streamed copyrighted video.