diff --git a/changes/changelog.d/549.documentation b/changes/changelog.d/549.documentation
new file mode 100644
index 0000000000000000000000000000000000000000..a98dc80b2cb8c5d05306344420bb1bfeb0bae86a
--- /dev/null
+++ b/changes/changelog.d/549.documentation
@@ -0,0 +1 @@
+Technical documentation of music federation (#549)
diff --git a/docs/federation.rst b/docs/federation.rst
deleted file mode 100644
index 08658b12e47aec99386e3740e6939c7a76eb5254..0000000000000000000000000000000000000000
--- a/docs/federation.rst
+++ /dev/null
@@ -1,56 +0,0 @@
-Federation
-==========
-
-Each Funkwale instance can federates its music library with other instances
-of the network. This means that an instance A can acquire music from instance B
-and share its own library with an instance C.
-
-We support various levels of controls for federation-related features.
-
-Managing federation
--------------------
-
-Federation management is only available to instance admins and users
-who have the proper permissions. You can disable federation completely
-at the instance level by editing the ``federation__enabled`` :ref:`setting <instance-settings>`.
-
-On the front end, assuming you have the proper permission, you will see
-a "Federation" link in the sidebar.
-
-
-Acquire music via federation
-----------------------------
-
-Instance libraries are protected by default. To access another instance
-library, you have to follow it. Each Funkwhale instance gets a dedicated
-ActivityPub Actor you can follow via the username "library@yourinstance.domain".
-
-When submitted, a follow request will be sent to
-the other instance which can accept or deny it. Once your follow request
-is accepted, you can start browsing the other instance library
-and import music from it.
-
-By default, we do not duplicate audio files from federated tracks, to reduce
-disk usage on your instance. When someone listens to a federated track,
-the audio file is requested on the fly from the remote instance, and
-store in a local cache. It is automatically deleted after a configurable
-amount of time if it was not listened again in the meantime.
-
-If you want to mirror a remote instance collection, including its audio files,
-we offer an option for that.
-
-We also support an "autoimport" mode for each remote library. When enabled,
-any new track published in the remote library will be directly imported
-in your instance.
-
-Share music via federation
---------------------------
-
-Federation is enabled by default, but requires manually approving
-each other instance asking for access to library. This is by design,
-to ensure your library is not shared publicly without your consent.
-
-However, if you're confident about federating publicly without manual approval,
-you can set the ``federation__music_needs_approval`` :ref:`setting <instance-settings>` to false.
-Follow requests will be accepted automatically and followers
-given access to your library without manual intervention.
diff --git a/docs/federation/index.rst b/docs/federation/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..cbc8cd1554f55967a97268874058d7092602a817
--- /dev/null
+++ b/docs/federation/index.rst
@@ -0,0 +1,615 @@
+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 authentify messages
+
+.. _ActivityPub: https://www.w3.org/TR/activitypub/
+.. _HTTP Signatures: https://tools.ietf.org/id/draft-cavage-http-signatures-01.html
+.. _Webfinger: https://tools.ietf.org/html/rfc7033
+.. _JSON-LD signatures: https://w3c-dvcg.github.io/ld-signatures/
+.. _ActivityStreams: https://www.w3.org/TR/activitystreams-core/
+.. _ActivityStreams vocabulary: https://www.w3.org/TR/activitystreams-vocabulary/
+
+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:
+
+- `Federation models <https://code.eliotberriot.com/funkwhale/funkwhale/blob/develop/api/funkwhale_api/federation/models.py>`_
+
+
+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:
+
+1. 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
+2. 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.
+   This ``Delivery`` 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 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 match 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:
+
+- `Routing logic for activities <https://code.eliotberriot.com/funkwhale/funkwhale/blob/develop/api/funkwhale_api/federation/routes.py>`_
+- `Delivery logic for activities <https://code.eliotberriot.com/funkwhale/funkwhale/blob/develop/api/funkwhale_api/federation/tasks.py>`_
+
+
+Supported activities
+--------------------
+
+.. _activity-follow:
+
+Follow
+^^^^^^
+
+Supported on
+************
+
+- :ref:`object-Library` objects
+
+Example of library follow
+*************************
+
+.. code-block:: json
+
+  {
+    "@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.
+- Manuel 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 follows 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 must be
+inaccessible to the actor.
+
+Checks
+******
+
+Before handling the activity, Funkwhale will ensure the library's owner is
+the activity recipient.
+
+.. _activity-accept:
+
+Accept
+^^^^^^
+
+Supported on
+************
+
+- :ref:`activity-follow` objects
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "@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",
+    },
+  }
+
+In this example, Bob accepts Alice's follow.
+
+Internal logic
+**************
+
+When an :ref:`activity-accept` is received with a :ref:`activity-follow` object, the corresponding follow
+is marked as accepted in the database.
+
+For library follows, this means that the actor will receive future
+activities occuring within this library, such as :ref:`activity-create` :ref:`object-audio`,
+:ref:`activity-delete` :ref:`object-audio` or :ref:`activity-delete` :ref:`object-library`
+
+The follow actor will also be able to browse the library pages and download the library's
+audio files. Have a look at :ref:`library-access` for more details.
+
+
+Checks
+******
+
+Before handling the activity, Funkwhale will ensure the accept comes from
+the library's owner.
+
+.. _activity-undo:
+
+Undo
+^^^^
+
+Supported on
+************
+
+- :ref:`activity-follow` objects
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "@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",
+    },
+  }
+
+In this example, Alice is notifying Bob she's undoing her follow.
+
+Internal logic
+**************
+
+When an undo is received, the corresponding follow is deleted from the database.
+
+Checks
+******
+
+Before handling the activity, Funkwhale will ensure the undo actor is the
+follow actor.
+
+.. _activity-create:
+
+Create
+^^^^^^
+
+Supported on
+************
+
+- :ref:`object-audio` objects
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "@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": {}
+  }
+
+.. note::
+
+  Refer to :ref:`object-audio` to see the structure of the ``object`` attribute.
+
+Internal logic
+**************
+
+When a :ref:`activity-create` is received with an :ref:`object-audio` object, Funkwhale will persist
+a local upload and bind it to the proper library and track. If no local track
+match the audio metadata, a track is created using the ``metadata`` attribute
+from the :ref:`object-audio` object.
+
+Checks
+******
+
+Before handling the activity, Funkwhale will ensure the activity actor and
+the audio library's actor are the same.
+
+If no local actor follows the audio's library, the activity will be discarded.
+
+.. _activity-delete:
+
+Delete
+^^^^^^
+
+Supported on
+************
+
+- :ref:`object-audio` objects
+- :ref:`object-Library` objects
+
+Example (on :ref:`object-Library`)
+************************
+
+.. code-block:: json
+
+  {
+    "@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"
+    }
+  }
+
+Example (on :ref:`object-audio`)
+**********************
+
+.. code-block:: json
+
+  {
+    "@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"
+      ]
+    }
+  }
+
+In this example, Bob notifies the followers of their library that 3 objects were deleted.
+
+.. note::
+
+  For performance reason, when deleting :ref:`object-audio` objects, Funkwhale support
+  either a list of ids or a single id.
+
+Internal logic
+**************
+
+When a :ref:`activity-delete` is received, the corresponding objects are deleted immediatly
+from the database.
+
+Checks
+******
+
+Before handling deletion, Funkwhale ensure the actor initiating the activity
+is the owner of the deleted :ref:`object-audio` or :ref:`object-Library`.
+
+Supported objects
+-----------------
+
+.. _object-artist:
+
+Artist
+^^^^^^
+
+.. note::
+
+  This object is not standard.
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "type": "Artist",
+    "id": "https://awesome.music/federation/music/artists/73c32807-a199-4682-8068-e967f734a320",
+    "name": "Metallica",
+    "published": "2018-04-08T12:19:05.920415+00:00",
+    "musicbrainzId": "65f4f0c5-ef9e-490c-aee3-909e7ae6b2ab"
+  }
+
+Structure
+*********
+
+- **id** (required): a uri identifying the artist over federation
+- **name** (required): a name for the artist
+- **published** (required): the publication date of the artist (on the federation)
+- **musicbrainzId** (optional): the musicbrainz artist id
+
+.. _object-album:
+
+Album
+^^^^^
+
+.. note::
+
+  This object is not standard.
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "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"
+    },
+    "artists": [
+      {}
+    ]
+  }
+
+Structure
+*********
+
+- **id** (required): a uri identifying the album over federation
+- **name** (required): the title of the album
+- **artists** (required): a list of :ref:`object-artist` objects involved in the album
+- **published** (required): the publication date of the entity (on the federation)
+- **released** (optional): the release date of the album
+- **musicbrainzId** (optional): the musicbrainz release id
+- **cover** (optional): a `Link` object representing the album cover
+
+.. _object-track:
+
+Track
+^^^^^
+
+.. note::
+
+  This object is not standard.
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "type": "Track",
+    "id": "https://awesome.music/federation/music/tracks/82ece296-6397-4e26-be90-bac5f9990240",
+    "name": "For Whom the Bell Tolls",
+    "position": 3,
+    "published": "2018-10-02T19:49:35.822537+00:00",
+    "musicbrainzId": "771ab043-8821-44f9-b8e0-2733c3126c6d",
+    "artists": [
+      {}
+    ],
+    "album": {}
+  }
+
+Structure
+*********
+
+- **id** (required): a uri identifying the track over federation
+- **name** (required): the title of the track
+- **position** (required): the position of the :ref:`object-track` in the album
+- **published** (required): the publication date of the entity (on the federation)
+- **musicbrainzId** (optional): the musicbrainz recording id
+- **album** (required): the :ref:`object-album` that contains the track
+- **artists** (required): a list of :ref:`object-artist` objects involved in the track (they can differ fro mthe album artists)
+
+.. _object-library:
+
+Library
+^^^^^^^
+
+.. note::
+
+  This object is not standard but inherits its behaviour and properties from
+  Actor and Collection.
+
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "type": "Library",
+    "id": "https://awesome.music/federation/music/libraries/dc702491-f6ce-441b-9da0-cecbed08bcc6",
+    "actor": "https://awesome.music/federation/actors/MyNameIsTroll",
+    "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",
+  }
+
+
+Structure
+*********
+
+- **id** (required): a uri identifying the library over federation
+- **actor** (required): the id of the actor managing the library
+- **name** (required): the name of the library
+- **followers** (required): the id of the library's followers collection
+- **totalItems** (required): the number of audio objects available in the library
+- **first** (required): the URL of the first page of the library
+- **last** (required): the URL of the last page of the library
+- **summary** (optional): a description for the library
+
+.. note::
+
+  Crawling library pages requires authentication and an approved follow, unless the library is
+  public.
+
+.. _object-audio:
+
+Audio
+^^^^^
+
+.. note::
+
+  This object `is specified in ActivityStreams <https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audio>`_,
+  but Funkwhale needs non-standard attributes to handle it.
+
+Example
+*******
+
+.. code-block:: json
+
+  {
+    "type": "Audio",
+    "id": "https://awesome.music/federation/music/uploads/88f0bc20-d7fd-461d-a641-dd9ac485e096",
+    "name": "For Whom the Bell Tolls - Ride the Lightning - Metallica",
+    "size": 8656581,
+    "bitrate": 320000,
+    "duration": 213,
+    "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/v1/listen/82ece296-6397-4e26-be90-bac5f9990240/?upload=88f0bc20-d7fd-461d-a641-dd9ac485e096",
+      "type": "Link",
+      "mediaType": "audio/mpeg"
+    }
+  }
+
+Structure
+*********
+
+- **id** (required): a uri identifying the audio over federation
+- **name** (required): a human-friendly title for the audio (We concatenate track name, album title and artist name)
+- **size** (required, non-standard): the size of the audio, in bytes
+- **bitrate** (required, non-standard): the bitrate of the audio, in bytes/s
+- **duration** (required): the duration of the audio, in seconds
+- **library** (required, non-standard): the id of the :ref:`object-Library` object that contains the object
+- **published** (required): the publication date of the entity (on the federation)
+- **updated** (required): the last update date of the entity (on the federation)
+- **url** (required): a ``Link`` object with an ``audio/`` mediaType where the audio file is downloadable
+- **track** (required, non-standard): the :ref:`object-track` the :ref:`object-audio` is bound to
+
+.. note::
+
+  Accessing the Audio file via its url requires authentication and an approved follow on the upload library,
+  unless the library is public.
+
+
+.. _library-access:
+
+Audio fetching on restricted libraries
+--------------------------------------
+
+:ref:`object-library` and :ref:`object-audio` url objects may require additional authentications
+to be accessed.
+
+For :ref:`object-library` objects:
+
+- If the library is public, library pages can be accessed without restriction
+- Otherwise, the HTTP request must be signed by an actor with an approved follow on the library
+
+
+For :ref:`object-audio` url objects:
+
+- If the audio's library is public, audio file can be accessed without restriction
+- Otherwise, the HTTP request must be signed by an actor with an approved follow on the audio's library
diff --git a/docs/index.rst b/docs/index.rst
index b0d89f003fd8bd8cb1030f59bd9155d675e6d0e3..ef02db64ba181c3dc1cd2def81b3b3821f7f870b 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -19,7 +19,7 @@ Funkwhale is a self-hosted, modern free and open-source music server, heavily in
    configuration
    troubleshooting
    importing-music
-   federation
+   federation/index
    api
    third-party
    contributing