PeerTube
Install the published universal-paywall plugin — per-view support/tips settled on-chain, no PeerTube fork.
What gets metered
a counted video view — the plugin registers PeerTube's action:api.video.viewed server hook, which fires only after PeerTube's own anti-fraud watch-time threshold counts the view.
- Who pays: the viewer, identified by an
x-payer-userheader stamped by a viewer client or the browser-extension adaptor (anonymous views are metered-and-skipped, never charged) - Who gets paid: the channel wallet (creator key =
video.channelId ?? video.id)
Attach surface — no fork
PeerTube's sanctioned plugin loader: Admin → Plugins → install peertube-plugin-universal-paywall. A separately published plugin, not a core edit — PeerTube has no native view webhook, so the plugin slot is the permissionless attachment point (enabled upstream by PeerTube PR #6300, req.rawBody).
Quickstart
Operator install — no code, no fork:
1. Admin → Plugins → search & install `universal-paywall`
(or install from disk for a local build).
2. Configure the plugin settings:
Facilitator URL = https://facilitator.example
Facilitator API key = FACILITATOR_API_KEY=…
Price per view (µUSDC) = 1000
Viewer wallet map = {"<payerKey>":"0x…"}
Channel wallet map = {"<channelId>":"0x…"}
3. A staked viewer watches a video → counted view → hook →
metered charge → facilitator batches → StakeVault settles.
All values above are placeholders — supply your own facilitator, wallets, and rate.
The shared prerequisites (a deployed StakeVaultFactory, a running
facilitator, a wallet registry) are covered in the admin overview.
Verify
node packages/peertube-plugin/e2e-player-docker.mjs Real headless-browser player against a live PeerTube 7.3.0 (postgres + redis): counted view → hook → on-chain settle.
Notes & caveats
Tips first — support while you watch
The plugin's default framing is support, not a gate. A viewer who has staked sends a small amount per counted view — the video stays public, and viewers who haven't opted in simply watch for free (their views are metered-and-skipped, never blocked). Gating a video behind payment is the same mechanics with the access check turned on — a mode, not the point.
This ordering comes straight from the PeerTube community: the seven-year-old monetization discussion (issue #1586) asks again and again for ways to support creators and instances — and is consistently wary of paywalls. The plugin is built to match.
Operator install, in full
- Admin → Plugins → search and install
universal-paywall(or install from disk for a local build). -
Configure the settings:
- Facilitator URL / Facilitator API key — your Universal Paywall facilitator.
- Price per view (micro-USDC) — e.g.
1000(a tenth of a cent per counted view). - Viewer wallet map — JSON
{"<payerKey>": "0x…"}. - Channel wallet map — JSON
{"<channelId>": "0x…"}.
- Done — no restart of PeerTube core, no fork, no database surgery.
How a view becomes a payment
PeerTube fires the action:api.video.viewed server hook only when its own
anti-fraud logic counts the view — a watch-time threshold plus
viewer-stats processing, the same debounce that guards the public view counter. That
means:
- Skimming past a thumbnail or bouncing after two seconds never charges anyone.
- The plugin inherits PeerTube's own view-fraud protection instead of inventing its own.
- Each charge carries a per-view
reffor idempotency — a view can't be double-billed. - The creator is keyed on
video.channelId ?? video.id(the hook's video object is anMVideoImmutablethat doesn't always carrychannelId— a real bug found and fixed by the end-to-end player test).
Fund the whole stack — revenue splits
Because the payee is just a wallet map, a counted view can pay more than one party — the ask that recurs throughout #1586 is funding every layer that makes the video possible:
| Recipient | Why |
|---|---|
| The creator (channel wallet) | made the video — the default and primary payee. |
| The instance host | pays for the bandwidth and storage that serve it. |
| PeerTube / Framasoft | builds the software both depend on. |
Splits are a wallet-map configuration on the operator side; the rail itself just
settles rate × units to each resolved payee.
Verified on a real PeerTube
The self-contained plugin bundle was installed into a live PeerTube 7.3.0 (postgres + redis): it installs, registers the view hook and its settings, is enabled, and is configurable through the plugin settings API. A real headless-browser player session then drove a counted view through the hook to an on-chain settle:
node packages/peertube-plugin/e2e-player-docker.mjs Design context and the full recipe: deploy/peertube/README.md · the plugin README · PeerTube #1586 (the discussion this design answers).
Links
- Recipe:
packages/integrations/deploy/peertube/README.md— the authoritative attach instructions in the repo. - Plugin package README (peertube-plugin-universal-paywall)
- PeerTube issue #1586 — the 7-year monetization discussion this answers
- Integration Playbook — how integrations like this one are built.
- Testing plan — this platform's row in the L1→L4 matrix.