Skip to main content

The Complete Guide to ActivityPub Development in 2025

· 6 min read
SocialDocs Team
Documentation Team

ActivityPub has become the backbone of the decentralized social web. With over 13 million users across Mastodon, Lemmy, PeerTube, Pixelfed, and dozens of other platforms, understanding ActivityPub is essential for developers building the next generation of social applications.

This guide provides everything you need to start building with ActivityPub — from core concepts to production deployment.

What is ActivityPub?

ActivityPub is a W3C Recommendation that defines how servers communicate in a federated social network. Unlike centralized platforms like Twitter or Facebook, ActivityPub enables independent servers to communicate with each other, creating an interconnected "Fediverse."

Key benefits:

  • No single point of control — Users can choose their server or run their own
  • Interoperability — A Mastodon user can follow a PeerTube channel
  • Data portability — Users can migrate between servers
  • Open standard — Anyone can implement it

Core Concepts

Before diving into code, understand these fundamental concepts:

Actors

An Actor is any entity that can perform actions — usually a user account, but also bots, groups, or services.

{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Person",
"id": "https://example.com/users/alice",
"inbox": "https://example.com/users/alice/inbox",
"outbox": "https://example.com/users/alice/outbox",
"preferredUsername": "alice",
"name": "Alice"
}

Activities

Activities represent actions: Create, Follow, Like, Announce (boost), Delete, and more.

{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Create",
"actor": "https://example.com/users/alice",
"object": {
"type": "Note",
"content": "Hello, Fediverse!"
}
}

Federation

Federation is how servers exchange activities. When Alice follows Bob on a different server, her server sends a Follow activity to Bob's inbox.

Getting Started: Your First ActivityPub Server

Ready to build? Here's the minimal implementation path:

Step 1: WebFinger Discovery

Implement the /.well-known/webfinger endpoint so other servers can find your users:

app.get('/.well-known/webfinger', (req, res) => {
const resource = req.query.resource;
const [, user, domain] = resource.match(/acct:(.+)@(.+)/);

res.json({
subject: resource,
links: [{
rel: 'self',
type: 'application/activity+json',
href: `https://${domain}/users/${user}`
}]
});
});

→ Full guide: WebFinger Implementation

Step 2: Actor Endpoint

Return your user's profile in ActivityPub format:

app.get('/users/:username', (req, res) => {
res.json({
'@context': [
'https://www.w3.org/ns/activitystreams',
'https://w3id.org/security/v1'
],
type: 'Person',
id: `https://example.com/users/${req.params.username}`,
inbox: `https://example.com/users/${req.params.username}/inbox`,
outbox: `https://example.com/users/${req.params.username}/outbox`,
preferredUsername: req.params.username,
publicKey: {
id: `https://example.com/users/${req.params.username}#main-key`,
owner: `https://example.com/users/${req.params.username}`,
publicKeyPem: PUBLIC_KEY
}
});
});

→ Full guide: Building an Actor

Step 3: Inbox for Receiving Activities

Accept incoming activities from other servers:

app.post('/users/:username/inbox', async (req, res) => {
// Verify HTTP Signature
await verifySignature(req);

const activity = req.body;

switch (activity.type) {
case 'Follow':
await handleFollow(activity);
break;
case 'Create':
await handleCreate(activity);
break;
// ... handle other types
}

res.status(202).send('Accepted');
});

→ Full guide: Handling Incoming Activities

Step 4: HTTP Signatures

All inbox requests must be cryptographically signed. This proves the request came from who it claims:

const crypto = require('crypto');

function signRequest(privateKey, keyId, method, url, body) {
const digest = crypto.createHash('sha256').update(body).digest('base64');
const date = new Date().toUTCString();

const signingString = [
`(request-target): ${method.toLowerCase()} ${new URL(url).pathname}`,
`host: ${new URL(url).host}`,
`date: ${date}`,
`digest: SHA-256=${digest}`
].join('\n');

const signature = crypto.sign('RSA-SHA256',
Buffer.from(signingString), privateKey).toString('base64');

return { date, digest: `SHA-256=${digest}`, signature };
}

→ Full guide: Authentication and Security

Essential Implementation Patterns

Following and Followers

The follow flow is fundamental to federation:

  1. Alice sends Follow activity to Bob's inbox
  2. Bob's server sends Accept back to Alice's inbox
  3. Alice adds Bob to her following list
  4. Bob's future posts are delivered to Alice's inbox

→ Full guide: Following and Followers

Creating Posts

Wrap content in a Create activity and deliver to followers:

async function createPost(author, content) {
const note = {
type: 'Note',
id: `https://example.com/notes/${uuid()}`,
attributedTo: author.id,
content: content,
to: ['https://www.w3.org/ns/activitystreams#Public'],
cc: [`${author.id}/followers`]
};

const activity = {
type: 'Create',
actor: author.id,
object: note,
to: note.to,
cc: note.cc
};

await deliverToFollowers(activity, author);
}

→ Full guide: Posts and Replies

Platform Compatibility

Different Fediverse platforms have specific requirements:

PlatformKey Considerations
MastodonUses toot: namespace, requires discoverable flag
LemmyUses Group actors for communities
PeerTubeVideo-focused with custom extensions
PixelfedImage-focused, supports Image objects

→ Full guides: Mastodon Compatibility, Lemmy Compatibility

Libraries and Tools

Don't build from scratch. Use established libraries:

JavaScript/TypeScript

Python

Go

  • go-fed — Complete ActivityPub library
  • pub — Lightweight library

Rust

→ Full directory: Ecosystem Libraries

Testing Your Implementation

Before going live:

  1. Use ngrok to expose your local server
  2. Test WebFinger with the lookup tool
  3. Try following from a real Mastodon account
  4. Check signatures with the signature tester

→ Full guide: Testing Your Implementation

Production Checklist

Before deploying:

  • HTTPS only (required for federation)
  • HTTP Signatures implemented
  • Rate limiting on inbox
  • Content sanitization (prevent XSS)
  • Delivery retry queue
  • Dead server tracking

→ Full checklist: Compliance Checklist

Community Resources

Join the ActivityPub developer community:

Next Steps

Ready to build? Start with these resources:

  1. What is the Fediverse? — Conceptual foundation
  2. Building an Actor — Your first implementation
  3. ActivityPub Specification — Protocol deep-dive

The decentralized social web is growing fast. Whether you're building the next Mastodon competitor, adding federation to an existing app, or creating something entirely new, ActivityPub provides the foundation.

Welcome to the Fediverse. Let's build together.


Have questions? Join the discussion on SocialHub or open an issue on GitHub.