Fedify is a TypeScript framework that handles federation, signatures, discovery, activity vocabulary, and delivery behind type-safe APIs, so you ship features, not specs. It's modular, too: adopt the whole thing or just the parts you need, and it never dictates how your app is built.
npm install @fedify/fedifyWhat's Fedify?
The fediverse is millions of accounts spread across thousands of independent servers, all talking through shared protocols. Implementing those protocols by hand is a lot of subtle, security-critical work. Fedify handles all of it, actors and activity vocabulary, signatures, discovery, and queued delivery, so a post on your server reaches Mastodon, Misskey, Lemmy, and the rest, correctly and securely. It's that broad, yet modular: take only the pieces you need, and it won't decide how the rest of your app is structured.
Read the full introduction →Why Fedify?
Going federated means getting a whole pile of standards right, and keeping them right as they evolve. Fedify implements them so you don't have to, and exposes them through one coherent, typed API.
Looks like this
Map a route to a typed actor and you get a working, discoverable, cryptographically-signed ActivityPub endpoint, no manual JSON-LD, no signature plumbing.
Follow the tutorial →import { createFederation, Person } from "@fedify/fedify";
const federation = createFederation({ kv });
federation.setActorDispatcher(
"/users/{identifier}",
async (ctx, identifier) => {
const user = await db.getUser(identifier);
if (user == null) return null;
return new Person({
id: ctx.getActorUri(identifier),
preferredUsername: identifier,
name: user.name,
inbox: ctx.getInboxUri(identifier),
});
},
);Type-safe vocabulary
Fedify models the whole Activity Vocabulary as immutable, type-safe objects, backed by a real JSON-LD processor and exposed as a fully typed API.
getActor() and getObject() to fetch and hydrate remote objects on demand.// In JSON-LD, these four are all equivalent:
"to": "as:Public"
"to": ["as:Public"]
"to": "https://www.w3.org/ns/activitystreams#Public"
"to": ["https://www.w3.org/ns/activitystreams#Public"]
// With the Vocabulary API you read one typed value:
note.toId
// → URL "https://www.w3.org/ns/activitystreams#Public"const activity = await Activity.fromJsonLd(json);
// Just the URI, no network request:
activity.actorId;
// → URL "https://example.com/users/alice"
// getActor() fetches and hydrates the actor:
const actor = await activity.getActor();
actor?.name; // "Alice"Portable objects · FEP-ef61
Give actors and posts IDs based on a decentralized identifier (DID), independent of any server's domain. Fedify can serve these objects through gateways, sign and deliver their activities, and verify portable objects from other servers.
Object Integrity Proofs authenticate the objects themselves, wherever they are hosted. Your application manages storage and synchronization between gateways.
Portable objects guide →ap+ef61://did:key:z6Mk…/actor Signed with the DID's key Reliable at scale
Outgoing activities go through a message queue, so a flaky recipient never costs you a post. For large audiences, a two-stage fan-out returns instantly and delivers to every inbox in the background.
Outgoing activities go through a queue and retry with exponential backoff, so a flaky server never costs you a post.
A two-stage fan-out returns instantly and delivers to thousands of inboxes in the background, each retried on its own.
Run the queue on the backend you already operate, and swap it later without touching your application code.
Run the queue on the broker you prefer
On Netlify Functions, Fedify processes queued deliveries through Async Workloads events, without a long-running queue worker.
Background tasks
Send digest emails, rebuild timelines, or fetch link previews using the same queue infrastructure that delivers your activities. Define a task once, then enqueue it from any federation context.
import { z } from "zod";
const sendDigest = federation.defineTask("sendDigest", {
schema: z.object({
userId: z.string(),
since: z.date(),
}),
handler: async (_ctx, data) => {
await sendDigestEmail(data.userId, data.since);
},
});
await ctx.enqueueTask(sendDigest, {
userId: "alice",
since: new Date(),
}, { delay: { minutes: 30 } });Interaction controls
Use @fedify/interaction-controls for likes, replies, boosts, quotes, and featuring actors in recommendation collections. Typed helpers evaluate policies and create and verify interaction requests and authorizations.
The helpers support GoToSocial interaction controls, FEP-044f quote authorization, and FEP-7aa9 featured recommendation collections. Your application handles delivery, storage, and moderation.
Interaction controls guide →automaticmanualdeniedWorks with your stack
Fedify doesn't take over your app. It runs as middleware on the domain and port you already have, using content negotiation to pick out federation traffic and leaving the rest of your routes alone. First-party packages cover the frameworks below. Scaffold a project with fedify init. For AdonisJS, add Fedify to an existing app on Node.js with node ace add @fedify/adonisjs.
Fedify is built on the web-standard Request and Response, so dropping it into anything with middleware takes about a dozen lines.
import { federation } from "./federation.ts";
export default (request: Request, next) =>
federation.fetch(request, {
contextData: undefined,
onNotFound: next, // not a federation request
onNotAcceptable: next, // fall back to your HTML
});Your data, your schema
Your actors, posts, and followers live in your own database, in whatever schema you already have. Fedify reads them through dispatcher callbacks you write, so the data model stays yours. For its own internal state, a cache and some federation bookkeeping, it relies on a pluggable KvStore: keep it in memory while you develop, or back it with Redis, PostgreSQL, PGlite, MySQL, SQLite, Deno KV, Netlify Blobs, or Cloudflare Workers KV.
Developer tooling
Use the fedify CLI to inspect remote objects and test what your server sends. @fedify/debugger adds a live dashboard of inbox and outbox activity.
@fedify/lint checks actor properties and media upload setup. In ESLint and Oxlint, it can also flag unhandled outbox delivery promises.
fedify lookup --authorized-fetchInspect actors and objects, including portable IDs. Choose gateways with --gateway and set a timeout for each request with --timeout.fedify inboxSpin up an ephemeral, public inbox to see exactly what your server sends.fedify tunnelExpose your local server to the public internet for live testing.fedify initScaffold a project with a smoke test that starts the app and resolves a local actor.fedify nodeinfoInspect any instance's software, version, and stats.Built for production
Fedify is instrumented with OpenTelemetry out of the box, so the whole federation lifecycle shows up as traces and metrics in the observability stack you already run.
ActivityPub API
The ActivityPub API, the client-to-server (C2S) side of the protocol, is something of a holy grail in the fediverse, and Fedify is ready for it. Route POST requests to an actor's outbox through typed listeners and authorize them however you like.
authorize() hook plugs in OAuth, access tokens, or sessions.federation
.setOutboxListeners("/users/{identifier}/outbox")
.on(Create, async (ctx, activity) => {
await saveAndDeliver(ctx, activity);
})
.authorize(async (ctx, id) =>
(await getSession(ctx.request))?.user === id);An open source project, built and maintained by people in the fediverse.

Contributing since 2024

Contributing since 2025

Contributing since 2025
Install Fedify and have a federated actor running in minutes.
Need a hand? Ask the community on GitHub Discussions or Matrix, or tag #Fedify in the fediverse.
Fedify is free and open source. Meet our sponsors →
Backed by an investment from