tv-anarchy/docs/roadmap.md
Natalie ca1871f5dd feat(@applications/tv-anarchy): add roku device support
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-09 21:37:34 -07:00

177 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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/<hash>`, 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 dutiesfloorreaper tick periodically for launchd, and
`fleet serve` exposes `/registry /custody /reaper /peers_for/<hash>` 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.
### 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/transcodeHLS
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-<tag>-roku.zip` asset + a push-deploy step
(dev channels are uploaded TO the stick: `curl --digest -u rokudev:<pass>
-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 <infohash|title>` 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.