Verified against Package registries and forge APIs (npm, PyPI, crates.io, RubyGems, Packagist, Go proxy, GitHub, Codeberg) on 2026-10-15; @fedify/* 2.4.4 on 2026-10-15.
Fedify
Fedify is a TypeScript framework for building federated server applications powered by ActivityPub. It is the most actively developed ActivityPub framework as of October 2026.
Overview
| Property | Value |
|---|---|
| Language | TypeScript |
| Runtimes | Deno, Node.js, Bun |
| License | MIT |
| Website | fedify.dev |
| Repository | github.com/fedify-dev/fedify |
| Packages | @fedify/fedify (core), @fedify/vocab (Activity Vocabulary classes), plus adapters |
| Latest release | 2.4.4 (2026-10-12) |
| Status | Active |
:::info Version note The examples below target Fedify 2.x. Two changes trip up people coming from 1.x tutorials:
- Vocabulary classes (
Person,Note,Follow, …) are imported from@fedify/vocab, not@fedify/fedify. - Route templates use
{identifier}(e.g./users/{identifier}), and the sender passed tosendActivityis{ identifier }.
Always check the Fedify manual for the version you install. :::
Key Features
Protocol Support
- ActivityPub server-to-server federation
- WebFinger and NodeInfo built in
- HTTP Signatures — both draft-cavage and RFC 9421 HTTP Message Signatures
- Object Integrity Proofs (FEP-8b32) and Linked Data Signatures
- Portable objects (FEP-ef61) via
verifyPortableObject
Inbound activities are verified before your inbox listeners see them; unverified deliveries go to an opt-in onUnverifiedActivity hook.
Developer Experience
- Type-safe vocabulary — TypeScript classes for the Activity Vocabulary and common extensions (e.g. Mastodon's
Emoji,PropertyValue) - Framework adapters — Express, Fastify, Hono, Koa, Elysia, h3, NestJS, Next.js, SvelteKit, Astro, Cloudflare Workers
- CLI —
lookup,inbox,tunnel,webfinger,nodeinfo,init,benchand more
Infrastructure
- Key–value stores and queues — PostgreSQL, MySQL, SQLite, Redis, AMQP, Deno KV
- Delivery — retries with exponential backoff and a per-host circuit breaker
- Observability — OpenTelemetry tracing and metrics
Installation
Node.js / Bun
npm install @fedify/fedify @fedify/vocab
Deno
import { createFederation } from "jsr:@fedify/fedify";
import { Person } from "jsr:@fedify/vocab";
Quick Start
The snippets below were typechecked against @fedify/fedify and @fedify/vocab 2.4.4 (strict mode). db stands for your own data layer.
Basic Federation Setup
import { createFederation, MemoryKvStore } from "@fedify/fedify";
import { Person } from "@fedify/vocab";
const federation = createFederation<void>({
kv: new MemoryKvStore(), // use a persistent store in production
});
federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
const user = await db.getUser(identifier); // your own data layer
if (!user) return null;
return new Person({
id: ctx.getActorUri(identifier),
preferredUsername: identifier,
name: user.displayName,
inbox: ctx.getInboxUri(identifier),
outbox: ctx.getOutboxUri(identifier),
followers: ctx.getFollowersUri(identifier),
following: ctx.getFollowingUri(identifier),
});
});
A real actor also needs key pairs so it can sign outgoing requests: chain .setKeyPairsDispatcher(...) onto setActorDispatcher(...) — see the actor manual.
Handling Inbox Activities
import { Accept, Follow, Undo } from "@fedify/vocab";
federation
.setInboxListeners("/users/{identifier}/inbox", "/inbox")
.on(Follow, async (ctx, follow) => {
if (ctx.recipient == null || follow.actorId == null) return; // shared inbox / malformed
const follower = await follow.getActor(ctx);
if (follower == null) return;
await db.addFollower(ctx.recipient, follow.actorId);
await ctx.sendActivity(
{ identifier: ctx.recipient },
follower,
new Accept({ actor: ctx.getActorUri(ctx.recipient), object: follow }),
);
})
.on(Undo, async (ctx, undo) => {
const object = await undo.getObject(ctx);
if (object instanceof Follow && ctx.recipient != null && undo.actorId != null) {
await db.removeFollower(ctx.recipient, undo.actorId);
}
});
ctx.recipient is the identifier of the addressed actor, or null when the activity arrived at the shared inbox. Signature verification has already happened by the time a listener runs.
Sending Activities
import { Create, Note, PUBLIC_COLLECTION } from "@fedify/vocab";
import { Temporal } from "temporal-polyfill"; // built in on Deno
// `ctx` comes from federation.createContext(request, contextData),
// or from inside a dispatcher/listener.
const note = new Note({
id: ctx.getObjectUri(Note, { identifier, id: postId }), // requires setObjectDispatcher
attribution: ctx.getActorUri(identifier),
content: "Hello, Fediverse!",
published: Temporal.Now.instant(),
to: PUBLIC_COLLECTION,
cc: ctx.getFollowersUri(identifier),
});
await ctx.sendActivity(
{ identifier },
"followers",
new Create({
id: new URL(`${note.id}#create`),
actor: ctx.getActorUri(identifier),
object: note,
to: PUBLIC_COLLECTION,
cc: ctx.getFollowersUri(identifier),
}),
);
Sending to "followers" requires a followers collection dispatcher. See Sending activities.
Framework Integration
Express
import express from "express";
import { integrateFederation } from "@fedify/express";
const app = express();
app.set("trust proxy", true);
app.use(integrateFederation(federation, (req) => undefined));
Hono
import { Hono } from "hono";
import { federation as fedifyMiddleware } from "@fedify/hono";
const app = new Hono();
app.use(fedifyMiddleware(federation, (c) => undefined));
Fastify
import Fastify from "fastify";
import { fedifyPlugin } from "@fedify/fastify";
const fastify = Fastify();
await fastify.register(fedifyPlugin, {
federation,
contextDataFactory: () => undefined,
});
Other adapters are listed in the integration manual.
Persistent Storage and Queues
PostgreSQL
import { PostgresKvStore, PostgresMessageQueue } from "@fedify/postgres";
import postgres from "postgres";
const sql = postgres("postgres://localhost/myapp");
const federation = createFederation<void>({
kv: new PostgresKvStore(sql),
queue: new PostgresMessageQueue(sql),
});
Redis
import { RedisKvStore, RedisMessageQueue } from "@fedify/redis";
import { Redis } from "ioredis";
const federation = createFederation<void>({
kv: new RedisKvStore(new Redis()),
queue: new RedisMessageQueue(() => new Redis()),
});
Activity Vocabulary
import {
// Actors
Person, Group, Organization, Application, Service,
// Objects
Note, Article, Image, Video, Audio, Document,
// Activities
Create, Update, Delete, Follow, Accept, Reject, Like, Announce, Undo,
// Collections
Collection, OrderedCollection, CollectionPage, OrderedCollectionPage,
// Common extensions
Emoji, PropertyValue,
} from "@fedify/vocab";
CLI Tools
npm install -g @fedify/cli
fedify lookup @user@example.com # fetch and pretty-print an actor or object
fedify inbox # ephemeral inbox that logs incoming activities
fedify tunnel 3000 # expose a local server over public HTTPS
fedify webfinger @user@example.com
fedify nodeinfo example.com
See the CLI documentation for all commands.
Comparison with Other JavaScript Options
| Feature | Fedify | microfed | activitypub-express |
|---|---|---|---|
| Status (Oct 2026) | Active | Experimental (0.0.x) | Dormant since Feb 2024 |
| TypeScript | ✅ Native | ❌ | ❌ |
| Runtimes | Deno/Node/Bun | Node | Node |
| RFC 9421 signatures | ✅ | ❌ (draft-cavage) | ❌ |
| Object Integrity Proofs | ✅ | ❌ | ❌ |
| Type-safe vocabulary | ✅ | ❌ | ❌ |
| Complexity | Higher | Low | Medium |
When to Use Fedify
Good for:
- Production servers that need to interoperate with Mastodon and the wider fediverse
- TypeScript projects that want type safety and built-in request verification
- Apps on any of the supported web frameworks
Consider alternatives if:
- You only need a bot → BotKit (built on Fedify)
- You want to study or self-host a complete single-user server → Hollo (built on Fedify)
Resources
- Documentation — Manual and API reference
- Tutorial — Step-by-step microblog tutorial
- Examples — Sample applications
- Matrix and GitHub Discussions — Community support
- Security advisories — Keep your version current
See Also
- JavaScript Libraries — Other JS/TS options
- Building an Actor — Actor implementation guide
- HTTP Signatures — Signature details