FEP-5624: Per-object reply control policies
by Claire claire.fep-1d7d@sitedethib.com submited on 2022-08-23
Summary
Sometimes, users may want to share an information or a story without inviting replies from outside their circles or from anyone at all. In particular, individuals may want to restrict who can reply to them in order to avoid “reply guys” or limit outright harassment, while instutions may want to disable replies on their posts to provide information without having to deal with a moderation burden.
This can be broken into an advisory part advertising what sets of actors are expected to be able to reply, and a collaborative verification process where third-parties check with the actor being replied to that the reply is indeed allowed.
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].
In the remaining of this FEP, “distribution authority” (or “authority” for short) refers to an actor that controls the distribution and audience of replies. The purpose of this wording is to make this FEP applicable both for models where replies are first-class posts, and for “post and comments” models where comments only exist in the context of a post and the post author decides who gets to see the comments. In the absence of extensions, the “authority” is the author of the post being replied to.
Declaring a reply policy
In order to advertise who is allowed to reply to an object, an author MAY set the canReply
(http://joinmastodon.org/ns#canReply
) property on their objects. If set, this property MUST be an empty array or one or more actors or collections.
To ease implementation, collections SHOULD be restricted to one of the following:
- as:Public
, to indicate that anyone can reply
- the authority’s followers collection (if defined)
- the authority’s followed collection (if defined)
In addition, canReply
SHOULD contain every actor mentioned in the original object.
Whenever one of these collections is used, the receiving end can easily know whether they are expected to be able to reply.
Example object
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"toot": "http://joinmastodon.org/ns#",
"canReply": "toot:canReply"
}
],
"attributedTo": "https://example.com/users/1",
"id": "https://example.com/users/1/statuses/1",
"type": "Note",
"content": "Hello world",
"canReply": "https://www.w3.org/ns/activitystreams#Public"
}
Checking whether the user can reply and submitting the reply to the authority
When an object with canReply
is set, it SHOULD be conveyed in human-readable form to the user if possible, for instance with something like “Only mentioned users can reply” or “Only people Authority follows and mentioned users can reply”.
The software SHOULD NOT offer the user to reply unless it is directly mentioned in the object’s tag
attribute or listed in canReply
(either directly or through a collection), or canReply
contains a collection for which the recipient cannot efficiently check the membership of the would-be replier.
After locally verifying that the replier should be allowed to reply, the replier’s end SHOULD POST
the Create
activity for the reply to the authority’s inbox only, and consider the reply to be pending approval.
Receiving and accepting a reply
When receiving a reply to an object with a canReply
property, the authority decides whether the reply is acceptable.
If the reply is considered acceptable, the authority MUST reply with an ApproveReply
(http://joinmastodon.org/ns#ApproveReply
) activity with the object
property set to the id
of the reply object, and its inReplyTo
property set to the object it is in reply to.
That ApproveReply
activity SHOULD be publicly dereferenceable and MUST be dereferenceable by all parties allowed to see the original post. It MUST NOT embed its object
nor its inReplyTo
as to avoid possible information leaks.
Additionally, the authority MAY forward an accepted reply according to its own rules.
If the reply is considered unacceptable, the authority SHOULD reply with a RejectReply
(http://joinmastodon.org/ns#RejectReply
) activity. This activity MAY be publicly accessible, but this is not a requirement.
The reason for using the new activity types ApproveReply
and RejectReply
is to be explicit about the purpose of the approval, as one could imagine other kinds of approvals, and remaining implicit may cause ambiguities with other potential extensions.
Example ApproveReply
activity
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"toot": "http://joinmastodon.org/ns#",
"ApproveReply": "toot:ApproveReply"
}
],
"actor": "https://example.com/users/1",
"id": "https://example.com/reply_approvals/1",
"type": "ApproveReply",
"object": "https://example.org/users/bob/statuses/3",
"inReplyTo": "https://example.com/users/1/statuses/1"
}
Example RejectReply
activity
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"toot": "http://joinmastodon.org/ns#",
"RejectReply": "toot:RejectReply"
}
],
"actor": "https://example.com/users/1",
"id": "https://example.com/reply_approvals/1",
"type": "RejectReply",
"object": "https://example.org/users/bob/statuses/3"
}
Receiving approval and distributing the reply
After sending the initial Create
, the replier SHOULD wait for an ApproveReply
activity such as described above.
Once the ApproveReply
has been received, the replier SHOULD add a replyApproval
(http://joinmastodon.org/ns#replyApproval
) property to their reply object pointing to the ApproveReply
activity they received, and then MAY send a Create
activity with the modified object to its intended audience.
If it instead receives a RejectReply
, the reply SHOULD be immediately deleted and the replier MAY be notified.
Example reply object with replyApproval
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"toot": "http://joinmastodon.org/ns#",
"canReply": "toot:canReply",
"replyApproval": "toot:replyApproval"
}
],
"attributedTo": "https://example.com/users/1",
"id": "https://example.org/users/bob/statuses/3",
"type": "Note",
"content": "@alice hello!",
"inReplyTo": "https://example.com/users/1/statuses/1",
"canReply": "https://www.w3.org/ns/activitystreams#Public",
"replyApproval": "https://example.com/reply_approvals/1",
"tag": {
"type": "Mention",
"href": "https://example.com/users/1"
}
}
Verifying third-party replies
When processing a reply from a remote actor to a remote authority, a recipient SHOULD discard any reply that does not match any of the following conditions:
- the object it is in reply to does not set a canReply
property
- the object has been received through the remote authority
- the object it is in reply to has a canReply
containing the as:Public
collection
- the author of the reply appears in a Mention
object in the tag
property of the object it is in reply to
- the object it is in reply to sets a non-empty canReply
, and replyApproval
can be dereferenced and is a valid ApproveReply
activity
To be considered valid, the ApproveReply
activity referenced in replyApproval
MUST satisfy the following properties:
- its actor
property is the authority
- its authenticity can be asserted
- its object
property is the reply under consideration
- its inReplyTo
property matches that of the reply under consideration
In addition, if the reply is considered valid, but has no valid replyApproval
despite the object it is in reply to having a canReply
property, the recipient MAY hide the reply from certain views.
Revoking a previously-accepted reply
The authority may want to perform /a posteriori/ moderation of their replies.
To do this, the authority SHOULD send a RejectReply
activity to the sender and the reply’s audience, with the reply URI as the object
property. The object
property MUST NOT be embedded, as to avoid possible information leaks.
The URI at which the previously-offered ApproveReply
was available should return HTTP 404 or redirect to the newly-issued RejectReply
activity.
Handling a revocation
Upon receiving a RejectReply
activity for a previously-accepted reply, third-parties SHOULD check that the RejectReply
is valid and SHOULD delete or hide the revoked reply if it is.
To be considered valid, the RejectReply
activity MUST satisfy the following properties:
- its actor
property is the authority
- its authenticity can be asserted
- its object
property is the reply under consideration
Deployment considerations
Because it is unrealistic to expect all implementations and deployments to implement this proposal at the same time, deployment SHOULD be gradual, with verification of third-party replies only performed once the other steps are widely implemented. To encourage adoption without breaking compatibility altogether, implementations MAY want to hide non-validated replies from certain views (e.g. requiring a click to see “hidden replies”, or not showing the reply to non-followers).
Security considerations
By not adding a hash or copy of the reply in the ApproveReply
activity, malicious actors could exploit this in a split horizon setting, sending different versions of the same activity to different actors. This is, however, already a concern in pretty much all contexts in ActivityPub, and enshrining that information in the ApproveReply
activity would have many drawbacks:
- significantly more complex implementation
- inability to change the JSON-LD representation after the fact
- possibly leaking private information if the
ApproveReply
activity is publicly dereferenceable
Implementations
None so far.
References
- [RFC-2119] S. Bradner, Key words for use in RFCs to Indicate Requirement Levels
Copyright
CC0 1.0 Universal (CC0 1.0) Public Domain Dedication
To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.