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:
- Removed deprecated APIs:
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).- NodeInfo
software.versionis nowstring: Changed fromSemVertostringto handle non-SemVer version strings (Change NodeInfosoftware.versionfield type fromSemVertostring#366, Change NodeInfosoftware.versionfield type fromSemVertostring#433). - Content negotiation moved to middleware: Dispatchers are no longer called for non-ActivityPub content types (Lift up checks to notAcceptable to the middleware #434).
- Default idempotency changed to
"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/fedify package with @fedify/fedify/vocab, @fedify/fedify/runtime, and @fedify/fedify/x/* submodules has been split into focused, independent packages:
| Old import | New package | Purpose |
|---|---|---|
@fedify/fedify/vocab |
@fedify/vocab |
Activity Vocabulary type-safe classes |
@fedify/fedify/runtime |
@fedify/vocab-runtime |
Vocabulary runtime infrastructure |
@fedify/fedify/x/hono |
@fedify/hono |
Hono integration |
@fedify/fedify/x/sveltekit |
@fedify/sveltekit |
SvelteKit integration |
@fedify/fedify/x/denokv |
@fedify/denokv |
Deno KV adapter |
@fedify/fedify/x/cfworkers |
@fedify/cfworkers |
Cloudflare Workers adapter |
@fedify/fedify/x/fresh |
@fedify/fresh |
Fresh 2.0 integration (new) |
The old import paths (@fedify/fedify/vocab and @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 package
The generated Activity Vocabulary classes (e.g., Create, Note, Person, Follow) are now in the standalone @fedify/vocab package. This separation enables:
- Smaller bundle sizes for applications that only need vocabulary types
- Independent versioning of vocabulary definitions
- Custom vocabulary extensions via the new
@fedify/vocab-toolspackage
// Before (still works but deprecated): import { Create, Note } from "@fedify/fedify/vocab"; // After: import { Create, Note } from "@fedify/vocab";
@fedify/vocab-runtime: Vocabulary runtime package
Core runtime utilities for vocabulary processing—DocumentLoader, LanguageString, cryptographic key utilities, and multibase encoding—have been extracted into @fedify/vocab-runtime:
// Before (still works but deprecated): import { LanguageString } from "@fedify/fedify/runtime"; // After: import { LanguageString } from "@fedify/vocab-runtime";
Note that @fedify/vocab re-exports LanguageString, DocumentLoader, and RemoteDocument from @fedify/vocab-runtime, so downstream consumers typically do not need to depend on @fedify/vocab-runtime directly.
@fedify/vocab-tools: Custom vocabulary generation
The new @fedify/vocab-tools package provides the code generation infrastructure that Fedify itself uses to generate Activity Vocabulary classes. This enables you to extend ActivityPub with custom vocabulary types:
- Runtime-agnostic: works on Deno, Node.js, and Bun
- Programmatic API for generating vocabulary classes from YAML schema files
- Integrated with the new
fedify generate-vocabCLI command
@fedify/webfinger: Standalone WebFinger client
WebFinger functionality has been extracted into a standalone package for applications that need WebFinger lookup without the full Fedify framework:
import { lookupWebFinger } from "@fedify/webfinger"; const result = await lookupWebFinger("@user@example.com");
@fedify/fresh: Fresh 2.0 integration
The deprecated @fedify/fedify/x/fresh module (designed for Fresh 1.x) has been replaced by the new @fedify/fresh package with full Fresh 2.0 support:
import { integrateHandler } from "@fedify/fresh"; import { federation } from "./federation.ts"; export const handler = integrateHandler(federation, () => undefined);
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
import { createFederation } from "@fedify/fedify"; import { createFederationDebugger } from "@fedify/debugger"; const innerFederation = createFederation({ /* ... */ }); const federation = createFederationDebugger(innerFederation); // Use `federation` as a drop-in replacement—the dashboard is at /__debug__/
That's it. createFederationDebugger() wraps your existing Federation object 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:
- Traces list: All captured ActivityPub traces with trace IDs, activity types, activity count, and timestamps, with auto-polling for real-time updates
- Trace detail: Per-trace view showing activity direction (inbound/outbound), type, actor, inbox URL, signature verification details (HTTP Signatures, LD Signatures), expandable activity JSON, and log records with level, category, and message
- JSON API: Programmatic access at
/__debug__/api/tracesand/__debug__/api/logs/:traceId
Authentication
Protect the dashboard in shared environments with built-in authentication:
const federation = createFederationDebugger(innerFederation, { auth: { password: "my-secret" }, // Or: auth: { username: "admin", password: "secret" } // Or: auth: (request) => request.headers.get("X-Forwarded-For") === "127.0.0.1" });
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 a sink property for manual LogTape configuration.
To support this, Fedify now injects traceId and spanId into 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/relay package and the fedify relay CLI command.
@fedify/relay package
ActivityPub relays are critical fediverse infrastructure that help smaller instances participate in content distribution. The new @fedify/relay package provides a ready-to-use relay server implementation:
import { createRelay } from "@fedify/relay"; import { MemoryKvStore } from "@fedify/fedify"; const relay = createRelay("mastodon", { kv: new MemoryKvStore(), origin: new URL("https://relay.example.com"), subscriptionHandler: async (ctx, subscriber) => { // Approve or reject subscriptions return "accepted"; }, }); // Use relay.fetch() to handle incoming requests
The package supports two relay protocols as defined in FEP-ae0c:
- Mastodon-style (
"mastodon"): Direct activity forwarding with one-way following and immediate subscription acceptance. ForwardsCreate,Update,Delete,Move, andAnnounceactivities. Broader fediverse compatibility. - LitePub-style (
"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 relay CLI command
For quick testing and development, the new fedify relay command spins up an ephemeral relay server:
# Start a Mastodon-compatible relay with public tunnel fedify relay # LitePub relay with persistent storage fedify relay --protocol litepub --persistent ./relay.db # Accept only specific instances fedify relay --accept-follow "mastodon.social,hachyderm.io" # Reject specific instances fedify relay --reject-follow "spam.example.com"
By default, the relay server is tunneled to the public internet for external access. Use --no-tunnel to 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 Delete activity can arrive at remote instances before the Create activity 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 orderingKey option in MessageQueueEnqueueOptions guarantees FIFO processing for messages sharing the same key, while allowing messages with different keys to be processed in parallel:
// Activities for the same note are delivered in order await ctx.sendActivity(sender, recipients, createNote, { orderingKey: noteId, }); await ctx.sendActivity(sender, recipients, deleteNote, { orderingKey: noteId, });
When orderingKey is specified in SendActivityOptions, 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 MessageQueue implementations have been updated:
| Backend | Ordering mechanism |
|---|---|
InProcessMessageQueue |
Built-in FIFO per key |
PostgresMessageQueue |
SELECT FOR UPDATE SKIP LOCKED |
SqliteMessageQueue |
Row-level ordering |
RedisMessageQueue |
Redis Streams |
AmqpMessageQueue |
rabbitmq_consistent_hash_exchange plugin |
WorkersMessageQueue |
Workers KV locks (best-effort) |
Note for custom implementations: If you have a custom
MessageQueueimplementation, you must add support for theorderingKeyoption in theenqueue()method. Messages with the sameorderingKeymust be processed in FIFO order.
Permanent 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 Gone or 404 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:
federation.setOutboxPermanentFailureHandler(async (ctx, values) => { const { inbox, activity, error, statusCode, actorIds } = values; // Clean up followers pointing to the failed inbox for (const actorId of actorIds) { await removeFollower(actorId, inbox); } });
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 404 and 410 are treated as permanent failures. Customize this via permanentFailureStatusCodes:
const federation = createFederation({ // ... permanentFailureStatusCodes: [404, 410, 451], // Add 451 Unavailable For Legal Reasons });
SendActivityError
The new SendActivityError class 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 Accept header. 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
- Requests with
Accept: text/html(e.g., browser requests) no longer reach your dispatchers—they are passed through to your web framework - The
onNotAcceptablecallback is triggered at the middleware level before dispatchers are invoked - Applications that relied on dispatchers being called for all content types need to adjust their routing logic
This 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:
- The same activity was legitimately delivered to multiple personal inboxes on the same server
- Software like Pixelfed reused activity IDs
- Shared inbox implementations needed activities to reach each intended recipient independently
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:
federation .setInboxListeners("/inbox/{identifier}", "/inbox") .withIdempotency("per-origin") // Explicitly set old behavior .on(Follow, async (ctx, follow) => { // ... });
KvStore.list() is now required
The list() method on the KvStore interface, introduced as optional in Fedify 1.10.0, is now required in 2.0.0 (#499, #506).
interface KvStore { get(key: KvKey): Promise<unknown>; set(key: KvKey, value: unknown, options?: KvStoreSetOptions): Promise<void>; delete(key: KvKey): Promise<void>; // Now required: list(prefix?: KvKey): AsyncIterable<KvStoreListEntry>; }
All official KvStore implementations already support this method: MemoryKvStore, SqliteKvStore, PostgresKvStore, RedisKvStore, DenoKvStore, and WorkersKvStore.
If you have a custom KvStore implementation, you must add a list() method that enumerates all entries whose keys start with the given prefix.
New @fedify/lint package
The new @fedify/lint package 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:
- Actor property requirements (ID, inbox, outbox, followers, public keys, assertion methods)
- URL pattern validation
- Collection filtering implementation checks
Two presets are available: recommended (default) and strict.
New @fedify/create and @fedify/init packages
Creating new Fedify projects is now easier than ever with the new @fedify/create package (#351, contributed by ChanHaeng Lee):
npm init @fedify # npm pnpm create @fedify # pnpm yarn create @fedify # yarn bunx @fedify/create # Bun
This provides the familiar npm init workflow that JavaScript developers expect, without needing to install the full @fedify/cli toolchain. The core initialization logic lives in the @fedify/init package, which is shared by both @fedify/create and the fedify init CLI command.
SqliteMessageQueue
The @fedify/sqlite package now includes SqliteMessageQueue, a MessageQueue implementation 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:
import { SqliteMessageQueue } from "@fedify/sqlite"; const queue = new SqliteMessageQueue("./queue.db"); await queue.initialize();
SqliteMessageQueue supports the new orderingKey option 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-vocab command
Generate Activity Vocabulary classes from schema files using the new fedify generate-vocab command. This uses @fedify/vocab-tools internally and enables extending ActivityPub with custom vocabulary types (#444, #458, contributed by ChanHaeng Lee).
Improved fedify init
The fedify init command has been improved with better DX (#397, #435, contributed by ChanHaeng Lee):
- Prompts for confirmation when the target directory is not empty
- Re-prompts when options are not specified or invalid
- Now supports Elysia as a web framework option (Add ElysiaJS option to
fedify init#460, Add ElysiaJS option tofedify init#496, contributed by Hyeonseo Kim)
fedify lookup --traverse
The fedify lookup command now supports traversing multiple collections in a single command with the -t/--traverse option (#408, #449, contributed by Jiwon Kwon).
--tunnel-service option
The fedify lookup, fedify inbox, and fedify relay commands now support a --tunnel-service option 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):
- System-wide: /etc/xdg/fedify/config.toml
- User-level: ~/.config/fedify/config.toml
- Project-level: .fedify.toml
- Custom: via
--configoption
All command options (inbox, lookup, webfinger, nodeinfo, tunnel, relay) can be configured through these files. Use --ignore-config to skip configuration file loading.
Other changes
Intl.Locale replaces LanguageTag
The @phensley/language-tag dependency has been replaced with the standardized Intl.Locale class (#280, #392, contributed by Jang Hanarae):
// Before: const lang: LanguageTag = langString.language; // After: const locale: Intl.Locale = langString.locale;
NodeInfo version as string
NodeInfo software.version is now string instead of SemVer to properly handle non-SemVer version strings in accordance with the NodeInfo specification (#366, #433, contributed by Hyeonseo Kim). The parseSemVer() and formatSemVer() functions have been removed.
KvCacheParameters.rules type relaxed
The rules option type now accepts Temporal.DurationLike in addition to Temporal.Duration, making it easier to specify cache durations:
// Before: had to use Temporal.Duration.from() rules: [[new URL("https://example.com"), Temporal.Duration.from({ hours: 1 })]], // After: plain objects work too rules: [[new URL("https://example.com"), { hours: 1 }]],
Bug fixes in database adapters
- @fedify/redis: Fixed a race condition in
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). - @fedify/postgres: Fixed a race condition in
PostgresMessageQueue.initialize()where concurrent calls could run DDL statements in parallel. Also fixedlisten()spawning many concurrentpoll()calls when a burst ofNOTIFYsignals arrived. - @fedify/fedify: Fixed unbounded memory consumption when activity delivery fails with large error responses.
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/testing package now includes testMessageQueue(), a reusable test harness for standardized testing of MessageQueue implementations (#477, #526, contributed by ChanHaeng Lee). It covers common operations including enqueue(), enqueue() with delay, enqueueMany(), multiple listeners, and (optionally) ordering key tests.
Elysia Deno support
The @fedify/elysia package 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:
// Vocabulary types // Before: import { Create, Note, Person } from "@fedify/fedify/vocab"; // After: import { Create, Note, Person } from "@fedify/vocab"; // Runtime utilities // Before: import { LanguageString } from "@fedify/fedify/runtime"; // After: import { LanguageString } from "@fedify/vocab-runtime"; // Framework integrations // Before: import { federation } from "@fedify/fedify/x/hono"; // After: import { federation } from "@fedify/hono";
Step 2: Replace removed APIs
// Before: const federation = createFederation({ documentLoader: myLoader, // Removed contextLoader: myContextLoader, // Removed }); // After: const federation = createFederation({ documentLoaderFactory: (handle) => myLoader, contextLoaderFactory: (handle) => myContextLoader, }); // Before: import { fetchDocumentLoader } from "@fedify/fedify/runtime"; // After: import { getDocumentLoader } from "@fedify/vocab-runtime"; // Before: ctx.sendActivity({ handle: "alice" }, recipients, activity); // After: ctx.sendActivity({ identifier: "alice" }, recipients, activity);
Step 3: Update LanguageTag usage
// Before: import { LanguageTag } from "@phensley/language-tag"; const lang: LanguageTag = langString.language; // After: const locale: Intl.Locale = langString.locale; // Or construct from string: const locale = new Intl.Locale("en-US");
Step 4: Update NodeInfo version handling
// Before: import { parseSemVer } from "@fedify/fedify"; const version: SemVer = software.version; // After: const version: string = software.version; // Parse yourself if needed: const parts = version.split(".");
Step 5: Review content negotiation
Dispatchers now only fire for requests with ActivityPub-compatible Accept headers. 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:
// Before (1.x): Dispatcher was called for ALL requests, including browsers. // Some apps relied on this for side effects or manual content negotiation: federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => { // This code ran even for Accept: text/html requests in 1.x. // In 2.0, this is ONLY called for ActivityPub content types. return new Person({ /* ... */ }); }); // After (2.0): If you need to handle both HTML and ActivityPub at the same URL, // rely on the onNotAcceptable callback in your middleware integration: return await federation.fetch(request, { contextData, onNotFound: async (request) => await next(request), onNotAcceptable: async (request) => { // Fedify calls this when the route matches but Accept is not ActivityPub. // Forward to your web framework to render HTML: const response = await next(request); if (response.status !== 404) return response; return new Response("Not Acceptable", { status: 406, headers: { "Content-Type": "text/plain", Vary: "Accept" }, }); }, });
Step 6: Set idempotency strategy explicitly (if needed)
// To keep the old 1.x behavior: federation .setInboxListeners("/inbox/{identifier}", "/inbox") .withIdempotency("per-origin"); // Or accept the new default (recommended): // "per-inbox" is now the default—no code change needed
Step 7: Implement KvStore.list() (custom implementations only)
If you have a custom KvStore implementation, add the list() method:
class MyKvStore implements KvStore { // ... existing methods ... async *list(prefix?: KvKey): AsyncIterable<KvStoreListEntry> { // Enumerate entries matching the prefix for (const [key, value] of this.entries()) { if (!prefix || keyStartsWith(key, prefix)) { yield { key, value }; } } } }
Acknowledgments
Fedify 2.0.0 represents an extraordinary collaborative effort. Special thanks to:
- ChanHaeng Lee (@2chanhaeng) — Modular architecture,
@fedify/vocab,@fedify/vocab-runtime,@fedify/vocab-tools,@fedify/init,@fedify/create,@fedify/lint,@fedify/fedify/x/*separation - Jiwon Kwon (@sij411) —
@fedify/relay,fedify relaycommand,fedify lookup --traverse,--tunnel-serviceoption, CLI configuration files, Redis race condition fix - Hyeonseo Kim (@dodok8) —
@fedify/fresh(Fresh 2.0), Elysia framework support, NodeInfo version handling - Hasang Cho (@crohasang) —
contextLoader/documentLoaderfactory migration - Jang Hanarae (@Palcimer) —
Intl.Localemigration - Emelia Smith (@ThisIsMissEm) — Content negotiation middleware improvements
And 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.