Fedify: an ActivityPub server framework's avatar

Fedify: an ActivityPub server framework

@fedify@hollo.social ยท 8 following ยท 723 followers

:fedify: Fedify is a TypeScript library for building federated server apps powered by ActivityPub and other standards, so-called fediverse. It aims to eliminate the complexity and redundant boilerplate code when building a federated server app, so that you can focus on your business logic and user experience.

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

๐ŸŽ‰ Excited to announce that is now on Open Collective! Support the project's development starting at:

  • Backer (from $5/mo)
  • Supporter (from $25/mo)
  • Sponsor (from $100/mo)
  • Corporate Sponsor (from $500/mo)
  • Custom donations welcome

Your support will help us maintain and improve Fedify. Check it out here:

https://opencollective.com/fedify

:fedify:

Fedify's Open Collective page showing the project logo, description as โ€œA TypeScript library for building federated server apps powered by ActivityPub and other standardsโ€, and five contribution tiers starting from $5/month Backer to $500/month Corporate Sponsor, with custom contribution options available.
ALT text detailsFedify's Open Collective page showing the project logo, description as โ€œA TypeScript library for building federated server apps powered by ActivityPub and other standardsโ€, and five contribution tiers starting from $5/month Backer to $500/month Corporate Sponsor, with custom contribution options available.
Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

Fedify is an server framework in & . It aims to eliminate the complexity and redundant boilerplate code when building a federated server app, so that you can focus on your business logic and user experience.

The key features it provides currently are:

If you're curious, take a look at the website! There's comprehensive docs, a demo, a tutorial, example code, and more:

https://fedify.dev/

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

We're pleased to announce that has been included in the Nivenly Fediverse Security Fund program!

The @nivenly Foundation has launched a security bounty fund to support contributors who identify and help fix vulnerabilities in popular software. Both Fedify and @hollo are among the selected projects that meet their responsible security disclosure requirements.

This program will run from Aprilโ€“September 2025, with bounties of $250โ€“$500 USD for high and critical security vulnerabilities.

We're honored to be recognized alongside other established fediverse projects like Mastodon, Misskey, and Lemmy. This further encourages our commitment to maintaining strong security practices.

If you're interested in contributing to Fedify's security, please follow our responsible disclosure process outlined in our SECURITY.md file.

Learn more about the program:

https://nivenly.org/blog/2025/04/01/nivenly-fediverse-security-fund/

Nicolas Borboรซn's avatar
Nicolas Borboรซn

@nborboen@social.epfl.ch

What to learn more about the and ? This tutorial (fedify.dev/tutorial/microblog) from @fedify is a really good starter. It explains how to implement a really simple server and see how communication exchange are done with AP.

https://fedify.dev/ website
ALT text detailshttps://fedify.dev/ website
https://github.com/fedify-dev/fedify
ALT text detailshttps://github.com/fedify-dev/fedify
Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social ยท Reply to Fedify: an ActivityPub server framework's post

ใ‚‚ใ—ใ‹ใ—ใŸใ‚‰ใ”ๅญ˜ใ˜ใชใ„ใ‹ใ‚‚ใ—ใ‚Œใพใ›ใ‚“ใŒใ€Fedifyใซใฏ DiscordใจMatrixใฎใ‚ณใƒŸใƒฅใƒ‹ใƒ†ใ‚ฃใŒใ‚ใ‚Šใพใ™ใ€‚ใ“ใ“ใงใฏใ€ใ‚ตใƒใƒผใƒˆใ‚’ๅ—ใ‘ใŸใ‚Šใ€ๆฉŸ่ƒฝใซใคใ„ใฆ่ญฐ่ซ–ใ—ใŸใ‚Šใ€ActivityPubใ‚„ใƒ•ใ‚งใƒ‡ใƒฌใƒผใƒ†ใƒƒใƒ‰ใ‚ฝใƒผใ‚ทใƒฃใƒซใƒใƒƒใƒˆใƒฏใƒผใ‚ฏใซใคใ„ใฆ่ฉฑใ—ๅˆใ†ใ“ใจใŒใงใใพใ™ใ€‚

ใŠๅฅฝใฟใฎใ‚ณใƒŸใƒฅใƒ‹ใƒ†ใ‚ฃใซใ”ๅ‚ๅŠ ใใ ใ•ใ„ใ€‚ใฉใกใ‚‰ใฎใƒใƒฃใƒณใƒใƒซใงใ‚‚ใ€Fedifyใ‚„ใƒ•ใ‚งใƒ‡ใƒฌใƒผใ‚ทใƒงใƒณ้–ข้€ฃใฎใƒˆใƒ”ใƒƒใ‚ฏใซใคใ„ใฆๆดป็™บใช่ญฐ่ซ–ใŒ่กŒใ‚ใ‚Œใฆใ„ใพใ™ใ€‚

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social ยท Reply to Fedify: an ActivityPub server framework's post

ํ˜น์‹œ ๋ชจ๋ฅด๊ณ  ๊ณ„์…จ๋‹ค๋ฉด, Fedify๋Š” Discord์™€ Matrix ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ์šด์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ณณ์—์„œ ๋„์›€์„ ๋ฐ›๊ฑฐ๋‚˜, ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฑฐ๋‚˜, ActivityPub์™€ ์—ฐํ•ฉ ์†Œ์…œ ๋„คํŠธ์›Œํฌ์— ๋Œ€ํ•ด ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„์˜ ์„ ํ˜ธ๋„์— ๋”ฐ๋ผ ์–ด๋А ์ปค๋ฎค๋‹ˆํ‹ฐ๋“  ์ฐธ์—ฌํ•ด ์ฃผ์„ธ์š”. ๋‘ ์ฑ„๋„ ๋ชจ๋‘ Fedify์™€ ์—ฐํ•ฉ ๊ด€๋ จ ์ฃผ์ œ์— ๋Œ€ํ•œ ํ™œ๋ฐœํ•œ ๋…ผ์˜๊ฐ€ ์ด๋ฃจ์–ด์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social ยท Reply to Fedify: an ActivityPub server framework's post

่‡ชๅˆ†ใ ใ‘ใฎActivityPubใ‚’ไฝœใ‚ŠใŸใ„ใ‘ใฉใ€ไฝ•ใ‹ใ‚‰ๅง‹ใ‚ใ‚Œใฐ่‰ฏใ„ใฎใ‹ๅˆ†ใ‹ใ‚‰ใชใ„ใงใ™ใ‹๏ผŸFedifyใฎๅ…ฌๅผใƒใƒฅใƒผใƒˆใƒชใ‚ขใƒซใ€Œ่‡ชๅˆ†ใ ใ‘ใฎใƒ•ใ‚งใƒ‡ใ‚ฃใƒใƒผใ‚นใฎใƒžใ‚คใ‚ฏใƒญใƒ–ใƒญใ‚ฐใ‚’ไฝœใ‚ใ†๏ผใ€ใ‚’่ชญใ‚“ใงใฟใฆใใ ใ•ใ„๏ผ

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social ยท Reply to Fedify: an ActivityPub server framework's post

์ž์‹ ๋งŒ์˜ ๊ตฌํ˜„์„ ํ•˜๊ณ  ์‹ถ์ง€๋งŒ, ์–ด๋””์„œ ์‹œ์ž‘ํ•ด์•ผ ํ• ์ง€ ๋ชจ๋ฅด๊ฒ ๋‚˜์š”? ๊ณต์‹ ํŠœํ† ๋ฆฌ์–ผ์ธ 〈๋‚˜๋งŒ์˜ ์—ฐํ•ฉ์šฐ์ฃผ ๋งˆ์ดํฌ๋กœ๋ธ”๋กœ๊ทธ ๋งŒ๋“ค๊ธฐ〉๋ฅผ ์ฝ๊ณ  ๋”ฐ๋ผํ•ด ๋ณด์„ธ์š”!

Shrutarshi Basu's avatar
Shrutarshi Basu

@basus@mastodon.social

I should suck it up and learn to program properly in TypeScript. There's a growing list of software that I'd like to use and extend that uses TypeScript (Obsidian, Lume, Fedify), not to mention its utility for web programming. The language itself is pretty good, though I'm not thrilled by a lot of the tooling and ecosystem around it. Hopefully there's a way of writing usable code in it that keeps the messiness at bay.

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

Want to build your own implementation, but don't know where to start? Read and follow 's official tutorial, Creating your own federated microblog, and get started!

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

๐Ÿ“ฃ Exciting news! Fedify CLI is now available via Homebrew!

If you're using on macOS or on Linux, you can now install our CLI toolchain with a simple command:

brew install fedify

This makes it even easier to get started with building your federated server app. Try it out and let us know what you think!

geeknews_bot's avatar
geeknews_bot

@geeknews_bot@sns.lemondouble.com

Show GN: ๋‚˜๋งŒ์˜ ์—ฐํ•ฉ์šฐ์ฃผ(fediverse) ๋งˆ์ดํฌ๋กœ๋ธ”๋กœ๊ทธ ๋งŒ๋“ค๊ธฐ
------------------------------
์ด ํŠœํ† ๋ฆฌ์–ผ์€ [Fedify] ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ [ActivityPub] ํ”„๋กœํ† ์ฝœ ๊ธฐ๋ฐ˜์˜ ๋งˆ์ดํฌ๋กœ๋ธ”๋กœ๊ทธ ์„œ๋น„์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ActivityPub์€ ๋‹ค์–‘ํ•œ ์†Œ์…œ ๋„คํŠธ์›Œํฌ ์„œ๋น„์Šค๋“ค์ด ์„œ๋กœ ์—ฐ๋™๋  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ถ„์‚ฐํ˜• ์†Œ์…œ ๋„คํŠธ์›Œํ‚น ํ”„๋กœํ† ์ฝœ๋กœ, ์ด๋ฅผ ํ†ตํ•ด [Mastodon], [Misskey] ๊ฐ™์€ ์„œ๋น„์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋…โ€ฆ
------------------------------
https://news.hada.io/topic?id=20508&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

Hacker News's avatar
Hacker News

@hkrn@mstdn.social

Creating your own federated microblog
L: fedify.dev/tutorial/microblog
C: news.ycombinator.com/item?id=4
posted on 2025.04.24 at 05:37:57 (c=0, p=8)

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

Hey folks! We're excited to share a preview of a new API coming in 1.6 that should make structuring larger federated apps much cleaner: FederationBuilder.

As your Fedify applications grow, you might encounter circular dependency issues when registering dispatchers and listeners across multiple files. The new FederationBuilder pattern helps solve this by separating the configuration phase from instantiation.

Instead of this:

// federation.ts
import { createFederation } from "@fedify/fedify";

export const federation = createFederation<AppContext>({
  kv: new DbKvStore(), 
  queue: new RedisMessageQueue(),
  // Other options...
});

// Now we need to import this federation instance in other files
// to register dispatchers and listeners...

You can now do this:

// builder.ts
import { createFederationBuilder } from "@fedify/fedify";

export const builder = createFederationBuilder<AppContext>();

// other files can import and configure this builder...
// actors.ts
import { builder } from "./builder.ts";
import { Person } from "@fedify/fedify";

builder.setActorDispatcher("/users/{handle}", async (ctx, handle) => {
  // Actor implementation
});
// inbox.ts
import { builder } from "./builder.ts";
import { Follow } from "@fedify/fedify";

builder.setInboxListeners("/users/{handle}/inbox", "/inbox")
  .on(Follow, async (ctx, follow) => {
    // Follow handling
  });
// main.ts โ€” Only create the Federation instance at startup
import { builder } from "./builder.ts";

// Build the Federation object with actual dependencies
export const federation = await builder.build({
  kv: new DbKvStore(),
  queue: new RedisMessageQueue(),
  // Other options...
});

This pattern helps avoid circular dependencies and makes your code more modular. Each part of your app can configure the builder without needing the actual Federation instance.

The full documentation will be available when 1.6 is released, but we wanted to share this early with our community. Looking forward to your feedback when it lands!

Want to try it right now? You can install the development version from JSR or npm:

# Deno
deno add jsr:@fedify/fedify@1.6.0-dev.777+1206cb01

# Node.js
npm add @fedify/fedify@1.6.0-dev.777

# Bun
bun add @fedify/fedify@1.6.0-dev.777

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

Hey folks! We're excited to share a preview of a new API coming in 1.6 that should make structuring larger federated apps much cleaner: FederationBuilder.

As your Fedify applications grow, you might encounter circular dependency issues when registering dispatchers and listeners across multiple files. The new FederationBuilder pattern helps solve this by separating the configuration phase from instantiation.

Instead of this:

// federation.ts
import { createFederation } from "@fedify/fedify";

export const federation = createFederation<AppContext>({
  kv: new DbKvStore(), 
  queue: new RedisMessageQueue(),
  // Other options...
});

// Now we need to import this federation instance in other files
// to register dispatchers and listeners...

You can now do this:

// builder.ts
import { createFederationBuilder } from "@fedify/fedify";

export const builder = createFederationBuilder<AppContext>();

// other files can import and configure this builder...
// actors.ts
import { builder } from "./builder.ts";
import { Person } from "@fedify/fedify";

builder.setActorDispatcher("/users/{handle}", async (ctx, handle) => {
  // Actor implementation
});
// inbox.ts
import { builder } from "./builder.ts";
import { Follow } from "@fedify/fedify";

builder.setInboxListeners("/users/{handle}/inbox", "/inbox")
  .on(Follow, async (ctx, follow) => {
    // Follow handling
  });
// main.ts โ€” Only create the Federation instance at startup
import { builder } from "./builder.ts";

// Build the Federation object with actual dependencies
export const federation = await builder.build({
  kv: new DbKvStore(),
  queue: new RedisMessageQueue(),
  // Other options...
});

This pattern helps avoid circular dependencies and makes your code more modular. Each part of your app can configure the builder without needing the actual Federation instance.

The full documentation will be available when 1.6 is released, but we wanted to share this early with our community. Looking forward to your feedback when it lands!

Want to try it right now? You can install the development version from JSR or npm:

# Deno
deno add jsr:@fedify/fedify@1.6.0-dev.777+1206cb01

# Node.js
npm add @fedify/fedify@1.6.0-dev.777

# Bun
bun add @fedify/fedify@1.6.0-dev.777

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social ยท Reply to Fedify: an ActivityPub server framework's post

ใ‚‚ใ—ใ‹ใ—ใŸใ‚‰ใ”ๅญ˜ใ˜ใชใ„ใ‹ใ‚‚ใ—ใ‚Œใพใ›ใ‚“ใŒใ€Fedifyใซใฏ DiscordใจMatrixใฎใ‚ณใƒŸใƒฅใƒ‹ใƒ†ใ‚ฃใŒใ‚ใ‚Šใพใ™ใ€‚ใ“ใ“ใงใฏใ€ใ‚ตใƒใƒผใƒˆใ‚’ๅ—ใ‘ใŸใ‚Šใ€ๆฉŸ่ƒฝใซใคใ„ใฆ่ญฐ่ซ–ใ—ใŸใ‚Šใ€ActivityPubใ‚„ใƒ•ใ‚งใƒ‡ใƒฌใƒผใƒ†ใƒƒใƒ‰ใ‚ฝใƒผใ‚ทใƒฃใƒซใƒใƒƒใƒˆใƒฏใƒผใ‚ฏใซใคใ„ใฆ่ฉฑใ—ๅˆใ†ใ“ใจใŒใงใใพใ™ใ€‚

ใŠๅฅฝใฟใฎใ‚ณใƒŸใƒฅใƒ‹ใƒ†ใ‚ฃใซใ”ๅ‚ๅŠ ใใ ใ•ใ„ใ€‚ใฉใกใ‚‰ใฎใƒใƒฃใƒณใƒใƒซใงใ‚‚ใ€Fedifyใ‚„ใƒ•ใ‚งใƒ‡ใƒฌใƒผใ‚ทใƒงใƒณ้–ข้€ฃใฎใƒˆใƒ”ใƒƒใ‚ฏใซใคใ„ใฆๆดป็™บใช่ญฐ่ซ–ใŒ่กŒใ‚ใ‚Œใฆใ„ใพใ™ใ€‚

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social ยท Reply to Fedify: an ActivityPub server framework's post

ํ˜น์‹œ ๋ชจ๋ฅด๊ณ  ๊ณ„์…จ๋‹ค๋ฉด, Fedify๋Š” Discord์™€ Matrix ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ์šด์˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ณณ์—์„œ ๋„์›€์„ ๋ฐ›๊ฑฐ๋‚˜, ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฑฐ๋‚˜, ActivityPub์™€ ์—ฐํ•ฉ ์†Œ์…œ ๋„คํŠธ์›Œํฌ์— ๋Œ€ํ•ด ๋Œ€ํ™”๋ฅผ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„์˜ ์„ ํ˜ธ๋„์— ๋”ฐ๋ผ ์–ด๋А ์ปค๋ฎค๋‹ˆํ‹ฐ๋“  ์ฐธ์—ฌํ•ด ์ฃผ์„ธ์š”. ๋‘ ์ฑ„๋„ ๋ชจ๋‘ Fedify์™€ ์—ฐํ•ฉ ๊ด€๋ จ ์ฃผ์ œ์— ๋Œ€ํ•œ ํ™œ๋ฐœํ•œ ๋…ผ์˜๊ฐ€ ์ด๋ฃจ์–ด์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

In case you weren't aware, has both and communities where you can get help, discuss features, or just chat about and federated social networks.

Feel free to join either community based on your preference. Both channels have active discussions about Fedify and federation topics.

erick's avatar
erick

@almosterick@mastodon.social

We at @index@www.avant.dev have contributed to @fedify - the framework enabling @index@activitypub.ghost.org integration with the fediverse.

As social networks build walls, Fedify creates critical infrastructure for an open web. Their tools simplify federation protocols, making decentralized communication accessible.

Support: opencollective.com/fedify

Jaeyeol Lee (a.k.a. kodingwarrior) :vim:'s avatar
Jaeyeol Lee (a.k.a. kodingwarrior) :vim:

@kodingwarrior@silicon.moe

๋ญ”๊ฐ€ ์ด๊ฑฐ ๋งŒ๋“ค๋‹ค๋ณด๋‹ˆ๊นŒ fedify๋ฅผ ์ด์šฉํ•œ sns ๋งŒ๋“ค๊ธฐ ์Šคํƒ€ํ„ฐํ‚ท ๊ฐ™์€๊ฒŒ ๋งŒ๋“ค์–ด์ง€๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Œ.

* ๋…ธํŠธ ์ž‘์„ฑ
* ํŒ”๋กœ/์–ธํŒ”
* ํƒ€์ž„๋ผ์ธ
* ํ”„๋กœํ•„
* ๋ธ”๋ฝ/๋ธ”๋ฝํ•ด์ œ

์ด๋Ÿฐ ๊ฒƒ๋“ค์ด ๋ฏธ๋ฆฌ ๊ตฌํ˜„์€ ๋˜์–ด์žˆ๊ณ  ๋‚˜๋จธ์ง€ ๊ธฐ๋Šฅ์€ ์š”๋ น๊ป ์ฑ„์šฐ๋ฉด ๋˜๋Š”

ๆดช ๆฐ‘ๆ†™ (Hong Minhee)'s avatar
ๆดช ๆฐ‘ๆ†™ (Hong Minhee)

@hongminhee@hollo.social

Don't build from scratch! It's complex. See why using the framework is the smarter way to develop for the fediverse in my new post:

https://hackers.pub/@hongminhee/2025/why-use-fedify

ๆดช ๆฐ‘ๆ†™ (Hong Minhee)'s avatar
ๆดช ๆฐ‘ๆ†™ (Hong Minhee)

@hongminhee@hackers.pub


So, you're captivated by the fediverseโ€”the decentralized social web powered by protocols like ActivityPub. Maybe you're dreaming of building the next great federated app, a unique space connected to Mastodon, Lemmy, Pixelfed, and more. The temptation to dive deep and implement ActivityPub yourself, from the ground up, is strong. Total control, right? Understanding every byte? Sounds cool!

But hold on a sec. Before you embark on that epic quest, let's talk reality. Implementing ActivityPub correctly isn't just one task; it's like juggling several complex standards while riding a unicycleโ€ฆ blindfolded. Itโ€™s hard.

That's where Fedify comes in. It's a TypeScript framework designed to handle the gnarliest parts of ActivityPub development, letting you focus on what makes your app special, not reinventing the federation wheel.

This post will break down the common headaches of DIY ActivityPub implementation and show how Fedify acts as the super-powered pain reliever, starting with the very foundation of how data is represented.

Challenge #1: Data Modelingโ€”Speaking ActivityStreams & JSON-LD Fluently

At its core, ActivityPub relies on the ActivityStreams 2.0 vocabulary to describe actions and objects, and it uses JSON-LD as the syntax to encode this vocabulary. While powerful, this combination introduces significant complexity right from the start.

First, understanding and correctly using the vast ActivityStreams vocabulary itself is a hurdle. You need to model everythingโ€”posts (Note, Article), profiles (Person, Organization), actions (Create, Follow, Like, Announce)โ€”using the precise terms and properties defined in the specification. Manual JSON construction is tedious and prone to errors.

Second, JSON-LD, the encoding layer, has specific rules that make direct JSON manipulation surprisingly tricky:

  • Missing vs. Empty Array: In JSON-LD, a property being absent is often semantically identical to it being present with an empty array. Your application logic needs to treat these cases equally when checking for values. For example, these two Note objects mean the same thing regarding the name property:
    // No name property
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": "โ€ฆ"
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "name": [],
      "content": "โ€ฆ"
    }
  • Single Value vs. Array: Similarly, a property holding a single value directly is often equivalent to it holding a single-element array containing that value. Your code must anticipate both representations for the same meaning, like for the content property here:
    // Single value
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": "Hello"
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": ["Hello"]
    }
  • Object Reference vs. Embedded Object: Properties can contain either the full JSON-LD object embedded directly or just a URI string referencing that object. Your application needs to be prepared to fetch the object's data if only a URI is given (a process called dereferencing). These two Announce activities are semantically equivalent (assuming the URIs resolve correctly):
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // Embedded objects:
      "actor": {
        "type": "Person",
        "id": "http://sally.example.org/",
        "name": "Sally"
      },
      "object": {
        "type": "Arrive",
        "id": "https://sally.example.com/arrive",
        /* ... */
      }
    }
    // Equivalent to:
    {
      "@context":
      "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // URI references:
      "actor": "http://sally.example.org/",
      "object": "https://sally.example.com/arrive"
    }

Attempting to manually handle all these vocabulary rules and JSON-LD variations consistently across your application inevitably leads to verbose, complex, and fragile code, ripe for subtle bugs that break federation.

Fedify tackles this entire data modeling challenge with its comprehensive, type-safe Activity Vocabulary API. It provides TypeScript classes for ActivityStreams types and common extensions, giving you autocompletion and compile-time safety. Crucially, these classes internally manage all the tricky JSON-LD nuances. Fedify's property accessors present a consistent interfaceโ€”non-functional properties (like tags) always return arrays, functional properties (like content) always return single values or null. It handles object references versus embedded objects seamlessly through dereferencing accessors (like activity.getActor()) which automatically fetch remote objects via URI when neededโ€”a feature known as property hydration. With Fedify, you work with a clean, predictable TypeScript API, letting the framework handle the messy details of AS vocabulary and JSON-LD encoding.

Challenge #2: Discovery & Identityโ€”Finding Your Actors

Once you can model data, you need to make your actors discoverable. This primarily involves the WebFinger protocol (RFC 7033). You'd need to build a server endpoint at /.well-known/webfinger capable of parsing resource queries (like acct: URIs), validating the requested domain against your server, and responding with a precisely formatted JSON Resource Descriptor (JRD). This JRD must include specific links, like a self link pointing to the actor's ActivityPub ID using the correct media type. Getting any part of this wrong can make your actors invisible.

Fedify simplifies this significantly. It automatically handles WebFinger requests based on the actor information you provide through its setActorDispatcher() method. Fedify generates the correct JRD response. If you need more advanced control, like mapping user-facing handles to internal identifiers, you can easily register mapHandle() or mapAlias() callbacks. You focus on defining your actors; Fedify handles making them discoverable.

// Example: Define how to find actors
federation.setActorDispatcher(
  "/users/{username}",
  async (ctx, username) => { /* ... */ }
);
// Now GET /.well-known/webfinger?resource=acct:username@your.domain just works!

Challenge #3: Core ActivityPub Mechanicsโ€”Handling Requests and Collections

Serving actor profiles requires careful content negotiation. A request for an actor's ID needs JSON-LD for machine clients (Accept: application/activity+json) but HTML for browsers (Accept: text/html). Handling incoming activities at the inbox endpoint involves validating POST requests, verifying cryptographic signatures, parsing the payload, preventing duplicates (idempotency), and routing based on activity type. Implementing collections (outbox, followers, etc.) with correct pagination adds another layer.

Fedify streamlines all of this. Its core request handler (via Federation.fetch() or framework adapters like @fedify/express) manages content negotiation. You define actors with setActorDispatcher() and web pages with your framework (Hono, Express, SvelteKit, etc.)โ€”Fedify routes appropriately. For the inbox, setInboxListeners() lets you define handlers per activity type (e.g., .on(Follow, ...)), while Fedify automatically handles validation, signature verification, parsing, and idempotency checks using its KV Store. Collection implementation is simplified via dispatchers (e.g., setFollowersDispatcher()); you provide logic to fetch a page of data, and Fedify constructs the correct Collection or CollectionPage with pagination.

// Define inbox handlers
federation.setInboxListeners("/inbox", "/users/{handle}/inbox")
  .on(Follow, async (ctx, follow) => { /* Handle follow */ })
  .on(Undo, async (ctx, undo) => { /* Handle undo */ });

// Define followers collection logic
federation.setFollowersDispatcher(
  "/users/{handle}/followers",
  async (ctx, handle, cursor) => { /* ... */ }
);

Challenge #4: Reliable Delivery & Asynchronous Processingโ€”Sending Activities Robustly

Sending an activity requires more than a simple POST. Networks fail, servers go down. You need robust failure handling and retry logic (ideally with backoff). Processing incoming activities synchronously can block your server. Efficiently broadcasting to many followers (fan-out) requires background processing and using shared inboxes where possible.

Fedify addresses reliability and scalability using its MessageQueue abstraction. When configured (highly recommended), Context.sendActivity() enqueues delivery tasks. Background workers handle sending with automatic retries based on configurable policies (like outboxRetryPolicy). Fedify supports various queue backends (Deno KV, Redis, PostgreSQL, AMQP). For high-traffic fan-out, Fedify uses an optimized two-stage mechanism to distribute the load efficiently.

// Configure Fedify with a persistent queue (e.g., Deno KV)
const federation = createFederation({
  queue: new DenoKvMessageQueue(/* ... */),
  // ...
});
// Sending is now reliable and non-blocking
await ctx.sendActivity({ handle: "myUser" }, recipient, someActivity);

Challenge #5: Securityโ€”Avoiding Common Pitfalls

Securing an ActivityPub server is critical. You need to implement HTTP Signatures (draft-cavage-http-signatures-12) for server-to-server authenticationโ€”a complex process. You might also need Linked Data Signatures (LDS) or Object Integrity Proofs (OIP) based on FEP-8b32 for data integrity and compatibility. Managing cryptographic keys securely is essential. Lastly, fetching remote resources risks Server-Side Request Forgery (SSRF) if not validated properly.

Fedify is designed with security in mind. It automatically handles the creation and verification of HTTP Signatures, LDS, and OIP, provided you supply keys via setKeyPairsDispatcher(). It includes key management utilities. Crucially, Fedify's default document loader includes built-in SSRF protection, blocking requests to private IPs unless explicitly allowed.

Challenge #6: Interoperability & Maintenanceโ€”Playing Nicely with Others

The fediverse is diverse. Different servers have quirks. Ensuring compatibility requires testing and adaptation. Standards evolve with new Federation Enhancement Proposals (FEPs). You also need protocols like NodeInfo to advertise server capabilities.

Fedify aims for broad interoperability and is actively maintained. It includes features like ActivityTransformers to smooth over implementation differences. NodeInfo support is built-in via setNodeInfoDispatcher().

Challenge #7: Developer Experienceโ€”Actually Building Your App

Beyond the protocol, building any server involves setup, testing, and debugging. With federation, debugging becomes harderโ€”was the message malformed? Was the signature wrong? Is the remote server down? Is it a compatibility quirk? Good tooling is essential.

Fedify enhances the developer experience significantly. Being built with TypeScript, it offers excellent type safety and editor auto-completion. The fedify CLI is a powerful companion designed to streamline common development tasks.

You can quickly scaffold a new project tailored to your chosen runtime and web framework using fedify init.

For debugging interactions and verifying data, fedify lookup is invaluable. It lets you inspect how any remote actor or object appears from the outside by performing WebFinger discovery and fetching the object's data. Fedify then displays the parsed object structure and properties directly in your terminal. For example, running:

$ fedify lookup @fedify-example@fedify-blog.deno.dev

Will first show progress messages and then output the structured representation of the actor, similar to this:

// Output of fedify lookup command (shows parsed object structure)
Person {
  id: URL "https://fedify-blog.deno.dev/users/fedify-example",
  name: "Fedify Example Blog",
  published: 2024-03-03T13:18:11.857Z, // Simplified timestamp
  summary: "This blog is powered by Fedify, a fediverse server framework.",
  url: URL "https://fedify-blog.deno.dev/",
  preferredUsername: "fedify-example",
  publicKey: CryptographicKey {
    id: URL "https://fedify-blog.deno.dev/users/fedify-example#main-key",
    owner: URL "https://fedify-blog.deno.dev/users/fedify-example",
    publicKey: CryptoKey { /* ... CryptoKey details ... */ }
  },
  // ... other properties like inbox, outbox, followers, endpoints ...
}

This allows you to easily check how data is structured or troubleshoot why an interaction might be failing by seeing the actual properties Fedify parsed.

Testing outgoing activities from your application during development is made much easier with fedify inbox. Running the command starts a temporary local server that acts as a publicly accessible inbox, displaying key information about the temporary actor it creates for receiving messages:

$ fedify inbox
โœ” The ephemeral ActivityPub server is up and running: https://<unique_id>.lhr.life/
โœ” Sent follow request to @<some_test_account>@activitypub.academy.
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ Actor handle: โ”‚ i@<unique_id>.lhr.life                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚   Actor URI:  โ”‚ https://<unique_id>.lhr.life/i          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Actor inbox: โ”‚ https://<unique_id>.lhr.life/i/inbox    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Shared inbox: โ”‚ https://<unique_id>.lhr.life/inbox      โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Web interface available at: http://localhost:8000/

You then configure your developing application to send an activity to the Actor inbox or Shared inbox URI provided. When an activity arrives, fedify inbox only prints a summary table to your console indicating that a request was received:

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚     Request #: โ”‚ 2                                   โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Activity type: โ”‚ Follow                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  HTTP request: โ”‚ POST /i/inbox                       โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ HTTP response: โ”‚ 202                                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚       Details  โ”‚ https://<unique_id>.lhr.life/r/2    โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Crucially, the detailed information about the received requestโ€”including the full headers (like Signature), the request body (the Activity JSON), and the signature verification statusโ€”is only available in the web interface provided by fedify inbox. This web UI allows you to thoroughly inspect incoming activities during development.

Screenshot of the Fedify Inbox web interface showing received activities and their details.
The Fedify Inbox web UI is where you view detailed activity information.

When you need to test interactions with the live fediverse from your local machine beyond just sending, fedify tunnel can securely expose your entire local development server temporarily. This suite of tools significantly eases the process of building and debugging federated applications.

Conclusion: Build Features, Not Plumbing

Implementing the ActivityPub suite of protocols from scratch is an incredibly complex and time-consuming undertaking. It involves deep dives into multiple technical specifications, cryptographic signing, security hardening, and navigating the nuances of a diverse ecosystem. While educational, it dramatically slows down the process of building the actual, unique features of your federated application.

Fedify offers a well-architected, secure, and type-safe foundation, handling the intricacies of federation for youโ€”data modeling, discovery, core mechanics, delivery, security, and interoperability. It lets you focus on your application's unique value and user experience. Stop wrestling with low-level protocol details and start building your vision for the fediverse faster and more reliably. Give Fedify a try!

Getting started is straightforward. First, install the Fedify CLI using your preferred method. Once installed, create a new project template by running fedify init your-project-name.

Check out the Fedify tutorials and Fedify manual to learn more. Happy federating!

ๆดช ๆฐ‘ๆ†™ (Hong Minhee)'s avatar
ๆดช ๆฐ‘ๆ†™ (Hong Minhee)

@hongminhee@hackers.pub


So, you're captivated by the fediverseโ€”the decentralized social web powered by protocols like ActivityPub. Maybe you're dreaming of building the next great federated app, a unique space connected to Mastodon, Lemmy, Pixelfed, and more. The temptation to dive deep and implement ActivityPub yourself, from the ground up, is strong. Total control, right? Understanding every byte? Sounds cool!

But hold on a sec. Before you embark on that epic quest, let's talk reality. Implementing ActivityPub correctly isn't just one task; it's like juggling several complex standards while riding a unicycleโ€ฆ blindfolded. Itโ€™s hard.

That's where Fedify comes in. It's a TypeScript framework designed to handle the gnarliest parts of ActivityPub development, letting you focus on what makes your app special, not reinventing the federation wheel.

This post will break down the common headaches of DIY ActivityPub implementation and show how Fedify acts as the super-powered pain reliever, starting with the very foundation of how data is represented.

Challenge #1: Data Modelingโ€”Speaking ActivityStreams & JSON-LD Fluently

At its core, ActivityPub relies on the ActivityStreams 2.0 vocabulary to describe actions and objects, and it uses JSON-LD as the syntax to encode this vocabulary. While powerful, this combination introduces significant complexity right from the start.

First, understanding and correctly using the vast ActivityStreams vocabulary itself is a hurdle. You need to model everythingโ€”posts (Note, Article), profiles (Person, Organization), actions (Create, Follow, Like, Announce)โ€”using the precise terms and properties defined in the specification. Manual JSON construction is tedious and prone to errors.

Second, JSON-LD, the encoding layer, has specific rules that make direct JSON manipulation surprisingly tricky:

  • Missing vs. Empty Array: In JSON-LD, a property being absent is often semantically identical to it being present with an empty array. Your application logic needs to treat these cases equally when checking for values. For example, these two Note objects mean the same thing regarding the name property:
    // No name property
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": "โ€ฆ"
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "name": [],
      "content": "โ€ฆ"
    }
  • Single Value vs. Array: Similarly, a property holding a single value directly is often equivalent to it holding a single-element array containing that value. Your code must anticipate both representations for the same meaning, like for the content property here:
    // Single value
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": "Hello"
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": ["Hello"]
    }
  • Object Reference vs. Embedded Object: Properties can contain either the full JSON-LD object embedded directly or just a URI string referencing that object. Your application needs to be prepared to fetch the object's data if only a URI is given (a process called dereferencing). These two Announce activities are semantically equivalent (assuming the URIs resolve correctly):
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // Embedded objects:
      "actor": {
        "type": "Person",
        "id": "http://sally.example.org/",
        "name": "Sally"
      },
      "object": {
        "type": "Arrive",
        "id": "https://sally.example.com/arrive",
        /* ... */
      }
    }
    // Equivalent to:
    {
      "@context":
      "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // URI references:
      "actor": "http://sally.example.org/",
      "object": "https://sally.example.com/arrive"
    }

Attempting to manually handle all these vocabulary rules and JSON-LD variations consistently across your application inevitably leads to verbose, complex, and fragile code, ripe for subtle bugs that break federation.

Fedify tackles this entire data modeling challenge with its comprehensive, type-safe Activity Vocabulary API. It provides TypeScript classes for ActivityStreams types and common extensions, giving you autocompletion and compile-time safety. Crucially, these classes internally manage all the tricky JSON-LD nuances. Fedify's property accessors present a consistent interfaceโ€”non-functional properties (like tags) always return arrays, functional properties (like content) always return single values or null. It handles object references versus embedded objects seamlessly through dereferencing accessors (like activity.getActor()) which automatically fetch remote objects via URI when neededโ€”a feature known as property hydration. With Fedify, you work with a clean, predictable TypeScript API, letting the framework handle the messy details of AS vocabulary and JSON-LD encoding.

Challenge #2: Discovery & Identityโ€”Finding Your Actors

Once you can model data, you need to make your actors discoverable. This primarily involves the WebFinger protocol (RFC 7033). You'd need to build a server endpoint at /.well-known/webfinger capable of parsing resource queries (like acct: URIs), validating the requested domain against your server, and responding with a precisely formatted JSON Resource Descriptor (JRD). This JRD must include specific links, like a self link pointing to the actor's ActivityPub ID using the correct media type. Getting any part of this wrong can make your actors invisible.

Fedify simplifies this significantly. It automatically handles WebFinger requests based on the actor information you provide through its setActorDispatcher() method. Fedify generates the correct JRD response. If you need more advanced control, like mapping user-facing handles to internal identifiers, you can easily register mapHandle() or mapAlias() callbacks. You focus on defining your actors; Fedify handles making them discoverable.

// Example: Define how to find actors
federation.setActorDispatcher(
  "/users/{username}",
  async (ctx, username) => { /* ... */ }
);
// Now GET /.well-known/webfinger?resource=acct:username@your.domain just works!

Challenge #3: Core ActivityPub Mechanicsโ€”Handling Requests and Collections

Serving actor profiles requires careful content negotiation. A request for an actor's ID needs JSON-LD for machine clients (Accept: application/activity+json) but HTML for browsers (Accept: text/html). Handling incoming activities at the inbox endpoint involves validating POST requests, verifying cryptographic signatures, parsing the payload, preventing duplicates (idempotency), and routing based on activity type. Implementing collections (outbox, followers, etc.) with correct pagination adds another layer.

Fedify streamlines all of this. Its core request handler (via Federation.fetch() or framework adapters like @fedify/express) manages content negotiation. You define actors with setActorDispatcher() and web pages with your framework (Hono, Express, SvelteKit, etc.)โ€”Fedify routes appropriately. For the inbox, setInboxListeners() lets you define handlers per activity type (e.g., .on(Follow, ...)), while Fedify automatically handles validation, signature verification, parsing, and idempotency checks using its KV Store. Collection implementation is simplified via dispatchers (e.g., setFollowersDispatcher()); you provide logic to fetch a page of data, and Fedify constructs the correct Collection or CollectionPage with pagination.

// Define inbox handlers
federation.setInboxListeners("/inbox", "/users/{handle}/inbox")
  .on(Follow, async (ctx, follow) => { /* Handle follow */ })
  .on(Undo, async (ctx, undo) => { /* Handle undo */ });

// Define followers collection logic
federation.setFollowersDispatcher(
  "/users/{handle}/followers",
  async (ctx, handle, cursor) => { /* ... */ }
);

Challenge #4: Reliable Delivery & Asynchronous Processingโ€”Sending Activities Robustly

Sending an activity requires more than a simple POST. Networks fail, servers go down. You need robust failure handling and retry logic (ideally with backoff). Processing incoming activities synchronously can block your server. Efficiently broadcasting to many followers (fan-out) requires background processing and using shared inboxes where possible.

Fedify addresses reliability and scalability using its MessageQueue abstraction. When configured (highly recommended), Context.sendActivity() enqueues delivery tasks. Background workers handle sending with automatic retries based on configurable policies (like outboxRetryPolicy). Fedify supports various queue backends (Deno KV, Redis, PostgreSQL, AMQP). For high-traffic fan-out, Fedify uses an optimized two-stage mechanism to distribute the load efficiently.

// Configure Fedify with a persistent queue (e.g., Deno KV)
const federation = createFederation({
  queue: new DenoKvMessageQueue(/* ... */),
  // ...
});
// Sending is now reliable and non-blocking
await ctx.sendActivity({ handle: "myUser" }, recipient, someActivity);

Challenge #5: Securityโ€”Avoiding Common Pitfalls

Securing an ActivityPub server is critical. You need to implement HTTP Signatures (draft-cavage-http-signatures-12) for server-to-server authenticationโ€”a complex process. You might also need Linked Data Signatures (LDS) or Object Integrity Proofs (OIP) based on FEP-8b32 for data integrity and compatibility. Managing cryptographic keys securely is essential. Lastly, fetching remote resources risks Server-Side Request Forgery (SSRF) if not validated properly.

Fedify is designed with security in mind. It automatically handles the creation and verification of HTTP Signatures, LDS, and OIP, provided you supply keys via setKeyPairsDispatcher(). It includes key management utilities. Crucially, Fedify's default document loader includes built-in SSRF protection, blocking requests to private IPs unless explicitly allowed.

Challenge #6: Interoperability & Maintenanceโ€”Playing Nicely with Others

The fediverse is diverse. Different servers have quirks. Ensuring compatibility requires testing and adaptation. Standards evolve with new Federation Enhancement Proposals (FEPs). You also need protocols like NodeInfo to advertise server capabilities.

Fedify aims for broad interoperability and is actively maintained. It includes features like ActivityTransformers to smooth over implementation differences. NodeInfo support is built-in via setNodeInfoDispatcher().

Challenge #7: Developer Experienceโ€”Actually Building Your App

Beyond the protocol, building any server involves setup, testing, and debugging. With federation, debugging becomes harderโ€”was the message malformed? Was the signature wrong? Is the remote server down? Is it a compatibility quirk? Good tooling is essential.

Fedify enhances the developer experience significantly. Being built with TypeScript, it offers excellent type safety and editor auto-completion. The fedify CLI is a powerful companion designed to streamline common development tasks.

You can quickly scaffold a new project tailored to your chosen runtime and web framework using fedify init.

For debugging interactions and verifying data, fedify lookup is invaluable. It lets you inspect how any remote actor or object appears from the outside by performing WebFinger discovery and fetching the object's data. Fedify then displays the parsed object structure and properties directly in your terminal. For example, running:

$ fedify lookup @fedify-example@fedify-blog.deno.dev

Will first show progress messages and then output the structured representation of the actor, similar to this:

// Output of fedify lookup command (shows parsed object structure)
Person {
  id: URL "https://fedify-blog.deno.dev/users/fedify-example",
  name: "Fedify Example Blog",
  published: 2024-03-03T13:18:11.857Z, // Simplified timestamp
  summary: "This blog is powered by Fedify, a fediverse server framework.",
  url: URL "https://fedify-blog.deno.dev/",
  preferredUsername: "fedify-example",
  publicKey: CryptographicKey {
    id: URL "https://fedify-blog.deno.dev/users/fedify-example#main-key",
    owner: URL "https://fedify-blog.deno.dev/users/fedify-example",
    publicKey: CryptoKey { /* ... CryptoKey details ... */ }
  },
  // ... other properties like inbox, outbox, followers, endpoints ...
}

This allows you to easily check how data is structured or troubleshoot why an interaction might be failing by seeing the actual properties Fedify parsed.

Testing outgoing activities from your application during development is made much easier with fedify inbox. Running the command starts a temporary local server that acts as a publicly accessible inbox, displaying key information about the temporary actor it creates for receiving messages:

$ fedify inbox
โœ” The ephemeral ActivityPub server is up and running: https://<unique_id>.lhr.life/
โœ” Sent follow request to @<some_test_account>@activitypub.academy.
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ Actor handle: โ”‚ i@<unique_id>.lhr.life                  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚   Actor URI:  โ”‚ https://<unique_id>.lhr.life/i          โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  Actor inbox: โ”‚ https://<unique_id>.lhr.life/i/inbox    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Shared inbox: โ”‚ https://<unique_id>.lhr.life/inbox      โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Web interface available at: http://localhost:8000/

You then configure your developing application to send an activity to the Actor inbox or Shared inbox URI provided. When an activity arrives, fedify inbox only prints a summary table to your console indicating that a request was received:

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚     Request #: โ”‚ 2                                   โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Activity type: โ”‚ Follow                              โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚  HTTP request: โ”‚ POST /i/inbox                       โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ HTTP response: โ”‚ 202                                 โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚       Details  โ”‚ https://<unique_id>.lhr.life/r/2    โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

Crucially, the detailed information about the received requestโ€”including the full headers (like Signature), the request body (the Activity JSON), and the signature verification statusโ€”is only available in the web interface provided by fedify inbox. This web UI allows you to thoroughly inspect incoming activities during development.

Screenshot of the Fedify Inbox web interface showing received activities and their details.
The Fedify Inbox web UI is where you view detailed activity information.

When you need to test interactions with the live fediverse from your local machine beyond just sending, fedify tunnel can securely expose your entire local development server temporarily. This suite of tools significantly eases the process of building and debugging federated applications.

Conclusion: Build Features, Not Plumbing

Implementing the ActivityPub suite of protocols from scratch is an incredibly complex and time-consuming undertaking. It involves deep dives into multiple technical specifications, cryptographic signing, security hardening, and navigating the nuances of a diverse ecosystem. While educational, it dramatically slows down the process of building the actual, unique features of your federated application.

Fedify offers a well-architected, secure, and type-safe foundation, handling the intricacies of federation for youโ€”data modeling, discovery, core mechanics, delivery, security, and interoperability. It lets you focus on your application's unique value and user experience. Stop wrestling with low-level protocol details and start building your vision for the fediverse faster and more reliably. Give Fedify a try!

Getting started is straightforward. First, install the Fedify CLI using your preferred method. Once installed, create a new project template by running fedify init your-project-name.

Check out the Fedify tutorials and Fedify manual to learn more. Happy federating!

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

Fetching remote objects or actors often involves handling lookups, content negotiation, and then parsing potentially untyped JSON.

With , it's much simpler: use Context.lookupObject(). Pass it a URI (e.g., https://instance.tld/users/alice) or a handle (e.g., @alice@instance.tld), and Fedify handles the lookup and content negotiation automatically.

The real power comes from the return value: a type-safe Activity Vocabulary object, not just raw JSON. This allows you to confidently access properties and methods directly. For example, you can safely traverse account moves using .getSuccessor() like this:

let actor = await ctx.lookupObject("@alice@instance.tld");
while (isActor(actor)) {
  const successor = await actor.getSuccessor();
  if (successor == null) break;
  actor = successor;
}
// actor now holds the latest account after moves

This is readily available in handlers where the Context object is provided (like actor dispatchers or inbox listeners).

Focus on your app's logic, not protocol boilerplate!

Learn more: https://fedify.dev/manual/context#looking-up-remote-objects

Jaeyeol Lee (a.k.a. kodingwarrior) :vim:'s avatar
Jaeyeol Lee (a.k.a. kodingwarrior) :vim:

@kodingwarrior@silicon.moe

Fedify ํ†ต์œผ๋กœ ํ”„๋กฌํ”„ํŠธ ๋„ฃ์„๋•Œ Repomix ์„ค์ •๊ฐ’์€ ์ด๋ ‡๊ฒŒ ๊ฐ€์•ผ๊ฒ ๋‹ค.
platform.openai.com/docs/model ๊ธฐ์ค€์œผ๋กœ ๋ดค์„๋•Œ Context Window Length๋Š” 200K ํ† ํฐ ์ •๋„ ๋˜๋Š”๋ฐ, ์—ฌ๊ธฐ์— ์–ด๋–ป๊ฒŒ๋“  ์ฅ์–ด์งœ๋‚ด๋ ค๋ฉด ignore pattern์„ ์š”๋ ‡๊ฒŒ ๊ฐ€๋Š”๊ฒŒ ์ข‹์„ ๋“ฏ

```
docs/**/*.*, src/codegen/__snapshots__/class.test.ts.snap, src/vocab/__snapshots__/vocab.test.ts.snap, CHANGES.md
```

ๆดช ๆฐ‘ๆ†™ (Hong Minhee)'s avatar
ๆดช ๆฐ‘ๆ†™ (Hong Minhee)

@hongminhee@hollo.social ยท Reply to ๆดช ๆฐ‘ๆ†™ (Hong Minhee)'s post

Fedify ํ”„๋กœ์ ํŠธ์˜ ๋ฉ”์ธํ…Œ์ด๋„ˆ๋กœ์„œ 2025ๅนด(๋…„) ์˜คํ”ˆ์†Œ์Šค ์ปจํŠธ๋ฆฌ๋ทฐ์…˜ ์•„์นด๋ฐ๋ฏธ ๅƒ่ˆ‡ๅž‹(์ฐธ์—ฌํ˜•) ๋ฉ˜ํ† ๅœ˜(๋‹จ)์— ้ธๆ‹”(์„ ๋ฐœ)๋˜์—ˆ์Šต๋‹ˆ๋‹ค!

5ๆœˆ(์›”) 21ๆ—ฅ(์ผ)(ๆฐด(์ˆ˜))๋ถ€ํ„ฐ 6ๆœˆ(์›”) 22ๆ—ฅ(์ผ)(ๆ—ฅ(์ผ))๊นŒ์ง€ ๋ฉ˜ํ‹ฐ๋„ ๅ‹Ÿ้›†(๋ชจ์ง‘)ํ•œ๋‹ค๊ณ  ํ•˜๋‹ˆ ๋งŽ์€ ๅƒ่ˆ‡(์ฐธ์—ฌ) ๋ฐ”๋ž๋‹ˆ๋‹ค.

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

๐Ÿ“ฃ Exciting news! Fedify CLI is now available via Homebrew!

If you're using on macOS or on Linux, you can now install our CLI toolchain with a simple command:

brew install fedify

This makes it even easier to get started with building your federated server app. Try it out and let us know what you think!

๋ด„๋ณ„ ใฏใ‚‹ใผใ— Haruboshi๐ŸŒŸ's avatar
๋ด„๋ณ„ ใฏใ‚‹ใผใ— Haruboshi๐ŸŒŸ

@haruboshi@aoharu.place

๊ทธ๋Ÿฌ๊ณ ๋ณด๋‹ˆ ๋ฌธ๋“ ์ƒ๊ฐ์ด ๋‚œ ๊ฑด๋ฐ ํ•ด์ปค์Šค.ํŽ์ด (@hollo@hollo.social)์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ @hongminhee@hollo.social ๋‹˜์ด Fedify(@fedify@hollo.social) ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“  ์ž์ฒด ActivityPub ์†Œํ”„ํŠธ์›จ์–ด๋ผ๋Š” ๊ฑธ ์•Œ๊ฒŒ ๋œ ๊ฑด ๊ฝค ๋์ง€๋งŒ ๊ทธ๋ ‡๊ฒŒ ๊ทœ๋ชจ๊ฐ€ ์ œ๋ฒ• ๋˜๋Š” ์„œ๋น„์Šค๊ฐ€ ์ € ๋ถ„ ์†Œ์œ ์˜ ํ™ˆ์„œ๋ฒ„์—์„œ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ฒŒ ๋œ ๊ฑด ์–ผ๋งˆ ์ „์˜ ์ผ์ด๋‹ค.

ใตใ‰ใฎ :inkan: :sabakan: (่‡ชๅพ‹ใ—ใ‚)'s avatar
ใตใ‰ใฎ :inkan: :sabakan: (่‡ชๅพ‹ใ—ใ‚)

@fono@ma.fono.jp

Fedifyใ€้–‹็™บ่€…ไฝ“้จ“ใŒ่‰ฏใ™ใŽใฆๆ„Ÿๅ‹•ใ—ใŸใ€‚

ใตใ‰ใฎ :inkan: :sabakan: (่‡ชๅพ‹ใ—ใ‚)'s avatar
ใตใ‰ใฎ :inkan: :sabakan: (่‡ชๅพ‹ใ—ใ‚)

@fono@ma.fono.jp

Fedifyใ€ใƒ‡ใƒ•ใ‚ฉใƒซใƒˆใงObservabilityใŒใ‚ใ‚‹ใฎๆœฌๅฝ“ใซๆœ‰ใ‚Š้›ฃใ„ใ€‚

ๆดช ๆฐ‘ๆ†™ (Hong Minhee)'s avatar
ๆดช ๆฐ‘ๆ†™ (Hong Minhee)

@hongminhee@hollo.social

FYI Fedify implements FEP-8fcf, so you can just pass the syncCollection: true option to turn on followers collection synchronization if you use Fedify!

https://mastodon.social/@dansup/114225342328813426

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

We're incredibly honored to announce that (@index) has become a formal sponsor of Fedify through Open Collective!

This is a significant milestone for our project, and we're deeply grateful to @johnonolan and the entire Ghost team for their support and recognition of our work in the ecosystem.

Ghost's social web integration built on is a perfect example of how open standards can connect different publishing platforms in the fediverse. Their backing over the past months has been invaluable, and this formal sponsorship will help ensure Fedify remains sustainable as we continue to develop and improve the framework.

If you're building with ActivityPub or interested in federated applications, please consider joining Ghost in supporting open source development through our Open Collective:

https://opencollective.com/fedify

Every contribution, no matter the size, helps us maintain and enhance the tools that make the fediverse more accessible to developers. Thank you for being part of this journey with us! :fedify: โค๏ธ :ghost:

John O'Nolan's avatar
John O'Nolan

@johnonolan@mastodon.xyz

Ghost's social web integration is built on the fantastic work of @hongminhee and the Fedify.dev framework.

We've been backing work on the project for 6 months or so, and now we're thrilled to be formal sponsor of the project on Open Collective!

If you're building with Fedify and ActivityPub, please consider joining us to keep helping to make great open source work sustainable โค๏ธ Every little helps

opencollective.com/fedify

Fedify: an ActivityPub server framework's avatar
Fedify: an ActivityPub server framework

@fedify@hollo.social

We're incredibly honored to announce that (@index) has become a formal sponsor of Fedify through Open Collective!

This is a significant milestone for our project, and we're deeply grateful to @johnonolan and the entire Ghost team for their support and recognition of our work in the ecosystem.

Ghost's social web integration built on is a perfect example of how open standards can connect different publishing platforms in the fediverse. Their backing over the past months has been invaluable, and this formal sponsorship will help ensure Fedify remains sustainable as we continue to develop and improve the framework.

If you're building with ActivityPub or interested in federated applications, please consider joining Ghost in supporting open source development through our Open Collective:

https://opencollective.com/fedify

Every contribution, no matter the size, helps us maintain and enhance the tools that make the fediverse more accessible to developers. Thank you for being part of this journey with us! :fedify: โค๏ธ :ghost:

John O'Nolan's avatar
John O'Nolan

@johnonolan@mastodon.xyz

Ghost's social web integration is built on the fantastic work of @hongminhee and the Fedify.dev framework.

We've been backing work on the project for 6 months or so, and now we're thrilled to be formal sponsor of the project on Open Collective!

If you're building with Fedify and ActivityPub, please consider joining us to keep helping to make great open source work sustainable โค๏ธ Every little helps

opencollective.com/fedify

Older โ†’