The dream of the decentralized web often collides with the reality of the five-dollar virtual private server. For developers building Fediverse nodes, the challenge is rarely the high-level logic of social networking, but rather the brutal efficiency required to keep a server alive on limited hardware. This week, the development of sukhi-fedi highlighted a critical architectural pivot that many indie developers face: the moment when a convenient library becomes a resource liability. The tension between using a high-level framework to accelerate development and the need for lean, predictable memory consumption has led to a complete teardown of the project's federation layer.
The Architectural Purge of the Bun Runtime
To achieve a leaner footprint, sukhi-fedi has completely removed the fedify library, a Bun-based JavaScript runtime implementation, and replaced it with a custom federation engine written entirely in Elixir. This was not a minor refactor but a significant engineering effort involving the creation of 12 new files and approximately 2,100 lines of original code. The goal was to eliminate the dependency on an external runtime for the most critical part of the server: the ActivityPub federation logic.
The scope of the replacement covered the most complex components of the federation stack. The team had to reimplement the vocab class and the essential converters, specifically the toJsonLd and fromJsonLd functions that handle the translation of messages into the JSON-LD format required by the Fediverse. Beyond data transformation, the implementation required native support for request signing and verification, adhering to both the draft-cavage and RFC 9421 standards. This included building a custom document loader and implementing LD signatures, as well as handling the input and output of JSON Web Keys (JWK), which serve as the public key standard for secure communication between servers.
The primary driver for this migration was a stark disparity in memory efficiency. During endurance testing on small-scale servers with 512MB to 768MB of RAM, the Elixir-native implementation operated stably at around 130MB. In contrast, the Bun worker encountered Out Of Memory (OOM) failures as soon as it hit the 120MB mark, causing the entire process to crash. For a server intended to run on minimal infrastructure, this volatility was unacceptable.
The Hidden Cost of Polyglot Plumbing
While the memory numbers provide a clear justification, the deeper insight lies in the removal of what developers call plumbing. When a system splits its logic between two different runtimes—in this case, Elixir for the main application and Bun for the federation library—it introduces a layer of inter-process communication (IPC) overhead that degrades performance and increases complexity.
In the previous architecture, every single signature verification required a cumbersome data pipeline. The system had to preserve the raw original data and transport it across the process boundary to the Bun worker. If the request had passed through a tunnel, the system had to perform additional work to restore the original addresses before the signature could be validated. Even the keys stored in the database had to be wrapped into JWK format and shipped over to the separate Bun process. This constant wrapping, unwrapping, and transporting of data created a boundary that increased latency and introduced multiple points of failure.
To ensure the new Elixir implementation was flawless, the developers employed a deterministic verification pipeline. Because Ed25519 signatures and URDNA2015 normalization—the process of ordering document elements to ensure a consistent hash—are deterministic, the same input always produces the same output. The team used the existing fedify library as a ground-truth oracle. By feeding the same data into both the old JavaScript implementation and the new Elixir code, they could verify that the resulting signatures matched character-for-character. This approach allowed them to migrate the core logic with absolute confidence in its integrity.
This shift toward a single runtime allows sukhi-fedi to handle data processing and signature verification within the same memory space, eliminating the need for JWK packaging and address restoration. It transforms the federation layer from a separate, fragile service into an integrated part of the Elixir BEAM virtual machine, leveraging Elixir's inherent strengths in concurrency and fault tolerance.
Amidst these changes, the broader ecosystem continues to evolve. The fedify team is currently developing DrFed, an ActivityPub debugging tool available at https://drfed.org/. Supported by NLnet NGI0 and released under the AGPL-3.0 license, DrFed aims to diagnose bottlenecks and errors across the federation chain, including DNS, TLS, HTTP, and JSON-LD signatures. It provides a step-by-step debugger to track the two primary signing methods, offering a way for other developers to avoid the same pitfalls sukhi-fedi encountered.
For developers deciding between a library and a custom implementation, the choice depends on the specific constraints of the project. If a project requires a full-featured framework—including inbox listeners, dispatchers, and the createFederation utility—fedify remains a powerful choice, as evidenced by its use in services like Ghost, hackers.pub, and Hollo. However, when operating under extreme resource constraints, such as servers with less than 512MB of RAM, the risk of OOM errors makes a native implementation the safer path.
Furthermore, if a developer has already implemented high-level federation logic—such as idempotency handling, authorized fetch, or instance actor management—the cost of implementing the low-level primitives like JSON-LD conversion and signing is often lower than the cost of maintaining a separate runtime. When the architectural boundary between the main application and the library begins to require more code for data transport than for actual logic, it is a clear signal that the system needs to be unified.
This transition proves that in the world of decentralized infrastructure, the most significant optimization is often the removal of the bridge between two different languages.




