-
Eliot Berriot authoredVerifieddd23dfa0
Funkwhale Federation
This documentation section is more technical, and targets people who want to understand how Funkwhale's federation works.
Technologies and standards
Funkwhale's federation is built on top of the following technologies:
- ActivityPub as the high-level federation protocol
- HTTP Signatures as the primary mean to authenticate messages
- Webfinger to easily retrive resources using human-friendly names
- ActivityStreams and ActivityStreams vocabulary as the mean to structure messages
Support for the following is planned but not implemented-yet:
- JSON-LD signatures as an alternate mean to authenticate messages
Philosophy
Our goal is to stick to the specifications as much as possible, to ensure compatibility with existing applications such as Mastodon, Peertube, Plume, Pleroma or PixelFed.
However, this is not always possible for all our use cases. The ActivityPub and ActivityStreams specifications are really high-level and do not always fit our use cases. For such cases, we will use an ad-hoc solution, and document it here.
There are plenty of projects built using ActivityPub, and our goal is not to support all the existing activities. Instead, we want to support activities and objects that make sense for Funkwhale use cases, such as follows or likes.
If you think this document is not accurate or find evidence that Funkwhale is not behaving according to the behaviour documented here, please file a bug on our issue tracker, as we consider this a bug.
Internal logic
This section is relevant if you're interested in how we handle things internally in our application code.
Database schema
As much as possible, we try to map our internal model and database schema to ActivityPub entities, as this makes things easier to deal with.
We store received activities payload directly in the database before we attempt to process or deliver them. Storing the activities unlock some interesting use cases, such as debugging federation issues, replaying deliveries, or reprocess historical activities that were not supported before.
Each local user is bound to an Actor
. Remote and local actors share the same
database table and all federated entities (such as uploads) are linked to an Actor
and not to a user. This means that, internally, in general, there is no distinction between
local and remote users.
Links:
Activity creation and delivery
When a local actor is making an action that should trigger an Activity
, which roughly is equivalent
to posting an activity to an outbox, we create an object, with the proper payload and store it in our
Activity
table. We then trigger two kind of deliveries:
- A delivery to local recipients: for each local recipient, we create an
InboxItem
, linked to the activity. A local actor's feed is then made of all the available inbox items, which can also have a read/unread status - A delivery to remote recipients: we collect all inboxes and shared inbox urls from remote recipients,
and create a
Delivery
object in our database, linked to the initial activity and the inbox or shared inbox url. ThisDelivery
object is then used by our worker to post the activity content to the url.
Receiving an activity from a remote actor in a local inbox is basically the same, but we skip step 2.
Funkwhale does not support all activities, and we have a basic routing logic to handle specific activities, and discard unsupported ones. Unsupported activities are still received and stored though.
If a delivered activity matches one of our routes, a dedicated handler is called,
which can trigger additionnal logic. For instance, if we receive a :ref:`activity-create` activity
for an :ref:`object-audio` object, our handler will persist the proper data in our local Upload
table, retrieve the audio cover, etc.
Links:
Supported activities
Follow
Supported on
- :ref:`object-Library` objects
Example of library follow
{
"@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"
}
In this example, Alice is following the :ref:`object-library` described in object
, which is
owned by Bob.
Internal logic
When a follow is received on a :ref:`object-Library`, Funkwhale will behave differently depending on the visibility of the library:
- Automatic accept, when the library is public: a notification is sent to the library owner, and an :ref:`activity-accept` is sent automatically to the follow actor.
- Manual accept, in all other cases: a notification is sent to the library owner. After manual approval from the owner, an :ref:`activity-accept` is sent to the follow actor.
Funkwhale uses library follow status to grant access to the follow actor. If a library is not public and an actor does not have an approved follow, library content will be inaccessible to the actor.
Checks
Before handling the activity, Funkwhale will ensure the library's owner is the activity recipient.
Accept
Supported on
- :ref:`activity-follow` objects
Example
{
"@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",
},
}