Fabio Manganiello

Songhive β€” A fully self-hosted and federated music platform

What if your music server had a social network attached, and your social network could play music?

.

The problem

If you've ever tried to self-host your music, you've probably noticed that every tool solves exactly one half of the problem.

Media servers (Jellyfin, Navidrome, plain Subsonic) are great at the library half: point them at your FLAC collection, get streaming, transcoding, mobile apps. But they're single-player islands. Nobody else can discover your music, there's no social layer, and sharing a track means copy-pasting a file or a link that probably doesn't resolve outside your LAN, or creating accounts on your server for every person you want to share content with.

Social music platforms (Funkwhale being the closest thing) add the federation half, but the social experience is mostly library-oriented: you follow someone's library and sync their files. It's a distribution network, not a place where people talk about music. Moreover, most of those open platforms were never thought with revenue models in mind. Payments were never integrated. They're mostly places for artists to freely share their creations, not for them to monetize them.

Streaming services (Spotify, Apple Music, TIDAL) are widespread, but they usually don't come with true built-in social features besides following someone else's playlists, or the "Share to Facebook/Instagram/TikTok" button. And consumers don't own anything: the catalog can shrink overnight, and shared URLs can often be viewed only by people who already have an account on the platform. Music creators don't have much of a say either. They get fractions of a cent, they don't fully control distribution (and often they need an intermediary just to distribute their music, which takes a share of the revenue too), and they often have to release to multiple music platforms just because those platforms don't talk with one another. Most importantly, your listening history is the product.

Bandcamp tries to fix some of the issues, namely by putting content creators at the center and letting them own their own sales and monetization process (including sales of things like vinyls, CDs and merchandise). But it's still a centralized service whose ownership changed hands several times. Users and artists are just one round of layoffs away from seeing the platform they invested in rot and/or enshittify, with no direct compatible alternatives. The social media aspect is also not something Bandcamp particularly invested in as well. The focus has increasingly shifted to sales, social interactions are basically limited to a "community" tab on artist pages, where artists can post updates and people can reply to. Any social interactions stay strictly confined to a tab on a centralized platform.

Perhaps the old incarnation of Google Music helped filled an important gap on user-generated content (it allowed users to upload up to 50,000 tracks to the cloud for free, making music collection sitting on old hard drives available on-the-go), but it didn't last long, and it didn't cover any social media and sharing needs.

And MySpace β€” the thing everyone actually remembers fondly β€” was never really any of these. It was a place where a band had a page, you could play their song right there, leave a comment, and add them as a friend. Nobody has successfully rebuilt that.

Songhive is an attempt to meet all these needs in one package: a self-hosted music streaming server where tracks, albums, playlists and posts are first-class ActivityPub objects. Think Funkwhale meets Mastodon β€” your music library lives on your hardware, or in any cloud storage you want to plug to it, but your music (and your posts about music) can travel across the fediverse, be followed from Mastodon, replied to, boosted, and quoted.

Screenshot of an instance's home page on desktop Screenshot of an instance's home page on mobile Screenshot of a Subsonic app (Substreamer on Android) connected to a Songhive server

Who is this for

  • Artists: publish tracks with a "Publish on the Fediverse" checkbox and they appear as playable Audio objects in Mastodon timelines. People can reply, boost and quote them from their existing fediverse account β€” no new signup required on your side. Your artist page, releases and playlists all expose RSS/Atom feeds, and blogs that support Webmentions will send you a notification when they link to your song. Native integration with Stripe also makes it easier for you to connect a payout account and put some of your tracks or albums for sale. You can choose how much of a paid song you want to share for streaming (only a sample, the full song or nothing), and how much users should pay for it. No shares of your revenue are taken besides the share taken by Stripe to process the transaction.
  • Bands who need an easy and flexible way to share their recordings, rehearsals, playlists of covers etc. Those who have played in a band know this issue quite well. Spotify playlists for covers to play are only good as long as everyone in the band has a premium Spotify account. YouTube playlists often result in precious rehearsal time wasted waiting for ads to end. Not to mention those rehearsal.mp3 files shared between WhatsApp chats, emails and Dropbox or Google Drive folders, with no easy way of bringing them all together under the same roof - especially when you're looking for those recordings during a rehearsal. Songhive makes it easier to share whole tracks, albums or playlists with full control over the level of visibility. It makes it easier to integrate 3rd-party music streaming services so you can sync your playlist of covers and easily share it from a proxy platform without everyone having to have a Spotify or TIDAL premium account. It makes it easy to just plug that Google Drive folder where you carelessly dropped all the recordings of your band's rehearsal and sort them, tag them and share them as consistent collections with a single URL.
  • Self-hosters / library curators: point Songhive at your local folders, an S3 bucket, a WebDAV/SFTP share, Dropbox, Google Drive β€” or even import your collections from Jellyfin or TIDAL β€” and get a full streaming stack with transcoding, Subsonic mobile apps, Last.fm scrobbling, listening stats and automatic MusicBrainz/cover-art enrichment. Fine-grained visibility (private / local / public) means your 4,000-track hoard stays private while the three songs you actually wrote are public. Not only, but Songhive also taps into an issue that open projects like these often fail to address: how to provide incentives to sysadmins and self-hosters to run their servers. Especially when it comes to things like a music streaming server which, in terms of CPU, storage and bandwidth usage, is not free - and often not even cheap. Songhive provides the option for sysadmins to have a Paid registration mode. The admin can decide how much they want to charge users to use their instance and upload music to it, and how often they want to charge them. Subscriptions are handled transparently by Stripe and can be cancelled at any time. And admins can also set user quotas to keep things sane and prevent abuse.
  • People who want a MySpace-style music social network: run an instance for your scene, your label, your collective. Users get profiles, statuses with mentions and hashtags, threaded replies, quotes, notifications, and moderation tools (mute/block, reports, instance-level defederation). Your instance federates with Mastodon, so it's not a walled garden unless you want it to be.
  • Musicians with a multi-room house or a community radio streak: server-side outputs let playback keep running on the server after you close the tab β€” cast to Snapcast multi-room speakers, relay to an Icecast station, or host your own HTTP radio mountpoint with fan-out to unlimited listeners.

What you get

The streaming stack

The core is a serious music library and streaming server:

  • Library management with artists, albums, tracks, playlists and dynamic radio stations. Uploads get automatic tag extraction, duplicate detection, and metadata enrichment from MusicBrainz and the Cover Art Archive β€” including cover art and artist images fetched for you.
  • Streaming with on-the-fly transcoding (MP3, OGG, FLAC, AAC, Opus), HTTP range requests, and a transcode cache, with per-user/per-role bitrate caps (handy if your uplink or mobile data is precious).
  • Subsonic API compatibility, enabled by default. This is the sleeper feature: every Subsonic-compatible mobile/desktop client β€” Substreamer, Symfonium, Ultrasonic, Supersonic, Tempus and friends β€” works against your instance out of the box. You get a decade's worth of mature third-party players for free, and API tokens keep your real password out of them.
  • Mopidy integration via the mopidy-songhive extension if that's your jam. - Listening history, favorites and scrobbling to Last.fm and Libre.fm out of the box, plus a personal listening-stats dashboard (top artists, calendar and hour-of-day heatmaps, release-year breakdowns) rendered with ECharts.
A screenshot of the listening stats page, showing number of played tracks and top genres grouped by date
A screenshot of the listening stats page, showing number of played tracks and top genres grouped by date
A screenshot of the listening stats page, showing listened tracks grouped by release years and listening hours
A screenshot of the listening stats page, showing listened tracks grouped by release years and listening hours
  • Bulk downloads: select tracks/collections and get an async-built ZIP archive.
  • Server-side outputs β€” Spotify-Connect-style. The browser is just a remote control; audio is rendered by the server via a dedicated stream worker. Three providers: an Icecast relay (with live ICY metadata updates), a Snapcast provider for whole-home audio, and native HTTP mountpoints (/streams/<mount>) served directly by Songhive with per-listener cursors over Redis Streams β€” no external radio server required. Playback keeps playing after you close the tab, and multiple tabs/devices stay in sync.
  • Payments support out of the box β€” set up a Stripe account and connect it to the instance, and configure it as a marketplace account if you also want to allow musicians to wire their payout accounts and sell music through your platform.

A screenshot of a track uploaded by a musician, showing the options to set up payments for it A screenshot of the membership page on an instance with paid subscriptions

The social layer

This is where Songhive diverges from every other self-hosted media server. Federation is full ActivityPub (via the pubby library), and music entities are social objects:

  • Every user is an ActivityPub actor, reachable at @user@instance via WebFinger, with Mastodon-style alias URLs. Mastodon users can follow, mention and interact with you from their own client.
  • Tracks are Audio objects. Publishing is explicit and opt-in per upload β€” your private library never leaks to the Fediverse by accident. Published tracks render as playable audio in Mastodon and Akkoma timelines, with descriptions, hashtags and link-backs to the track page.
  • Mastodon-style statuses with Markdown, mentions, hashtags, threaded replies, quotes and link preview cards. Reply to a track, boost an album drop, quote-post a playlist, or simply post some words to Mastodon and use it like a normal Mastodon account.
  • Funkwhale-compatible music federation: full bidirectional music federation with Funkwhale and other Songhive instances β€” remote libraries, albums, artists and tracks resolve as browsable resources you can follow and add to your collection.
  • Remote discovery: paste a Fediverse handle or URL into the search box and it's dereferenced, cached and browsable β€” SSRF-guarded and domain-moderated.
  • Real notifications over WebSocket (in-app, real-time), with per-type email and daily-digest preferences, plus optional OS-native push via VAPID WebPush.
  • Mastodon-grade moderation: users mute/block local and remote actors; admins limit/suspend actors, defederate or restrict instances, and review reports β€” optionally forwarding them to the reported actor's home instance via ActivityPub Flag.
  • RSS/Atom everywhere: every profile, artist, playlist, library, tag and genre has RSS 2.0 and Atom feeds with <link rel="alternate"> discovery, so even non-fediverse readers can subscribe.
A Songhive post with a published song viewed from Mastodon
A Songhive post with a published song viewed from Mastodon

Sharing and privacy

  • Three-level visibility on tracks, albums, playlists and libraries: private, local (instance-only), or public.
  • Share grants (give a specific user access) and revocable share links β€” a short URL that renders a preview page with an audio player for anyone, content-negotiated so API clients get JSON and crawlers get OpenGraph metadata.
  • Embeddables: public tracks and collections embed on any web page via <audio> tags, Markdown links, a <script> widget or a no-JS iframe. Media endpoints send Access-Control-Allow-Origin, so fediverse clients can embed your audio too.
  • Webmentions: bidirectional link-backs for public content. A blog that links to your song sends you a notification; content you share or comment on sends a Webmention back to the source. All outbound/inbound fetches on this path are SSRF-guarded.
Screenshot of the "Share Track" modal
Screenshot of the "Share Track" modal

The platform bits

  • Multi-user with open, invite-only, approval-required or closed registration; per-user profile visibility.
  • OAuth2 provider for third-party apps, plus API tokens for scripts and Subsonic clients.
  • Flexible storage: local filesystem, S3-compatible object storage.
  • External libraries β€” the feature that sells it to people with existing collections. Attach external music storage to a Songhive library: local folders (with a filesystem watchdog that picks up changes automatically), S3, SFTP, WebDAV, Dropbox, Google Drive, or plain HTTP. Entity-backed providers go further: Jellyfin and TIDAL import tracks, albums, artists and playlists as first-class library entries with lazily-fetched contents β€” audio bytes stay remote and are proxied at play time, so provider credentials never reach the client. Metadata can be written back to the provider, and Dropbox/TIDAL connect via OAuth straight from the settings form.
  • Metadata write-back: enriched tags are written into the audio files themselves, using audio-only content hashing β€” so re-tagging or swapping cover art never changes the stored path or invalidates the content hash. Since it support metadata write-back to the original file, you can also wire your existing music library on an old hard drive and use Songhive to fix broken audio metadata tags.

How it compares

Songhive Funkwhale Jellyfin/Navidrome Mastodon Spotify Bandcamp
Self-hosted βœ… βœ… βœ… βœ… ❌ ❌
Streaming + transcoding βœ… βœ… βœ… ❌ βœ… βœ…
Subsonic API (mobile apps) βœ… βœ… βœ… ❌ ❌ ❌
Scrobbling + listening stats βœ… partial partial ❌ βœ… ❌
ActivityPub social (posts, replies, boosts) βœ… ❌ ❌ βœ… ❌ ❌
Federated music objects playable in Mastodon βœ… partial (library-oriented) ❌ ❌ ❌ ❌
Fine-grained sharing (grants, links, embeds) βœ… partial ❌ ❌ ❌ partial
Server-side outputs (Icecast/Snapcast) βœ… ❌ ❌ ❌ (Spotify Connect, proprietary) ❌
External library import (S3, WebDAV, Jellyfin, TIDAL…) βœ… partial ❌ ❌ ❌ ❌
Support for payments to artists βœ… ❌ ❌ ❌ βœ… βœ…
Support for subscriptions to fund platform maintainers βœ… ❌ ❌ ❌ βœ… βœ…
You own the catalog βœ… βœ… βœ… βœ… ❌ you can download the content you pay for

Quickstart

The fastest path is the Docker bootstrap script, which drops a docker-compose.yml, an nginx config, and starter config.toml/.env files into the current directory:

curl -fsSL https://git.fabiomanganiello.com/songhive/raw/branch/main/docker/bootstrap.sh | sh

Set your user's UID/GID in .env (so the bind-mounted volumes stay yours), fill in the few required values in config.toml:

[auth]
secret_key = "..."  # python -c "import secrets; print(secrets.token_urlsafe(64))"

[storage]
local_path = "./data/media"

[federation]
enabled = false
# instance_domain = "music.example.com"  # uncomment to join the fediverse

then:

docker compose up
# Or, if you want to enable local HTTP stream mount points too
docker compose up --profile streams

Create the admin user:

docker compose exec songhive songhive admin create-user \
    --username admin --email admin@example.com --password secret --admin

Open http://localhost:8000/ and start uploading. The web UI, Swagger UI (/swagger-ui/) and OpenAPI spec (/openapi.json) are all served by the same process. There's also a pip install songhive path (the PyPI package ships the built web UI), systemd units under config/systemd/, and an install.sh that wires the whole thing up β€” see the README for those.

Everything else β€” federation, Webmentions, RSS feeds, scrobbling keys, registration mode, bitrate caps β€” is a config knob, and most of it is enabled by default. All config.toml entries can also be overridden with SONGHIVE_* environment variables.

Under the hood

  • Backend: FastAPI (REST) mounted inside Tornado via a2wsgi β€” Tornado natively handles WebSockets, audio streaming with range requests, and HTTP stream mountpoints, while FastAPI handles everything else. (There's a non-obvious reason for the explicit thread pool in the WSGI bridge: without it, a federated fetch that resolves back to your own instance deadlocks the event loop. The architecture doc tells the whole war story.)
  • Data: PostgreSQL (SQLite works for small setups) + SQLAlchemy 2 async, Pydantic v2 everywhere, Alembic migrations.
  • Async work: Celery + Redis with three queues β€” celery for interactive work, scrobbles for latency-sensitive submissions, and bulk for the per-item fan-out jobs (a fresh external library sync can enqueue tens of thousands of enrichment tasks; without the split they'd starve scrobbles and federation delivery for hours).
  • Storage: content-addressable files (SHA-256) with audio-only hashing, so tag/cover-art rewrites don't move files or invalidate hashes.
  • Frontend: Vue 3 + TypeScript + Pinia, fully i18n'd with RTL support.
  • Security posture: JWT + opaque rotating refresh tokens, double-submit CSRF, Redis sliding-window rate limiting, SSRF-guarded federation and Webmention fetches with redirect revalidation, Fernet-encrypted provider secrets, RFC 7807 error handling.
  • License: AGPL-3.0.

Caveats, honestly

  • It's a real stack: Postgres, Redis, a Celery worker, ffmpeg. The Docker compose file makes this a non-issue, but it's not a single static binary. It can run on a RaspberryPi 4 or 5 with decent storage if you want to run it for yourself and your family, but if you want to have more tracks and more users you may need a beefier machine.
  • Federation with the big platforms is only as good as ActivityPub support for audio β€” Mastodon renders playable audio attachments, but it treats Audio as a converted type, so Songhive also emits Note variants when you share a track as a post. The interop details with Akkoma, Funkwhale and friends are handled, but the fediverse music landscape is still young.
  • TIDAL imports federate metadata-only (a browse link, not hosted audio) β€” for obvious reasons the audio bytes stay with the provider.

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.
⭐ 12 πŸ” 9 πŸ“£ 1 πŸ”— 8 ✍️ 1

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.

The Yonder home timeline
The Yonder home timeline

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.
A quote post rendered in a thread
A quote post rendered in a thread

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-sdk crate.
  • 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-required or closed.

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.json with 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.
The accountimport/export tab in Yonder, showing custom import options over API crawlers
The accountimport/export tab in Yonder, showing custom import options over API crawlers

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.

Account migration page
Account migration page

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 /followers endpoint, 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 /context endpoint will trigger /fetch_remote_context behind 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

This is something that for some reason Mastodon removed at some point, but that I find very useful.

Screenshot of Yonder's hashtag page, showing filters and temporal selection
Screenshot of Yonder's hashtag page, showing filters and temporal selection

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.

Screenshot of Yonder's direct messages page, grouped by conversation
Screenshot of Yonder's direct messages page, grouped by conversation

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.blog that supports Webmentions posts a link to example.social/status/123, example.social will 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.blog in a post on example.social, and example.blog supports 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.

A Yonder post with inline LaTeX formulas
A Yonder post with inline LaTeX formulas

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.

A Yonder post with inline LaTeX formulas shown in the Mastodon UI, with LaTeX formula falling back to media attachments, and inline links to the images
A Yonder post with inline LaTeX formulas shown in the Mastodon UI, with LaTeX formula falling back to media attachments, and inline links to the images

Inline images

Yonder supports inline images via standard Markdown:

![This is an inline image](https://example.com/image.png)
Screenshot of Yonder's hashtag page, showing filters and temporal selection
Screenshot of Yonder's hashtag page, showing filters and temporal selection

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

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

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

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

⭐ 3 πŸ” 2
Fabio Manganiello Author

Support for live stream capture has now been added to #songhive.

Screenshot of a live stream card, with the microphone for live recording shown at the top
Screenshot of a live stream card, with the microphone for live recording shown at the top

You can stream live recordings from your sound devices directly to an HTTP mount point, or to Icecast.

Simply click the microphone icon on top of the stream card, and you're ready to recover directly from your computer and stream to the Web.

Perfect for:

  • Live music shows. Share a link that anyone can join, plug your instruments and play.
  • Live podcasts.
  • Live radio. Play your favourite songs to the stream, and talk to your listeners between tracks.

You can find my streams (and those of @nemo@mas.to) on manganiello.music/streams.

⭐ 3 πŸ” 4
Matthew Skelton ⭐
Nada ⭐
tootbrute πŸ”
tootbrute ⭐
Harito πŸ”
David Culley ⭐
xyhhx :PunkFelix: ⭐
Aslak Raanes ⭐
Jens FinkhΓ€user πŸ”
Jens FinkhÀuser ⭐
Fou πŸ”
DHeadshot's Alt ⭐
Fabio Manganiello