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

The downside of this solution is that sender can insert arbitrary hidden text by creating .quote-inline elements (unless clients require inner text to match the RE: <URL> template exactly).

They can, but I’m not sure how much of that is an issue. It’s also already something that senders can do using some class names in multiple implementations. I am uneasy with mandating a exact content format here, especially since implementations may e.g. want to make that part an actual link, which would lead us to have to specify the exact HTML.

One solution I was thinking of since the original Object Links FEP appeared regarding this was to match the a element by the Link’s href and walking it’s parent elements until one has a textContent matching the name of the object link.

This seems like it would match quite a few use cases (crucially, both in cases where Link.name == a.textContent and in cases where there are surrounding decorations like the RE: prefix), and the only thing it’d require out of producers would be to wrap the text in any HTML element (even a plain span without any classes would do)

My main concern about this is I’m unsure about how many HTML tooling actually supports computing textContent.

I think the stamp can apply to other scopes without becoming ambiguous. Essentially, it would specify allowed activities, including actors, objects, and targets. So your example would become something like this

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

But again, please don’t think of this as an objection. It can be tackled as it’s own topic. And I see no reason that multiple kinds of authz tokens can’t coexist, so the existence of this specialized one doesn’t preclude a future generic token

1 Like

So, essentially moving the QuoteAuthorization to a generic Authorization with an activity property. I don’t see a fundamental difference in both approaches, but I can see why you’d consider this cleaner. I’m personally fine with this change!

1 Like

I was thinking more about this and wondered what we really wanted to achieve here. If we don’t require the RE: <URL> template in the spec, then people can still insert arbitrary hidden text, it would just have to also be in the Link.name, but in both cases this would be hidden from the user, and in both cases, inspecting the ActivityStreams object would surface it. Using the textContent would just make it harder to implement for clients which do not have convenient HTML parsing and CSS addressing.

the example seems confusing semantically; right now, it looks like an Authorization activity performed by bob and attributed to both alice and bob.

from a data modeling perspective i don’t particularly have any opposition to a base Authorization class that can be subclassed as LikeAuthorization, ShareAuthorization, ReplyAuthorization, QuoteAuthorization, and so on as needed.

i think it’s also possible to have a more generic “link approval” that can be an attribute of a generic web link? so take the approvedBy and make it not only a property of as:Link but also an attribute of Link headers or a/link HTML nodes? RFC 8288 - Web Linking – although it’s unclear when something is a string and when something is a URI

1 Like

I would personally like some usage such as:

<a href="https://quote.example/url" class="quote">RE: https://quote.example/url</a>

as it uses the same pattern already done for mentions and tags, e.g.

<a href="https://quote.example/@alice" class="mention">@alice@quote.example</a>
<a href="https://quote.example/topic" class="hashtag">#topic</a>

I’m not :100: sure about how rel relations work, see RFC 8288, but one might also consider the rel attribute, i.e.

<a href="https://quote.example/url" rel="quote">RE: https://quote.example/url</a>
<a href="https://quote.example/alice" rel="mention">@alice@quote.examplel</a>
<a href="https://quote.example/topic" rel="tag">#topic</a>

However, harmonizing these things should probably be the topic of a different FEP.

1 Like

We would like to create one FEP to standardise representing links to ActivityPub objects in content.

A big issue we have right now is that it is up to the receiving server or client to try to guess if a link has an AP representation or not, for example to decide if the link should be opened in-client or as a web URL, or to display them with some special UI (like Discord does when you paste a link to a message). We think that the authoring software should be the one doing this work. Object links are a good candidate for this but have a few missing things (I dont remember the details), so we are planning to try to harmonize and standardize this.

1 Like

After another round of thinking it would probably be enough to specify that if a link contains the class tag that one should look in the tag property of the object to obtain more information. Using a class also has the advantage that one could extend it to a span, e.g. use

<span class="tag">RE: <a href="https://host.example/obj">host.example/obj</a></span>

instead of

<a href="https://host.example/obj" class="tag">RE: host.example/obj</a></span>

Examples

{
  "content": "<a href='https://host.example/objId' class='tag'>host.example/objId</a>",
  "tag": [{
      "type": "Link",
      "href": "https://host.example/objId",
      "mediaType": "application/activity+json",
      "name": "host.example/objId"
  }]
}

to link to an ActivityPub object and

{
  "content": "<a href='https://host.example/page' class='tag'>host.example/page</a>",
  "tag": [{
      "type": "Link",
      "href": "https://host.example/page",
      "mediaType": "text/html",
      "name": "host.example/page"
  }]
}

to link to a webpage. Similarly, one can use

{
  "content": "<a href='https://host.example/alice' class='tag'>@alice</a>",
  "tag": [{
      "type": "Mention",
      "href": "https://host.example/alice",
      "name": "@alice"
  }]
}

for a mention. I’ll leave adding Hashtag to the reader.

Why not link relation?

The link relation tag is probably inappropriate for this, see HTML Standard

1 Like

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: Interaction Policy - GoToSocial Documentation

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.

3 Likes

We are of course not interested in breaking compatibility for the sake of breaking compatibility.

Reviewing the current proposal and GoToSocial’s current documentation, I will try to highlight the differences in the two proposals.

Advertising a quote policy

https://docs.gotosocial.org/en/latest/federation/interaction_policy/#overview

GoToSocial’s interaction policies do not define a quote policy, but following the pattern of canLike and so on, a natural extension would be canQuote.

The current proposal has toot:acceptsQuotesFrom which is roughly equivalent to gts:interactionPolicy.gts:canQuote.gts:always.

There are three differences:

  • vocabulary itself, the differences here are uninteresting
  • attribute nesting in GoToSocial’s case, while the current proposal has a flat top-level attribute. I still don’t think there is a strong argument for nesting attributes here, and it makes things more awkward. There is a precedent for that in ActivityPub through the endpoints attribute on actors, though
  • under the current proposal, you can only declare whether quotes are likely to be accepted with no more granularity, but GoToSocial has always and requiresApproval. I am not sure whether this distinction is worthwhile, and always might be a misnomer there (what it says is that approval is expected to be immediate and automatic, while requiresApproval says manual user approval is required)

While I have hang-ups with the last two differences, I don’t think they are a show-stopper by themselves.

Approval stamps

https://docs.gotosocial.org/en/latest/federation/interaction_policy/#approval-objects

GoToSocial’s documentation defines LikeApproval, ReplyApproval and AnnounceApproval that are analogous to the QuoteAuthorization defined in this proposal. GTS’ documentation is less clear and much more lenient on the definition of the attributes, but this uses the same object, target and attributedTo attributes as the current proposal.

Apart from the *Approval naming scheme being different to the *Authorization naming scheme, this is actually very similar (which makes sense since it has been adopted from in-progress discussion on the current proposal).

This also means that the objections against the re-use of object and target attributes are valid against both the current proposal and GTS’ implementation.

Quote request

The notion of quote requests is only found in the current proposal.

This makes sense, as GoToSocial does not implement quotes and did not attempt specifying them yet. But this is still a slight departure in the general approval flow, as we will come back to in the next section.

Requesting, obtaining and validating approval

https://docs.gotosocial.org/en/latest/federation/interaction_policy/#requesting-obtaining-and-validating-approval

The approval flow described in this proposal and that described in GoToSocial’s documentation are pretty different. Of course, GoToSocial not defining quote posts, we cannot have an exact direct comparison, but the closest to that is replies: if GoToSocial handled replies the same way that this proposal does, it would require a Reply activity that would be used to ask the replied-to-actor for an approval stamp to attach to the reply Note object, but this is not the case.

Instead, GoToSocial special-cases delivery with “DO NOT DISTRIBUTE THE ACTIVITY FURTHER THAN THIS AT THIS POINT”.

This requires S2S behaviour changes that we have been careful to avoid in our current specification with this following flow:

This is probably one of the most fundamental differences between GTS’ specification and ours. We could always re-use the vocabulary and specify a different flow for Quote and QuoteAuthorization, but having ad-hoc flows seems ugly and error-prone. Furthermore, this would be advocating for a specification that does require S2S special-casing, which we wanted to avoid in the first place.

Server behavior considerations are discussed at length in the current proposal: Pre-FEP: Quote posts, quote policies and quote controls

Revocation of a quote post

This is completely missing from GoToSocial’s specification, but it is not fundamentally incompatible with it. This could easily be added.

Opportunistic re-verification of quote approvals

This is completely missing from GoToSocial’s specification, but it is not fundamentally incompatible with it. This could easily be added.

1 Like

It seems like the biggest show stopper for mastodon adopting the same / similar thing to GtS is the fact that Reply or Like or Announce are sent only to the interactee, when trying to get approval, rather than ReplyRequest, LikeRequest, or AnnounceRequest or whatever else. I’d be happy to make changes in GtS to use these types (or something similar) instead if it provides better compatibility with “pure” activity pub servers that use the c2s api. Not an issue. The rest of the differences seem mostly stylistic to me I think, unless I’m missing anything!

Edit: maybe it’s worth renaming Quote to QuoteRequest or something like that to clarify that you’re sending it to seek approval.

Edit2: I would stand behind the “always” and “approvalRequired” distinction as I think it can provide useful UI benefits. V. similar to the “manuallyApprovesFollowers” flag for follow requests.

1 Like

I strongly agree that the “always” and “approvalRequired” distinction is very much useful!

I have now submitted an actual FEP: fep/fep/044f/fep-044f.md at main - fediverse/fep - Codeberg.org

This re-uses GoToSocial’sinteractionPolicy with some adjustments we have discussed with them.

1 Like

It appears that I was mistaken, and the use of FEP-e232 object links with a rel value of https://misskey-hub.net/ns#_misskey_quote was limited to a single Misskey fork which is currently under “limited maintenance”.

I think the use of FEP-e232 object links still kind of makes sense, and having everything (quoted object reference and approval) under a single object makes sense to us, but there is no reason to stick with https://misskey-hub.net/ns#_misskey_quote and we could go with a reference to the FEP itself, if it’s something that makes sense for other implementers as well.

We also have started implementing it in Mastodon, with specific limitations (only the first quote of a post is considered, interaction policies are flattened to a simpler model).

Our plan is to have inbound support for quote policies and quote posts in the next Mastodon release, and outbound policies and quote authoring in the next after that.

1 Like

Due to the earlier observation about https://misskey-hub.net/ns#_misskey_quote object links actually being only supported by FoundKey (to our knowledge), we are considering the following changes to the FEP:

  • switch from such object links to as:quoteUrl (linking to the quoted object; this is the most widely existing way to represent quote posts, despite not being specified yet) and a new quoteAuthorization property (holding a link to the corresponding QuoteAuthorization object)
  • rehome quoteAuthorization and every http://joinmastodon.org/ns# property defined in the FEP to a namespace under https://w3id.org/fep/044f/
  • revisit the wording around “ill-intentioned server”s in the “Security considerations” section to acknowledge that implementations may simply be unaware of the quote verification mechanism
  • eventually provide a static self-sufficient JSON-LD context document for everything defined in this FEP

The main drawback of these changes is that the use of as:quoteUrl and quoteAuthorization makes it more awkward to match authorization with quotes when there are multiple of them (which is technically allowed, but which we expect to be exceedingly rare, and which Mastodon does not plan on supporting). However, under the assumption that there is at most one quote, the implementation should not be more complex.

We will soon move forward with these changes unless there is opposition to them.

FoundKey is not the only implementation that produces links with https://misskey-hub.net/ns#_misskey_quote relation type. And most FEP-e232 implementations probably don’t check the relation type when consuming links because there is no such requirement in the FEP.

not all object links should be considered quotes.

i dislike that this property hijacks the authoritative w3.org namespace and also mistakenly refers to the object as a Url. i also think that “widely existing” is being overstated as a benefit for two reasons:

  • _misskey_quote is even more widely used
  • compatibility with existing representations is not inherently always a good thing

Interesting, do you have more examples? In any case, as:quoteUrl, fedibird:quoteUri and _misskey_quote seem to be by far the most common ways to represent a quote in the current fediverse.

As trwnh said, not all object links should be considered quotes, and we might want quotes to not be displayed the same way as generic object links, so I’m not sure this is an advantage for using object links for this purpose.

I hear you, I dislike as:quoteUrl for re-using ActivityStreams’ namespace without being defined there (though we have a bunch of other properties that already suffer from this) and being called an Url for no good reason.

  • I guess we could use _misskey_quote, but it feels even weirder wrt. the naming scheme, and I’m not sure this is actually more widely implemented than as:quoteUrl
  • this is a complex issue because we want to enforce quote controls, which are a new concept, but I believe there are still multiple benefits for otherwise remaining compatible: outgoing quotes would behave as expected, incoming self-quotes would behave as expected (they do not require an authorization stamp), and other unverifiable incoming quotes would be clearly identified as such