FEP-5624: Per-object reply control policies

this is why i think we shouldn’t check from inReplyTo.attributedTo in all cases. the solution to “replies vs comments” is to have a different abstraction at a different layer – that of the conversation.

put another way: there are two separate but very similar concerns here:

  1. controlling who participates in a conversation
  2. controlling who links to you via inReplyTo

the FEP in its current state does indeed fall short for the first concern, since it currently “hardcodes” checking the inReplyTo. whether this is an acceptable outcome or not depends on the goal we are trying to achieve. that’s enough to satisfy the second concern, but the second concern is the less useful of the two in a future where every implementation should understand what a “conversation” is – in that future, a conversation is not defined purely/only by the reply chain, but instead by some conversational authority that determines which posts are allowed in that conversational context and which ones are not. (and we’re punting the problem of multiple-authority down the road.)


on linking by reference

once again i think this comes down to the two similar-but-distinct concerns we have: controlling a conversation vs controlling a reference. the former has implicit authority by what you choose to publish on your own site. the latter has ambient authority by what others choose to publish on their own site.

what the current FEP tries to do is override the ambient authority of references, and replace it with a token representing explicit authority derived from the author of the linked resource. its only relevance to the “conversation” is in assuming that a mere reference is enough to establish a conversation. the end result is the same, of course – an advisory signal to “please don’t show this object as valid”.

in that way, the current FEP is less about controlling replies (i.e. the conversation) and more about controlling links (i.e. the reference). it is more akin to “mention control” or “link control” and is going to be very difficult to implement anything useful other than what amounts to saying “i approve this message”. the question is, of course, why a third-party observer should care whether you approve of it or not – if you own the conversation, then it would make sense to exercise control over the authenticity of its record. but if you don’t have a concept of a “conversation” at all, then you expect others to also understand that inReplyTo is somehow special. you end up with a pseudo conversational context that is owned by no one, and thus cannot be controlled. unless you hardcode authority to the immediate parent. and why should the immediate parent have authority, again? because we expect inReplyTo to be doing “double duty” and also representing some shadow conversation? maybe this is fine, but it’s awkward.


the “conversation”, as seen by mastodon/pleroma/etc

we wouldn’t have to. audience control could be done by setting audience and then addressing that. collections owned by the original author would depend on that actor to perform inbox forwarding.

this again goes back to separate concerns:

  • whether the post exists on its own (as a “first-class” object)
  • whether to show the post in a thread/conversation (validity or “approval”)

the former is an implementation detail. the latter requires a knowable authority.

you can have replies exist as first-class objects but still require approval from a conversation authority, as long as you set the conversation authority to yourself – you either claim authority over immediate replies (in effect starting a new conversational context that you control), or you defer to existing authority (in effect participating in the current conversation similarly to a “comment”), or you claim null authority (the current default, no conversation). in each of these cases, the signalling of conversational context exists in parallel to how you construct the thread. you’re free to construct based on inReplyTo still – but you are at least aware that a larger conversation might exist and that someone might own it and maybe moderate it.

side note, for the sake of C2S i decided against having context being the root post, although for monolithic impls this doesn’t matter – you would just assign context as a recursive reference on the root post, as ID generation is done at the same time as authoring the activity. (not so for C2S – ids are generated after the activity is defined, so you’d need an Update for either the post or for the context. it’s maybe easier to update the context than it is to update the post, and you’d have to update the context anyway to set the id of the root post, unless your server auto-generated a context for each activity, which i’m not sure is a good idea.)

there’s also the consideration that a conversational authority may not be the same as the author of the root post in a chain, so it makes sense to have a separate context object representing the conversation.

but mainly, the use of context to signal a conversation doesn’t have to be a “comments model only” thing. it can fit the reply model too. i wouldn’t make the use of context contingent on “if” comments are in use. i would think of it as a higher-level abstraction that works regardless of whether you have replies or comments, and in fact, it is an abstraction that clearly denotes authority.

if there is a concern to be raised here, it is not in tying reply control to audience control, but rather, tying reply control to threading in general. which doesn’t have to be the case, either… what you “lose” by having variable context and not simply copying it over blindly, is that you no longer have “a quick way to tell that two posts are somewhere in the same thread”. but in most cases, the thread and the conversation are going to be the same. i do agree that there should be consideration of what happens when you willingly change the context though – as described above, it seems to me like this is effectively turning your post into a quote of sorts. based purely on context alone, it is a different thread; based purely on inReplyTo alone, you are still in the same thread. but context may be null, so you can’t thread purely on context alone. or can you?

(tangentially: mastodon still uses ostatus:conversation while pleroma uses context for this “thread identifier” that doesn’t resolve to anything. you could keep ostatus:conversation for this “quick way to tell that two posts are somewhere in the same thread” that it is currently used for, if it would still be useful.)

all possible cases

i think you would maybe have to enforce that context resolves to something? a context that does not resolve is a context that cannot be used to signal ownership, authority, or control. at minimum, you need to resolve context.attributedTo as an actor (or multiple actors, if multiple authority is ever tackled). it would be useful to also resolve context.context to either the immediate parent or the root post in the chain. or you could have the approval-based properties live on the context object?

but aside from that you have the following breakdown:

  1. there is no resolvable context. there are no controls.
  • you do not set a context on your reply. you are signaling no control.

    • implementations may allow your reply to exist as a first-class object, or they may discard it.
  • you set a context on your reply. you are signaling control over immediate replies (and any descendants that inherit this context).

    • implementations may allow your reply to exist as a first-class object, or they may discard it.
    • implementations may show your reply in a different thread.
    • implementations may choose to render the inReplyTo as metadata above your new thread.
  1. there is a resolvable context. this implies controls.
  • you inherit the context for your reply. you are signaling participation in the same conversation. you gain approval from the context owner, who may be the immediate parent, the root parent, or some other moderating actor.

    • implementations should verify your approval came from the context.attributedTo. if it does not validate, implementations may discard your reply or otherwise mark it unapproved.
  • you set your own context for your reply. you are signaling control over immediate replies (and any descendants that inherit this context). the previous context owner no longer has control over this subthread, since it is effectively a new conversation.

    • implementations may allow your reply to exist as a first-class object, or they may discard it.
    • implementations may(/should?) show your reply in a different thread.
    • implementations may choose to render the inReplyTo as metadata above your new thread.
  • you set null context for your reply. you are signaling no control over replies or descendants. the previous owner no longer has control over this subthread, since it is effectively a new conversation.

    • implementations may allow your reply to exist as a first-class object, or they may discard it.
    • implementations may(/should?) show your reply in a different thread.
    • implementations may choose to render the inReplyTo as metadata above your new thread.

implementation and ux

we can make some observations based on the above:

  • whether the reply exists as a first-class object is a largely separate concern and an implementation detail.
  • the additional considerations are largely only relevant when changing the context
    • therefore, it might make sense to consider if the context of the immediate parent matches the context of the object; implementations may wish to add additional checks or limitations on context-switching, although they probably don’t have to. this is only really relevant for determining if the inReplyTo should be rendered above a given post or not (and whether that chain should recurse upwards to form a thread).

the implementation could look something like this for mastodon:

  • if reply controls are enabled, set a context
  • if a context is present and resolvable, copy it unless you want to set your own reply controls
    • expose this choice to the end user via API / client UI.

basically, users should be aware of when they are participating in someone else’s conversation, and they should be aware that they are giving up control over replies by doing so.

there is also a UX choice to be made about whether having per-post reply controls is even desirable or good UX within the same thread… imagine a back-and-forth between two people who both declare they approve replies. a third party can choose to make a post inReplyTo a favorable authority while actually replying to someone else who would otherwise reject their posts – that is, variable authority implies disjoint and conflicting authority. following the current FEP, if someone makes an approved reply but doesn’t add their own reply controls, they become a trojan horse for other people to reply through them and bypass approval entirely.

(worth noting that twitter treats both reply-controls and circle posts as an implicit comment-model when it comes to authority and approval – these controls only exist for the root post. so twitter doesn’t have these issues. this may imply it is a good idea to do the same in mastodon.)