Yonder β An uncompromising ActivityPub server
A single-binary, Mastodon-compatible ActivityPub server written in Rust
Reasons for yet another ActivityPub server
The beginning β Mastodon
I've been running Mastodon-compatible services for a while, starting with my first now defunct Mastodon instance at social.platypush.tech.
Mastodon is a great gateway drug for the Fediverse, but it soon started to feel tight for me.
Features often land in Mastodon only when other federated platforms have already embraced them for a while (e.g. post edits), and they often land in ways that are incompatible with what other platforms have already implemented (e.g. quotes).
And many other features that I wanted for a rich social media experience (emoji reactions, Markdown support, configurable number of profile fields, configurable maximum number of characters, configurable number of media attachments, Webmentions, backfilling of statuses and other activities from remote instances, code blocks, inline images, custom frontends, LaTeX formulas, a simpler and more complete process for account migrations...) just never landed at all.
I ended up, like many others, maintaining my own fork of Mastodon, with all the patches for features I wanted and that would have probably never been merged upstream.
At some point I realized that keeping my own fork up-to-date with all the upstream changes wasn't worth the effort, and I started to look for a better way to run Mastodon-compatible services.
Another factor that influenced my decision was Mastodon's unjustifiably high resource footprint.
My old instance used to run on a dedicated VPS with 8 GB of RAM and 500 GB of disk space.
It only had me and another active user on it.
And yet all the federated load, with all of its eager operations managed by a bloated Ruby-on-Rails + Sidekiq stack, was sufficient to choke everything, even if I regularly ran cleanups.
Down the rabbit whole β Akkoma
At some point I started looking around for federated alternatives compatible with Mastodon that had a much lower resource footprint and provided most of the features I wanted.
I soon discovered Akkoma, and I've used it for a few years as my main driver - the last instance was manganiello.eu.
Akkoma filled many of Mastodon's gaps that mattered to me:
- Markdown posts
- Configurable number of post characters
- Configurable number of profile fields
- Code blocks (no syntax highlighting though)
- Emoji reactions
- Custom frontends (but not customizable on a user level)
It still left some gaps, especially some compatibility gaps with Mastodon (indexable flag on accounts, support for Mastodon quotes, remote profile badge verification...), but for a long while I was satisfied with what it provided.
Until recently I was also the maintainer of the AUR akkoma package (now looking for a new maintainer).
But after a while I also started to feel uncomfortable with Akkoma's limitations.
Mastodon-compatible quotes were a dealbreaker, and my implementation failed to get approved upstream - ending up on the fork I ran on my instance.
Other features, like full bidirectional verification for profile badges and support for the indexable flag, also ended up living only on my fork.
Other issues, like proper compatibility with Audio activities from e.g. Funkwhale or Songhive, also made the experience less pleasant.
Akkoma however proved to show much more clemency towards my hardware resources than Mastodon did.
But the price to pay was to implement the features I wanted in a language (Elixir) that was quite exotic and unfamiliar to me (even though I ended up learning a lot from it).
I also couldn't find much documentation about e.g. Akkoma jobs or configuration.
I found Akkoma's documentation to be quite minimal in terms of content.
Understanding some configuration options or what job to run for a particular task often required me to look at the code itself.
Another issue with Akkoma was with the frontend.
I know that the default pleroma-fe can be quite polarizing, and that people either love it or hate it.
I belonged to the category of those who, no matter what, never managed to like it.
Luckily Akkoma could be configured to use alternative frontends, and I ended up liking the style of Soapbox, and even more that of its fork Mangane.
But I still felt that those frontends weren't particularly rich in terms of features, nor particularly curated around the edges.
Plus, the admin panel was an afterthough provided as a separate frontend rather than a first-class citizen like in Mastodon, and the UX was also poorly curated.
Still, the Soapbox/Mangane style ended up heavily inspiring the design of the default Yonder frontend.
Building Yonder
I started building Yonder (source code, Github mirror) a while ago, as a minimal PoC for an ActivityPub server in Rust .
It's been on a back burner on my hard drive for a while, but now I feel that it's mature enough to be my main driver on the Fediverse.
Yonder is a self-hosted, Mastodon-compatible ActivityPub server. The entire service β HTTP API, federation, streaming, media, moderation, admin panel, background jobs β is one binary. The only services it needs are PostgreSQL and Redis.
It speaks the Mastodon client API (v1 and v2), so Tusky, Ivory, Elk, Phanpy, Mona and every other Mastodon app just works when pointed at your instance.
It also implements a good subset of the Pleroma/Akkoma API dialects, which means that clients like Moshidon and other Pleroma-compatible apps can also access those features (e.g. Markdown content type for posts and custom emojis).
It speaks ActivityPub, so your users federate with Mastodon, Akkoma, Pleroma, Misskey and the rest of the Fediverse.

Name and meaning
Yonder (adv. or adj., /ΛjaΚndΙ/) is an Old English term for "over there". Something distant but usually within sight.
I chose it as the name for this project because it relays, together with the logo (a large circle connected to three other circles), the idea of connecting things distant but within reach.
What you get
Posting & timelines
- Rich posts in plain text or Markdown, with content warnings, polls, media attachments with editable alt text, scheduled posts, editing and saved drafts.
- Quote posts with author authorization (FEP-044f-style, with fallback to FEP-e232 Pleroma/Akkoma/Misskey compatible quotes), custom emoji reactions, replies, boosts and likes.
- Every timeline Mastodon has: home, local, federated, hashtag timelines, user-defined lists β with real-time updates over the standard Mastodon streaming WebSocket.
- Hashtags you can follow into your home feed and server-side filters (
warn/hide/blur) that apply across every timeline.

Social & federated
- Full ActivityPub federation via Pubby, with durable at-least-once delivery. I built Pubby as a general-purpose ActivityPub library that takes care of the all the plumbing behind the scenes, initially with Python bindings to power Madblog and Songhive, and it gained Rust bindings with features parity with the Python SDK in order to power Yonder - available as
pubby-sdkcrate. - Notifications in-app over WebSocket, Web Push (VAPID) and email, with per-type preferences.
- A progressive Web app (PWA) optimized for mobile and with support for native notifications.
- The bundled Web UI ships in five languages including full RTL support, with light and dark themes and custom accent colors.
- Custom emojis β per-instance emoji and emoji packs.
- Custom number of characters, custom number of profile fields, custom number of media attachments and custom number of pinned posts.
- RSS and Atom feeds for profiles, tags and lists; oEmbed/embeddable status cards.
Moderation & administration
- User-side: mutes, blocks, reports (optionally forwarded to the remote instance via
Flag), server-side content filters. - Instance-side: user limiting and suspension, domain blocks, federation relays, and a comprehensive admin panel delivered as a first-class citizen.
- Every registration policy:
open,invite-only,approval-requiredorclosed.
Platform
- One single Rust binary β stack:
axum,sqlx,tokio. - OAuth2 provider with authorization-code + PKCE, device flow, and scoped revocable API tokens.
- Pluggable frontends: the bundled Vue 3 SPA is just the default; alternative frontends can be installed alongside it. Users can pick their favourite frontend, preferences are stored in the browser session.
- OpenAPI 3 spec at
/openapi.jsonwith bundled Swagger UI. - Safety defaults: argon2id passwords, SSRF-guarded remote fetches, Redis-backed rate limits, TOTP two-factor.
What's new
Account migration
Account migration is a first-class feature: moving an account to or from another server, followers and all, is a supported flow, compatible with Mastodon and Akkoma.
On top of that, Yonder supports importing activities from the old account in two ways:
- Standard CSV import/export (compatible with Mastodon/Pleroma/Akkoma)
- OAuth2-authenticated API fetches.

You login to your old account through Yonder's interface, you pick what you want to import from your old account (including private messages, likes, boosts, replies, etc.), click "Start remote import", and everything from your old account will be copied over - as a native activity that originated from the new account, with the original timestamps respected.
The user can also decide whether the imported activities will fan out to federated instances or only be stored on the local database - prevents avoidable federation noise over imports, the activities can be selectively federated by simply boosting them or editing an old post.
The only thing that Yonder doesn't support is importing received likes, boosts and replies - for the simple reason that it doesn't want to forge federated activities or rewire threads in ways that don't reflect what other instances see.
The UX of the account migration flow has also been re-thought as a step-by-step process (connecting the old account over OAuth2, importing the data over API crawlers or CSV backups, reviewing the import process, creating an alias and moving followers), making the process much more intuitive and mistakes harder.

See the full federated picture all the time
One of the most confusing (and often infuriating) things about the Fediverse is that what you see on your local instance is often a very small subset of what's actually available on the Fediverse (unless you are on a large instance like mastodon.social, and even in that case many activities from the fringes of the Fediverse are lost).
Your instance only receives what its peers send to it. And an instance's peers depend on who your users follow, which relays you wired your instance to, and who your users interact with.
This has an unintended consequence: that content that never reaches your instance is invisible to you. Unless you trigger an explicit fetch, e.g. through a search query or some import job. And even then, you probably won't see a lot of likes, boosts and replies a remote activity has received, nor the real number of followers of a user.
Yonder solves this problem by adding a couple of new endpoints to the standard Mastodon API, and slightly modifying how some of the existing endpoints behave in order to maintain back-compatibility with existing apps.
/api/v1/accounts/{id}/sync_remote. This allows users to explicitly sync the latest<n>activities from the account{id}from the remote instance to the local database. If your Mastodon instance sees an account for the first time, and no local users follow that account, their timeline will probably appear empty, and they will show as having zero followers/followed accounts. Yonder fixes this visibility gap by adding a "Sync with<remote-instance>" button next to remote profiles, which triggers a remote fetch for their next<n>activities.- Followers and followed accounts are also synchronized with the remote instance, so you see the actual numbers. Upon calls to the
/followersendpoint, the list is lazily fetched and you can navigate it from your local instance - without having to open the original profile URL to see the actual numbers. /api/v1/statuses/{id}/fetch_remote_context. This endpoint is called by the Yonder frontend whenever you expand a post that belongs to a remote thread. What it does is to fetch behind the scenes all the statuses that belong to the same parent line, with a default maximum depth of 20. This, in my opinion, matches the expected behavior from a social app user. If I see a comment from a friend on another thread and I click on it, the most relevant thing I want to see is the direct path of statuses from the root of the thread down to the status I clicked on, plus the next 2-3 direct children. The client ends up seeing exactly the same context as the instance of the post's author. This is back-compatible also with existing Mastodon apps - calls to the/contextendpoint will trigger/fetch_remote_contextbehind the scenes if needed, and a refresh of the status 2-3 seconds later should show all the expanded remote posts in that path. Siblings and descendants from other paths are not fetched by default, but they are fetched if the user expands statuses on those paths. This approach means that fetches of remote threads provide a much more complete picture than that provided by Mastodon by default (you get to see exactly the same view as the author of that post, not only the subset explicitly send to your instance), while simultaneously being more efficient than what e.g. Mastodon does (fetch overhead is capped by the depth of the path, set to 20 by default, instead of trying to eagerly fetch the whole tree with no boundaries).
Another handful of useful API extensions
POST/DELETE /api/v1/accounts/{id}/subscribe: subscribe/unsubscribe to an account's activities without publicly following them.GET /api/v1/trends/tags: browse global or local trending hashtags, with support for pagination and time ranges.POST /api/v1/admin/accounts/{id}/reset_password: programmatically send a user password reset link directly to their email.POST /api/v1/admin/statuses/{id}/action: apply granular moderation to individual statuses (delete, mark as sensitive, add content warning).
Trending hashtags
This is something that for some reason Mastodon removed at some point, but that I find very useful.

Yonder makes it easy not only to follow hashtags, but to have a view of all the hashtags that are trending over the local instance or over all the known Fediverse, over a given period of time, and with support for pagination.
Direct messages as real conversations
Direct messages in Yonder are real conversations, not just an ungrouped timeline of cards with a lock on them.

This is similar to what users expects by social networks like X, Facebook or Instagram - private messages are not a timeline, they are grouped conversations, and it should be easy to find a private message from a month ago even if you received hundreds of messages on a single unrelated conversation in the meantime.
Webmentions
Webmentions are first-class citizens supported in both directions.
What this means is that:
- If a blog or federated actor
example.blogthat supports Webmentions posts a link toexample.social/status/123,example.socialwill process the Webmention and render it as a normal reply or reaction. The Webmentions actor will also be transparently rendered as a virtual user on the platform. - If you link
example.blogin a post onexample.social, andexample.blogsupports Webmentions, then the target will receive a Webmention request, and your social activity may be natively rendered under the blog article.
Webmentions are powered by my webmentions library - which, just like Pubby, was initially built in Python to support Webmentions on Madblog and Songhive, and then ported to Rust with features parity with the Python SDK - available as webmentions-sdk crate.
LaTeX
Instances like mathstodon.xyz run their own forks of Mastodon with support for LaTeX formulas.
But they use MathJax, which means that formulas are only rendered on the frontend, and only if you see them from that instance.
Yonder goes one step further and it adds rendering of LaTeX formulas directly on the backend.
You can provide your rendering script, set math.enabled = true in your configuration, and set math.command to your LaTeX rendering command.
Any command that takes the LaTeX formula on the stdin and returns an image on stdout works.
The repository ships a latex-math-render.sh script that is already compatible with tectonic and pdflatex.

Being rendered on the backend, the formulas are also rendered on clients that don't support frontend rendering through MathJax.
If the server or the client support inline images (e.g. Phanpy), then formulas will be rendered inline.
If it supports math-type attachments, then the formulas will be rendered at the bottom of the post.
Otherwise, they will graciously fall back to image attachments, with LaTeX-style references ((1), (2)...) as links in the post.

Inline images
Yonder supports inline images via standard Markdown:


Embeddables
Embed your posts in blog, Websites and anywhere on the Web. Click on the context menu of a post, select "Embed", and copy the snippet:
<div class="yonder-embed" data-status="{status-id}">
<a href="https://{instance}/statuses/{status-id}/embed">Post by @{user}@{instance}</a>
</div>
<script async src="https://{instance}/embed.js" charset="utf-8"></script>
<noscript>
<iframe src="https://{instance}/statuses/{status-id}/embed" width="100%" height="400" frameborder="0" loading="lazy" title="Post by @{user}@{instance}" />
</noscript>
An example:
Reactions to the status are fetched from the instance, if the user updates their status the referring page will update as well. If the status is deleted, it will also be deleted from the referring page. And Webmentions towards the Yonder target will also be processed if supported by the referring page, so the referring page will also be shown under the thread as a federated reply.
How it compares
I want to be honest here rather than hype-y, because "yet another Mastodon-compatible server" deserves scrutiny.
A feature matrix would be the natural way to do this, but matrices are painful to read on a phone, so here's a plain-language summary of where each implementation actually stands (as of late 2026). All of them speak ActivityPub and the Mastodon client API (Yonder implements both the v1 and v2 dialects plus a subset of the Pleroma/Akkoma extensions).
| Yonder | Mastodon | Akkoma/Pleroma | |
|---|---|---|---|
| Language/runtime | Rust (single binary) | Ruby + Node + optional ES | Elixir |
| Required services | Postgres + Redis | Postgres + Redis (+ ES for search) | Postgres (+ ES optional) |
| Mastodon client API | β v1+v2 | β (it's the reference) | β |
| ActivityPub federation | β | β | β |
| Quote posts | β (both FEP-044f and FEP-e232) | β (limited to FEP-044f) | β (limited to FEP-e232) |
| Markdown posts | β | β | β |
| Emoji reactions | β | β | β |
| Webmentions | β both directions | β | β |
| LaTeX math | β server-side | β (only on custom forks, and only on the frontend) | β |
| Account/followers migration (both directions) | β | β | β |
| Data export/import | β both over CSV backups and API crawlers | β οΈ only CSV backups | β οΈ only CSV backups |
| Alternative frontends | β installable and configurable on a per-user basis | β | β οΈ installable on instance level, not customizable on a user level |
| RSS/Atom feeds | β | β | β |
| On-demand fetch of remote content (followers, reactions, replies) | β | β | β |
| Admin panel | β (built-in) | β (built-in) | β οΈ (as a separate frontend) |
| Battle-testing | new (v1.0.x) | a decade | ~5 years |
Quickstart
Docker
Official Docker image provided on quay.io/yonder/yonder:
docker-compose setup (with Redis, Postgres and separate worker service):
git clone https://github.com/blacklight/yonder
cd yonder
cp .env.example .env # set PUID/PGID, domain
cp config.toml.example config.toml
docker compose up
Crate
Or grab the binary directly β it's on crates.io and in the GitHub releases:
cargo install yonder-server
# Copy and edit the configuration file
wget -O config.toml https://git.fabiomanganiello.comn/yonder/raw/branch/main/config.toml.example
yonder serve
# Separate process for workers (recommended)
yonder worker
Binary release download
VERSION="1.0.3" # IMPORTANT: Set to the version you want
wget -O yonder.tar.xz https://github.com/blacklight/yonder/releases/download/v${VERSION}/yonder-${VERSION}-x86_64-unknown-linux-gnu.tar.xz
tar -xf yonder.tar.xz && cd yonder-${VERSION}-x86_64-unknown-linux-gnu
cp config.toml.example config.toml
./yonder serve
# Separate process for workers (recommended)
./yonder worker
Setup
Config is a single config.toml (every key also settable via YONDER_* env vars or the admin panel). Federation is on by default, registration modes are configurable, and yonder config list dumps every knob.
Create your admin user:
yonder user create --username alice --admin
Or, from your docker-compose directory:
docker compose run --rm yonder user create --username alice --admin
Open the instance, log in, and verify federation works:
curl 'https://mastodon.social/.well-known/webfinger?resource=acct:you@your.instance'
Try it
- Source: github.com/blacklight/yonder (AGPL-3.0)
- Live instance: manganiello.tech β my own Yonder instance, federated with the wider Fediverse
- API docs:
/openapi.jsonand/swagger-uion any running instance - e.g. manganiello.tech/swagger-ui
AI usage disclosure
In the following table, ποΈ means "manual" and π€ means "aided by AI agents".
| Feature | ποΈ | π€ |
|---|---|---|
| Initial architecture | β | |
| Feature planning | β | |
| Coding | β | β |
| Testing | β | |
| Code reviews | β | β |
| Deployment | β | |
| User documentation | β | |
| Development documentation | β | β |
| Commit messages | β | β |
| Release notes | β | |
| Translations | β | |
| This blog article | β |
Appendix: My personal federated stack
Yonder is the latest product I've built for the Fediverse.
My journey with building products for the Fediverse from scratch rather than contributing only to established projects began around the beginning of this year, as I started growing dissatisfied with the features of many established products.
My main use cases for federated platforms are:
- π Blogging
- π΅ Music publishing and federation
- π’ General-purpose social media
And here's how my journey through self-hosted Fediverse platform went over the years.
Blogging
- Journey: WriteFreely β Madblog
- Blog article
- My instances:
Madblog was my starting point to start experimenting building software for the Fediverse.
The use-case was simple: a fully federated blogging platform that ran from a folder of Markdown files.
Single-actor instance, no support for following other users, no timelines, no admin panel, no login, no database, but full bidirectional federation based on text files and email notifications.
Music
- Journey: Funkwhale β Songhive
- Blog article
- My instance:
I built Songhive to cover many gaps I found in Funkwhale when it came to federation support, and many gaps in existing self-hosted music catalog solutions.
It's a hybrid between a music catalog with support for external sources, a platform for musicians to share and sell their content, and a music social network.
Federation-wise, it offers more features than Madblog (e.g. following users and managing timelines), but its main goal is not to provide all the features that Mastodon provides - just a sensible subset that makes it easy to share music and audio activities.
Social media
Reactions
How to interact with this page
Webmentions
To interact via Webmentions, send an activity that references this URL from a platform that supports Webmentions, such as Lemmy, WordPress with Webmention plugins, or any IndieWeb-compatible site.
ActivityPub
- Follow @fabio@manganiello.blog on your ActivityPub platform (e.g. Mastodon, Misskey, Pleroma, Lemmy).
- Mention @fabio@manganiello.blog in a post to feature on the Guestbook.
- Search for this URL on your instance to find and interact with the post.
- Like, boost, quote, or reply to the post to feature your activity here.
ππ¦ #Yonder is now ready to be tested - even though it should still be considered in a beta phase.