Skip to content

TypeScript · ActivityPub · Open source

Build for the fediverse,
skip the boilerplate.

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/fedify
Runs onNode.jsDenoBunCloudflare Workers

What's Fedify?

A federation framework, not just a protocol library.

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?

Federation is a stack of specs. Fedify implements it.

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

Define an actor. Fedify handles the wire.

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

Type-safe objects, no JSON-LD headaches

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.

  • Standard-compliant outputbuild an object and toJsonLd() emits valid, interoperable JSON-LD.
  • One shape to readevery equivalent JSON-LD form normalizes to the same typed value.
  • References, dereferencedgetActor() and getObject() accessors fetch and hydrate remote objects on demand.
  • Secure by defaultan origin-based security model (FEP-fe34) re-fetches cross-origin objects to prevent spoofing.
Vocabulary guide →
// 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

Reliable delivery, even to huge audiences

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.

Queued and retried

Outgoing activities go through a queue and retry with exponential backoff, so a flaky server never costs you a post.

Fan-out for scale

A two-stage fan-out returns instantly and delivers to thousands of inboxes in the background, each retried on its own.

Pluggable brokers

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

  • and more

Works with your stack

Drops into the web framework you already use

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.

  • and more

No official package? No problem.

Fedify is built on the web-standard Request and Response, so dropping it into anything with middleware takes about a dozen lines.

Custom middleware guide →
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

Fedify never owns your data model

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.

Key–value store guide →

Plays well with others

Interoperates with the software people already use

  • and more

Developer tooling

A toolchain for building and debugging

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.

Explore the CLI →

Built for production

See exactly what your server is doing

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.

  • Distributed tracesevery step is a span: HTTP, inbox, outbox, fan-out, signatures, and WebFinger.
  • Rich metricscounters and histograms for delivery, queue depth, signature verification, and more.
  • Any OTLP backendship to Grafana, Jaeger, Sentry, or Deno's built-in OpenTelemetry.
  • Structured logs tootraceable JSON logging via LogTape, correlated by request and message IDs.
OpenTelemetry guide →

ActivityPub API

Accept posts straight from your clients

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.

  • Typed outbox listenershandle client posts per activity type, exactly like inbox listeners.
  • Auth your wayan authorize() hook plugs in OAuth, access tokens, or sessions.
  • Standard, not bespokeclients speak the real ActivityPub outbox, so there's no custom API to maintain.
Outbox listeners guide →
federation
  .setOutboxListeners("/users/{identifier}/outbox")
  .on(Create, async (ctx, activity) => {
    await saveAndDeliver(ctx, activity);
  })
  .authorize(async (ctx, id) =>
    (await getSession(ctx.request))?.user === id);

Ready to join the fediverse?

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 →

Also from the Fedify team:BotKitDrFed

Backed by an investment from