Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
We Distribute
  1. Home
  2. Fediverse
  3. ActivityPub
  4. Pre-FEP: Quote posts, quote policies and quote controls

Pre-FEP: Quote posts, quote policies and quote controls

Scheduled Pinned Locked Moved ActivityPub
3 Posts 3 Posters 69 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • claire@socialhub.activitypub.rocksC This user is from outside of this forum
    claire@socialhub.activitypub.rocksC This user is from outside of this forum
    claire@socialhub.activitypub.rocks
    wrote on last edited by
    #1

    Hi!

    As you may be aware, Quote Posts are an often-requested feature for Mastodon, but this is something we are careful to implement properly, offering a proper framework for users to consent to being quoted.

    Of course, no mechanism will be airtight, there will always be ways to quote someone else, by manually repeating their words, using screenshots and so on. Nevertheless, providing a framework for consent, and adding friction in the absence of consent, is worthwhile. Many existing social network services which feature Quote Posts also offer users at least some control over who can Quote them (on the most basic end, Twitter does not allow people to quote someone who blocked them, while Bluesky, Tumblr and formely the now defunct Cohost allow users to opt out of being quoted, and Bluesky allows to “detach“/revoke individual quotes). We strongly think that such mechanisms are useful, even if there are always ways around them.

    Our goal with Quote Posts is allow people to quote users who have consented to it, notifying them in the process, and allowing seamless display and navigation of such quotes. We also want to allow users to later revoke individual quotes if they need to, or change their quoting policy after the fact so that e.g. a post getting viral does not lead to an unmanageable amount of quotes and notifications.

    To this end, we are proposing the following draft, which we will shortly adapt into one or more FEPs.

    Thanks @trwnh for their reviews and suggestions, particularly around using result and instrument to split concerns and avoid requiring new server-side side-effects.

    Keep in mind this is not final. In particular, we are considering re-using GoToSocial's interaction policies instead of our custom acceptsQuotesFrom property if they get fixed to address our concerns.

    There is also an opportunity to change the representation of quotes themselves, though we used the combination of FEP-e232 and https://misskey-hub.net/ns#_misskey_quoterel attribute for compatibility with existing implementations.

    FEP-xxxx: Quote posts and quote control

    Summary

    This is a work-in-progress document describing Mastodon's proposed way of representing quote posts, users' choices regarding whether their posts can be quoted and by whom (quote policies), and a mechanism for servers to verify compliance with such policies.

    This document describes protocol considerations, which do not necessarily translate directly to User Experience considerations. For instance, the use of the approval mechanism described in this document does not imply that the user's approval is manual.

    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 document, “quoted object” refers to the object being quoted, “original author” to its author, and “quote post” refers to the object quoting the “quoted object”.

    Representation of a quote post

    A “quote post” is represented as an object with an Object Link (FEP-e232) to a “quoted object” using https://misskey-hub.net/ns#_misskey_quote as a link relation.

    Example

    { "@context": [ "https://www.w3.org/ns/activitystreams" ], "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", } ]}

    Advertising a quote policy

    Users may not want their posts to be quoted, or not by everyone. Therefore, we offer a way to declare quote policies using a new acceptsQuotesFrom (http://joinmastodon.org/ns#acceptsQuotesFrom) attribute.

    The author of the object, as well as mentioned users, can always quote the object, regardless of whether they appear in acceptsQuotesFrom.

    To advertise a policy of disallowing all quotes, acceptQuotesFrom MUST contain the object author's identifier as its single value. This is because an empty array is equivalent to a missing property under JSON-LD canonicalization.

    acceptsQuotesFrom SHOULD be restricted to individual actors, the special public collection https://www.w3.org/ns/activitystreams#Public, the author's followers collection, and the author's following collection.

    Example

    { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "acceptsQuotesFrom": { "@id": "toot:acceptsQuotesFrom", "@type": "@id" } } ], "acceptsQuotesFrom": "https://example.com/users/alice/followers", "type": "Note", "id": "https://example.com/users/alice/statuses/1", "attributedTo": "https://example.com/users/alice", "to": "https://www.w3.org/ns/activitystreams#Public", "content": "I allow my followers to quote this post"}

    Approval stamps

    In order to enforce a policy, we rely on approval stamps, a mechanism used to tell third-party servers that a quote is approved, regardless of the current state of the policy.

    Quote approval stamps are objects of the type QuoteAuthorization (http://joinmastodon.org/ns#QuoteAuthorization).

    The object attribute MUST reference the accepted quote post, the target attribute MUST reference the quoted object, and the attributedTo MUST correspond to the author of the quoted object.

    A QuoteAuthorization object MUST be dereferenceable by all parties allowed to see the original post, and MAY be publicly dereferenceable. It MUST NOT embed its object as to avoid possible information leaks. For the same reason, it MUST NOT embed its target object if the server is unable to verify that the party dereferencing the object has permission to see the quoted object.

    When a third-party attempts to dereference the QuoteAuthorization, the target MAY be inlined if the third-party has permission to access the quoted object. This is so that the third-party does not have to perform a second request to access the quoted object.

    Example of QuoteAuthorization

    The following stamp can be used to prove that actor https://example.com/users/alice has accepted https://example.org/users/bob/statuses/1 as a quote of her post https://example.com/users/alice/statuses/1:

    { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "QuoteAuthorization": "toot:QuoteAuthorization" } ], "type": "QuoteAuthorization", "id": "https://example.com/users/alice/stamps/1", "attributedTo": "https://example.com/users/alice", "object": "https://example.org/users/bob/statuses/1", "target": "https://example.com/users/alice/statuses/1"}

    Verifying a QuoteAuthorization

    To be considered valid for a particular quote post, the QuoteAuthorization object referenced in quoteApproval MUST satisfy the following properties:

    • its object is the quote post under consideration
    • its target property is the quoted object
    • its attributedTo property is the author of its object
    • the authenticity of the object can be asserted

    Revoking a QuoteAuthorization

    An approval stamp can be revoked by Deleteing the stamp.

    Quote request activity

    The Quote (http://joinmastodon.org/ns#Quote) activity type is introduced to request approval for a quote post.

    The Quote activity uses the object property to refer to the quoted object, and the instrument property to refer to the quote post.

    Example Quote activity

    { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": { "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", } ] }}

    Receiving, accepting or rejecting quote posts

    When receiving a Quote activity, the original author decides (either manually or automatically) whether the quote is acceptable. Software that automatically accepts quotes on the author's behalf should notify the author of such quotes according to their notification settings.

    The receiving end MAY inspect the instrument of the Quote itself to decide whether it is acceptable.

    If the quote post is considered acceptable, the original author MUST reply with an Accept activity with the Quote activity as its object, and a QuoteAuthorization as its result.

    If the quote post is considered unacceptable, the authority SHOULD reply with a Reject activity with the Quote activity as its object.

    Example Accept

    { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Accept", "to": "https://example.com/users/bob", "id": "https://example.com/users/alice/activities/1234", "actor": "https://example.com/users/alice", "object": { "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": "https://example.org/users/bob/statuses/1" }, "result": "https://example.com/users/alice/stamps/1"}

    Example Reject

    { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Reject", "to": "https://example.com/users/bob", "id": "https://example.com/users/alice/activities/1234", "actor": "https://example.com/users/alice", "object": { "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": "https://example.org/users/bob/statuses/1" }}

    Requesting, obtaining and validating approval

    In order to get approval, the quote post author MUST send a Quote (http://joinmastodon.org/ns#Quote) activity to the author of the quote post, with the quoted object as its object property and the quote post as its instrument.

    The quote post SHOULD be inlined in the instrument property and, if not, it SHOULD dereferenceable by the recipient at this point, as the author of the quoted object may want to inspect it to decide whether to accept the quote.

    The quote post author MAY wait until they receive an Approve or Reject activity before sending the post's Create activity to its intended audience.Doing so is possible for ActivityPub servers that implement the current proposal, and avoids having to issue an Update soon afterwards the Create for the quote post.It is however not possible to implement for ActivityPub clients, which will likely need to issue a Create before the Quote activity.

    Rejection

    If the author of the quote post receives a Reject activity from the quoted object's author to their Quote activity, they MUST consider the quote post to be explicitly rejected.

    If the implementation waits for the Accept before issuing a Create, this MAY translate as the inability to publish the quote post.

    Otherwise, it MAY translate as a Delete to outright remove the quote post, or an Update to remove the quote part from the post.

    Acceptance

    If the author of the quote receives an Approve activity, they MUST add a reference to its result in the approvedBy property of the relevant object link.

    Depending on whether they already sent a Create activity to the quote post's intended audience, they SHOULD send a Create activity or an Update activity with the updated approvedBy property.

    Example updated Note object

    { "@context": [ "https://www.w3.org/ns/activitystreams", { "gts": "https://gotosocial.org/ns#", "approvedBy": { "@id": "gts:approvedBy", "@type": "@id" } } ], "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", "approvedBy": "https://example.com/users/alice/stamps/1" } ]}

    Verifying third-party quote posts

    When processing a quote post from a remote actor, a recipient MUST consider them unapproved unless any of those conditions apply:

    • the author of the quote post and that of the original post are the same (same attributedTo)
    • the author of the quote post is mentioned in the original post
    • approvedBy exists, can be dereferenced and is a valid QuoteAuthorization activity for this object

    Revocation of a quote post

    Revoking a previously-accepted quote post

    The original author may want to perform /a posteriori/ moderation of the quote posts, or block a quote poster in particular.

    To do this, the original actor MUST Delete the QuoteAuthorization. They SHOULD send the Delete activity to the quote post's author and any recipient it has reasons to think has accessed the quote post.

    The original author MUST NOT embed the object nor the target of the QuoteAuthorization, so as to avoid potential information leakage.

    Handling a revocation

    Upon receiving a Delete activity for a previously-verified QuoteAuthorization, third-parties MUST check that the Delete is valid and MUST subsequently consider the quote post unapproved.

    Additionally, if the recipient owns the quote post, it MUST forward the Delete to the audience of the quote post.

    Opportunistic re-verification of quote approvals

    Because getting revocation properly forwarded depends on the good will of the revoked post's author, it may be necessary to have other means of checking whether an approval has been revoked.

    For this reason, recipients SHOULD re-check the approvedBy document when an already-known quote post is accessed for the first time in a given period of time.

    Server behavior considerations

    This proposal has been made with great care to not require new server behavior, allowing ActivityPub clients to implement this proposal without requiring generic ActivityPub server software to implement additional logic.

    In particular, this is the reason the approval stamp is a separate object rather than the Accept itself. Indeed, nothing in the ActivityPub specification would cause a Reject or Undo activity to invalidate the Accept activity itself, which means it would not be suitable as an approval stamp.While ActivityPub does not technically forbid Accept activities to be the target of a Delete activity, we have found no precedent for that, and we anticipate that deleting activities might not be correctly handled across the fediverse.For this reason, we opted to use a separate object that can be directly managed by an ActivityPub client, for instance by issuing a Create activity ahead of sending the Accept activity, and that can be deleted with the usual mechanism.

    An alternative approach we considered is using a dedicated endpoint to check for approval of a quote. This would effectively allow externalizing approval verification to a separate mechanism, but while this would not require new server behavior, this would still require a new server component to be specified, which is why we opted for the mechanism described in this specification instead.

    Security considerations

    Servers not implementing this FEP will still be able to quote the post and provide no dogpiling-reducing friction. There is unfortunately nothing we can do about that.

    Effectively revoking quote posts relies on the participation of the quote poster's server to effectively reach the audience of the quote post, so an ill-intentioned server could quote post then refuse to forward the revocation. Still, this can arise on well-intentioned servers too, so the feature remains useful. Opportunistic re-verification of quote approvals should also help.

    By not adding a hash or copy of the reply in the QuoteAuthorization object, 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 QuoteAuthorization object would have many drawbacks:

    • significantly more complex implementation
    • inability to change the JSON-LD representation after the fact
    • possibly leaking private information if the QuoteAuthorization object 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.

    trwnh@socialhub.activitypub.rocksT dumspterqueer@socialhub.activitypub.rocksD 2 Replies Last reply
    0
    • claire@socialhub.activitypub.rocksC claire@socialhub.activitypub.rocks

      Hi!

      As you may be aware, Quote Posts are an often-requested feature for Mastodon, but this is something we are careful to implement properly, offering a proper framework for users to consent to being quoted.

      Of course, no mechanism will be airtight, there will always be ways to quote someone else, by manually repeating their words, using screenshots and so on. Nevertheless, providing a framework for consent, and adding friction in the absence of consent, is worthwhile. Many existing social network services which feature Quote Posts also offer users at least some control over who can Quote them (on the most basic end, Twitter does not allow people to quote someone who blocked them, while Bluesky, Tumblr and formely the now defunct Cohost allow users to opt out of being quoted, and Bluesky allows to “detach“/revoke individual quotes). We strongly think that such mechanisms are useful, even if there are always ways around them.

      Our goal with Quote Posts is allow people to quote users who have consented to it, notifying them in the process, and allowing seamless display and navigation of such quotes. We also want to allow users to later revoke individual quotes if they need to, or change their quoting policy after the fact so that e.g. a post getting viral does not lead to an unmanageable amount of quotes and notifications.

      To this end, we are proposing the following draft, which we will shortly adapt into one or more FEPs.

      Thanks @trwnh for their reviews and suggestions, particularly around using result and instrument to split concerns and avoid requiring new server-side side-effects.

      Keep in mind this is not final. In particular, we are considering re-using GoToSocial's interaction policies instead of our custom acceptsQuotesFrom property if they get fixed to address our concerns.

      There is also an opportunity to change the representation of quotes themselves, though we used the combination of FEP-e232 and https://misskey-hub.net/ns#_misskey_quoterel attribute for compatibility with existing implementations.

      FEP-xxxx: Quote posts and quote control

      Summary

      This is a work-in-progress document describing Mastodon's proposed way of representing quote posts, users' choices regarding whether their posts can be quoted and by whom (quote policies), and a mechanism for servers to verify compliance with such policies.

      This document describes protocol considerations, which do not necessarily translate directly to User Experience considerations. For instance, the use of the approval mechanism described in this document does not imply that the user's approval is manual.

      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 document, “quoted object” refers to the object being quoted, “original author” to its author, and “quote post” refers to the object quoting the “quoted object”.

      Representation of a quote post

      A “quote post” is represented as an object with an Object Link (FEP-e232) to a “quoted object” using https://misskey-hub.net/ns#_misskey_quote as a link relation.

      Example

      { "@context": [ "https://www.w3.org/ns/activitystreams" ], "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", } ]}

      Advertising a quote policy

      Users may not want their posts to be quoted, or not by everyone. Therefore, we offer a way to declare quote policies using a new acceptsQuotesFrom (http://joinmastodon.org/ns#acceptsQuotesFrom) attribute.

      The author of the object, as well as mentioned users, can always quote the object, regardless of whether they appear in acceptsQuotesFrom.

      To advertise a policy of disallowing all quotes, acceptQuotesFrom MUST contain the object author's identifier as its single value. This is because an empty array is equivalent to a missing property under JSON-LD canonicalization.

      acceptsQuotesFrom SHOULD be restricted to individual actors, the special public collection https://www.w3.org/ns/activitystreams#Public, the author's followers collection, and the author's following collection.

      Example

      { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "acceptsQuotesFrom": { "@id": "toot:acceptsQuotesFrom", "@type": "@id" } } ], "acceptsQuotesFrom": "https://example.com/users/alice/followers", "type": "Note", "id": "https://example.com/users/alice/statuses/1", "attributedTo": "https://example.com/users/alice", "to": "https://www.w3.org/ns/activitystreams#Public", "content": "I allow my followers to quote this post"}

      Approval stamps

      In order to enforce a policy, we rely on approval stamps, a mechanism used to tell third-party servers that a quote is approved, regardless of the current state of the policy.

      Quote approval stamps are objects of the type QuoteAuthorization (http://joinmastodon.org/ns#QuoteAuthorization).

      The object attribute MUST reference the accepted quote post, the target attribute MUST reference the quoted object, and the attributedTo MUST correspond to the author of the quoted object.

      A QuoteAuthorization object MUST be dereferenceable by all parties allowed to see the original post, and MAY be publicly dereferenceable. It MUST NOT embed its object as to avoid possible information leaks. For the same reason, it MUST NOT embed its target object if the server is unable to verify that the party dereferencing the object has permission to see the quoted object.

      When a third-party attempts to dereference the QuoteAuthorization, the target MAY be inlined if the third-party has permission to access the quoted object. This is so that the third-party does not have to perform a second request to access the quoted object.

      Example of QuoteAuthorization

      The following stamp can be used to prove that actor https://example.com/users/alice has accepted https://example.org/users/bob/statuses/1 as a quote of her post https://example.com/users/alice/statuses/1:

      { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "QuoteAuthorization": "toot:QuoteAuthorization" } ], "type": "QuoteAuthorization", "id": "https://example.com/users/alice/stamps/1", "attributedTo": "https://example.com/users/alice", "object": "https://example.org/users/bob/statuses/1", "target": "https://example.com/users/alice/statuses/1"}

      Verifying a QuoteAuthorization

      To be considered valid for a particular quote post, the QuoteAuthorization object referenced in quoteApproval MUST satisfy the following properties:

      • its object is the quote post under consideration
      • its target property is the quoted object
      • its attributedTo property is the author of its object
      • the authenticity of the object can be asserted

      Revoking a QuoteAuthorization

      An approval stamp can be revoked by Deleteing the stamp.

      Quote request activity

      The Quote (http://joinmastodon.org/ns#Quote) activity type is introduced to request approval for a quote post.

      The Quote activity uses the object property to refer to the quoted object, and the instrument property to refer to the quote post.

      Example Quote activity

      { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": { "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", } ] }}

      Receiving, accepting or rejecting quote posts

      When receiving a Quote activity, the original author decides (either manually or automatically) whether the quote is acceptable. Software that automatically accepts quotes on the author's behalf should notify the author of such quotes according to their notification settings.

      The receiving end MAY inspect the instrument of the Quote itself to decide whether it is acceptable.

      If the quote post is considered acceptable, the original author MUST reply with an Accept activity with the Quote activity as its object, and a QuoteAuthorization as its result.

      If the quote post is considered unacceptable, the authority SHOULD reply with a Reject activity with the Quote activity as its object.

      Example Accept

      { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Accept", "to": "https://example.com/users/bob", "id": "https://example.com/users/alice/activities/1234", "actor": "https://example.com/users/alice", "object": { "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": "https://example.org/users/bob/statuses/1" }, "result": "https://example.com/users/alice/stamps/1"}

      Example Reject

      { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Reject", "to": "https://example.com/users/bob", "id": "https://example.com/users/alice/activities/1234", "actor": "https://example.com/users/alice", "object": { "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": "https://example.org/users/bob/statuses/1" }}

      Requesting, obtaining and validating approval

      In order to get approval, the quote post author MUST send a Quote (http://joinmastodon.org/ns#Quote) activity to the author of the quote post, with the quoted object as its object property and the quote post as its instrument.

      The quote post SHOULD be inlined in the instrument property and, if not, it SHOULD dereferenceable by the recipient at this point, as the author of the quoted object may want to inspect it to decide whether to accept the quote.

      The quote post author MAY wait until they receive an Approve or Reject activity before sending the post's Create activity to its intended audience.Doing so is possible for ActivityPub servers that implement the current proposal, and avoids having to issue an Update soon afterwards the Create for the quote post.It is however not possible to implement for ActivityPub clients, which will likely need to issue a Create before the Quote activity.

      Rejection

      If the author of the quote post receives a Reject activity from the quoted object's author to their Quote activity, they MUST consider the quote post to be explicitly rejected.

      If the implementation waits for the Accept before issuing a Create, this MAY translate as the inability to publish the quote post.

      Otherwise, it MAY translate as a Delete to outright remove the quote post, or an Update to remove the quote part from the post.

      Acceptance

      If the author of the quote receives an Approve activity, they MUST add a reference to its result in the approvedBy property of the relevant object link.

      Depending on whether they already sent a Create activity to the quote post's intended audience, they SHOULD send a Create activity or an Update activity with the updated approvedBy property.

      Example updated Note object

      { "@context": [ "https://www.w3.org/ns/activitystreams", { "gts": "https://gotosocial.org/ns#", "approvedBy": { "@id": "gts:approvedBy", "@type": "@id" } } ], "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", "approvedBy": "https://example.com/users/alice/stamps/1" } ]}

      Verifying third-party quote posts

      When processing a quote post from a remote actor, a recipient MUST consider them unapproved unless any of those conditions apply:

      • the author of the quote post and that of the original post are the same (same attributedTo)
      • the author of the quote post is mentioned in the original post
      • approvedBy exists, can be dereferenced and is a valid QuoteAuthorization activity for this object

      Revocation of a quote post

      Revoking a previously-accepted quote post

      The original author may want to perform /a posteriori/ moderation of the quote posts, or block a quote poster in particular.

      To do this, the original actor MUST Delete the QuoteAuthorization. They SHOULD send the Delete activity to the quote post's author and any recipient it has reasons to think has accessed the quote post.

      The original author MUST NOT embed the object nor the target of the QuoteAuthorization, so as to avoid potential information leakage.

      Handling a revocation

      Upon receiving a Delete activity for a previously-verified QuoteAuthorization, third-parties MUST check that the Delete is valid and MUST subsequently consider the quote post unapproved.

      Additionally, if the recipient owns the quote post, it MUST forward the Delete to the audience of the quote post.

      Opportunistic re-verification of quote approvals

      Because getting revocation properly forwarded depends on the good will of the revoked post's author, it may be necessary to have other means of checking whether an approval has been revoked.

      For this reason, recipients SHOULD re-check the approvedBy document when an already-known quote post is accessed for the first time in a given period of time.

      Server behavior considerations

      This proposal has been made with great care to not require new server behavior, allowing ActivityPub clients to implement this proposal without requiring generic ActivityPub server software to implement additional logic.

      In particular, this is the reason the approval stamp is a separate object rather than the Accept itself. Indeed, nothing in the ActivityPub specification would cause a Reject or Undo activity to invalidate the Accept activity itself, which means it would not be suitable as an approval stamp.While ActivityPub does not technically forbid Accept activities to be the target of a Delete activity, we have found no precedent for that, and we anticipate that deleting activities might not be correctly handled across the fediverse.For this reason, we opted to use a separate object that can be directly managed by an ActivityPub client, for instance by issuing a Create activity ahead of sending the Accept activity, and that can be deleted with the usual mechanism.

      An alternative approach we considered is using a dedicated endpoint to check for approval of a quote. This would effectively allow externalizing approval verification to a separate mechanism, but while this would not require new server behavior, this would still require a new server component to be specified, which is why we opted for the mechanism described in this specification instead.

      Security considerations

      Servers not implementing this FEP will still be able to quote the post and provide no dogpiling-reducing friction. There is unfortunately nothing we can do about that.

      Effectively revoking quote posts relies on the participation of the quote poster's server to effectively reach the audience of the quote post, so an ill-intentioned server could quote post then refuse to forward the revocation. Still, this can arise on well-intentioned servers too, so the feature remains useful. Opportunistic re-verification of quote approvals should also help.

      By not adding a hash or copy of the reply in the QuoteAuthorization object, 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 QuoteAuthorization object would have many drawbacks:

      • significantly more complex implementation
      • inability to change the JSON-LD representation after the fact
      • possibly leaking private information if the QuoteAuthorization object 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.

      trwnh@socialhub.activitypub.rocksT This user is from outside of this forum
      trwnh@socialhub.activitypub.rocksT This user is from outside of this forum
      trwnh@socialhub.activitypub.rocks
      wrote on last edited by
      #2

      regarding object and target having a domain of Activity, my suggestion was/is to define new properties specific to the stamp object and its QuoteAuthorization type. there's no real reason to stick to AS2 Vocab here. if you were to reuse AS2 Vocab then the only properties applicable are context (signaling purpose) and inReplyTo (signaling response). this technically works in a semantic sense, but only in a broad general sense. "the authorization stamp exists in context of the quoted post, and it is a response to the quote post."

      the more specific way to describe it is to instead define something like authorizationOf and authorizationScope or whatever names you wanted to bikeshed.

      1 Reply Last reply
      0
      • claire@socialhub.activitypub.rocksC claire@socialhub.activitypub.rocks

        Hi!

        As you may be aware, Quote Posts are an often-requested feature for Mastodon, but this is something we are careful to implement properly, offering a proper framework for users to consent to being quoted.

        Of course, no mechanism will be airtight, there will always be ways to quote someone else, by manually repeating their words, using screenshots and so on. Nevertheless, providing a framework for consent, and adding friction in the absence of consent, is worthwhile. Many existing social network services which feature Quote Posts also offer users at least some control over who can Quote them (on the most basic end, Twitter does not allow people to quote someone who blocked them, while Bluesky, Tumblr and formely the now defunct Cohost allow users to opt out of being quoted, and Bluesky allows to “detach“/revoke individual quotes). We strongly think that such mechanisms are useful, even if there are always ways around them.

        Our goal with Quote Posts is allow people to quote users who have consented to it, notifying them in the process, and allowing seamless display and navigation of such quotes. We also want to allow users to later revoke individual quotes if they need to, or change their quoting policy after the fact so that e.g. a post getting viral does not lead to an unmanageable amount of quotes and notifications.

        To this end, we are proposing the following draft, which we will shortly adapt into one or more FEPs.

        Thanks @trwnh for their reviews and suggestions, particularly around using result and instrument to split concerns and avoid requiring new server-side side-effects.

        Keep in mind this is not final. In particular, we are considering re-using GoToSocial's interaction policies instead of our custom acceptsQuotesFrom property if they get fixed to address our concerns.

        There is also an opportunity to change the representation of quotes themselves, though we used the combination of FEP-e232 and https://misskey-hub.net/ns#_misskey_quoterel attribute for compatibility with existing implementations.

        FEP-xxxx: Quote posts and quote control

        Summary

        This is a work-in-progress document describing Mastodon's proposed way of representing quote posts, users' choices regarding whether their posts can be quoted and by whom (quote policies), and a mechanism for servers to verify compliance with such policies.

        This document describes protocol considerations, which do not necessarily translate directly to User Experience considerations. For instance, the use of the approval mechanism described in this document does not imply that the user's approval is manual.

        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 document, “quoted object” refers to the object being quoted, “original author” to its author, and “quote post” refers to the object quoting the “quoted object”.

        Representation of a quote post

        A “quote post” is represented as an object with an Object Link (FEP-e232) to a “quoted object” using https://misskey-hub.net/ns#_misskey_quote as a link relation.

        Example

        { "@context": [ "https://www.w3.org/ns/activitystreams" ], "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", } ]}

        Advertising a quote policy

        Users may not want their posts to be quoted, or not by everyone. Therefore, we offer a way to declare quote policies using a new acceptsQuotesFrom (http://joinmastodon.org/ns#acceptsQuotesFrom) attribute.

        The author of the object, as well as mentioned users, can always quote the object, regardless of whether they appear in acceptsQuotesFrom.

        To advertise a policy of disallowing all quotes, acceptQuotesFrom MUST contain the object author's identifier as its single value. This is because an empty array is equivalent to a missing property under JSON-LD canonicalization.

        acceptsQuotesFrom SHOULD be restricted to individual actors, the special public collection https://www.w3.org/ns/activitystreams#Public, the author's followers collection, and the author's following collection.

        Example

        { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "acceptsQuotesFrom": { "@id": "toot:acceptsQuotesFrom", "@type": "@id" } } ], "acceptsQuotesFrom": "https://example.com/users/alice/followers", "type": "Note", "id": "https://example.com/users/alice/statuses/1", "attributedTo": "https://example.com/users/alice", "to": "https://www.w3.org/ns/activitystreams#Public", "content": "I allow my followers to quote this post"}

        Approval stamps

        In order to enforce a policy, we rely on approval stamps, a mechanism used to tell third-party servers that a quote is approved, regardless of the current state of the policy.

        Quote approval stamps are objects of the type QuoteAuthorization (http://joinmastodon.org/ns#QuoteAuthorization).

        The object attribute MUST reference the accepted quote post, the target attribute MUST reference the quoted object, and the attributedTo MUST correspond to the author of the quoted object.

        A QuoteAuthorization object MUST be dereferenceable by all parties allowed to see the original post, and MAY be publicly dereferenceable. It MUST NOT embed its object as to avoid possible information leaks. For the same reason, it MUST NOT embed its target object if the server is unable to verify that the party dereferencing the object has permission to see the quoted object.

        When a third-party attempts to dereference the QuoteAuthorization, the target MAY be inlined if the third-party has permission to access the quoted object. This is so that the third-party does not have to perform a second request to access the quoted object.

        Example of QuoteAuthorization

        The following stamp can be used to prove that actor https://example.com/users/alice has accepted https://example.org/users/bob/statuses/1 as a quote of her post https://example.com/users/alice/statuses/1:

        { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "QuoteAuthorization": "toot:QuoteAuthorization" } ], "type": "QuoteAuthorization", "id": "https://example.com/users/alice/stamps/1", "attributedTo": "https://example.com/users/alice", "object": "https://example.org/users/bob/statuses/1", "target": "https://example.com/users/alice/statuses/1"}

        Verifying a QuoteAuthorization

        To be considered valid for a particular quote post, the QuoteAuthorization object referenced in quoteApproval MUST satisfy the following properties:

        • its object is the quote post under consideration
        • its target property is the quoted object
        • its attributedTo property is the author of its object
        • the authenticity of the object can be asserted

        Revoking a QuoteAuthorization

        An approval stamp can be revoked by Deleteing the stamp.

        Quote request activity

        The Quote (http://joinmastodon.org/ns#Quote) activity type is introduced to request approval for a quote post.

        The Quote activity uses the object property to refer to the quoted object, and the instrument property to refer to the quote post.

        Example Quote activity

        { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": { "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", } ] }}

        Receiving, accepting or rejecting quote posts

        When receiving a Quote activity, the original author decides (either manually or automatically) whether the quote is acceptable. Software that automatically accepts quotes on the author's behalf should notify the author of such quotes according to their notification settings.

        The receiving end MAY inspect the instrument of the Quote itself to decide whether it is acceptable.

        If the quote post is considered acceptable, the original author MUST reply with an Accept activity with the Quote activity as its object, and a QuoteAuthorization as its result.

        If the quote post is considered unacceptable, the authority SHOULD reply with a Reject activity with the Quote activity as its object.

        Example Accept

        { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Accept", "to": "https://example.com/users/bob", "id": "https://example.com/users/alice/activities/1234", "actor": "https://example.com/users/alice", "object": { "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": "https://example.org/users/bob/statuses/1" }, "result": "https://example.com/users/alice/stamps/1"}

        Example Reject

        { "@context": [ "https://www.w3.org/ns/activitystreams", { "toot": "http://joinmastodon.org/ns#", "Quote": "toot:Quote" } ], "type": "Reject", "to": "https://example.com/users/bob", "id": "https://example.com/users/alice/activities/1234", "actor": "https://example.com/users/alice", "object": { "type": "Quote", "id": "https://example.com/users/bob/statuses/1/quote", "actor": "https://example.com/users/bob", "object": "https://example.com/users/alice/statuses/1", "instrument": "https://example.org/users/bob/statuses/1" }}

        Requesting, obtaining and validating approval

        In order to get approval, the quote post author MUST send a Quote (http://joinmastodon.org/ns#Quote) activity to the author of the quote post, with the quoted object as its object property and the quote post as its instrument.

        The quote post SHOULD be inlined in the instrument property and, if not, it SHOULD dereferenceable by the recipient at this point, as the author of the quoted object may want to inspect it to decide whether to accept the quote.

        The quote post author MAY wait until they receive an Approve or Reject activity before sending the post's Create activity to its intended audience.Doing so is possible for ActivityPub servers that implement the current proposal, and avoids having to issue an Update soon afterwards the Create for the quote post.It is however not possible to implement for ActivityPub clients, which will likely need to issue a Create before the Quote activity.

        Rejection

        If the author of the quote post receives a Reject activity from the quoted object's author to their Quote activity, they MUST consider the quote post to be explicitly rejected.

        If the implementation waits for the Accept before issuing a Create, this MAY translate as the inability to publish the quote post.

        Otherwise, it MAY translate as a Delete to outright remove the quote post, or an Update to remove the quote part from the post.

        Acceptance

        If the author of the quote receives an Approve activity, they MUST add a reference to its result in the approvedBy property of the relevant object link.

        Depending on whether they already sent a Create activity to the quote post's intended audience, they SHOULD send a Create activity or an Update activity with the updated approvedBy property.

        Example updated Note object

        { "@context": [ "https://www.w3.org/ns/activitystreams", { "gts": "https://gotosocial.org/ns#", "approvedBy": { "@id": "gts:approvedBy", "@type": "@id" } } ], "type": "Note", "id": "https://example.com/users/bob/statuses/1", "attributedTo": "https://example.com/users/bob", "to": [ "https://www.w3.org/ns/activitystreams#Public", "https://example.com/users/alice" ], "content": "I am quoting alice's postRE: https://example.com/users/alice/statuses/1", "tag": [ { "type": "Link", "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "href": "https://example.com/users/alice/statuses/1", "rel": "https://misskey-hub.net/ns#_misskey_quote", "approvedBy": "https://example.com/users/alice/stamps/1" } ]}

        Verifying third-party quote posts

        When processing a quote post from a remote actor, a recipient MUST consider them unapproved unless any of those conditions apply:

        • the author of the quote post and that of the original post are the same (same attributedTo)
        • the author of the quote post is mentioned in the original post
        • approvedBy exists, can be dereferenced and is a valid QuoteAuthorization activity for this object

        Revocation of a quote post

        Revoking a previously-accepted quote post

        The original author may want to perform /a posteriori/ moderation of the quote posts, or block a quote poster in particular.

        To do this, the original actor MUST Delete the QuoteAuthorization. They SHOULD send the Delete activity to the quote post's author and any recipient it has reasons to think has accessed the quote post.

        The original author MUST NOT embed the object nor the target of the QuoteAuthorization, so as to avoid potential information leakage.

        Handling a revocation

        Upon receiving a Delete activity for a previously-verified QuoteAuthorization, third-parties MUST check that the Delete is valid and MUST subsequently consider the quote post unapproved.

        Additionally, if the recipient owns the quote post, it MUST forward the Delete to the audience of the quote post.

        Opportunistic re-verification of quote approvals

        Because getting revocation properly forwarded depends on the good will of the revoked post's author, it may be necessary to have other means of checking whether an approval has been revoked.

        For this reason, recipients SHOULD re-check the approvedBy document when an already-known quote post is accessed for the first time in a given period of time.

        Server behavior considerations

        This proposal has been made with great care to not require new server behavior, allowing ActivityPub clients to implement this proposal without requiring generic ActivityPub server software to implement additional logic.

        In particular, this is the reason the approval stamp is a separate object rather than the Accept itself. Indeed, nothing in the ActivityPub specification would cause a Reject or Undo activity to invalidate the Accept activity itself, which means it would not be suitable as an approval stamp.While ActivityPub does not technically forbid Accept activities to be the target of a Delete activity, we have found no precedent for that, and we anticipate that deleting activities might not be correctly handled across the fediverse.For this reason, we opted to use a separate object that can be directly managed by an ActivityPub client, for instance by issuing a Create activity ahead of sending the Accept activity, and that can be deleted with the usual mechanism.

        An alternative approach we considered is using a dedicated endpoint to check for approval of a quote. This would effectively allow externalizing approval verification to a separate mechanism, but while this would not require new server behavior, this would still require a new server component to be specified, which is why we opted for the mechanism described in this specification instead.

        Security considerations

        Servers not implementing this FEP will still be able to quote the post and provide no dogpiling-reducing friction. There is unfortunately nothing we can do about that.

        Effectively revoking quote posts relies on the participation of the quote poster's server to effectively reach the audience of the quote post, so an ill-intentioned server could quote post then refuse to forward the revocation. Still, this can arise on well-intentioned servers too, so the feature remains useful. Opportunistic re-verification of quote approvals should also help.

        By not adding a hash or copy of the reply in the QuoteAuthorization object, 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 QuoteAuthorization object would have many drawbacks:

        • significantly more complex implementation
        • inability to change the JSON-LD representation after the fact
        • possibly leaking private information if the QuoteAuthorization object 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.

        dumspterqueer@socialhub.activitypub.rocksD This user is from outside of this forum
        dumspterqueer@socialhub.activitypub.rocksD This user is from outside of this forum
        dumspterqueer@socialhub.activitypub.rocks
        wrote on last edited by
        #3

        Just wanna add to this convo that we've been working with interaction policies in GoToSocial for a while now, and the protocol extension described here is extensible to include a canQuote property on an interaction policy: https://docs.gotosocial.org/en/latest/federation/interaction_policy/

        Reply, Like, and Announce controls and approvals etc are already running in GoToSocial since v0.17.0 last year. It would be pretty neato if Mastodon could reuse the interaction policy properties and approval flow for quote posts, insofar as that's possible. Have discussed this in the Mastodon discord with Claire as well, but just putting it here too.

        1 Reply Last reply
        0
        Reply
        • Reply as topic
        Log in to reply
        • Oldest to Newest
        • Newest to Oldest
        • Most Votes


        • Login

        • Don't have an account? Register

        • Login or register to search.
        Powered by NodeBB Contributors
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • World
        • Users
        • Groups