mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Maintainer principle: put as much effort into the static bitvid client as possible before branching into any separate application. - bitvid-bridge-plan: prominent "PRIORITY PRINCIPLE — static bitvid first" banner up top; the Bridge (a second app) is deliberately DEFERRED until the static client's live features (Go-Live Tier-1/2 MVP) ship and are maximally developed; build it only when live proves demand for its sovereignty/power features. - live-publish-plan: new "Build order & the static-first principle" section with the recommended sequence — 1) Shorts (#16b), 2) Live ingest (#16 watch foundation), 3) Go-Live MVP (#16c P0-1, Tiers 1&2, client-side only, no Bridge/VPS), then 4) the Bridge later only if warranted. Co-Authored-By: Claude Opus 4.8 <[email protected]>
336 lines
19 KiB
Markdown
336 lines
19 KiB
Markdown
# BitVid Bridge — cross-platform streaming helper app — Dev Plan
|
||
|
||
Companion to **`docs/live-publish-plan.md`** (#16c) — this expands its **Phase 7
|
||
(Local Bridge app)** into its own plan. The Bridge is the piece that fills the one
|
||
gap a static web client can't: **receiving and routing live media** on the
|
||
creator's machine.
|
||
Status: **FUTURE / exploratory — all design decisions LOCKED** (maintainer
|
||
accepted every recommendation), but **DELIBERATELY DEFERRED** — see the priority
|
||
principle below.
|
||
|
||
> **⛔ PRIORITY PRINCIPLE — static bitvid first (maintainer).** Put as much effort
|
||
> into the **static bitvid client** as possible before branching into any *separate
|
||
> application*. The Bridge is a second app (own repo, toolchain, cross-platform
|
||
> packaging, code-signing cost), so **do not start it** until the static client's
|
||
> live features have shipped and are maximally developed — specifically the Go-Live
|
||
> **Tier-1/2 MVP** (external-provider + serverless, zero infra; see the publish
|
||
> plan's provider spectrum). Rule of thumb: **exhaust what the static client can do
|
||
> before writing a second app.** Build the Bridge only once live has shipped *and*
|
||
> shown real demand for the sovereignty/power features it adds (local recording +
|
||
> full-WebTorrent VOD, no-third-party, multi-restream, hardware/appliance, capture).
|
||
|
||
Goal (maintainer, *when it's time*): a **super-simple, easy-to-install,
|
||
easy-to-use** cross-platform app (Windows / macOS / Linux) that makes going live on
|
||
bitvid turnkey.
|
||
|
||
---
|
||
|
||
## Core decision
|
||
|
||
The Bridge is a **local media receiver + router**, not a Nostr identity holder.
|
||
It pairs a **native media core** (receive/record/restream/upload) with an
|
||
**embedded bitvid "Studio" webview** that does all Nostr signing. The two stay
|
||
logically separated inside one app:
|
||
|
||
```
|
||
BitVid Bridge (one installable app)
|
||
├── Native media core → RTMP/SRT/WHIP ingest, HLS/WebRTC out, record, restream,
|
||
│ S3 upload (never sees the Nostr key)
|
||
└── Embedded Studio webview → bitvid client that signs NIP-53 + publishes VOD
|
||
(talks to the core over in-app IPC, not localhost HTTP)
|
||
```
|
||
|
||
**Why an embedded webview instead of "the website controls a localhost server":**
|
||
it solves two hard problems at once —
|
||
1. **No browser↔localhost pain.** An HTTPS page calling `http://127.0.0.1` hits
|
||
mixed-content + Private-Network-Access preflight/permission rules. In-app IPC
|
||
(Tauri commands / Wails bindings) sidesteps all of it.
|
||
2. **Signing stays in the web layer.** The webview is a bitvid client, so it signs
|
||
exactly as the browser does; the native core never touches the key. This
|
||
honors the publish plan's Rule 1 ("BitVid signs; Bridge routes") while still
|
||
being one self-contained app.
|
||
|
||
**Where the Bridge sits (see the publish plan's three-tier provider spectrum):**
|
||
- **Tier 1 (platform embed)** and **Tier 2 (serverless RTMP→HLS: Livepeer/Mux/
|
||
Cloudflare)** need **no Bridge and no VPS** — Tier 2 is the recommended
|
||
"no-server, native playback" default. So the Bridge is the **Tier-3 (advanced /
|
||
full-sovereignty)** option, not the default on-ramp.
|
||
- **The Bridge earns its place** by: no third party at all, **local recording +
|
||
full-WebTorrent VOD**, multi-restream hub, hardware/appliance input, turnkey UX,
|
||
and later built-in capture (no OBS).
|
||
|
||
**Hybrid that avoids a VPS entirely (important):** a **local** Bridge can
|
||
**record locally + restream to a Tier-2 serverless provider** which serves the
|
||
public HLS. The home Bridge then **never needs to be publicly reachable** (no VPS,
|
||
no tunnel/port-forward — it's *outbound* to the provider), yet the creator still
|
||
gets local recording + a full-WebTorrent VOD, and viewers get native HLS. This is
|
||
the sweet spot for most sovereignty-minded creators: **local Bridge for
|
||
record/WebTorrent/control + serverless for reach.**
|
||
|
||
---
|
||
|
||
## Decisions — ✅ ALL LOCKED
|
||
|
||
The maintainer accepted every recommendation below; each *Recommendation* line is
|
||
now the **locked choice**. Summary:
|
||
|
||
| # | Decision | Locked |
|
||
|---|----------|--------|
|
||
| 1 | App framework | **Wails (Go)** — one Go binary for desktop + headless node (ties to D7); Tauri was the close alt |
|
||
| 2 | Control model | **Embedded Studio webview** primary; optional localhost API later |
|
||
| 3 | Capture vs receive | **Receiver-first MVP**; built-in capture as a fast-follow (Phase 5) |
|
||
| 4 | Webview signer | **NIP-46 + encrypted-nsec**, NIP-46 preferred |
|
||
| 5 | Ingest protocols | **RTMP for MVP**; SRT + WHIP later |
|
||
| 6 | Recording upload | **Bundled rclone** (multi-provider, resumable) |
|
||
| 7 | One codebase, two modes | **Yes** — desktop Bridge == self-hosted Media Node run `--headless` |
|
||
| 8 | Transcode | **Pass-through only** (`-c copy`) |
|
||
| 9 | Code signing | **Project owns** Apple notarization + Windows cert (budgeted) |
|
||
| 10 | License / telemetry | **Match bitvid; no telemetry by default** |
|
||
|
||
D1 resolves to **Go/Wails** specifically because D7 (one codebase, two modes) is a
|
||
"yes": a single Go binary serves the desktop Bridge (with UI) and the headless
|
||
self-hosted Media Node — Go being MediaMTX's own language makes that clean.
|
||
|
||
> **DECISION 1 — App framework.** Go/**Wails** vs **Tauri** vs Electron.
|
||
> *Recommendation: **Wails (Go)** or **Tauri (Rust)** — both give a web UI (reuse
|
||
> bitvid's components/tokens) and small binaries; Electron is ~100MB+ and heavy,
|
||
> against "simple install." Slight lean **Wails/Go**: the media stack is Go-native
|
||
> and **MediaMTX is written in Go**, so it's one language, one small static binary,
|
||
> trivial cross-compile. Tauri is a very close second with a more mature app-shell
|
||
> + updater. Pick based on team comfort (Go vs Rust).*
|
||
|
||
> **DECISION 2 — Control model.** Embedded Studio webview (self-contained app) vs
|
||
> website-drives-localhost-API vs both.
|
||
> *Recommendation: **embedded webview primary** (dodges PNA/mixed-content + keeps
|
||
> signing in the web layer). Optionally expose a **localhost API later** for people
|
||
> who want to drive it from the bitvid website (with the PNA/CORS work done then).*
|
||
|
||
> **DECISION 3 — Capture vs receive.** MVP receives (OBS/hardware → Bridge) vs push
|
||
> built-in screen/camera capture earlier.
|
||
> *Recommendation: **receiver-first MVP** — works with the OBS/encoders people
|
||
> already have and ships fast. **Built-in capture (no OBS)** is the "super simple"
|
||
> headline but is effectively a mini-OBS (encoder is hard) → make it a fast-follow
|
||
> phase, not MVP.*
|
||
|
||
> **DECISION 4 — Signer support in the Bridge webview.** Since the webview signs,
|
||
> which login methods? **NIP-46 remote signer** (no key on device — safest), the
|
||
> **encrypted-nsec** flow the web app already has (key encrypted on-device), or
|
||
> both. NIP-07 extensions generally aren't available inside an app webview.
|
||
> *Recommendation: **both NIP-46 + encrypted-nsec**, NIP-46 preferred/first. Reuse
|
||
> the web app's existing session/signer code so there's nothing new to audit.*
|
||
|
||
> **DECISION 5 — Ingest protocols for MVP.** RTMP only, or RTMP + SRT + WHIP.
|
||
> *Recommendation: **RTMP for MVP** (universal — OBS/hardware all speak it), add
|
||
> **SRT** (better over lossy networks) and **WHIP** (WebRTC ingest, low latency)
|
||
> in a later phase. MediaMTX supports all three, so it's mostly UI/config.*
|
||
|
||
> **DECISION 6 — Recording upload mechanism.** Bundle **rclone** (one binary,
|
||
> speaks R2/B2/S3/MinIO + resumable) vs a native S3 SDK in the core vs reuse
|
||
> bitvid's browser S3 upload from the webview.
|
||
> *Recommendation: **rclone** bundled as a sidecar — dead-simple multi-provider
|
||
> support + resumable uploads of large recordings, matches the storage providers
|
||
> bitvid already supports. Native SDK only if we want to drop the extra binary.*
|
||
|
||
> **DECISION 7 — One codebase, two modes?** The server-side **Media Node** (#16c)
|
||
> and the local **Bridge** are ~the same thing (MediaMTX + session API + upload +
|
||
> restream). Build **one codebase that runs headless (Media Node) or with the
|
||
> embedded UI (Bridge)?**
|
||
> *Recommendation: **yes** — "self-host the Media Node" (publish-plan DECISION 1b)
|
||
> becomes literally "run the Bridge in `--headless` mode on a VPS." Big reuse win;
|
||
> steers DECISION 1 toward Go (server + desktop from one Go binary).*
|
||
|
||
> **DECISION 8 — Transcode.** Pass-through (`-c copy`) only, or offer local
|
||
> transcode profiles?
|
||
> *Recommendation: **pass-through only** (same as the publish plan). Require
|
||
> H.264/AAC in; the Bridge repackages/forwards without re-encoding — keeps CPU low
|
||
> and the app light. Transcoding is a later, opt-in power feature.*
|
||
|
||
> **DECISION 9 — Code signing + distribution ownership.** "Easy install" *requires*
|
||
> **Apple notarization** (Developer ID — or macOS Gatekeeper blocks it) and a
|
||
> **Windows Authenticode cert** (or SmartScreen scares users). Who holds the certs
|
||
> and pays (~$99/yr Apple + Windows cert)? Linux = AppImage + `.deb`/`.rpm`
|
||
> (no signing gate). *Recommendation: the project owns the certs; budget for them —
|
||
> this is the true cost of "easy to install," not optional.*
|
||
|
||
> **DECISION 10 — License / telemetry.** Open-source license (match bitvid's) and
|
||
> **no telemetry by default** (opt-in crash reports at most). *Recommendation:
|
||
> same license as bitvid; privacy-first, opt-in only.*
|
||
|
||
---
|
||
|
||
## Architecture
|
||
|
||
```
|
||
BitVid Bridge
|
||
├── App shell (Tauri/Rust or Wails/Go) DECISION 1
|
||
│ └── Embedded Studio webview (bitvid client, signs NIP-53/VOD) DECISION 2,4
|
||
├── Native media core
|
||
│ ├── MediaMTX (bundled sidecar) → ingest RTMP/SRT/WHIP, HLS/WebRTC out, record
|
||
│ ├── FFmpeg (bundled) → copy-forward restreams (-c copy) DECISION 8
|
||
│ └── rclone (bundled) → upload recordings to R2/B2/S3/MinIO DECISION 6
|
||
├── Session/state manager → LiveSession model, status, tokens
|
||
├── IPC bridge → webview ↔ core (Tauri cmds / Wails bindings)
|
||
└── (optional) localhost API → for website control, later DECISION 2
|
||
```
|
||
|
||
- **Studio webview sourcing.** The webview loads the **instance's deployed bitvid
|
||
web app** (remote URL, cached for offline resilience) rather than a bundled copy.
|
||
Benefit: it's always current and **auto-inherits the instance's flags** (so
|
||
`FEATURE_LIVE_PUBLISH` off ⇒ no go-live, invariant 6) with no app update needed;
|
||
a trimmed bundled fallback covers the offline/first-run case. The instance URL is
|
||
configurable (a user may use more than one bitvid instance).
|
||
- **Media core = a local Media Node.** Same engine as the server-side node (#16c),
|
||
which is why DECISION 7 (one codebase, two modes) is attractive.
|
||
- **Stream keys / ingest URLs never leave the device** and never go into Nostr; the
|
||
webview only ever publishes the *playback* URL.
|
||
- **Status → Nostr:** MediaMTX hooks fire `input_connected / input_live /
|
||
input_lost / recording_finalized / upload_complete`; the core forwards these to
|
||
the webview over IPC; the **webview signs** the corresponding NIP-53 update
|
||
(planned → live → ended + recording).
|
||
|
||
---
|
||
|
||
## UX flow (the "super simple" target)
|
||
|
||
```
|
||
1. Install (signed installer / AppImage). DECISION 9
|
||
2. Open app → sign in (NIP-46 or encrypted nsec) in the Studio webview. DECISION 4
|
||
3. Fill title/thumbnail/tags → "Prepare stream" (publishes status=planned).
|
||
4. App shows one of:
|
||
a. RECEIVE MODE: local ingest URL + key to paste into OBS/encoder. DECISION 3
|
||
b. (later) CAPTURE MODE: pick a screen/window/camera → "Go Live".
|
||
5. Encoder connects → core detects input → webview publishes status=live.
|
||
6. (optional) toggle restream outputs (YouTube/Twitch/zap.stream). DECISION 5
|
||
7. "End" → status=ended → core finalizes recording → uploads → webview
|
||
offers "Publish recording as a BitVid video" (archive→VOD).
|
||
```
|
||
|
||
The whole point: **one window, sign in, go live, done** — and the recording
|
||
becomes a normal bitvid video automatically.
|
||
|
||
---
|
||
|
||
## Cross-platform packaging
|
||
|
||
- **Windows:** signed `.msi`/`.exe` (Authenticode). Auto-update.
|
||
- **macOS:** signed **+ notarized** `.dmg` (Developer ID) — mandatory. Universal
|
||
(arm64 + x86_64).
|
||
- **Linux:** **AppImage** (zero-install portable) + `.deb`/`.rpm`; consider Flatpak.
|
||
- **Bundled binaries per-platform:** MediaMTX, FFmpeg, rclone. FFmpeg is the size
|
||
hog (~50–80MB) — ship a **minimal build** (only the muxers/protocols we use:
|
||
flv/rtmp/mpegts/hls/webrtc, aac/h264 passthrough) to keep the installer lean.
|
||
- **Auto-updater** (both Tauri and Wails have one) with signed release artifacts.
|
||
|
||
---
|
||
|
||
## Phases
|
||
|
||
- **Phase 0 — Shell + media core (foundational).** App skeleton (DECISION 1),
|
||
bundle + supervise MediaMTX, embedded Studio webview, webview↔core IPC, signed
|
||
dev builds on all three OS. Sign-in works (DECISION 4).
|
||
- **Phase 1 — Receive → serve (the MVP).** Local RTMP ingest → serve HLS. For
|
||
public viewers the playback origin must be **reachable**, so either run the
|
||
Bridge **headless on a reachable host** (VPS — the self-hosted node) or
|
||
copy-forward from a local Bridge to that reachable node / an external provider
|
||
(never a bitvid-run node). Publish planned→live→ended NIP-53 from the webview via
|
||
MediaMTX input hooks. "OBS → your node → bitvid, one window."
|
||
*(NAT note: a home-only Bridge can't serve public HLS directly → either run
|
||
headless on a VPS, or use the hybrid: forward to a Tier-2 serverless provider
|
||
that serves public HLS — see Phase 3.)*
|
||
- **Phase 2 — Record → archive → VOD.** Local recording, finalize, rclone upload
|
||
to the user's storage, manifest → webview publishes a normal bitvid video
|
||
(reuses the `s`-tag/info.json/30078 flow). Optional torrent/webseed for full
|
||
WebTorrent VOD.
|
||
- **Phase 3 — Multi-restream (`FEATURE_LIVE_RESTREAM`).** One input → many outputs
|
||
(YouTube/Twitch/zap.stream/**serverless providers Livepeer/Mux/Cloudflare**/
|
||
custom RTMP/SRT), copy-only, per-output status + retry/backoff. Keys stay local;
|
||
optional public mirror links as `r` tags. **The serverless preset enables the
|
||
no-VPS hybrid** — a local Bridge records locally + forwards to Livepeer/Mux,
|
||
which serves the public HLS (the `streaming` URL in the NIP-53 event). This is
|
||
arguably the flagship Bridge workflow for home creators.
|
||
- **Phase 4 — Turnkey UX + packaging.** One-click go-live, OBS auto-config or
|
||
OBS-WebSocket control, signed/notarized installers + AppImage, auto-update.
|
||
- **Phase 5 — Built-in capture (no OBS).** Screen/window/camera capture + encode
|
||
inside the app (`getDisplayMedia`/`getUserMedia` → WebCodecs/FFmpeg). The "super
|
||
simple" headline: go live with no external encoder.
|
||
- **Phase 6 — SRT/WHIP ingest + WebRTC low-latency** (DECISION 5).
|
||
- **Phase 7 — Headless "Media Node" mode + hardware / "BitVid Box".** Run the same
|
||
binary headless as the self-hosted Media Node (DECISION 7); harden for
|
||
appliance/headless use — the seed of a physical device.
|
||
|
||
---
|
||
|
||
## Engineering rules (invariants)
|
||
|
||
1. **The native core never sees the Nostr key.** Only the webview signs.
|
||
2. **Ingest URLs/keys stay on the device; only playback URLs reach Nostr.**
|
||
3. **Pass-through first** — copy H.264/AAC, don't transcode (DECISION 8).
|
||
4. **Reuse bitvid's web client** for the Studio webview (session, signer, publish,
|
||
upload-modal-style VOD flow) — minimize net-new signing/auth code.
|
||
5. **Optional, not required** — a creator can still stream OBS → an external
|
||
provider without the Bridge; the Bridge *adds* self-hosting + convenience/power
|
||
(and bitvid never runs a node — blanket rule).
|
||
6. **Respect the connected instance's config.** The embedded Studio webview loads
|
||
a specific bitvid instance, so it inherits that instance's flags — if the
|
||
instance has `FEATURE_LIVE_PUBLISH` **off**, the Bridge shows go-live as
|
||
unavailable for it (nothing to publish to). The Bridge is only useful pointed
|
||
at an instance that has publishing enabled; surface that clearly rather than
|
||
failing opaquely.
|
||
7. **Bind the local ingest to loopback only.** The RTMP/SRT receiver listens on
|
||
`127.0.0.1` (not `0.0.0.0`) by default so it isn't exposed to the LAN;
|
||
appliance/headless mode (Phase 7) can opt into a wider bind with auth.
|
||
|
||
---
|
||
|
||
## Risks / watch-items
|
||
|
||
- **Code signing is the real "easy install" cost (DECISION 9).** Apple
|
||
notarization + Windows cert are non-negotiable for a frictionless install and
|
||
cost money + setup. Budget for them up front.
|
||
- **Bundle size vs simplicity.** MediaMTX + FFmpeg + rclone add up; a minimal
|
||
FFmpeg build and per-platform packaging keep it reasonable, but it won't be
|
||
tiny. Trade-off vs "super light."
|
||
- **Built-in capture is a mini-OBS (DECISION 3/Phase 5).** Reliable capture +
|
||
encode across GPUs/OSes is genuinely hard — don't let it block the receiver MVP.
|
||
- **Signing during long streams.** Same issue as the publish plan (DECISION 2
|
||
there): the webview must stay running to sign status updates + heartbeats;
|
||
handle sleep/crash gracefully (NIP-53 treats no-update-in-~1h as ended).
|
||
- **Webview signer support.** NIP-07 extensions typically aren't present in an app
|
||
webview → lean on NIP-46 / encrypted-nsec (DECISION 4).
|
||
- **Encoder compatibility.** Pass-through needs H.264/AAC + sane keyframes;
|
||
detect + warn on incompatible input rather than failing silently.
|
||
- **Scope creep.** This app can sprawl (capture, transcode, appliance). Ship the
|
||
receiver MVP (Phase 0–1) first; everything else is additive.
|
||
- **Instance without publishing enabled.** If a user points the Bridge at an
|
||
instance that has `FEATURE_LIVE_PUBLISH` off, go-live must degrade gracefully
|
||
(clear "publishing isn't enabled on this instance" state), not error obscurely
|
||
(invariant 6).
|
||
- **Local ingest exposure.** A misconfigured wide bind would expose an unauth'd
|
||
RTMP receiver on the network — default to loopback (invariant 7); require auth
|
||
for any non-loopback bind.
|
||
- **Reachability / NAT (self-hosting reality) — largely solved by the hybrid.** A
|
||
**home-only Bridge behind NAT can't *serve* public HLS**. Two clean answers:
|
||
(1) the **hybrid** — the local Bridge *forwards* (outbound) to a Tier-2
|
||
serverless provider that serves the public HLS, so the home box needs no public
|
||
reachability at all (recommended for laptops/home); (2) run the Bridge
|
||
**headless on a reachable VPS** for full self-hosting (no third party). Only (2)
|
||
needs a VPS; document both. This is why headless mode (Phase 7 / DECISION 7) and
|
||
restream-to-serverless (Phase 3 preset) both matter.
|
||
|
||
---
|
||
|
||
## Sources
|
||
|
||
- **MediaMTX** (ingest/playback/record/forward/control API):
|
||
https://mediamtx.org/docs/kickoff/introduction
|
||
- **OBS WebSocket** (built into OBS 28+):
|
||
https://obsproject.com/kb/remote-control-guide
|
||
- App frameworks: [Tauri](https://tauri.app/) · [Wails (Go)](https://wails.io/) ·
|
||
packaging binaries (MediaMTX, [FFmpeg](https://ffmpeg.org/),
|
||
[rclone](https://rclone.org/)).
|
||
- Companion plans: `docs/live-publish-plan.md` (#16c, this expands its Phase 7),
|
||
`docs/live-ingest-plan.md` (#16, watch side + P2P-assisted HLS).
|
||
- bitvid code reused by the Studio webview: session/signer (NIP-46 + encrypted
|
||
nsec), the upload/publish flow, `js/utils/storagePointer.js` (`s`-tag →
|
||
`.info.json`) for archive→VOD.
|