Fedify 2.0.0: Modular architecture, debug dashboard, and relay support #580
dahlia
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Fedify is a TypeScript framework for building ActivityPub servers that participate in the fediverse. It reduces the complexity and boilerplate typically required for ActivityPub implementation while providing comprehensive federation capabilities.
We are thrilled to announce Fedify 2.0.0, the most significant release in Fedify's history. This major version brings a fundamentally restructured modular architecture, a real-time debug dashboard, ActivityPub relay support, ordered message delivery, permanent failure handling, and many more improvements across the entire ecosystem.
Fedify 2.0.0 is the culmination of months of collaborative effort from the Fedify community, including significant contributions from Korea's OSSCA (Open Source Contribution Academy) participants. This release includes breaking changes that require careful migration—please review the Migration guide section below.
Breaking changes at a glance
Before diving into the new features, here is a summary of breaking changes that require attention when upgrading from Fedify 1.x:
contextLoader,documentLoaderoptions,CreateFederationOptions,fetchDocumentLoader(), and{ handle: string }parameter forms have been removed (Remove deprecated APIs for Fedify 2.0 #376).LanguageTagreplaced byIntl.Locale: TheLanguageString.languageproperty is nowLanguageString.localeof typeIntl.Locale(Migrate from @phensley/language-tag toIntl.Localefor Fedify 2.0 #280, Migrate fromLanguageTagtoIntl.Localefor representing language tags #392).software.versionis nowstring: Changed fromSemVertostringto handle non-SemVer version strings (Change NodeInfosoftware.versionfield type fromSemVertostring#366, Change NodeInfosoftware.versionfield type fromSemVertostring#433)."per-inbox": Was"per-origin"in 1.x (Posting the same activity ID to different inboxes does not result in correct delivery #441).@fedify/fedify/x/*modules removed: Use dedicated@fedify/*packages instead (Remove deprecated@fedify/fedify/x/*modules in Fedify 2.0 #391).KvStore.list()is now required: Was optional since 1.10.0 (MakeKvStore.list()required in 2.0.0 #499, MakeKvStore.list()method required instead of optional #506).@fedify/fedify/vocabdeprecated: Use@fedify/vocabinstead (Extract vocab from @fedify/fedify into @fedify/vocab #437, Extract vocab from@fedify/fedifyinto@fedify/vocab#517).@fedify/fedify/runtimedeprecated: Use@fedify/vocab-runtimeinstead (@fedify/vocabbundles its ownLanguageStringclass, breakinginstanceofchecks #560).MessageQueueinterface extended: All implementations must support the neworderingKeyoption (Activities can be delivered out of order, causing federation issues #536, AddorderingKeyoption toMessageQueueEnqueueOptionsfor ordered message delivery #538).Modular architecture
Fedify 2.0.0 introduces a fundamental restructuring of the package architecture. What was previously a monolithic
@fedify/fedifypackage with@fedify/fedify/vocab,@fedify/fedify/runtime, and@fedify/fedify/x/*submodules has been split into focused, independent packages:@fedify/fedify/vocab@fedify/vocab@fedify/fedify/runtime@fedify/vocab-runtime@fedify/fedify/x/hono@fedify/hono@fedify/fedify/x/sveltekit@fedify/sveltekit@fedify/fedify/x/denokv@fedify/denokv@fedify/fedify/x/cfworkers@fedify/cfworkers@fedify/fedify/x/fresh@fedify/freshThe old import paths (
@fedify/fedify/vocaband@fedify/fedify/runtime) still work as re-exports for backward compatibility, but they are deprecated and will be removed in a future version. The@fedify/fedify/x/*modules have been fully removed—you must migrate to the dedicated packages.This modularization was primarily contributed by ChanHaeng Lee (@2chanhaeng).
@fedify/vocab: Activity Vocabulary packageThe generated Activity Vocabulary classes (e.g.,
Create,Note,Person,Follow) are now in the standalone@fedify/vocabpackage. This separation enables:@fedify/vocab-toolspackage@fedify/vocab-runtime: Vocabulary runtime packageCore runtime utilities for vocabulary processing—
DocumentLoader,LanguageString, cryptographic key utilities, and multibase encoding—have been extracted into@fedify/vocab-runtime:Note that
@fedify/vocabre-exportsLanguageString,DocumentLoader, andRemoteDocumentfrom@fedify/vocab-runtime, so downstream consumers typically do not need to depend on@fedify/vocab-runtimedirectly.@fedify/vocab-tools: Custom vocabulary generationThe new
@fedify/vocab-toolspackage provides the code generation infrastructure that Fedify itself uses to generate Activity Vocabulary classes. This enables you to extend ActivityPub with custom vocabulary types:fedify generate-vocabCLI command@fedify/webfinger: Standalone WebFinger clientWebFinger functionality has been extracted into a standalone package for applications that need WebFinger lookup without the full Fedify framework:
@fedify/fresh: Fresh 2.0 integrationThe deprecated
@fedify/fedify/x/freshmodule (designed for Fresh 1.x) has been replaced by the new@fedify/freshpackage with full Fresh 2.0 support:This was contributed by Hyeonseo Kim (@dodok8).
Real-time debug dashboard
Fedify 2.0.0 introduces
@fedify/debugger, an embedded real-time ActivityPub debug dashboard that provides unprecedented visibility into your federation traffic during development.Quick setup
That's it.
createFederationDebugger()wraps your existingFederationobject and automatically sets up OpenTelemetry tracing, span export, and LogTape integration—no manual configuration needed.Dashboard features
The debug dashboard, accessible at
/__debug__/by default, provides:/__debug__/api/tracesand/__debug__/api/logs/:traceIdAuthentication
Protect the dashboard in shared environments with built-in authentication:
LogTape integration
The debugger automatically captures LogTape log records grouped by trace ID. In the simplified setup (without explicit
exporter), LogTape is auto-configured. For advanced setups, the returned object includes asinkproperty for manual LogTape configuration.To support this, Fedify now injects
traceIdandspanIdinto the LogTape context during request handling and queue processing, enabling log correlation with OpenTelemetry traces (#561, #564).ActivityPub relay support
Fedify 2.0.0 introduces first-class ActivityPub relay support through the new
@fedify/relaypackage and thefedify relayCLI command.@fedify/relaypackageActivityPub relays are critical fediverse infrastructure that help smaller instances participate in content distribution. The new
@fedify/relaypackage provides a ready-to-use relay server implementation:The package supports two relay protocols as defined in FEP-ae0c:
"mastodon"): Direct activity forwarding with one-way following and immediate subscription acceptance. ForwardsCreate,Update,Delete,Move, andAnnounceactivities. Broader fediverse compatibility."litepub"): Activities wrapped inAnnounce, bidirectional following with pending-then-accepted state. Designed for LitePub-aware servers.Relay management features include subscriber listing (
relay.listFollowers()), individual subscriber lookup (relay.getFollower()), and automatic signature verification.fedify relayCLI commandFor quick testing and development, the new
fedify relaycommand spins up an ephemeral relay server:By default, the relay server is tunneled to the public internet for external access. Use
--no-tunnelto run locally only.This feature was primarily contributed by Jiwon Kwon (@sij411).
Ordered message delivery
One of the most impactful changes in Fedify 2.0.0 is the introduction of ordering keys for message queues, solving the long-standing “zombie post” problem in ActivityPub federation (#536).
The problem
When a post is created and then quickly deleted, the
Deleteactivity can arrive at remote instances before theCreateactivity due to parallel message processing. This results in “zombie posts”—content that should have been deleted but persists because the delete was processed before the create.The solution
The new
orderingKeyoption inMessageQueueEnqueueOptionsguarantees FIFO processing for messages sharing the same key, while allowing messages with different keys to be processed in parallel:When
orderingKeyis specified inSendActivityOptions, the key is automatically transformed to${orderingKey}\n${recipientServerOrigin}during fan-out, ensuring per-recipient-server ordering while maintaining cross-server parallelism.Backend support
All official
MessageQueueimplementations have been updated:InProcessMessageQueuePostgresMessageQueueSELECT FOR UPDATE SKIP LOCKEDSqliteMessageQueueRedisMessageQueueAmqpMessageQueuerabbitmq_consistent_hash_exchangepluginWorkersMessageQueuePermanent delivery failure handling
Fedify 2.0.0 introduces a mechanism for handling permanent delivery failures when sending activities to remote inboxes (#548, #559).
The problem
Previously, when a remote inbox returned
410 Goneor404 Not Found, Fedify treated it as a transient failure and continued retrying. This wasted resources and provided no way for applications to clean up unreachable followers.setOutboxPermanentFailureHandler()The new
setOutboxPermanentFailureHandler()method lets you react to permanent failures:The handler receives:
inbox: The failing inbox URLactivity: TheActivityobject that failed to delivererror: ASendActivityErrorinstance with HTTP status code and response detailsstatusCode: The HTTP status codeactorIds: Actor IDs intended to receive the activity at this inbox (relevant for shared inbox delivery)Configuration
By default, HTTP status codes
404and410are treated as permanent failures. Customize this viapermanentFailureStatusCodes:SendActivityErrorThe new
SendActivityErrorclass provides structured error information for delivery failures, including the HTTP status code, inbox URL, and response body (limited to 1 KiB to prevent memory pressure from large error pages) (#569).Content negotiation at middleware level
Fedify 2.0.0 moves content type negotiation from individual dispatchers to the middleware layer (#434, contributed by Emelia Smith). This is a breaking change that improves compatibility with applications serving both HTML and ActivityPub content from the same URLs.
What changed
Previously, actor, object, and collection dispatchers were called for all incoming requests, regardless of the
Acceptheader. Applications had to handle content negotiation within dispatchers. Now, dispatchers are only invoked when the request accepts ActivityPub-compatible content types (application/activity+json,application/ld+json, etc.).Impact
Accept: text/html(e.g., browser requests) no longer reach your dispatchers—they are passed through to your web frameworkonNotAcceptablecallback is triggered at the middleware level before dispatchers are invokedThis change simplifies the common pattern of serving both a web page and an ActivityPub representation at the same URL, as the framework now handles the routing decision automatically.
Default idempotency changed to
"per-inbox"The default activity idempotency strategy has changed from
"per-origin"to"per-inbox"to align with standard ActivityPub behavior (#441).Why this matters
In Fedify 1.x, activity deduplication was per-origin by default—the same activity ID would be processed only once per receiving server, regardless of how many inboxes on that server it was delivered to. This caused issues when:
What changed
With
"per-inbox"as the new default, each inbox independently tracks which activity IDs it has seen. The same activity can be processed once per inbox, which is the standard ActivityPub behavior.To preserve the old behavior:
KvStore.list()is now requiredThe
list()method on theKvStoreinterface, introduced as optional in Fedify 1.10.0, is now required in 2.0.0 (#499, #506).All official
KvStoreimplementations already support this method:MemoryKvStore,SqliteKvStore,PostgresKvStore,RedisKvStore,DenoKvStore, andWorkersKvStore.If you have a custom
KvStoreimplementation, you must add alist()method that enumerates all entries whose keys start with the given prefix.New
@fedify/lintpackageThe new
@fedify/lintpackage provides shared linting configurations for consistent code quality in Fedify-based projects (#297, #494, contributed by ChanHaeng Lee).It supports both Deno Lint and ESLint, with 18 lint rules covering:
Two presets are available: recommended (default) and strict.
New
@fedify/createand@fedify/initpackagesCreating new Fedify projects is now easier than ever with the new
@fedify/createpackage (#351, contributed by ChanHaeng Lee):This provides the familiar
npm initworkflow that JavaScript developers expect, without needing to install the full@fedify/clitoolchain. The core initialization logic lives in the@fedify/initpackage, which is shared by both@fedify/createand thefedify initCLI command.SqliteMessageQueueThe
@fedify/sqlitepackage now includesSqliteMessageQueue, aMessageQueueimplementation using SQLite as the backing store (#477, #526, contributed by ChanHaeng Lee). This is ideal for development environments and small-scale, single-node production deployments:SqliteMessageQueuesupports the neworderingKeyoption for ordered message delivery.CLI enhancements
Native Node.js and Bun support
The Fedify CLI now runs natively on Node.js and Bun without requiring compiled binaries, providing a more natural JavaScript package experience (#374, #456, #457).
fedify generate-vocabcommandGenerate Activity Vocabulary classes from schema files using the new
fedify generate-vocabcommand. This uses@fedify/vocab-toolsinternally and enables extending ActivityPub with custom vocabulary types (#444, #458, contributed by ChanHaeng Lee).Improved
fedify initThe
fedify initcommand has been improved with better DX (#397, #435, contributed by ChanHaeng Lee):fedify init#460, Add ElysiaJS option tofedify init#496, contributed by Hyeonseo Kim)fedify lookup --traverseThe
fedify lookupcommand now supports traversing multiple collections in a single command with the-t/--traverseoption (#408, #449, contributed by Jiwon Kwon).#408: #408
#449: #449
--tunnel-serviceoptionThe
fedify lookup,fedify inbox, andfedify relaycommands now support a--tunnel-serviceoption to select the tunneling service (localhost.run, serveo.net, or pinggy.io) (#525, #529, #531, contributed by Jiwon Kwon).Configuration file support
The CLI now loads settings from TOML configuration files at multiple levels (#555, #566, contributed by Jiwon Kwon):
--configoptionAll command options (
inbox,lookup,webfinger,nodeinfo,tunnel,relay) can be configured through these files. Use--ignore-configto skip configuration file loading.Other changes
Intl.LocalereplacesLanguageTagThe
@phensley/language-tagdependency has been replaced with the standardizedIntl.Localeclass (#280, #392, contributed by Jang Hanarae):NodeInfo version as
stringNodeInfo
software.versionis nowstringinstead ofSemVerto properly handle non-SemVer version strings in accordance with the NodeInfo specification (#366, #433, contributed by Hyeonseo Kim). TheparseSemVer()andformatSemVer()functions have been removed.KvCacheParameters.rulestype relaxedThe
rulesoption type now acceptsTemporal.DurationLikein addition toTemporal.Duration, making it easier to specify cache durations:Bug fixes in database adapters
RedisMessageQueue.listen()where pub/sub notifications could be missed ifenqueue()was called immediately afterlisten()started (RedisMessageQueuetest intermittently times out in CI #515, Fix RedisMessageQueue race condition #532, contributed by Jiwon Kwon).PostgresMessageQueue.initialize()where concurrent calls could run DDL statements in parallel. Also fixedlisten()spawning many concurrentpoll()calls when a burst ofNOTIFYsignals arrived.SendActivityError.responseBodyis now limited to 1 KiB (Activity delivery error stores unbounded response body in memory, causing memory pressure under high failure rates #569).Testing utilities
The
@fedify/testingpackage now includestestMessageQueue(), a reusable test harness for standardized testing ofMessageQueueimplementations (#477, #526, contributed by ChanHaeng Lee). It covers common operations includingenqueue(),enqueue()with delay,enqueueMany(), multiple listeners, and (optionally) ordering key tests.Elysia Deno support
The
@fedify/elysiapackage now includes a deno.json configuration file for proper Deno tooling support (#460, #496).Migration guide
Step 1: Update import paths
Replace deprecated import paths with new packages:
Step 2: Replace removed APIs
Step 3: Update
LanguageTagusageStep 4: Update NodeInfo version handling
Step 5: Review content negotiation
Dispatchers now only fire for requests with ActivityPub-compatible
Acceptheaders. If your dispatcher contained logic for non-ActivityPub requests (e.g., rendering HTML or logging all visits), that code will no longer execute for browser requests:Step 6: Set idempotency strategy explicitly (if needed)
Step 7: Implement
KvStore.list()(custom implementations only)If you have a custom
KvStoreimplementation, add thelist()method:Acknowledgments
Fedify 2.0.0 represents an extraordinary collaborative effort. Special thanks to:
@fedify/vocab,@fedify/vocab-runtime,@fedify/vocab-tools,@fedify/init,@fedify/create,@fedify/lint,@fedify/fedify/x/*separation@fedify/relay,fedify relaycommand,fedify lookup --traverse,--tunnel-serviceoption, CLI configuration files, Redis race condition fix@fedify/fresh(Fresh 2.0), Elysia framework support, NodeInfo version handlingcontextLoader/documentLoaderfactory migrationIntl.LocalemigrationAnd to all community members who reported issues, provided feedback, and tested pre-release versions.
For the complete list of changes, bug fixes, and improvements, please refer to the CHANGES.md file in the repository.
Beta Was this translation helpful? Give feedback.
All reactions