Skip to content

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

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.