diff --git a/feps/fep-1b12.md b/feps/fep-1b12.md index ef7050b..88f0524 100644 --- a/feps/fep-1b12.md +++ b/feps/fep-1b12.md @@ -8,7 +8,7 @@ dateReceived: 2022-11-12 ## Summary -Internet forums are probably the oldest form of social media. They can be implemented with the Activitypub protocol, but such implementations may not necessarily be compatible with each other. This document defines a common subset of Activitypub which is used by different platforms to federate groups in a mutually compatible way. +Internet forums are one of the oldest forms of social media. This document describes how they are implemented in existing Activitypub platforms using `Group` actors. It also introduces a new property to indicate that a given object belongs to a group. ## History @@ -16,53 +16,33 @@ Friendica released federated forums in version [2019.03](https://github.com/frie Lemmy published the first public beta of federated groups in [v0.8.0 (October 2020)](https://join-lemmy.org/news/2020-10-20_-_Lemmy_Release_v0.8.0_-_Federation_beta!). +[FEP-400e] introduces publicly appendable collections, which can also be used to implement forums. However they are incompatible with the implementations described here. + ## Requirements The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this specification are to be interpreted as described in [RFC-2119]. ## Group actor -A federated forum is represented by a `Group` actor as specified in [Activity Vocabulary]. This actor is not directly controlled by a human, but can perform its main functionality in a fully automated way, essentially acting as a bot. +A federated forum is represented by a `Group` actor as specified in [Activity Vocabulary]. This actor is not directly controlled by a human, but can perform its main functionality in a fully automated way, essentially acting as a bot. It has the same general properties as any `Person` actor. -## Following a Group +### Following a Group -Public groups SHOULD support a standard `Follow`-`Accept` workflow. This ensures compatibility with existing implementations which support `Person` following using the same vocabulary. After receiving a valid `Follow` activity, the group SHOULD automatically respond with an `Accept`, and add the sender to its followers collection. +Public groups SHOULD support a standard `Follow`-`Accept` workflow. This ensures compatibility with existing implementations which support `Person` following using the same vocabulary. After receiving a valid `Follow` activity, the group SHOULD automatically respond with an `Accept/Follow`, and add the sender to its followers collection. Correspondingly actors can unfollow a group by sending an `Undo/Follow` activity. -Actors can unfollow a group by sending an `Undo/Follow` activity. +## Audience property -Private groups likely require a different mechanism to add followers, which is yet to be specified. +In order to render content in a forum, it is necessary to know which particular forum the content belongs to. This way users can navigate up from a thread to the forum's main page, or view metadata like the description or moderators. With current implementations there is no easy way to determine which forum a given activity or object belongs to, if any. -## Audience field - -Every activity and object which gets published in a community MUST specify the group actor identifier in the `audience` field. - -Example of a `Create/Page` activity: - -``` -{ - "@context": "https://www.w3.org/ns/activitystreams", - "type": "Create", - "id": "https://sally.example.org/a/2" - "actor": "https://sally.example.org", - "audience": "https://example.com/my-forum", - "object": { - "type": "Page", - "id": "https://sally.example.org/p/1", - "attributedTo": "https://sally.example.org", - "content": "Hello", - "audience": "https://example.org/my-forum" - } -} -``` - -Actors can send such an activity to the group inbox for publishing. +Currently there are different approaches to specify which group a given object or activity belongs to. Lemmy, Friendica and lotide put the group ID in the `to` field. Peertube uses `attributedTo`. Both properties have the problem that they are also used for different purposes by some platforms, and are represented as arrays. So to retrieve the group from a received object, an implementation needs to loop through these properties and resolve each URL, until it hits one which resolves to a `Group`. Clearly this is very inefficient. +To simplify this process, we propose to specify the group identifier in the `audience` property. This property is part of Activitystreams, but not yet used in the wild. This way no extension is necessary, and neither will it cause problems for existing implementations. Additionally, platforms can continue to federate the group identifier in the existing format for backwards compatibility. ## Threads and comments -Each `Group` actor represents a single forum. Each forum usually contains many user-submitted threads, which can be represented as `Page` objects. Forums can have comments which are usually represented as `Note`, and contain a field `inReplyTo` linking the thread it belongs to. Object representations and types may differ based on the requirements of each application. +Each `Group` actor represents a single forum. Forums contain many user-submitted threads, which can be represented by different objects depending on the implementation. Thread objects SHOULD have a `name` property which represents the thread title. -A thread: +Example thread: ``` { "type": "Page", @@ -73,7 +53,9 @@ A thread: } ``` -A comment in that thread: +Threads can have replies, which are usually represented as `Note`s. They MUST have a property `inReplyTo` referencing either the thread they belong to, or the parent reply in case of nested replies. + +Example reply: ``` { "type": "Note", @@ -85,20 +67,17 @@ A comment in that thread: } ``` -There are reference links provided for navigating up from a comment to the respective thread and group. Comments MUST provide a field `inReplyTo` linking to the thread they belong to, or to the parent comment in case of a nested reply. As mentioned before, both threads and comments MUST include a field `audience`, which allows resolving the group they belong to. - -Implementations MAY use the `context` field to reference the corresponding thread, as well as other comments or activities in the thread. - -To navigate down from a group to threads and comments, implementations MAY expose them as collections. Groups MAY have a `replies` collection which contains all threads. Each thread MAY again have a `replies` collection which lists all comments responding to the thread. +The properties `inReplyTo` and `audience` can be used to navigate up from a reply to a thread and forum. To navigate down from a group to threads and comments, groups MAY have a `replies` collection which contains all threads. Each thread MAY again have a `replies` collection which lists all top-level comments responding to the thread. ## The Announce activity The main task of a group is to distribute content among its followers. -When a group receives a activity in its inbox, it SHOULD perform some automatic validation, such as checking for domain and user blocks. Groups MAY require additional validation, such as accepting content only from followers, or even manual approval from group moderators. In case an activity fails these checks, the group MAY respond to the sender with a `Reject` activity. Groups MUST NOT forward any activities that do not have an audience which includes the group actor identifier. +When a group receives a activity in its inbox, it SHOULD perform some automatic validation, such as checking for domain and user blocks. Groups MAY require additional validation, such as accepting content only from followers, or even manual approval from group moderators. In case an activity fails these checks, the group MAY respond to the sender with a `Reject` activity. Groups MUST discard any activities which do not include the group identifier in the `audience` field. -In case the incoming activity is valid, the group MUST wrap it in an `Announce` activity, with the original activity as object: +In case the incoming activity is deemed valid, the group MUST wrap it in an `Announce` activity, with the original activity as object. The wrapped activity MUST be preserved exactly as it was received, without changing or removing any properties. This ensures that forwarded activities can be verified with [Object Integrity Proofs]. Announce activities SHOULD get added to the group outbox. If the group exposes collections of threads and comments, relevant items should also be added to them. +Example: ``` { "@context": "https://www.w3.org/ns/activitystreams", @@ -121,13 +100,9 @@ In case the incoming activity is valid, the group MUST wrap it in an `Announce` } ``` -When wrapping activities in this way, implementations SHOULD NOT make any changes to the wrapped activity, but preserve it exactly. This ensures that activities can be verified with [Object Integrity Proofs]. +After the group successfully verifies and wraps the received activity, it sends it to the inboxes of its followers. Followers then use the outer `Announce` activity to verify that the content was really approved by the group. After this step the `Announce` can be discarded and only the inner activity shown to users. -After the group successfully verifies and wraps the received activity, it sends it to the inboxes of all group followers. Followers then use the outer `Announce` activity to determine that the content was really approved by the group. After this step the `Announce` can be discarded and only the inner activity shown to users. - -This mechanism can be used to publish any possible activity type. Examples include `Announce/Like`, `Announce/Delete/Note` or `Announce/Undo/Like`. Implementations may choose not to Announce some activities. For example, certain activities may represent implementation-specific private actions. - -Announced activities SHOULD also get added to the group outbox. If the group exposes collections of threads and comments, relevant items should also be added to them. +This mechanism can be used to publish any possible activity type. Examples include `Announce/Like`, `Announce/Delete/Note` or `Announce/Undo/Like`. Implementations may choose not to forward some activity types which are considered private, for example `Follow` activities. ## Group moderation @@ -176,16 +151,17 @@ The actions which can be done by moderators are called moderation activities. Th If an group or group follower supports moderation, it MUST validate incoming moderation activities before further processing. Such activities MUST have an actor who is listed in `attributedTo`. Group followers MUST additionally verify that the moderation activity was announced by the group. -Implementations MAY also have site administrators which are listed in the site actor's `attributedTo` field. They are essentially moderators who have control over all groups on their instance. Verification works in the same way as group moderators, namely checking the actor against `attributedTo`, and checking that the activity was announced by the group. +Implementations SHOULD also accept moderation activities which come from the same instance where the community is hosted, under the assumption that these are sent by instance administrators. These moderation activities also need to be wrapped in `Announce` by the group. ## Implementations -This document is written based on existing group implementations in Lemmy, Friendica, Hubzilla, Lotide and Peertube. These already federate successfully in production since over two years. +This document is written based on existing group implementations in Lemmy, Friendica, Hubzilla, Lotide and Peertube. These already federate successfully in production. -The `audience` field and private groups are not yet implemented. Description of these aspects should serve as the basis for future changes. +The `audience` field is an exception as it is not in use yet. Lemmy will add support for it in version 0.17.0. ## References +- [FEP-400e] Gregory Klyushnikov, [FEP-400e: Publicly-appendable ActivityPub collections](./fep-400e.md) - [RFC-2119] S. Bradner, [Key words for use in RFCs to Indicate Requirement Levels](https://tools.ietf.org/html/rfc2119.html) - [Activity Vocabulary], James M Snell, Evan Prodromou, [Activity Vocabulary](https://www.w3.org/TR/activitystreams-vocabulary/) - [Object Integrity Proofs] silverpill, [FEP-8b32: Object Integrity Proofs](https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-8b32.md)