Federation
Vidra federates over two protocols that do different jobs, and they are independently switchable. An instance may run ActivityPub only, ATProto only, both, or neither.
| ActivityPub | ATProto | |
|---|---|---|
| What it is for | Federating channels and their videos to the fediverse | Identity (sign in with a Bluesky or any-PDS handle) and cross-posting public videos to Bluesky |
| Direction | Inbound and outbound | Identity is inbound; cross-posting is outbound only |
| Master switch | FEDERATION_ENABLED | ATPROTO_LOGIN_ENABLED for identity, ATPROTO_ENABLED for cross-posting |
| Default | Off | Off |
| Per-channel control | activitypub_enabled | atproto_enabled |
Both default to off, and while a switch is off its routes are not mounted at all — they answer 404 or 503 rather than existing and refusing. Turning federation on is a decision, not an accident.
ActivityPub
With FEDERATION_ENABLED=true and a configured PUBLIC_BASE_URL, the api
mounts the fediverse surfaces at the root, outside /api/v1 — they are a
federation contract, not part of the REST API:
| Route | What |
|---|---|
/.well-known/webfinger | Resolves acct:name@domain to an actor |
/.well-known/nodeinfo, /nodeinfo/2.1 | Instance discovery |
/accounts/{handle}, /video-channels/{handle} | Actor documents (Person and Group) |
/inbox, /accounts/{handle}/inbox, /video-channels/{handle}/inbox | Inbound activities — a shared inbox plus the per-actor inboxes the actor documents advertise, all through one signature-verifying handler |
/accounts/{handle}/followers · /following · /outbox | Read-only actor collections |
/video-channels/{handle}/followers · /following · /outbox | The same; the channel outbox is paged (?page=N) |
Users and channels are both federated actors — a Person and a Group — and each
needs a keypair. Those private keys are envelope-encrypted at rest under
FEDERATION_KEY_KEK.
FEDERATION_KEY_KEK is not rotatableThere is no re-wrap job. Nothing in the codebase re-encrypts persisted rows
under a new key-encryption key, so rotating it makes every stored federation
actor key unreadable. Worse, ATProtoKEK() and MFAKEK() fall back to
FederationKeyKEK when their own value is unset, so rotating it on an
instance that never set ATPROTO_KEY_KEK or MFA_KEY_KEK silently destroys
linked Bluesky app passwords and every TOTP secret too. Set all three
separately, and treat a rotation as a user-visible re-enrolment event.
Per-channel opt-out
A creator opts a channel out of either protocol from the studio's Channel tab (owner only). Both flags default to true, so existing channels keep federating exactly as before. The enforcement is concrete:
activitypub_enabled=false— inboundFollowis ignored, and outboundCreate/Update/Delete/Announceare skipped for that channel.atproto_enabled=false— the channel's published videos are never enqueued for a Bluesky cross-post.
ATProto
Two independent features, two switches.
Identity login (ATPROTO_LOGIN_ENABLED) lets a user sign in with a Bluesky
handle or any ATProto PDS, through
/api/v1/auth/atproto/start and /api/v1/auth/atproto/callback, with client
metadata served at /api/v1/auth/atproto/client-metadata.json. It keeps no
PDS tokens, so it needs no key-encryption key.
Cross-posting (ATPROTO_ENABLED) is outbound only: a user links a Bluesky
account at /api/v1/me/atproto, and their channel's public videos are enqueued
for posting there. The linked app password is envelope-encrypted at rest under
ATPROTO_KEY_KEK. With the switch off, the link, status and unlink endpoints
answer 503 and no auto-post worker runs.
What federates, and what does not
Federates: channel actors and account actors, follows in both directions,
and the video activities a channel publishes — Create, Update, Delete and
Announce.
Does not federate:
- Media bytes. Federation carries the activity, not the video file. A remote viewer still fetches media from your origin, your CDN or an IPFS gateway.
- Accounts. A remote follower is a remote actor, not a user on your instance.
- Moderation state. Blocks and reports are yours; they are not synchronised with peers.
- Anything, when the switch is off. With
FEDERATION_ENABLED=falsethe routes do not exist.
PeerTube, precisely
Vidra is not API-compatible with PeerTube — a PeerTube client, embed or plugin will not work against a Vidra instance, and Vidra is a clean-room implementation rather than a fork.
Federation is a separate question from API compatibility. ActivityPub is an open protocol and Vidra speaks it, which is what interoperating with the wider fediverse means. What Vidra deliberately does not claim is drop-in replaceability. If you are moving an existing PeerTube instance, the supported route is the one-way importer: Migrate from PeerTube.
VIDRA_TLS_MODE=plain-http exists for labs, LANs and air-gapped installs.
Federation and OAuth are https-only by design and will not work there.