Vibefed kb-fediverse-relays
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-fediverse-relays" ~/.claude/skills/reiver-vibefed-kb-fediverse-relays && rm -rf "$T"
skills/kb-fediverse-relays/SKILL.mdFediverse Relays — Complete Reference
Overview
A relay is a service-type ActivityPub actor that re-broadcasts public content between subscribed servers. It solves a core discovery problem: without relays, a new or small instance only sees content from accounts its users explicitly follow, leaving the federated timeline empty and hashtag search incomplete.
Relays function as "a commons of public content" — participating servers send their public posts to the relay, and the relay distributes them to all other subscribers. This is an admin-level operation, not something individual users control.
There is no separate relay specification. Relay behaviour is an application of the core ActivityPub spec, primarily using the
Announce
activity type and the standard Follow/Accept subscription flow.
1. The Relay Actor
A relay is represented as an ActivityPub actor, typically with
"type": "Application" or "type": "Service". It exposes the standard
endpoints needed for federation:
{ "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1" ], "type": "Application", "id": "https://relay.example.com/actor", "inbox": "https://relay.example.com/inbox", "publicKey": { "id": "https://relay.example.com/actor#main-key", "owner": "https://relay.example.com/actor", "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIB...\n-----END PUBLIC KEY-----" }, "url": "https://relay.example.com/actor", "endpoints": { "sharedInbox": "https://relay.example.com/inbox" } }
Key points:
- The relay actor's
is where subscribing servers send activitiesinbox - The
is used for HTTP Signature verificationpublicKey - Most relay implementations also expose
,/.well-known/webfinger
, and/.well-known/nodeinfo
for discovery/nodeinfo/2.0
2. The Subscription Handshake
Subscribing — Follow the Public collection
A server subscribes to a relay by sending a
Follow activity where the
object is the special public collection URI:
{ "@context": "https://www.w3.org/ns/activitystreams", "id": "https://instance.example/activities/relay-follow-1", "type": "Follow", "actor": "https://instance.example/actor", "object": "https://www.w3.org/ns/activitystreams#Public" }
Requirements:
- The
field must be exactlyobject
— this is what distinguishes a relay subscription from a normal user follow"https://www.w3.org/ns/activitystreams#Public" - The
must resolve to an object with bothactor
andinbox
propertiesendpoints.sharedInbox - The request must be signed with a valid HTTP Signature
Acceptance
On successful subscription, the relay responds with an
Accept:
{ "@context": "https://www.w3.org/ns/activitystreams", "id": "https://relay.example.com/activities/accept-1", "type": "Accept", "actor": "https://relay.example.com/actor", "object": { "type": "Follow", "actor": "https://instance.example/actor", "object": "https://www.w3.org/ns/activitystreams#Public" } }
Some relays require manual approval before sending the
Accept. The
subscribing server should handle an indefinite pending state gracefully.
Unsubscribing — Undo Follow
{ "@context": "https://www.w3.org/ns/activitystreams", "id": "https://instance.example/activities/undo-relay-follow-1", "type": "Undo", "actor": "https://instance.example/actor", "object": { "type": "Follow", "actor": "https://instance.example/actor", "object": "https://www.w3.org/ns/activitystreams#Public" } }
3. The Two Relay Protocols
Two protocols exist in the wild. They differ in how a server addresses the relay when subscribing, but the underlying ActivityPub mechanics are the same.
Mastodon Relay Protocol
- The subscribing server uses the relay's inbox endpoint IRI —
e.g.
https://relay.example.com/inbox - Uses HTTP Signatures for verification
- Uses the standard
/Follow
flow described aboveAccept - Subscription is performed at the server admin level only
LitePub / Pleroma Relay Protocol
- The subscribing server uses the relay's actor document IRI —
e.g.
https://relay.example.com/actor - The inbox is derived automatically from the actor document
- Allows any actor to communicate with relays, not just admin-controlled instance actors
- LitePub is a set of conventions extending ActivityPub
Which endpoint to use
| Platform | Subscription Endpoint |
|---|---|
| Mastodon | |
| Misskey and forks | |
| Pleroma / Akkoma | |
| snac | |
Both endpoints are typically served by the same relay software. A relay that supports both protocols will accept subscriptions at either endpoint.
4. How Relays Process Activities
Once a server is subscribed, it sends all its public activities to the relay's inbox. The relay then distributes them to all other subscribers using one of two mechanisms:
Message relaying (Announce wrapping)
For
Create and Announce activities, the relay wraps the object in a
new Announce activity attributed to the relay actor:
{ "@context": "https://www.w3.org/ns/activitystreams", "id": "https://relay.example.com/announces/789", "type": "Announce", "actor": "https://relay.example.com/actor", "object": "https://instance.example/users/alice/statuses/123", "to": ["https://www.w3.org/ns/activitystreams#Public"], "cc": [] }
This is the primary distribution mechanism. The receiving server sees an
Announce from the relay and fetches the original object if needed.
Message forwarding (direct re-delivery)
For
Update, Delete, Like, Undo, Move, Add, and Remove
activities, the relay POSTs the activity directly to all subscribers
unmodified — it does not wrap it in an Announce.
This is necessary because wrapping these activity types in
Announce
would change their semantics (e.g. an Announce{Delete} does not mean
the same thing as a Delete).
LD-Signatures and forwarding
Activities that carry valid linked-data signatures can be forwarded directly (the recipient can verify the original author's signature). Activities that lack LD-signatures must be wrapped in
Announce instead,
because the relay cannot prove the activity's origin otherwise.
Filtering rules
Most relay implementations apply these filters:
- Only activities addressed to
(inhttps://www.w3.org/ns/activitystreams#Public
orto
) are acceptedcc - Activities older than ~30 minutes are skipped
- Duplicate activity IDs are rejected to prevent reprocessing
5. Relay Endpoints
A typical relay exposes these HTTP endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
| GET | Returns the relay's ActivityPub actor document |
| POST | Receives activities from subscribers |
| GET | WebFinger discovery |
| GET | NodeInfo discovery document |
| GET | NodeInfo schema (software name, version, stats) |
Some implementations also expose:
— relay statistics (subscriber count, activity throughput)/stats
6. Types of Relays
General-purpose relays
Broadcast all public content from all subscribed servers. Maximum content discovery, but high resource cost. A general relay with a few large, active servers subscribed will generate significantly more traffic than one with many small servers.
Geographic / community relays
Restricted to servers in a specific region or community. Reduces volume while keeping content culturally relevant. Examples include relays limited to Australian/New Zealand instances or Canadian instances.
Topic-based / selective relays (FediBuzz model)
FediBuzz (relay.fedi.buzz) takes a fundamentally different approach. Instead of receiving activities from subscribers, it connects to Mastodon's Streaming API and allows per-subscriber customisation:
- Follow specific hashtags — only relay posts matching chosen tags
(supports smart matching: following
captures#dd
etc.)#dd1302 - Follow specific instances — only relay posts from chosen servers
- Language filtering — only relay posts in chosen languages
This dramatically reduces resource consumption compared to general relays. The trade-off is that it requires API access to source instances rather than using the standard relay subscription model.
7. Building a Relay — Implementation Guide
Minimum viable relay
A minimal relay implementation must:
- Serve an actor document at a stable URL with a valid
,publicKey
, andinboxendpoints.sharedInbox - Accept
activities where theFollow
isobject
— validate HTTP Signatures, store the subscriber, and respond withas:PublicAccept - Accept
— remove the subscriberUndo{Follow} - Accept incoming activities at the inbox — validate that they are addressed to Public, then distribute to all subscribers
- Distribute activities — for each subscriber, POST the activity
(or an
wrapping it) to the subscriber'sAnnouncesharedInbox - Sign all outgoing requests with HTTP Signatures using the relay actor's private key
Activity types to handle
| Incoming activity | Relay action |
|---|---|
(object = ) | Store subscriber; send |
| Remove subscriber |
| Wrap in ; deliver to all subscribers |
| Wrap in or forward; deliver to all subscribers |
| Forward directly to all subscribers |
| Forward directly to all subscribers |
| Forward directly to all subscribers |
(other than Follow) | Forward directly to all subscribers |
| Forward directly to all subscribers |
/ | Forward directly to all subscribers |
Delivery optimisation
- Always use
when available — sending one request per subscribing server instead of one per actorsharedInbox - Use a job queue for async delivery — a relay with 100 subscribers means 100 outgoing HTTP requests per incoming activity
- Implement retry logic with exponential backoff for failed deliveries
- Cache resolved actors and their public keys (typical TTL: ~2 days)
- Skip delivery to servers that have been consistently unreachable
HTTP Signatures
The relay must verify incoming HTTP Signatures and sign all outgoing requests. See the HTTP Signatures knowledge base for full details.
Notable relay-specific concern: when forwarding an activity, the relay signs the outgoing request with its own key. The
keyId in the
Signature header is the relay actor's key, not the original author's.
This is why some implementations extract the actor from the keyId
rather than from the activity's actor field — they may differ when a
relay forwards content.
8. Subscribing to a Relay — Admin Guide
Mastodon
- Log in with admin credentials
- Navigate to Preferences > Administration > Relays
- Select "Setup A Relay Connection"
- Enter the relay URL with the
suffix (e.g./inbox
)https://relay.example.com/inbox - Click "Save And Enable"
- Wait for approval if the relay requires it
Pleroma / Akkoma
Use the CLI:
mix pleroma.relay follow https://relay.example.com/actor
or:
pleroma_ctl relay follow https://relay.example.com/actor
Misskey and forks
Use the admin panel to add a relay with the
/inbox endpoint.
snac
Use the
/actor endpoint:
https://relay.example.com/actor
9. Resource Impact and Trade-offs
What relays cost
Subscribing to a relay increases:
- Bandwidth — every public post from every server on the relay is delivered to your inbox
- Storage — all relayed posts are stored locally (unless your software has a retention policy)
- Processing — each incoming activity must be validated, stored, and indexed
- Moderation burden — relayed content may include posts that violate your server's rules
Sizing guidelines
- A general relay with a few large, very active servers will produce significantly more traffic than one with many small servers
- Topic-based relays (FediBuzz) are much lighter because they filter content before delivery
- Monitor disk usage and database size after subscribing — some admins report rapid growth
When relays are most useful
- New instances with few users and an empty federated timeline
- Small instances that want better hashtag search coverage
- Niche instances that want to connect with related communities
When relays may not be appropriate
- Large instances that already have strong organic federation
- Resource-constrained servers that cannot handle the additional load
- Heavily moderated servers where unfiltered relay content creates moderation overhead
10. Moderation and Security
Content moderation gaps
Current relay implementations provide minimal moderation capabilities. A relay typically re-broadcasts everything it receives without content filtering. The subscribing server's own moderation tools (domain blocks, keyword filters) still apply to relayed content, but the volume can make moderation more difficult.
Recommendation: Verify a relay's content and subscriber list before subscribing. Check whether the relay operator maintains a blocklist.
No boost notifications
When a relay
Announces a post, the original author does not receive
a notification. The relay actor is the one performing the Announce, and
implementations treat relay announces differently from user boosts.
Domain blocking
Some relay implementations (notably aode-relay) support domain blocklisting and allowlisting:
- Blocklist mode: accept all subscribers except blocked domains
- Allowlist mode (restricted): only accept explicitly approved domains
API token concerns
Some relay services (e.g. FediBuzz) request API tokens from subscribing instances. This grants broader access than necessary — potentially including followers-only and mentioned-only posts, which contradicts the relay principle of only handling public content. Evaluate what access a relay service requests before providing credentials.
11. Standardisation Efforts
Current state
There is no formal relay specification. The relay concept emerged organically from Mastodon's implementation and was independently implemented by Pleroma using the LitePub protocol. This has led to protocol fragmentation.
FEP-a974
A Fediverse Enhancement Proposal (FEP-a974) exists to formalise relay behaviour, addressing:
- Standard subscription and unsubscription flows
- Activity acceptance criteria
- Relay actor type definitions
- Terminology standardisation
SocialHub discussions
The SocialHub ActivityPub forum has extensive ongoing discussion about relay standardisation, including debates about:
- What to call servers that publish to and consume from relays
- Whether relays should have moderation capabilities
- How to handle the two competing subscription protocols
12. Existing Relay Software
| Software | Language | Protocol Support | Key Feature |
|---|---|---|---|
| pub-relay | Crystal | Mastodon + LitePub | Rigorous HTTP Signature verification; LD-signature-aware forwarding |
| Activity-Relay | Go | Mastodon + LitePub | Redis-backed job queue; CLI admin; YAML config |
| ActivityRelay (Pleroma) | Python | LitePub | Mature (14 releases); configurable DB backends |
| aode-relay | Rust | Mastodon + LitePub | Lightweight; blocklist/allowlist; Prometheus metrics; Telegram admin bot |
| buzzrelay (FediBuzz) | Rust | Follow-only | Streaming API approach; hashtag/instance/language filtering; PostgreSQL |
All are licensed under AGPLv3.
Notable differences
- pub-relay is the closest to a reference implementation for the Mastodon relay protocol
- Activity-Relay requires Redis and has the most structured architecture (API server + job worker + control utility)
- aode-relay is the most feature-rich for moderation (blocklist, allowlist, restricted mode)
- buzzrelay is architecturally different — it connects to Mastodon's Streaming API rather than receiving activities at an inbox
13. Common Implementation Mistakes
- Wrong
in theobject
: subscribing with the relay actor's id as theFollow
instead ofobject
— the relay will treat this as a normal follow, not a relay subscriptionas:Public - Missing
: if the subscribing server's actor does not exposesharedInbox
, the relay may not be able to deliver activitiesendpoints.sharedInbox - Not handling both subscription protocols: if building a relay,
accept subscriptions at both
and/inbox
to support all platforms/actor - Wrapping
inDelete
: forwarding aAnnounce
asDelete
changes its semantics —Announce{Delete}
should be forwarded directlyDelete - Not filtering non-public activities: relays must only process
activities addressed to
— relaying non-public content violates user privacyas:Public - No deduplication: without tracking processed activity IDs, the same activity can be relayed multiple times if it arrives through different paths
- Not signing outgoing requests: all deliveries from the relay must be signed with the relay actor's key, or receiving servers will reject them
- Ignoring the
endpoint: serving only/actor
and not an actor document at/inbox
breaks Pleroma/Akkoma/snac subscription/actor