User following
Terminology
The following terminology is used throughout this document:
- User
A person with an account on a Funkwhale server or ActivityPub-enabled platform.
- Follow
The act of subscribing to a user’s activities to render them in a feed as they occur.
- Object
A collection of information - formatted as
JSON-LD
- that represents entities such as content, users, or activities performed in Funkwhale. See the ActivityPub specification for more details.- Activity
A verb that describes an action targeting an Object. This informs the receiving server what it needs to do with the object. For example:
Create
,Delete
,Undo
,Follow
,Block
.- Actor
An ActivityPub object representing an entity capable of performing actions. See the ActivityPub specification for more details.
- Requesting user
The user who sends a request. For example: If Bob requests to follow Alice, Bob is the requesting user.
- Target user
The user who is the subject of a request. For example: If Bob requests to follow Alice, Alice is the target user.
The issue
Funkwhale is an audio platform with an emphasis on social interaction across a federated network of servers. To this end, Funkwhale users should be able to interact with one another in meaningful ways.
Funkwhale users broadcast the following activity when using the software:
Favoriting content
Listening to content
Users across the federated web should be able to follow Funkwhale accounts to receive this activity in their streams.
Proposed solution
To facilitate this, Funkwhale uses the following mechanisms:
The Funkwhale client API for user to user interactions on the same server
ActivityPub for server-to-server (S2S) interactions between users
Users should be able to discover other users using the Webfinger protocol and render their public details in a manner compliant with the platform they use. Within Funkwhale, user activity should be rendered natively. Other Fediverse software may choose how to render this information.
This specification outlines the workflows for the following actions for local and remote requests:
User discovery
User follows
User unfollows
User blocking
User discovery
Funkwhale implements the Webfinger protocol for account discovery. When the requesting user enters the target user’s federation handle, Funkwhale should attempt to resolve the location of the target user by querying the acct:
resource with the URL encoded handle.
Webfinger request
$ curl -X GET \
-H "Content-type: application/json" \
'https://open.audio/.well-known/webfinger?resource=acct:user%40open.audio'
Response
{
"subject": "acct:user@open.audio",
"links": [
{
"rel": "self",
"href": "https://open.audio/federation/actors/user",
"type": "application/activity+json"
}
],
"aliases": ["https://open.audio/federation/actors/user"]
}
Web app behavior
When a requesting user enters the handle of a target user in the search bar, Funkwhale does the following:
Verifies the handle is well-formed
Forwards the request to the server
The server is responsible for dereferencing the Webfinger query. The server does the following:
Queries the domain for the account using Webfinger
If the domain doesn’t have a Webfinger endpoint, returns a meaningful error message. This must be displayed to the user.
If the server is subject to a domain filter, or is filtering the requesting server, returns a meaningful error message. This must be displayed to the user.
If no user is found on the domain, returns a meaningful error message.
If a matching user is found, the server should return the URL of the resource to the web app. The web app should then redirect the requesting user to a user page that shows the target user’s profile image and preferred username
If the target user has set their activity to public, the web app should render their Favorites and Recently Listened activity
Following users
Following a user is a process by which a requesting user subscribes to the activities of a target user. If the target user accepts the follow request, the requesting user receives any new activities in their home feed.
API behavior
Follow requests should be handled by an endpoint using a POST
request. This request must immediately return a status message to the client.
POST /api/v2/users/{id}/follow
When the server receives a follow
request, it creates a follow_request
object containing the status of the follow request which is used to display request information to the target user in their notifications.
Note
If the target user has configured their profile to be public, all Follows
are Accepted
immediately.
ActivityPub behavior
If the target user is on a different server to the requesting user, the request is handled using the ActivityPub Follow
activity:
A
Follow
activity is posted to the requesting user’s outbox collection with the target user as the recipientThe target user receives the request in their inbox collection
The target user then needs to
Accept
orReject
theFollow
If the target user accepts the follow, the requesting user is added to the target user’s following collection. The target user is added to the requesting user’s followers collection
If the target user rejects the follow, the requesting user is not added to the target user’s following collection. The target user is not added to the requesting user’s followers collection
Note
If the target user has configured their activity to be public, all Follows
are Accepted
immediately.
Web app behavior
In the Funkwhale web app, the requesting user sees a Follow button on the target user’s profile page. When they select this button, the following happens:
If the target user’s profile can be followed, an action button is displayed.
When the requesting user attempts the follow the target user, the button should change to a Pending status.
If the target user
Accepts
the follow request, the button should update to show a Following status.
Unfollowing users
Following a user is a process by which a requesting user unsubscribes from the activities of a target user. A requesting user may unfollow a target user unilaterally at any time to stop receiving updates.
API behavior
Follow requests should be handled by an endpoint using a POST
request. This request must immediately return a status message to the client.
POST /api/v2/users/{id}/unfollow
ActivityPub behavior
If the target user is on a different server to the requesting user, the request is handled using the ActivityPub Undo
activity:
An
Undo
activity is posted to the requesting user’s outbox collection with aFollow activity
as the targetThe target user is removed from the requesting user’s following collection
The target user receives the undo request in their inbox collection
The requesting user is removed from the target user’s followers collection
Web app behavior
When a requesting user unfollows a target user, the UI must update to visually indicate that the action has succeeded. All activities relating to the target user must be visually hidden.
Blocking users
When one user blocks another, no information may be shared between them. Blocking is a unilateral action that can be taken by both requesting and target actors to prevent the other from interacting with them.
API behavior
Block requests should be handled by an endpoint using a POST
request. This request must immediately return a status message to the client.
POST /api/v2/users/{id}/block
ActivityPub behavior
If the the blocked user is on a different server to the blocking user, the request is handled using the ActivityPub Block
activity with the blocked user’s Actor
as a target.
A
Block
activity is posted to the blocking user’s outbox collection with the blocked user’sActor
the targetIf the blocked user was previously in the blocking user’s following collection, they are removed
If the blocked user was previously in the blocking user’s followers collection, they are removed
Warning
As noted in the ActivityPub spec, the blocked user must not be informed of the Block
activity.
Web app behavior
When a blocking user blocks a blocked user, the UI must update to visually indicate that the action has succeeded. All activities relating to the blocked user must be visually hidden.
If a blocking user navigates to the profile of a blocked user who has blocked them, the UI must not reflect that they are blocked. The blocking user must be able to send a follow request which is not sent to the blocked user.
Unblocking users
Blocking users can unilaterally reverse blocks they have imposed on blocked users. This enables them to request to follow the blocked user’s activities again.
API behavior
Unblock requests should be handled by an endpoint using a POST
request. This request must immediately return a status message to the client.
POST /api/v2/users/{id}/unblock
ActivityPub behavior
If the blocked user is on a different server to the blocking user, the request is handled using the ActivityPub Undo
activity.
Web app behavior
When a blocking user unblocks a blocked user, the UI must update to visually indicate that the action has succeeded. The Follow button must become active and interactive again.
Availability
App frontend
CLI
Responsible parties
The following working groups are responsible for implementing this feature:
The Backend group is responsible for building the API endpoints and ActivityPub S2S logic
The Design group is responsible for drafting designs for the web app interactions
The Frontend group is responsible for implementing the designs from the Design group and adding support for the new API
The Documentation group is responsible for finalizing the specification of the feature and documenting it for users
Open questions
The API actions and endpoint names are placeholders. We need to decide what they should be called
What limitations are there when fetching activities from remote actors?
How should a user’s followers collection, following collection, and pending requests be displayed in the web app?
How are followed activities fetched and displayed to a user?
Minimum viable product
The MVP for this feature is to implement the backend logic to enable Funkwhale users to follow one another. This can be added to the web app using existing profile fetching logic.
Next steps
Once the backend logic is implemented, the frontend implementation should be revisited to improve the UX and discoverability. Additional features such as showing federated favorites on audio objects should also be considered.