Federation & Decentralization in Tunecamp β
Tunecamp leverages two primary technologies to enable a decentralized music ecosystem: ActivityPub for social federation and federated HTTP/NodeInfo discovery for finding other instances. It also provides full Subsonic API compatibility for mobile and desktop clients.
Federated Discovery (HTTP / NodeInfo gossip) β
Tunecamp discovers other instances by gossip over HTTP β there is no central relay and no shared registry. An instance crawls outward from a set of seed instances (its ActivityPub-followed TuneCamp sites plus TUNECAMP_FEDERATION_SEEDS), validates that each peer is a live TuneCamp via its NodeInfo, and stores the reachable set in local SQLite (federated_instances). Implemented in src/server/modules/network/federated-discovery.service.ts.
History: earlier versions used the Zen decentralized graph for instance signaling, and before that for user identity (SEA keypairs), Zen-first auth, wallet derivation, and cross-instance roaming. Zen has been removed entirely (PRs #369/#370/#372): authentication is username/password (JWT), discovery is the HTTP gossip described here, and catalogs are exchanged directly over HTTP. The
zendependency and allTUNECAMP_ZEN_*env vars are gone.
Key Roles β
- Seed-based crawl: discovery starts from AP-followed TuneCamp actors and
TUNECAMP_FEDERATION_SEEDS, then gossips outward (capped depth/breadth, dead instances pruned). - Liveness check: a peer is only added if it answers as a live TuneCamp (NodeInfo type-check), not merely HTTP 200.
- Music Discovery: the "Network" page reads the federated set, then fetches catalogs directly via HTTP (
/api/catalog), cached stale-while-revalidate. - Peer-track federation (opt-in): when an instance enables Federate Peer Tracks, its currently-shared peer tracks ride along in
/api/catalog/fulland appear on remote Network pages (taggedPEER), streamable β and, if downloads are allowed, importable β cross-instance. These entries are ephemeral and use a short cache window so they vanish quickly when the peer disconnects. See Peer Sharing. - One-click follow: an admin can follow an instance directly by URL.
Public endpoints β
GET /api/community/instanceβ this instance's own descriptor.GET /api/community/peersβ known peers (the gossip surface).GET /api/community/sitesβ aggregated discoverable sites (local + federated + ActivityPub). CORS-enabled for external directories.POST /api/community/registerβ self-registration: submit your instance URL to a directory instance and appear immediately, without waiting for the next 6-hour crawl. See below.
Self-registering with a directory β
Instead of waiting up to 6 hours for gossip to reach a directory instance, admins can self-register:
POST /api/community/register
Content-Type: application/json
{ "url": "https://your-instance.example.com" }The directory instance will:
- Probe your URL via NodeInfo (
/.well-known/nodeinfo) to verify it is a reachable TuneCamp instance. - Store the metadata immediately β your instance appears in
GET /api/community/sitesright away.
Responses:
| Status | Meaning |
|---|---|
200 OK | Registered. Appears in /api/community/sites immediately. |
400 | Missing or invalid url field. |
422 | URL is not a reachable TuneCamp instance (NodeInfo check failed). |
429 | Rate limited β 1 registration per IP per hour. Retry-After header included. |
The endpoint is public (no auth required) and CORS-enabled, so the community website can call it directly from the browser. The rate limit is enforced per IP to prevent abuse.
Configuration β
TUNECAMP_FEDERATION_SEEDS(backend): comma/space-separated origin URLs of seed instances to bootstrap discovery. Optional β AP-followed TuneCamp sites also seed the crawl.
ActivityPub: Fediverse Integration β
ActivityPub allows Tunecamp to communicate with other platforms like Mastodon, Pleroma, Funkwhale, and Lemmy.
Key Roles (Broadcaster Model) β
- Artist Profiles: Every artist on Tunecamp is an ActivityPub "Person" actor.
- Outbound Broadcasting: Tunecamp focuses on a "Broadcaster" model. When an artist publishes a new release or a post, Tunecamp broadcasts a "Create Note" activity to all followers across the Fediverse.
- Inbound Engagement: Users on other Fediverse instances can follow Tunecamp artists and like/favorite/comment on their releases and posts. Replies and comments are federated back to Tunecamp.
- Interoperability: Tunecamp supports WebFinger and standard ActivityPub inboxes/outboxes. Note: Tunecamp no longer maintains an internal, Mastodon-style consumption timeline or client-side following mechanics to prioritize lightweight performance.
Implementation Details β
- Keys: RSA 4096-bit keypairs are automatically generated for every artist.
- Attachments: Broadcasts include "Audio" attachments (direct stream links) and "Image" attachments (cover art).
- Public URL: Federation requires
TUNECAMP_PUBLIC_URLto be correctly configured withhttps.
Configuration β
TUNECAMP_PUBLIC_URL: Required for Federation.- ActivityPub relay (optional): To broadcast beyond direct followers, set the relay URL at runtime in the admin panel (stored as the
relayUrlsetting). This is not an environment variable.
Troubleshooting: "Public key not found" on delivery β
Remote servers (e.g. Mastodon) cache an actor's public key the first time they see it and keep verifying signatures against that cached copy. If an actor's key later changes β or if the same /users/{handle} URL previously served a different identity (for example a listener user actor before the account became an artist) β the remote keeps the stale key and rejects every activity with 401 {"error":"Public key not found for key .../users/{handle}#main-key"}.
Fix it from Admin β Identity β the artist card β "Refresh federation" (POST /api/admin/artists/:id/refresh-identity). This ensures the artist has a valid RSA key pair and broadcasts a signed Update(Person) to followers and the relay, forcing remotes to re-fetch the actor document and replace the cached key. The action is idempotent and safe to re-run.
RSS / Atom Feeds β
Beyond ActivityPub, Tunecamp can follow plain RSS/Atom feeds (podcasts, Owncast streams, blogs) so their items show up alongside federated content.
- Follow a feed:
POST /api/admin/network/rss/followwith the feed URL. - Storage: a followed feed is stored as a remote actor with
type = 'rss'; each item becomes aremote_contentrow. - Refresh: feeds are refreshed by the RSS service (
src/server/modules/network/rss.service.ts), independently of the ActivityPub outbox fetcher.
Funkwhale Compatibility β
Tunecamp is compatible with Funkwhale instances for music-specific federation.
How It Works β
- NodeInfo: Tunecamp exposes metadata at
/.well-known/nodeinfoincluding Funkwhale-compatible fields (library.federationEnabled,supportedUploadExtensions,funkwhaleVersion). - Federation Libraries:
GET /api/v1/federation/librariesreturns Tunecamp's music catalog in Funkwhale's expected format. - NodeInfo 2.0 API:
GET /api/v1/instance/nodeinfo/2.0provides instance metadata for Funkwhale-style discovery. - Actor Types: Artists are exposed as
["Person", "Artist", "MusicArtist"]with Funkwhale namespace extensions. - Audio Attachments: Release broadcasts include
Audioobjects withfunkwhale:bitrateandfunkwhale:durationproperties.
Subsonic API: Client Compatibility β
Tunecamp exposes a full Subsonic REST API at /rest (API version 1.16.1), enabling connection from any Subsonic-compatible client.
Authentication Methods β
| Method | Format | Description |
|---|---|---|
| Clear-text | p=password | Plain password in query |
| Hex-encoded | p=enc:hex | Password hex-encoded |
| Token+Salt | t=md5(password+salt)&s=salt | Secure token-based auth |
Scrobbling & Stats β
When a Subsonic client scrobbles a track (scrobble.view), Tunecamp records the play in the local SQLite database (play_history table). All scrobbling and playback statistics are stored locally on the server.
Architecture Summary β
| Feature | Technology | Scope |
|---|---|---|
| Artist Following | ActivityPub | External (Mastodon, etc) |
| Likes / Favorites | ActivityPub | External (Mastodon, etc) |
| Release Notification | ActivityPub | External (Mastodon, etc) |
| Funkwhale Federation | ActivityPub | External (Funkwhale) |
| Instance Discovery | Federated HTTP / NodeInfo gossip | Internal (Tunecamp Nodes) |
| Mobile Streaming | Subsonic API | External (Any client) |
| Starred / Favorites | Subsonic API | Local (per user) |