Vibefed kb-peertube
git clone https://github.com/reiver/vibefed
T=$(mktemp -d) && git clone --depth=1 https://github.com/reiver/vibefed "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/kb-peertube" ~/.claude/skills/reiver-vibefed-kb-peertube && rm -rf "$T"
skills/kb-peertube/SKILL.mdPeerTube — Complete Reference
Overview
PeerTube is a free, open-source, decentralized video streaming platform — the Fediverse's alternative to YouTube. Created by Chocobozzz and supported by the French non-profit Framasoft since October 2017, it federates via ActivityPub. As of October 2025, it has approximately 1,906 active instances, 815,000 registered users, and 1.56 million hosted videos. Licensed under AGPL-3.0.
PeerTube federations are asymmetrical — one instance can follow another to display their videos without the other having to reciprocate. This is fundamentally different from Mastodon's symmetrical user-follow model.
1. Technical Stack
| Layer | Technology |
|---|---|
| Backend language | TypeScript (Node.js) |
| Backend framework | Express |
| Database | PostgreSQL 12+ (with and extensions) |
| Cache / Queue | Redis (BullMQ for job queues) |
| ORM | Sequelize |
| Video processing | FFmpeg (LTS 4.3+) |
| OAuth | Built-in OAuth 2.0 |
| Web server | Nginx (official configs provided) |
| Package manager | pnpm |
| Frontend framework | Angular (TypeScript) |
| Playback | VideoJS, hls.js, P2P Media Loader |
| Frontend build | Webpack |
| Frontend CSS | SASS / Bootstrap |
| Mobile | Official app (iOS + Android, December 2024) |
| License | AGPL-3.0 |
| Runtime | Node.js LTS 20.19+ (excl. v21) or v22.12+ (excl. v23) |
Server Components
- REST API — handles client requests and video operations
- RTMP server — accepts live streaming input (port 1935/TCP, 1936/TCP for RTMPS)
- ActivityPub implementation — federation between instances
- Socket.io server — real-time client notifications
- Task scheduler — processes transcoding and ActivityPub delivery jobs via BullMQ
2. Actor Model
PeerTube uses a two-tier actor model that is central to how it federates video content:
- Person — represents user accounts
- Group — represents video channels, owned by Person actors
- Application — platform-level instance actor for federation and anonymous activities
A single Person can own multiple Group (channel) actors.
Person Actor (Account)
{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {"pt": "https://joinpeertube.org/ns#"} ], "type": "Person", "id": "https://peertube.example/accounts/alice", "preferredUsername": "alice", "name": "Alice", "summary": "<p>Bio text</p>", "inbox": "https://peertube.example/accounts/alice/inbox", "outbox": "https://peertube.example/accounts/alice/outbox", "following": "https://peertube.example/accounts/alice/following", "followers": "https://peertube.example/accounts/alice/followers", "publicKey": { "id": "https://peertube.example/accounts/alice#main-key", "owner": "https://peertube.example/accounts/alice", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" }, "endpoints": { "sharedInbox": "https://peertube.example/inbox" } }
Group Actor (Video Channel)
{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {"pt": "https://joinpeertube.org/ns#"} ], "type": "Group", "id": "https://peertube.example/video-channels/alice_channel", "preferredUsername": "alice_channel", "name": "Alice's Channel", "summary": "<p>Channel description</p>", "inbox": "https://peertube.example/video-channels/alice_channel/inbox", "outbox": "https://peertube.example/video-channels/alice_channel/outbox", "following": "https://peertube.example/video-channels/alice_channel/following", "followers": "https://peertube.example/video-channels/alice_channel/followers", "attributedTo": [ { "type": "Person", "id": "https://peertube.example/accounts/alice" } ], "publicKey": { "id": "https://peertube.example/video-channels/alice_channel#main-key", "owner": "https://peertube.example/video-channels/alice_channel", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----" }, "endpoints": { "sharedInbox": "https://peertube.example/inbox" } }
Key points:
- The Group actor's
points to the owning Person actorattributedTo - Users follow channels (Group), not accounts (Person) directly
- Both Person and Group actors are discoverable via WebFinger
- The shared inbox path is
/inbox - The Application instance actor handles platform-level federation and anonymous GET request signing
Interoperability with Non-Channel Platforms
For platforms that don't have a channel concept, PeerTube recommends:
- Simulate a Group actor representing the account, or
- Put the Person actor as a Group actor in
owned by a global Person actorattributedTo
Without a Group actor in
attributedTo, PeerTube cannot properly
federate the video.
3. Supported Activities
| Activity | Description |
|---|---|
| Approves follow requests |
| Channel announces a video (primary video distribution mechanism) |
| Comment moderation approval (v6.2+, implements FEP-5624) |
| Creates Video, Note (comment), CacheFile, Playlist |
| Removes Video, Note, Person, Group, Playlist, CacheFile |
| Thumbs down on a video (non-standard — most Fediverse software only has Like) |
| Federated report functionality |
| Subscribe to a channel or instance |
| Thumbs up on a video |
| Denies follow requests |
| Reverses Follow, Announce, Like, Dislike, CacheFile |
| Modifies Video, Note, Person, Group, Playlist, CacheFile |
| View count tracking |
How Video Publishing Works
When a user uploads a video:
- The Person actor sends a
activityCreate{Video} - The Group (channel) actor sends an
to its followersAnnounce{Video} - Followers of the channel receive the video via the Announce
This two-step flow is how PeerTube distributes videos — the channel
Announce is the primary distribution mechanism.
4. Object Types
Video Object
PeerTube uses
Video as the object type (not Note). This is an
extended ActivityStreams object with many custom properties:
{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {"pt": "https://joinpeertube.org/ns#"}, {"sc": "http://schema.org/"} ], "type": "Video", "id": "https://peertube.example/videos/watch/abc-123", "name": "My Video Title", "content": "<p>Video description</p>", "summary": "Content warning text", "sensitive": false, "published": "2025-06-15T12:00:00Z", "updated": "2025-06-15T13:00:00Z", "attributedTo": [ { "type": "Group", "id": "https://peertube.example/video-channels/alice_channel" }, { "type": "Person", "id": "https://peertube.example/accounts/alice" } ], "to": ["https://www.w3.org/ns/activitystreams#Public"], "cc": ["https://peertube.example/video-channels/alice_channel/followers"], "duration": "PT5M30S", "downloadEnabled": true, "waitTranscoding": true, "state": 1, "commentsPolicy": 0, "canReply": "https://www.w3.org/ns/activitystreams#Public", "isLiveBroadcast": false, "url": [ { "type": "Link", "mediaType": "text/html", "href": "https://peertube.example/videos/watch/abc-123" }, { "type": "Link", "mediaType": "video/mp4", "href": "https://peertube.example/static/streaming-playlists/hls/abc-123/master.m3u8", "height": 1080, "fps": 30 } ], "embedUrl": "https://peertube.example/videos/embed/abc-123", "tag": [ { "type": "Hashtag", "name": "#technology" } ] }
Key points about the Video object:
must contain both a Group (channel) and a Person (account) — the Group entry is required for federationattributedTo
is the video title (notname
as with Mastodon posts)summary
is the video description (HTML)content
is used for content warnings (like Mastodon)summary
uses ISO 8601 duration format (e.g.,duration
)PT5M30S
is an array with multiple entries for different formats and resolutions (HTML page, MP4 files, HLS playlists)url- If
is absent, the video has embedding restrictionsembedUrl
and a tag ofsensitive
indicate sensitive contenttype: SensitiveTag
Video Object Extensions
Live streaming properties:
— whether this is a live streamisLiveBroadcast
— whether to save the replay after stream endsliveSaveReplay
— persistent stream URL that can be reusedpermanentLive
— latency configuration for the streamlatencyMode
— array for scheduled broadcast timesschedules
Interaction controls:
— comment policy (integer value)commentsPolicy
— FEP-5624; when set to the public collection, comments require approval viacanReplyApproveReply
— boolean controlling whether viewers can downloaddownloadEnabled
State:
— whether to wait for transcoding before publishingwaitTranscoding
— current video state (integer)state
Note (Comments)
Comments are represented as
Note objects, compatible with Mastodon
and other text-based platforms:
{ "type": "Note", "id": "https://peertube.example/comments/456", "content": "<p>Great video!</p>", "inReplyTo": "https://peertube.example/videos/watch/abc-123", "attributedTo": "https://peertube.example/accounts/bob", "published": "2025-06-15T14:00:00Z", "to": ["https://www.w3.org/ns/activitystreams#Public"], "cc": ["https://peertube.example/accounts/alice"] }
When a Mastodon user replies to a PeerTube video post, the reply appears as a comment on the video in PeerTube.
CacheFile (Redundancy)
A non-standard object type used for PeerTube's video mirroring system:
{ "type": "CacheFile", "object": "https://peertube.example/videos/watch/abc-123", "expires": "2025-07-15T12:00:00Z", "url": { "type": "Link", "mediaType": "application/x-mpegURL", "href": "https://mirror.example/redundancy/hls/abc-123/master.m3u8" } }
Other Object Types
| Type | Purpose |
|---|---|
| Non-standard OrderedCollectionPage containing PlaylistElement items |
| Individual playlist entries with start/stop timestamps |
| Schema.org-derived object for aggregated viewer statistics |
5. Video Redundancy and CacheFile
PeerTube has a built-in redundancy system allowing instances to mirror videos from other instances, improving resilience and bandwidth sharing.
How Redundancy Works
- An admin configures a redundancy strategy (cache trending videos, recently uploaded videos, etc.)
- PeerTube periodically checks for new videos to duplicate
- When a video is selected, all resolution files are imported and stored locally
- A
ActivityPub message is sent to federated instancesCreate{CacheFile} - The caching instance is injected as a WebSeed by instances that receive this message — viewers can now download video chunks from the mirror
- When the cache is full, old videos are evicted and new ones selected
ActivityPub Flow
— sent when a mirror instance begins cachingCreate{CacheFile}
— sent when the cached copy is removedUndo{CacheFile}
Redundancy Strategies
- Most viewed — cache the most popular videos
- Recently added — cache the newest videos
- Complete mirror — use
with view count set to 0recently-added
Benefits
- If the origin server goes down, the video remains accessible from cache instances
- Servers share bandwidth for popular content
- P2P delivery benefits from additional WebSeeds
6. P2P Video Delivery
PeerTube uses HLS with WebRTC P2P for video playback:
- Video chunks are loaded via HTTP from the origin server and any mirrors (WebSeeds)
- Simultaneously, chunks are exchanged between viewers via WebRTC peer-to-peer connections
- Under optimal conditions (many concurrent viewers), P2P can reduce server bandwidth usage by 80-90%
- P2P efficacy diminishes with few concurrent viewers
Up until version 6.0.0, PeerTube also supported WebTorrent for P2P delivery. WebTorrent was dropped in v6.0 in favour of HLS-only P2P.
Privacy Consideration
P2P streaming exposes viewer IP addresses to other peers. This is inherent to how WebRTC works and should be considered when deploying PeerTube in privacy-sensitive contexts.
7. Live Streaming
PeerTube supports live streaming (introduced in v3.0, January 2021):
- RTMP ingestion — stream via OBS or any RTMP-compatible software (port 1935/TCP)
- RTMPS — encrypted streaming (port 1936/TCP)
- Live transcoding — real-time transcoding to multiple resolutions
- HLS output — live streams delivered via HLS with P2P WebRTC
- Permanent live streams — persistent stream URLs that can be reused
- Stream scheduling — schedule when a stream starts (v7.3+)
- Live chat — basic integration (v7.3+)
- Save replay — optionally save recordings as VOD after stream ends
- Federation — live streams federate to channel followers via
ActivityPub using Video objects with
isLiveBroadcast: true
Live transcoding is processed differently from VOD transcoding — it happens in real-time and can be offloaded to remote runners.
8. JSON-LD Context
PeerTube uses extended contexts with multiple custom namespaces:
"@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {"pt": "https://joinpeertube.org/ns#"}, {"sc": "http://schema.org/"} ]
| Namespace | Prefix | Purpose |
|---|---|---|
| — | ActivityStreams 2.0 vocabulary |
| — | W3C Security Vocabulary (publicKey) |
| | PeerTube-specific properties (commentsPolicy, downloadEnabled, isLiveBroadcast, etc.) |
| | Schema.org vocabulary (WatchAction, etc.) |
| | Lemmy compatibility (postingRestrictedToMods) |
When processing PeerTube JSON-LD documents, unknown properties from the
pt: namespace should be ignored rather than causing errors.
9. HTTP Signatures and WebFinger
HTTP Signatures
PeerTube signs ActivityPub messages with JSON Linked Data Signatures using the private key of the account that authored the action. HTTP Signatures are used for server-to-server authentication.
Each actor (Person, Group, Application) has its own key pair. The Application instance actor is used for platform-level operations.
WebFinger
PeerTube supports WebFinger at
/.well-known/webfinger for actor
discovery. Both Person (account) and Group (channel) actors are
discoverable:
- Account:
acct:alice@peertube.example - Channel:
acct:alice_channel@peertube.example
10. Cross-Platform Federation
Mastodon Interoperability
| Action on Mastodon | Result on PeerTube |
|---|---|
| Follow a PeerTube channel | Videos appear in Mastodon home timeline |
| Reply to a PeerTube video post | Appears as a comment on the video |
| Favourite a PeerTube video | Appears as a thumbs up (Like) |
| Boost a PeerTube video | Increases reach on federated timelines |
| Action on PeerTube | Result on Mastodon |
|---|---|
| Upload a video | Appears as a post in followers' timelines |
| Like a Mastodon comment | Standard Like activity |
| Dislike | Not processed — Mastodon ignores Dislike activities |
Dislike (Non-Standard)
PeerTube supports the
Dislike activity (thumbs down), which is
non-standard in the broader Fediverse. Most Fediverse software only
implements Like. Mastodon does not send or process Dislike
activities, so dislikes from PeerTube are silently ignored by Mastodon
and most other platforms.
Comment Approval (FEP-5624)
PeerTube v6.2+ supports comment moderation via the
ApproveReply
activity (implementing FEP-5624). When a video has canReply set to
the public collection, comments from remote instances require approval
before becoming visible. The origin PeerTube instance sends an
ApproveReply activity back to the commenter's instance when approved.
Asymmetrical Instance Following
PeerTube instances can follow other PeerTube instances to display their videos without the followed instance needing to reciprocate. This is an admin-level operation used for content discovery.
11. Plugin System and Remote Runners
Plugin System
- Plugins are installed via NPM in a dedicated directory
- The server
s the module and calls itsrequire
functionregister() - Hooks fire on application events (video creation, page initialization, transcoding, etc.)
- Themes operate as client-only plugins with CSS injection
- Plugins can add custom API routes, settings pages, and client-side components
Remote Runners
PeerTube can offload heavy processing to remote machines:
- Remote runners register with the instance using registration tokens
- They poll the REST API for pending jobs
- Supported tasks: VOD transcoding, Studio transcoding, Live transcoding, Automatic transcription
- When remote runners are enabled for a task, PeerTube stops processing that task locally
- The runner posts the job result back to PeerTube
Known limitation: Server-side transcoding plugins are disabled when remote runners are enabled for transcoding tasks.
12. Common Implementation Mistakes
- Missing Group actor in
: PeerTube requires videos to have anattributedTo
field containing a Group actor — without it, the video cannot be properly federated. If your platform does not have channels, simulate a Group actor or use the Person actor in the Group roleattributedTo - Treating PeerTube videos as Note objects: PeerTube uses
as the object type, notVideo
— code that only handlesNote
will miss all PeerTube content. Implement handling forNote
in your ActivityPub processortype: "Video" - Ignoring the two-tier
: PeerTube'sattributedTo
contains both a Group (channel) and a Person (account) — if you only read the first entry, you may attribute the video to the channel instead of the person, or vice versaattributedTo - Not handling
gracefully: PeerTube sendsDislike
activities that most Fediverse software does not understand — your implementation should ignore unknown activity types rather than erroringDislike - Assuming video URL is a single string: PeerTube's
field is an array with multiple entries for different formats (HTML page, MP4 at various resolutions, HLS playlists) — pick the appropriate entry based onurlmediaType - Not handling
: if you receive aCacheFile
activity, it means a remote instance is mirroring one of your videos — you should add the mirror as a WebSeed for that video, not reject the activity as unknownCreate{CacheFile} - Expecting symmetrical follows: PeerTube instance-level follows are asymmetrical — following an instance does not mean they follow you back. Don't assume mutual federation.
- Ignoring
absence: if a PeerTube Video object lacksembedUrl
, the video has embedding restrictions — do not attempt to generate an embed URL by guessing the patternembedUrl - Not supporting
: if your users comment on PeerTube videos with comment approval enabled, the comment won't be visible until the PeerTube instance sends back anApproveReply
— handle this pending state in your UI rather than showing it as postedApproveReply