TypeScript · ActivityPub · Open source
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.
See the full rationale →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() accessors 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"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
Works 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, and fedify init scaffolds a project for you.
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, MySQL, SQLite, Deno KV, or Cloudflare Workers KV.
Developer tooling
The fedify command-line tool turns the fiddly parts of ActivityPub development into one-liners, from inspecting remote objects to watching exactly what your own server sends. Alongside it, @fedify/lint catches common federation mistakes as you write, and @fedify/debugger adds a live dashboard of inbox and outbox activity.
fedify lookup --authorized-fetchInspect any actor or object, even the authorized-fetch-only ones.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 ready-to-run project in seconds.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);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