GeezerVison is showing fedify.dev — Fedify
Skip to content 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. - Work across servers
- Fedify negotiates HTTP signature formats and handles discovery through WebFinger and NodeInfo.
- Build on trust and consent
- Check object origins and integrity proofs, and use interaction helpers in your app to request and verify consent for quotes and recommendations.
- Keep identity across gateways
- Portable object IDs contain a DID instead of a host. Their integrity proofs can be verified without trusting the hosting gateway.
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 →federation.ts 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, dereferencedUse
getActor() and getObject() to fetch and hydrate remote objects on demand. - Secure by defaultAn origin-based security model (FEP-fe34) re-fetches cross-origin embedded objects with IDs. Portable IDs use cryptographic origins derived from their DIDs.
Vocabulary guide →recipients.ts // 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"
actor.ts 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 An identity beyond a single server 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 → One portable identityap+ef61://did:key:z6Mk…/actor Signed with the DID's key
Gateway ASame signed actor Gateway BSame signed actor 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 retriedOutgoing activities go through a queue and retry with exponential backoff, so a flaky server never costs you a post. Fan-out for scaleA two-stage fan-out returns instantly and delivers to thousands of inboxes in the background, each retried on its own. Pluggable brokersRun 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 Run your app's jobs in the background 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. - Typed and validateda Standard Schema, such as one from Zod or Valibot, checks payloads at enqueue time and again on the worker.
- Delays and retriespostpone jobs and retry failed ones under a configurable policy.
- Your existing queuereuse the outbox queue, or give tasks a queue and worker of their own.
Background tasks guide → tasks.ts 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 Build interactions around consent 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 → evaluatePolicy() automatic- Eligible for automatic approval.
manual- Needs the owner's approval.
denied- Not allowed by the policy.
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. Scaffold a project with fedify init. For AdonisJS, add Fedify to an existing app on Node.js with node ace add @fedify/adonisjs. middleware.ts 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, PGlite, MySQL, SQLite, Deno KV, Netlify Blobs, or Cloudflare Workers KV. Key–value store guide →Plays well with others Interoperates with the software people already usezsh — fedify $ fedify inbox --tunnel-service fedify.com.es \
--follow @[email protected]
✔ Ephemeral server up: https://f9285cf4974c86ab.fedify.com.es/
✔ Sent follow to @[email protected]
Actor handle [email protected]
Actor inbox https://f9285cf4974c86ab.fedify.com.es/i/inbox
Shared inbox https://f9285cf4974c86ab.fedify.com.es/inbox
▸ Received Follow POST /i/inbox 202
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.
- Inbox request reportsInspect signature and proof checks, keys used, authentication decisions, and request outcomes for accepted and rejected deliveries with onRequestFinished(), independently of trace sampling.
OpenTelemetry guide → trace · activitypub.inbox activitypub.inbox http_signatures.verify activitypub.dispatch_inbox_listener activitypub.send_activity http_signatures.sign 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 →outbox.ts federation
.setOutboxListeners("/users/{identifier}/outbox")
.on(Create, async (ctx, activity) => {
await saveAndDeliver(ctx, activity);
})
.authorize(async (ctx, id) =>
(await getSession(ctx.request))?.user === id);
The people behind FedifyAn open source project, built and maintained by people in the fediverse.  Hong Minhee 洪民憙Contributing since 2024  Jiwon Kwon 權祉源Contributing since 2025  ChanHaeng Lee 李璨行Contributing since 2025 Meet the team and contributors →Backed by an investment from
|