GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing docs.funkwhale.audio — Funkwhale federation — funkwhale 2.0.13 documentation Funkwhale federationNote We aim to keep this document up-to-date to reflect the current state of federation. If you notice an issue, please open a thread in our gitlab. Funkwhale is a federated platform. Funkwhale pods can share information between one another, and can also communicate with other Fediverse software. This article outlines which tools we use, our approach to federation, and how we implement standards. How to use federation in the user interfaceIf you have the fediverse handle of a user (’xx@xx.xx’), displayed in their profile, you can paste this address in the global search field. Funkwhale will then clone the profile onto your instance and you will be able to see their profile locally and follow their activities. The same works for rss feeds that are outside of the fediverse. Just enter the RSS feed URI into the search field and you can add it as a podcast or music channel. Technologies and standardsFunkwhale’s federation is built on top of the following technologies:
We plan for support Our philosophyFunkwhale aims to align with the above specifications where possible to maintain compatibility with other ActivityPub-aware platforms. This documentation covers where we have departed from the specifications. While we will always aim for compatibility with other services where it makes sense, Funkwhale to Funkwhale interaction is our focus. We use these standards to support activities and objects that fit in with our use cases, such as follows and likes. Internal logicThis section covers how we handle federation within the Funkwhale ecosystem. Database schemaWe use ActivityPub entities as a guide when creating our models and database schemas. This ensures compatibility with other ActivityPub projects. Funkwhale pods store received activities payloads in the database before attempting to process or deliver them. This enables us to debug federation issues, resend messages, or process historical activities that weren’t previously supported. Funkwhale users are associated to an Activity creation and deliveryActions carried out by a local actor can trigger an
When a local inbox receives an activity from a remote actor, it ends up in their inbox for them to handle. Funkwhale doesn’t support all activities. Our routing logic enables the software to handle supported activities and discard unsupported ones. When Funkwhale receives an activity it checks if there is a route to handle it. If there is, Funkwhale calls a dedicated handler. For example: if Funkwhale receives an
You can find the code for our routing logic here: Service actorFunkwhale uses a dedicated service actor to send messages or authenticate fetches. This actor isn’t associated to a user. You can query a pod’s nodeinfo endpoint to return the ID of the service actor in the Funkwhale considers a pod’s service actor to be an authoritative source for activities associated with all objects on its pod’s domain. If the service actor sends an activity linked to an object on its domain, remote pods will recognize its authority. Audio fetching on restricted libraries
Library
Supported activitiesFollowA follow enables actors to access and retrieve content from other actors as soon as it updates. Supported onInternal logicWhen Funkwhale receives a follow on a library object, it performs one of the following actions depending on the library’s visibility:
Funkwhale uses the library follow status to grant access to the actor who sent the follow request. If the library isn’t public and the owner doesn’t send an approval, the requesting actor can’t access the library’s content. For User’s actors the logic is the same but we use the privacy_level of the user instead of the public to automaically accept the Follow. ChecksFunkwhale ensures the activity is being sent to the library’s owner before handling it. ExampleIn this example, Alice sends a follow activity for a library object owned by Bob. { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {} ], "type": "Follow", "id": "https://music.rocks/federation/actors/Alice#follows/99fc40d7-9bc8-4c4a-add1-f637339e1ded", "actor": "https://music.rocks/federation/actors/Alice", "to": ["https://awesome.music/federation/actors/Bob"], "object": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6" } Supported ActivitiesActivities sent to followings users (or to the world if the account is set to public) are : AcceptThe Supported on
Internal logicWhen Funkwhale receives an ChecksFunkwhale ensures the activity is sent by the library’s owner before handling it. ExampleIn this example, Bob accepts a follow request from Alice. { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {} ], "type": "Accept", "id": "https://music.rocks/federation/actors/Alice#follows/99fc40d7-9bc8-4c4a-add1-f637339e1ded/accept", "to": ["https://music.rocks/federation/actors/Alice"], "actor": "https://awesome.music/federation/actors/Bob", "object": { "id": "https://music.rocks/federation/actors/Alice#follows/99fc40d7-9bc8-4c4a-add1-f637339e1ded", "type": "Follow", "actor": "https://music.rocks/federation/actors/Alice", "object": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6", }, } UndoSupported onInternal logicWhen Funkwhale receives an ChecksFunkwhale ensures the request actor is the same actor who sent the ExampleIn this example, Alice notifies Bob that she’s undoing her follow. { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {} ], "type": "Undo", "id": "https://music.rocks/federation/actors/Alice#follows/99fc40d7-9bc8-4c4a-add1-f637339e1ded/accept", "to": ["https://awesome.music/federation/actors/Bob"], "actor": "https://music.rocks/federation/actors/Alice", "object": { "id": "https://music.rocks/federation/actors/Alice#follows/99fc40d7-9bc8-4c4a-add1-f637339e1ded", "type": "Follow", "actor": "https://music.rocks/federation/actors/Alice", "object": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6", }, } CreateSupported onInternal logicNote See the When Funkwhale receives a ChecksFunkwhale ensures the activity actor and library owner are the same before handling the activity. If the associated library has no local followers, Funkwhale discards the activity. ExampleIn this example, Bob creates new content in his library and sends a message to its followers. { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {} ], "to": [ "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6/followers" ], "type": "Create", "actor": "https://awesome.music/federation/actors/Bob", "object": {} } UpdateSupported onInternal logicNote See the When Funkwhale receives an update associated with a ChecksFunkwhale performs different checks depending on the target of the update:
ExampleIn this example, Bob updates his library and sends a message to its followers. { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {} ], "to": [ "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6/followers" ], "type": "Update", "actor": "https://awesome.music/federation/actors/Bob", "object": {} } DeleteSupported onInternal logicWhen Funkwhale receives a ChecksFunkwhale ensures the actor initiating the activity is the owner of the associated object before handling it. Example
Library
In this example, Bob deletes a library and notifies its followers. { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {} ], "type": "Delete", "to": [ "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6/followers" ], "actor": "https://awesome.music/federation/actors/Bob", "object": { "type": "Library", "id": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6" } } In this example, Bob deletes three audio objects in a library and notifies the library’s followers. { "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", {} ], "type": "Delete", "to": [ "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6/followers" ], "actor": "https://awesome.music/federation/actors/Bob", "object": { "type": "Audio", "id": [ "https://awesome.music/federation/music/uploads/19420073-3572-48a9-8c6c-b385ee1b7905", "https://awesome.music/federation/music/uploads/11d99680-23c6-4f72-997a-073b980ab204", "https://awesome.music/federation/music/uploads/1efadc1c-a704-4b8a-a71a-b288b1d1f423" ] } } LikeWe send the Like object with its type : this allow proper routing and avoid useless database and network queries. Supported onExample{ "type": "Like", "id": "https://burn.patriachy//7b54d361-c513-4756-a085-13f97573237b", "object": { "Type": "Track", "id": "https://Le_Rn.areRacists//aebd2be4-49a1-4ef5-aadf-27bff1001d4d", }, "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "Hashtag": "as:Hashtag", }, ], } DislikeWe send the Dislike object with its type : this allow proper routing and avoid useless database and network queries. Supported onExample{ "type": "Dislike", "id": "https://burn.patriachy//7b54d361-c513-4756-a085-13f97573237b", "object": { "Type": "Track", "id": "https://Le_Rn.areRacists//aebd2be4-49a1-4ef5-aadf-27bff1001d4d", }, "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "Hashtag": "as:Hashtag", }, ], } ListenWe send the Listen object with its type : this allow proper routing and avoid useless database and network queries. Supported onExample{ "type": "Listen", "id": "https://makerichpeoplepay.forclimatechange//5355f8b7-bbcc-4285-9c13-d21748084cc1", "object": { "Type": "Track", "id": "https://Le_Rn.areRacists//aebd2be4-49a1-4ef5-aadf-27bff1001d4d", }, "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "Hashtag": "as:Hashtag", }, ], } BlockWe send the Block to allow remote server blocking content from the activity actor to the activity object (the blocked target). Example{ "to": ["https://node2.funkwhale.test/federation/actors/node2"], "type": "Block", "actor": "https://node1.funkwhale.test/federation/actors/node1", "object": { "id": "https://node2.funkwhale.test/federation/actors/node2", "url": [ { "href": "https://node2.funkwhale.test/@node2", "type": "Link", "mediaType": "text/html", } ], "icon": "None", "name": "node2", "type": "Person", "inbox": "https://node2.funkwhale.test/federation/actors/node2/inbox", "outbox": "https://node2.funkwhale.test/federation/actors/node2/outbox", "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "Hashtag": "as:Hashtag", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", }, ], }, "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "Hashtag": "as:Hashtag", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", }, ], } UnblockWe have a custom Example{ "to": ["https://node2.funkwhale.test/federation/actors/node2"], "type": "Unblock", "actor": "https://node1.funkwhale.test/federation/actors/node1", "object": { "id": "https://node2.funkwhale.test/federation/actors/node2", "url": [ { "href": "https://node2.funkwhale.test/@node2", "type": "Link", "mediaType": "text/html", } ], "icon": "None", "name": "node2", "type": "Person", "inbox": "https://node2.funkwhale.test/federation/actors/node2/inbox", "outbox": "https://node2.funkwhale.test/federation/actors/node2/outbox", "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "Hashtag": "as:Hashtag", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", }, ], }, "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "Hashtag": "as:Hashtag", "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", }, ], } Supported objectsArtistAn Properties
Example{ "type": "Artist", "id": "https://awesome.music/federation/music/artists/73c32807-a199-4682-8068-e967f734a320", "name": "Krav Boca", "published": "2018-04-08T12:19:05.920415+00:00", "musicbrainzId": "65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab" } ArtistCreditAn Properties
Example{ "type": "ArtistCredit", "id": "https://test.federation/federation/music/artistcredit/6dc0071c-0186-4f27-a234-fa5858774400", "artist": { "type": "Artist", "id": "https://white.info/6dc0071c-0186-4f27-a234-fa5858774400", "name": "Krav Boca", "published": "2024-12-22T21:54:46.391743+00:00", "musicbrainzId": "6dc0071c-0186-4f27-a234-fa5858774400", "attributedTo": null, "tag": [], "image": null }, "joinphrase": "feat. ", "credit": "Crav Boka", "index": null, "published": "2024-12-22T21:54:46.392309+00:00" } AlbumAn Properties
Example{ "type": "Album", "id": "https://awesome.music/federation/music/albums/69d488b5-fdf6-4803-b47c-9bb7098ea57e", "name": "Ride the Lightning", "released": "1984-01-01", "published": "2018-10-02T19:49:17.412546+00:00", "musicbrainzId": "589ff96d-0be8-3f82-bdd2-299592e51b40", "cover": { "href": "https://awesome.music/media/albums/covers/2018/10/02/b69d398b5-fdf6-4803-b47c-9bb7098ea57e.jpg", "type": "Link", "mediaType": "image/jpeg" }, "artist_credit": [ {} ] } TrackA Properties
Example{ "type": "Track", "id": "https://awesome.music/federation/music/tracks/82ece296-6397-4e26-be90-bac5f9990240", "name": "Shock! Extinction de masse", "position": 3, "published": "2018-10-02T19:49:35.822537+00:00", "musicbrainzId": "771ab043-8821-44f9-b8e0-2733c3126c6d", "artist_credit": [ {} ], "album": {} } LibraryNote Crawling library pages requires authentication and an approved follow unless the library is public. Auth from the service actor from a pod with an accepted followers is also accepted. A Properties
Example{ "type": "Library", "id": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6", "attributedTo": "https://awesome.music/federation/actors/Alice", "name": "My awesome library", "followers": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6/followers", "summary": "This library is for restricted use only", "totalItems": 4234, "first": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6?page=1", "last": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6?page=56", } AudioNote Accessing audio files requires authentication (unless the library is public) and either :
An Properties
Example{ "type": "Audio", "id": "https://awesome.music/federation/music/uploads/88f0bc20-d7fd-461d-a641-dd9ac485e096", "name": "Krav Boca - Mortem", "size": 8656581, "bitrate": 320000, "duration": "PT1312S", "library": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6", "updated": "2018-10-02T19:49:35.646372+00:00", "published": "2018-10-02T19:49:35.646359+00:00", "track": {}, "url": { "href": "https://awesome.music/api/v2/listen/82ece296-6397-4e26-be90-bac5f9990240/?upload=88f0bc20-d7fd-461d-a641-dd9ac485e096", "type": "Link", "mediaType": "audio/mpeg" } } AudioCollectionAn Properties
Example{ "type": "AudioCollection", "actor": "https://reid-little.com/users/kathrynjohns177", "audience": "everyone", "totalItems": 1, "items": [ { "type": "Audio", "id": "https://test.federation//3ea581cf-0d3d-496f-b594-fac8472f4208", "library": "https://test.federation/federation/music/libraries/0deaa9e8-eaa0-4601-ba4e-e1d999102b50", "name": "Patrick Huynh - Trouble late. - Notice media a.", "published": "2025-10-06T21:21:40.215654+00:00", "bitrate": 320, "size": 320, "duration": "PT5M20S", // truncated } ], "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "Hashtag": "as:Hashtag", }, ], } PlaylistPlaylists objects are a custom ordered collectionOrdered Collection containing Example{ "id": "https://node1.funkwhale.test/federation/music/playlists/c1c36f15-f49e-4da6-abd4-17b9b438c348", "attributedTo": "https://node1.funkwhale.test/federation/actors/node1", "totalItems": 0, "type": "Playlist", "current": "https://node1.funkwhale.test/federation/music/playlists/c1c36f15-f49e-4da6-abd4-17b9b438c348?page=1", "first": "https://node1.funkwhale.test/federation/music/playlists/c1c36f15-f49e-4da6-abd4-17b9b438c348?page=1", "last": "https://node1.funkwhale.test/federation/music/playlists/c1c36f15-f49e-4da6-abd4-17b9b438c348?page=1", "name": "zef", "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "Hashtag": "as:Hashtag", }, ], } PlaylistTrackAn element of a playlist. Example{ "type": "PlaylistTrack", "id": "https://test.federation/federation/music/playlists/2861fc4a-f3b6-4740-8586-c4573140b994", "track": "https://simon.biz//34d56bbd-5096-4ac7-ada9-2d11ea731317", "index": 0, "attributedTo": "https://wallace-salazar.com/users/ryanrachel953", "published": "2024-12-04T11:50:16.625013+00:00", "playlist": "https://test.federation/federation/music/playlists/1efba9b2-8218-4ac2-bdce-f9dd8bbd510c", }, Custom propertiesattributedToFunkwhale uses the Funkwhale also attributes all objects on a domain with the domain’s Service actor Scapping CollectionsThe The PlaylisTracks will be sent in a CollectionPage if a page is specified in the playlist url : { "id": "https://test.federation/federation/music/playlists/1efba9b2-8218-4ac2-bdce-f9dd8bbd510c?page=1", "partOf": "https://test.federation/federation/music/playlists/1efba9b2-8218-4ac2-bdce-f9dd8bbd510c", "totalItems": 5, "type": "CollectionPage", "first": "https://test.federation/federation/music/playlists/1efba9b2-8218-4ac2-bdce-f9dd8bbd510c?page=1", "last": "https://test.federation/federation/music/playlists/1efba9b2-8218-4ac2-bdce-f9dd8bbd510c?page=1", "items": [ { "type": "PlaylistTrack", "id": "https://test.federation/federation/music/playlists/2861fc4a-f3b6-4740-8586-c4573140b994", "track": "https://simon.biz//34d56bbd-5096-4ac7-ada9-2d11ea731317", "index": 0, "attributedTo": "https://wallace-salazar.com/users/ryanrachel953", "published": "2024-12-04T11:50:16.625013+00:00", "playlist": "https://test.federation/federation/music/playlists/1efba9b2-8218-4ac2-bdce-f9dd8bbd510c", }, { "type": "PlaylistTrack", "id": "https://test.federation/federation/music/playlists/96a46881-9544-438a-9e34-b7a1b5ecbc7a", "track": "https://fuller.info//a8977c57-5704-469a-a2ae-fa7b213bb370", "index": 1, "attributedTo": "https://wallace-salazar.com/users/ryanrachel953", "published": "2024-12-04T11:50:16.631200+00:00", "playlist": "https://test.federation/federation/music/playlists/1efba9b2-8218-4ac2-bdce-f9dd8bbd510c", }, ], "attributedTo": "https://wallace-salazar.com/users/ryanrachel953", "@context": [ "https://www.w3.org/ns/activitystreams", "https://w3id.org/security/v1", "https://funkwhale.audio/ns", { "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", "Hashtag": "as:Hashtag", }, ], } | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |