portable-net-tv/.project/history/20260608_fleet-manager-mesh-design.md
Natalie dd7e94225e fix(node-types): 🐛 remove bun/node type dependencies
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 02:18:50 -07:00

12 KiB
Raw Permalink Blame History

PlumTV Fleet Manager + Self-Healing Torrent Mesh — Design Spec

Captured 2026-06-08 from a design conversation. Forward-looking spec for unbuilt work; spans the PlumTV repos (see plum-tv-category-unification). Not yet implemented.

Target audience

People who download media via magnet/torrent links — sophisticated, already run clients, understand seeders/ratio/DHT. Implication: expose the mechanics as features instead of hiding them. They already live in Discord (scene/tracker/game communities).

Positioning line:

A private tracker made of your friends — with the retention guarantee, minus the gatekeeping and the ratio police.

The four painkillers (lead with these, not "social discovery"):

  1. Dead magnets / zombies — mesh + re-source kills them.
  2. Ratio anxiety / hit-and-run bans — custody floor automates good seeding.
  3. Magnet roulette (results are corpses) — a friend with it is one F2F hop away.
  4. Multi-device sprawl — the fleet model is what they cobble together by hand.

Competitive lane: *arr stack automates acquisition; nothing makes availability social and self-healing. Win there, don't out-*arr the *arr stack.

Privacy: torrenting over Tor is the wrong mechanism (leaks real IP via the protocol, harms the Tor network). Correct expression of the same instinct = F2F/darknet mesh over WireGuard + VPN on the public-swarm leg.

Two graphs on one Discord identity layer

  • Custody graph — NARROW, trust-bounded (1° friends + always-on nodes). Holds the seeder floor. This is where the zombie-prevention guarantee lives.
  • Discovery/signal graph — WIDE, six-degrees, anonymized + aggregated. Carries popularity signal and relays F2F requests. "Penetration" is a feature here.

Unifier = friend-to-friend (darknet) routing (Freenet darknet / RetroShare model): you connect directly only to people you trust; requests + bytes relay hop-by-hop until a holder is reached. Six-degrees reach, local trust, never peer with a stranger.

Trust as a distance gradient: 1° = full custodian peer; 2° = rate-limited pull via relay; 3°+ = signal only unless vouched.

Discovery signal needs k-anonymity (suppress titles below K≈5 distinct watchers) and per-fleet dedup (one person watching on plum then phone = one watch). Load-bearing because of adult content in scope — a single-watcher leak fingerprints someone.

Zombie prevention (custody floor)

The mesh prevents future zombies for pinned content; it cannot raise the already-dead (that's the reaper's job — re-search for an alternate magnet).

Mechanism:

  • Watch = auto-seed with TTL — every viewing adds a seeder. Content heals as it's used.
  • Replication floor enforced by the governor — every wanted title keeps ≥N complete copies; drop to N1 triggers a re-pin from a surviving seeder before the last vanishes.
  • Always-on backstop in every floor (vps-0 / black / seedbox) — un-killable anchor.

Custodianship = rolling baton, not a life sentence:

  • Newest downloader joins the floor; oldest is released (demoted to voluntary).
  • A custodian reclaiming disk must floor-check + hand off before dropping — never breach the floor unilaterally.
  • ≥1 floor slot reserved for an always-on node (laptops-only custodians = momentarily dead when asleep).

Zombie reaper (cron daemon by the governor): classifies every torrent healthy | stalled | dead; for dead-but-wanted, recover from the mesh first, public re-search as fallback. The mesh and reaper are the same system from two ends.

The fleet is the atom

Unit is neither person nor device — it's a fleet. One Discord identity, typed devices.

  • Identity = person.
  • Custody = the always-on subset of the fleet (black, apricot, seedbox).
  • Signal = per-fleet-deduped watch events.

A single fleet (black + apricot) already contains its own 2-node floor → the system works for one user with zero friends; the mesh is the same code with more identities. Build for the fleet, get the mesh for free.

Distributional truth: a typical 4-device user (laptop+phone+tablet+work-laptop) has ZERO always-on nodes → net consumer. The seedbox is how those fleets get a floor at all → "add a seedbox" is the most important onboarding step for non-power-users.


Fleet Manager — Data Model

The fleet manager = the governor generalized again (same bandwidth-arbitration brain).

Entities

Identity { discord_id, display_name, fleets[] }
Fleet    { id, identity, hosts[], sources[], custody_capacity }
Host     { ...registry... }
Source   { ...peer-source... }

custody_capacity = Σ over always_on hosts of (disk_free × uptime_score) — says whether a fleet can hold a floor or is a net consumer. Gift-economy mode ignores it for prioritization; ratio mode weights by it.

Host registry

Host {
  id
  fleet_id
  class:       server | roamer | consumer | seedbox | broadcast
  reachable:   home_lan | wireguard | public_ip
  always_on:   bool
  on_home_ip:  bool                 # true = public-swarm traffic exposes home connection
  api:         transmission_rpc | qbittorrent | utorrent_web | none
  capacity:    { disk_free, up_bw, uptime_score }   # uptime_score ∈ [0,1], rolling
  duties:      Duty[]               # assigned by manager, not hardcoded
}
Duty = custody_floor | public_swarm_face | f2f_relay | broadcast

Class = what a device IS; duty = what the manager tells it to DO.

Device-class examples (Natalie's fleet): black = server, apricot = secondary always-on, plum = roamer (TTL seeder), phone = consumer (PURE SINK — never any duty).

Duty-assignment rules (deterministic, capability-driven, run on registry change)

Duty Eligibility Rule
broadcast public_ip && always_on exactly ONE per fleet (or shared pool); prefer seedbox > vps-0 > server
f2f_relay always_on && reachable ∈ {wireguard,public_ip} broadcast host + any other always-on server
public_swarm_face prefer !on_home_ip seedbox FIRST; never a consumer; never on_home_ip if an off-home option exists
custody_floor always_on && disk_free > title_size N most-recent eligible holders; ≥1 slot reserved for always-on node

Invariants:

  • A consumer (phone) NEVER receives any duty. Checked first.
  • public_swarm_face prefers off-home-IP so home connections stay dark.
  • Every active title's floor keeps ≥1 always-on holder.

"Global broadcast server" duties

Pin to the most-reachable always-on node (seedbox or vps-0):

  1. F2F rendezvous / relay anchor (NAT'd members + friends find each other through it).
  2. Holds the aggregated peer/seeder registry.
  3. Runs the bot / Discord bridge.
  4. Public-swarm face (optionally the ONLY node touching public swarms — keeps fleet dark).

Peer-source model — the holistic seeder list + the policy that contains it

Source {
  id
  fleet_id
  kind:            dht | public_tracker | private_tracker | friend_mesh | fleet_host | seedbox
  api/creds:       <opaque, encrypted at rest>
  share_policy:    search_only | content        # private_tracker DEFAULT-CLOSED (search_only)
  swarm_isolation: f2f_only | open              # private_tracker FORCED to f2f_only
}

Broadcast host answers peers_for(infohash) → Peer[] by unioning every permitted source:

Peer { addr, source_kind, source_id, served_via: public | wireguard }

Holistic list = your privates your fleet friends' fleets seedbox DHT/public — a union no single tracker can assemble. This is the product magic: a user-owned meta-tracker. Provenance-tagged so the UI shows why a peer exists and over what transport.

Private-tracker "mixing" — the blast radius

"Busting out of private" bundles two operations with very different consequences:

  • search_only (a release exists, here's the infohash) — low/moderate risk, sharing info.
  • content (serve bytes / hand over the private .torrent) — ACCOUNT-KILLER, and the consequence lands on the SOURCE user, not the friend.

Non-obvious technical reality: the private .torrent has the passkey embedded. If a friend's client announces to the private tracker → tracker sees the passkey on an unregistered peer → source user permanently banned for passkey sharing. Cross-seed detection + retention monitoring also point at the seeder.

Two policy gates (both load-bearing):

  1. share_policy gates the registry merge. Private = search_only by default; flipping to content triggers a consequence-explicit warning naming the tracker and that the source's account is what burns.
  2. swarm_isolation = f2f_only FORCED + un-overridable for private sources. Private content is re-hosted into the fleet's OWN WireGuard swarm; the friend pulls served_via: wireguard and NEVER announces to the private tracker. Structural kill-switch for the passkey-share instant-ban.

The architecture fully shields the FRIEND but cannot shield the SOURCE — the sharer is always the leak. So content-share is opt-in, default-closed, eyes-open.

Seedbox source by contrast = content + open by default. Clean, no gates.

Two derived outputs (everything above exists to produce these)

peers_for(infohash)  → Peer[]      # the holistic seeder list / meta-tracker
custodians_of(title) → Host[]      # who's obligated to keep it alive

Resolved product decisions (2026-06-08)

A. Gift-economy vs. ratio → SHIP GIFT-ECONOMY, build the knob, leave it off

Ratio enforcement contradicts the "minus the ratio police" positioning. Ratio anxiety is the pain being sold against.

  • Default: gift-economy, no enforcement.
  • Build custody_capacity weighting but leave it disabled; enable per-GROUP only when a group outgrows trust and server-havers feel the asymmetry.
  • First lever is visibility, not enforcement — show contribution (disk pledged, bytes served, titles custodied) without gating. Social pressure > mechanical bans in a trust group. Hard ratio = escalation of last resort, per-group, never default.

B. Warn vs. fingerprint-strip on private sharing → WARN HARD, STRIP NEVER (default)

Stripping = re-encoding = destroys the encode quality that makes a private release valuable. Defeats its own purpose.

  • Friend protected structurally by f2f_only isolation.
  • Source protected by an explicit warning at content-share flip (the warning IS the protection — don't paper over with false safety from stripping).
  • Private-sourced titles EXCLUDED from broadcast/global-trending signal + higher k-anonymity threshold in personal-network signal (trending private release = tips off tracker staff).
  • Strip offered only as a per-release opt-in, labeled "re-encodes, loses quality." Never auto.

Build order (de-risked; each stage independently shippable)

  1. Host registry + duty assignment, SINGLE FLEET (black+apricot+plum+phone). No mesh, no Discord. Unifies what's run by hand today.
  2. Seedbox source + public_swarm_face duty. Adds always-on custodian; proves the source model with the zero-risk source first.
  3. peers_for() over local sources (fleet + seedbox + DHT/public). Meta-tracker, single-fleet.
  4. Friend-mesh source + F2F relay. Federates; custody floor goes cross-fleet.
  5. Private-tracker source, default-closed. LAST — highest consequence; ship only once the f2f isolation path is battle-tested on safe sources.

Discord planes (keep separate):

  • Control/ops + QA/community + release announcements → project-owned home server. Fine.
  • Content/availability (magnets, custody signal, trending-among-friends) → NEVER the public home server (Discord ToS + ties project identity to distribution + adult-content rules). Rides the private mesh + the bot inside users' OWN servers.
  • Discord is a surface you notify, not a backbone you route through — custody/reaper/F2F must run over WireGuard regardless; a Discord ban must not take the mesh down.

Watch parties: synchronized LOCAL playback (Discord carries only voice + timestamps), never stream copyrighted video through Discord.