-
-
Notifications
You must be signed in to change notification settings - Fork 84
Description
Summary
Create a new package @fedify/relay that provides a comprehensive implementation of ActivityPub relay protocols based on the FEP-ae0c specification. This package should enable developers to easily create and operate relay servers that facilitate content distribution across the fediverse, providing separate implementations for Mastodon-style and LitePub-style relay protocols through a common interface.
Problem
ActivityPub relays are crucial infrastructure components that help small instances participate effectively in the federated social network by acting as intermediary servers that distribute public content without requiring individual actor-following relationships. Currently, there is no standardized, easy-to-use implementation available for developers who want to create relay servers using modern JavaScript/TypeScript tooling.
The existing relay implementations in the ecosystem are often written in different languages (Rust, Go, Python) or are tightly coupled to specific platforms. This creates barriers for developers who want to build relay functionality into their Fedify-based applications or create standalone relay servers with the same developer experience that Fedify provides for ActivityPub servers.
FEP-ae0c documents two popular relay protocols that are widely deployed in the fediverse: Mastodon-style relays (which use LD signatures and follow the Public collection) and LitePub-style relays (which use bidirectional following relationships). These protocols have fundamental differences in their subscription mechanisms, activity publishing methods, and signature requirements, making it impractical to support both protocols simultaneously in a single relay instance. A comprehensive relay package should provide separate implementations for each protocol while maintaining a unified developer interface.
Proposed Solution
The @fedify/relay package should provide a common Relay interface implemented by protocol-specific classes: MastodonRelay and LitePubRelay. Both classes should follow the same architectural patterns as Fedify's Federation object, each offering a fetch() method that can be integrated with any web framework or runtime. The package should internally leverage Fedify's existing infrastructure for ActivityPub message handling, HTTP signatures, WebFinger support, and key–value storage.
The common interface should enable developers to work with either relay type through consistent APIs while allowing each implementation to handle protocol-specific requirements appropriately. Here's an example of how the API might look:
import { MastodonRelay, LitePubRelay, type Relay } from "@fedify/relay";
import { MemoryKvStore } from "@fedify/fedify";
// Common interface for both relay types
interface Relay {
fetch(request: Request, options?: { contextData?: any }): Promise<Response>;
setSubscriptionHandler(handler: SubscriptionRequestHandler): this;
setActivityHandler(handler: ActivityHandler): this;
getSubscribers(ctx: Context): Promise<string[]>;
approveSubscriber(ctx: Context, clientActor: string): Promise<void>;
blockSubscriber(ctx: Context, clientActor: string): Promise<void>;
}
// Mastodon-style relay (follows Public collection, uses LD signatures)
const mastodonRelay: Relay = new MastodonRelay({
kv: new MemoryKvStore(),
requireApproval: false,
maxSubscribers: 1000,
});
// LitePub-style relay (bidirectional following, uses Announce wrapping)
const litePubRelay: Relay = new LitePubRelay({
kv: new MemoryKvStore(),
requireApproval: true,
actorPath: "/relay", // LitePub requires /relay suffix
});Both implementations should handle their protocol-specific complexities automatically. For Mastodon-style relays, this includes proper LD signature verification and generation, handling Follow requests to the Public collection, and direct activity relaying with appropriate content types (Create, Update, Delete, Move). For LitePub-style relays, this includes bidirectional following relationships, Announce-wrapped activity distribution, and proper actor URI requirements (ending with /relay).
The common interface should provide utilities for subscriber management, activity deduplication, relay statistics, and content moderation while allowing protocol-specific customization:
// Both relay types support the same management interface
mastodonRelay.setSubscriptionHandler(async (ctx, clientActor, clientDomain) => {
const blockedDomains = ["spam.example", "blocked.example"];
return !blockedDomains.includes(clientDomain);
});
litePubRelay.setActivityHandler(async (ctx, activity, sender) => {
// LitePub-specific filtering (typically handles Announce activities)
return activity.type === "Announce";
});
// Unified deployment approach
const relays = new Map<string, Relay>();
relays.set("/mastodon-relay", mastodonRelay);
relays.set("/litepub-relay", litePubRelay);
Deno.serve((request) => {
const url = new URL(request.url);
const relay = relays.get(url.pathname.split('/').slice(0, 2).join('/'));
if (relay) {
return relay.fetch(request, { contextData: undefined });
}
return new Response("Not found", { status: 404 });
});Additionally, the package could include a RelayClient class for applications that need to subscribe to and publish content through relays, providing a high-level interface that can automatically detect and adapt to different relay protocols.
Alternatives Considered
One alternative would be to integrate relay functionality directly into the core Fedify package. However, this would increase the size and complexity of the main package for users who don't need relay functionality. A separate package allows for focused development and maintenance while keeping the core package lean.
Another approach would be to create separate packages for Mastodon-style and LitePub-style relays, such as @fedify/relay-mastodon and @fedify/relay-litepub. However, having a unified package with separate classes implementing a common interface provides better code reuse for shared functionality like subscriber management, activity filtering, and storage operations, while still maintaining clear protocol separation.
A third alternative would be to create this as a standalone sub-brand product similar to BotKit by Fedify, potentially called “RelayKit by Fedify” or similar. This approach would provide more marketing visibility and could position the relay functionality as a distinct product offering. However, this might create unnecessary fragmentation in the ecosystem and would require separate branding, documentation, and maintenance efforts that could be better consolidated under the main Fedify umbrella.
We also considered recommending existing relay implementations in other languages, but this would require developers to manage multiple technology stacks and wouldn't provide the seamless integration experience that a JavaScript/TypeScript solution built on Fedify would offer.
Scope / Dependencies
This feature would require creating a new package that depends on the core @fedify/fedify package and follows its architectural patterns. The implementation would need to handle several complex aspects:
Protocol Implementation: Separate implementations for Mastodon-style relays (with LD signature verification and Public collection following) and LitePub-style relays (with bidirectional following relationships and Announce-wrapped activities). Both should implement a common Relay interface to ensure consistent developer experience while handling protocol-specific requirements like Follow object differences, activity wrapping strategies, and signature verification methods.
Security and Verification: Implementation of Mastodon's non-standard LD signature algorithm for message verification, HTTP signature verification for all incoming requests, and proper key fetching and caching for signature validation. Each relay type must handle its specific security requirements while maintaining the same security standards.
Storage and State Management: Integration with Fedify's KvStore interface for persisting subscriber lists, activity deduplication data, approval queues, and relay statistics. Support for the same storage backends that Fedify supports (Redis, PostgreSQL, Deno KV, etc.). The common interface should abstract storage operations while allowing protocol-specific data structures.
Web Standards Integration: WebFinger support for relay actor discovery, NodeInfo support for relay metadata advertisement, and proper ActivityPub actor document generation with required properties for each relay type. LitePub relays must ensure proper /relay path handling, while Mastodon relays must support Public collection semantics.
Developer Experience: Comprehensive documentation with examples for different deployment scenarios and both relay protocols, TypeScript type definitions for all APIs including the common interface, integration examples for popular web frameworks showing how to deploy multiple relay types, and testing utilities for relay functionality that work across both implementations.
Interoperability Testing: The implementation should be tested against existing relay servers and clients in the wild to ensure compatibility with real-world fediverse infrastructure, including testing with Mastodon, Pleroma, Misskey, and other popular ActivityPub implementations. Both relay implementations must be verified to work correctly with their respective ecosystems.
The package should maintain the same high standards for documentation, testing, and developer experience that characterize the main Fedify project, and should be designed to evolve alongside changes in relay protocols and fediverse standards. The common interface design should allow for potential future relay protocol implementations while preserving backward compatibility.