Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable replies #319

Open
nclm opened this Issue Sep 3, 2018 · 30 comments

Comments

Projects
None yet
8 participants
@nclm
Copy link

nclm commented Sep 3, 2018

Almost all blogging platforms and a few social networks (for instance Dribbble) allow the author to disable replies on their post. So that ActivityPub-enabled platforms are up to usage on that, I think it’s necessary to have the possibility to send posts with a standardised “replies disabled” tag added to them.

(Not knowing the standard, I’m using the “tag”, “post”, etc. without the proper terminology.)

ActivityPub clients must at least:

  • Respect the “replies disabled” added to posts by either hiding, greying out, or crossing any “reply” icon or other interface used for replying or commenting.

Clients probably should:

  • Hide any replies to posts with “replies disabled” enabled, if ever some got through the net. At least to the author.

Clients can if they wish, or if it makes sense, do one or a few or all among these:

  • Enable or disable this feature by default.
  • Allow users to enable or disable this feature for each of their post.
  • Allow users to change their default posting preference.

For platforms that have no sort of interface for displaying comments or replies, it could also make sense for them to enable this feature by default (unless they are fine with people commenting the posts on other platforms even if the discussion does not federate back).

@nclm nclm referenced this issue Sep 3, 2018

Open

“Disable replies” feature #8565

1 of 1 task complete
@gobengo

This comment has been minimized.

Copy link

gobengo commented Sep 3, 2018

ActivityPub clients must at least:
Respect the “replies disabled” added to posts by either hiding, greying out, or crossing any “reply” icon or other interface used for replying or commenting.

If you mean "MUST" and not "must", then this may be philosophically contentious.

I am familiar with many reasons why someone might not want replies to a post. And they shouldn't have to host those replies on their server or even look at them.

But I personally don't think it's reasonable for anyone in a federated context to say "no one is allowed to reply to me". It's stepping a bit further then is fair in terms of free speech, which is any individuals' prerogative. But I don't think it should be baked in at the protocol level.

What's cool about federation is that this reply policy can be enforced not just at time of authoring replies, but at the OP's original server when replies are federated to it. If 'no replies' is indicated, the original author's server can just reject all replies coming into the inbox (or all replies with profanity or images or whatever).

With that said: Sure, why not have something like:

  • a vocabulary item that is a property on Objects (same domain as as:inReplyTo) that is like "replyPreferences": "http://your-vocabulary.com/replyPreferences/NONE". There could even be specific pre-baked policy URLs somewhere for "don't reply to me" or "don't reply with curse words" or "don't reply with images" or "don't reply with r-rated images"
  • language that says clients
    • SHOULD inform potential repliers about these preferences
    • MAY prevent replying if the replyPreferences indicate preferring no replies.
  • language that says that servers
    • SHOULD try to enforce the replyPreferences when receiving replies in the inbox
@nclm

This comment has been minimized.

Copy link
Author

nclm commented Sep 3, 2018

Oh sorry I edited my message while you were replying. For reference, I added: Clients probably should: Hide any replies to posts with “replies disabled” enabled, if ever some got through the net. At least to the author.

If this one becomes a must, then the first one can become a should or may. What is important is that an author never see a reply to something they posted with “no replies” checked on.

@kizu

This comment has been minimized.

Copy link

kizu commented Sep 3, 2018

Disallowing replies does not ever impede the “free speech”. Anyone would be able to use their platform to post replies, but the link between the replies and the original document shouldn't be enforced. Replies without a consent to replies would be basically violence and could be used as a harassment channel.

So I'm all for the MUST wording.

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 3, 2018

@cwebber

This comment has been minimized.

Copy link
Collaborator

cwebber commented Sep 5, 2018

So we have to be careful about "request policy" / "advisory policy" things that ask a good participant to behave correctly. That can still be useful, but it's important to remember what will happen if either a bad or negligent participant ignores the behavior. If we don't consider that, we might accidentally allow bad / negligent participants to be more abusive than they were before we implemented the protocol, because we will violate the expectations of good participants when what they suddenly see that intent is violated.

So let's consider what we can do. There's what we can do within current reply conventions (not very powerful, but advisory) and what we could do much better if we changed things more dramatically with a capabilities approach. I'll focus on the former, but if people are interested in the more capability-centric approach which can more powerfully restrict these things I could write it up as a separate post.

  • So, initially Alice makes a post about her cat with no explicit replyPreference, which defaults to "replies are ok". Her friend Bob replies, and Alice favorites his reply. This is the simplest case.

  • Alice makes a post about her recent dancing performance but marks it as replyPreference to NoReplies or some such value.

    • Bob's client politely disables the reply button, and Bob does not reply.
    • Mallet's client, through malice or negligence, does not disable the reply button, and Mallet distributes a snarky message inReplyTo Alice's post. We must remember that the protocol cannot prevent an inReplyTo another person's id that is already known, so instead we should ask, how do Alice and Bob's clients render the reply thread? If they were expecting that clients MUST not render the reply button or send replies, perhaps their clients would be lazy and expect that any inReplyTo objects must be fine because all clients are behaving correctly. But clearly this would be upset and hurtful to Alice. So we might say, their clients should actually expect that other clients might behave badly, in which case maybe they check the replyPreference to decide whether to allow any further replies. At any rate, there is nothing we can do to prevent bad or negligent actors to continue commenting on the post and to continue seeing those replies, even if nobody else does.
  • Alice makes a post about some food that she cooked and initially gets a reply from Bob that she likes, but then people start dogpiling and she decides to change her replyPreference, and well behaving clients respect this but Mallet still sends replies. This is the hardest case because a new problem appears: should we render the messages that happen before Alice updates her replyPreference?

    • If we say "no", Bob and Alice might be upset that Bob's comment now disappears, because Alice did not intend for previous posts to stop appearing, only new ones.
    • If we say "yes", we have now added a new complex state management problem where all participants need to tag posts as being displayed or not displayed comments depending on when the reply comes in.

Anyway, that's all to say that this is trickier than it appears, and writing the code to "enforce" whether or not the client MUST show the reply button or not does not actually enforce itself the behavior in the system, and there are complex decisions and state management problems we must consider if this feature is introduced.

Anyway, @nightpool is right, there's absolutely a couple of ways to do this from a capabilities perspective that get the behavior that people want to be able to more carefully control what are and aren't considered legitimate replies, but that's work-in-progress research on my end and may end up requiring that servers rethink how they do things.

@trwnh

This comment has been minimized.

Copy link

trwnh commented Sep 8, 2018

A proposal in follow-up to @cwebber: instead of a replyPreference, could this be done instead by changing the way replies work to refer to some secret replyID instead? Fundamentally, the problem is that you can refer to anything with an address; therefore, it logically follows that to disallow references to something, it's necessary to keep the address private. If the URI is used as the replyID, then you can reply to any post that you know exists. This replyID would be requested by a server on behalf of an Actor, and can be changed to null to disable replies at any time; it would be the responsibility of the Server to verify that the replyID is valid.

That might be closer to what you refer to as "capability perspective"; if creating an entirely separate replyID is too complicated right now, then perhaps it might suffice to generate a one-time secret replyProof of some sort, and this could be used for validation by other participants' clients.

Alternatively, clients should stop querying their own servers for replies to a post, and should instead query the authoring server for replies -- this would be less efficient since it cannot be fully cached, but would make the authoring server also the authoritative one; this would open the door to allowing each Actor to disallow or de-list specific replies, similarly to how you can delete other people's comments on Facebook or Instagram.

@cwebber

This comment has been minimized.

Copy link
Collaborator

cwebber commented Sep 8, 2018

@trwnh That is indeed similar to the capability perspective. The important idea here is that the actor you receive may not actually be the full most powerful reference to the actor (and its inbox), it may be a "facade" which has more limited powers and instead forwards some messages to the full-powered actor but filters others. So in this case, the facade lets you view the object read-only but does not give you access to replying by default. (Alternately, the actor for viewing and the actor for replying are just two entirely separate actors which coordinate behind the scenes.) We must also provide a way for the object to demonstrate which replies are valid.

I will work on a full writeup of how to do this "the right way" via capabilities. I had intended to hold off on this until later because I think it introduces several new concepts. However, it's probably time to put out a first draft so people can understand some of the powers possible when we introduce ocaps into the fediverse.

Expect a reply within the next several hours.

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 8, 2018

just to preempt the already-disclaimed-to-be-theoretical post with a more practical solution: we should allow objects to require that their actor Accepts an activity relating to them (announce, reply, etc) before other servers process it, in the same way we currently require Accepts for following

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 8, 2018

this seems similar to @kaniini's proposal but i'm not sure that there's even a need for a specific interaction inbox. Here's an example of how I think this could work in practice:

Alice posts a status they only want certain people to be able to reply to:

{
  "type": "Create",
  "to": "https://alice.com/followers",
  "actor": "https://alice.com/user",
  "object": {
    "id": "https://alice.com/my_private_status",
    "type": "Note",
    "requireAcceptedInteractions": true,
    "content": "Just got out of a bad meeting with my boss. Don't want to talk about it"
  }
}

Bella sends a tentative reply just to alice's inbox:

{
  "type": "Create",
  "to": "https://alice.com/user",
  "cc": ["https://alice.com/followers", "https://bella.com/followers"],
  "actor": "https://bella.com/user",
  "object": {
    "id": "https://bella.com/my_reply",
    "type": "Note",
    "content": "that sucks! hope you're feeling better",
    "inReplyTo": "https://alice.com/my_private_status"
  }
}

Alice either Accepts or Rejects this reply. if alice Accepts it, then she sends an Accept activity back to Bella, and then either of them can forward the Accept activity on to whoever they would have originally:

{
  "type": "Accept",
  "actor": "https://alice.com/user",
  "object": {
    "type": "Create",
    "to": "https://alice.com/user",
    "cc": ["https://alice.com/followers", "https://bella.com/followers"],
    "actor": "https://bella.com/user",
    "object": {
      "id": "https://bella.com/my_reply",
      "type": "Note",
      "content": "that sucks! hope you're feeling better",
      "inReplyTo": "https://alice.com/my_private_status"
    }
  }
}

So this boils down to a couple of rules:

  • If you're a third-party server, and you see a post with requireAcceptedInteractions, don't process activities that aren't embedded in an Accept from the author of the post
  • If you're the author of an object/activity with requireAcceptedInteractions, you should either Accept or Reject posts coming into your inbox that reference that object/activity, and optionally forward those Accepts/Rejects to relevant servers
  • If you're authoring an activity that references an activity/object with requireAcceptedInteractions, then you should only send it to the actor that created that activity/object, instead of the entire audience. If you receive an Accept back, you can then send that to the original recipients.

This relies on the definition of a couple iffy terms:

  • what does it mean to reference or interact with something? How strict should we be?
  • what actor is the "author" of any given activity, and is this always a 1:1 relationship?
  • who should be allowed to Accept or Reject activities for a given object?

but i think that in practice it would work pretty well.

@trwnh

This comment has been minimized.

Copy link

trwnh commented Sep 8, 2018

Hmm, Accept and Reject semantics sound like "approved" or "unapproved" comments -- not necessarily the same as preventing replies due to requiring oversight, but functionally similar enough in many cases. Although, I'm worried that asking a user to approve/disapprove replies is still forcing the user to at least view the replies, and thus receive them. Outside of a simple "Accept all replies" or "Reject all replies" boolean, this would necessitate a holding area for all tentative/unanswered inReplyTo activities, similar to follow requests.

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 8, 2018

@trwnh i don't think the idea that you're sending an Accept or Reject activity necessarily implies that you're requiring the user to look at each individual one. There are plenty of other procedures a server could run automatically for determining if a reply is valid ("only allow replies from mutuals", like tumblr has, for example)

@kaniini

This comment has been minimized.

Copy link

kaniini commented Sep 9, 2018

how do you prevent spoofing with Accept in this case?

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 9, 2018

wym?

@kaniini

This comment has been minimized.

Copy link

kaniini commented Sep 9, 2018

  1. who sends the Accept messages?

  2. if anyone can send the Accept messages how does forgery prevention work? since Pleroma use capability URIs extensively for forgery protection (and Mastodon use LDS), how can we ensure things are safe?

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 9, 2018

@kaniini my proposal for now was that the actor who created the object/who the object is attributed to is the only person who can send accept messages, although I admit that this is limited in The General Case. auth&auth is currently outside of the scope of the spec, but i would assume any method of ensuring the activity content is genuine would work, the proposal is agnostic, it just assumes you have some way of preventing forgery (like all of activitypub)

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 9, 2018

I'm not sure what you mean by pleroma using capability URIs extensively for forgery protection—this is the first i'm hearing about it. My understanding is that pleroma uses HTTP signatures for validating incoming federated activities and domain-based checks for imported activities.

@kaniini

This comment has been minimized.

Copy link

kaniini commented Sep 9, 2018

so say Elaine sends a Create that AliceAccepts.

it is addressed to Alice herself and Elaine's followers collection.

how does Alice forward those messages to Elaine's followers collection without walking it?

I think we need it to work this way:

  1. Alice sends the Accept back to Elaine.
  2. Elaine forwards the Accept to her followers, by referencing the Accept by IRI.
  3. Capability enforcement is achieved by fetching the Accept.
  4. If the Accept is fetchable, then the grant is valid, and the underlying referenced object is processed.

This could be optionally supplemented by LDS to avoid the fetch step if wanted.

@kaniini

This comment has been minimized.

Copy link

kaniini commented Sep 9, 2018

Pleroma always refetches any URI that is not local, to protect against forgery and enforce object integrity. It is one of the reasons we are opposed to inline signature schemes such as LDS. But that's beside the point right now, let's stick to the topic at hand :)

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 9, 2018

i don't think any of that is inconsistent with what I said, it just hardcodes two forms of authentication. (fetching the Accept and using LDS)

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 9, 2018

I don't think it's appropriate to refer to these Accept activities as "capabilities", since they're only judgements about single objects, not permissions to do things in the future

@kaniini

This comment has been minimized.

Copy link

kaniini commented Sep 9, 2018

That is precisely what object capability enforcement is.

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 9, 2018

I'm very sure that's incorrect (since it doesn't involve different permissions based on different references you hand to different people) but i don't want to get off topic on this discussion

@cjslep

This comment has been minimized.

Copy link

cjslep commented Sep 9, 2018

Re: @nightpool comment w/ concrete Accept / Reject proposal:

This proposal as I understand it is not backwards compatible with the delivery model outlined in the original ActivityPub spec. If a vanilla ActivityPub server is sending:

{
  "type": "Create",
  "to": "https://alice.com/user",
  "cc": ["https://alice.com/followers", "https://bella.com/followers", "https://example.com/arbitrary_onlookers"],
  "actor": "https://bella.com/user",
  "object": {
    "id": "https://bella.com/my_reply",
    "type": "Note",
    "content": "that sucks! hope you're feeling better",
    "inReplyTo": "https://alice.com/my_private_status"
  }
}

It will forward the response to the other ActivityPub servers, despite Alice's wish against it.

This is tangential to the problem you point out:

If you're a third-party server, and you see a post with requireAcceptedInteractions, don't process activities that aren't embedded in an Accept from the author of the post

Which in my mind is a huge limitation: all vanilla ActivityPub implementations are essentially spreading around replies unwanted to be received and shown by the original person. This is similar to the second bullet by @cwebber and remains unsolved by the proposal.

I think this is really the crux of the issue: vanilla AP implementations already violate the proposed concrete rules above. If this can't be solved, ActivityPub clients might as well provide a "don't show replies" UI checkbox when posting to achieve the same thing: not letting the recipient see the unwanted content, but still allowing the rest of the AP servers to spread information as allowed by the original AP protocol (w/o mucking up the original protocol).

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 9, 2018

@cjslep yes, I agree that this proposal doesn't do anything for servers that don't support it. Unfortunately, we have to make a choice here—either we extend activitypub is such a way that it breaks replies for all servers that don't respect disabling replies, or we do so in a way that is backwards compatible. I chose the latter option, but i guess there are also arguments for the former.

@cwebber

This comment has been minimized.

Copy link
Collaborator

cwebber commented Sep 12, 2018

There's a way we can do things that isn't backwards incompatible involving proofs/signatures with a certain proofPurpose. It's been a long day for me but I'll write this up tomorrow.

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 12, 2018

@cwebber you're telling me there's a backwards compatible way to prevent non-extended ActivityPub servers from passing around inReplyTo properties that reference the original post, to other non-extended servers? that seems impossible given that it's strictly a restriction on existing behavior.

or are you saying there's a backwards-incompatible-but-less-so way?

@cwebber

This comment has been minimized.

Copy link
Collaborator

cwebber commented Sep 12, 2018

The right approach which degrades gracefully is to add a new proof node with a proofPurpose that says whether or not a post is signed off on as a valid reply. So:

{"type": "Note",
 "content": "bla bla",
 "inReplyTo": ...,
 ...
 "signature": [
   {"proofPurpose": "acceptedReply",
    ... sig of person being replied to ...}]}

proofPurpose is a new field in ld-sigs/proofs to avoid confused deputy problems... signing an object because you made it vs because you are accepting the reply, etc, are different things.

Prior implementations of ActivityPub before this extension will still show all replies. New applications will know to only show the replies with the appropriate signature.

How to get your object signed by the original author? That's another post for me to write.

@nightpool

This comment has been minimized.

Copy link

nightpool commented Sep 12, 2018

@cwebber i don't see how that's significantly different then passing around a signed/dereferenced Accept object, from a backwards compatibility perspective

@kaniini

This comment has been minimized.

Copy link

kaniini commented Sep 18, 2018

keep in mind that use of ld-sigs is untenable in Pleroma

@trwnh

This comment has been minimized.

Copy link

trwnh commented Oct 11, 2018

Revisiting an earlier comment I made:

Alternatively, clients should stop querying their own servers for replies to a post, and should instead query the authoring server for replies -- this would be less efficient since it cannot be fully cached, but would make the authoring server also the authoritative one; this would open the door to allowing each Actor to disallow or de-list specific replies, similarly to how you can delete other people's comments on Facebook or Instagram.

With the recent hubbub about Google+'s death and renewed interest in Diaspora*, I came across this article again (https://schub.io/blog/2018/02/01/activitypub-one-protocol-to-rule-them-all.html) and specifically this bit (minorly edited for typos and clarity):

Let us have a look at the implementation in the diaspora* protocol first. We have a pretty easy rule: Whenever Bob interacts to something Alice shared, that interaction will be sent to Alice’s host and Alice’s host alone. Since interactions are designed to be relayable without losing the option to validate them, Alice’s host can forward these interactions to everyone who received the post in the first place. Alice’s host is the one who delivered the post, so it feels somewhat natural to also ask Alice’s host to distribute the interactions. That is, however, not the case with ActivityPub [...] Bob has to address individual recipients in his interaction. We have seen this implementation before and it is built to fail [...] a more sensible, reliable, and even more ActivityStreams-y way of handling replies would probably be adding the interaction to the replies collection and sending an Update.

Would this perhaps be simpler and less backwards-incompatible than mandating an Accept {Create {Note.inReplyTo}} response? Or is that functionally the same thing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.