FEP-e232: Object Links

source: fep/fep-e232.md at main - fep - Codeberg.org


---
authors: @silverpill
status: DRAFT
dateReceived: 2022-08-01
---

FEP-e232: Object Links

Summary

This document proposes a way to represent text-based links to [ActivityPub] objects which are similar to mentions. One example of such link is inline quote within the value of the content property, but this proposal is not limited to any particular use case.

Requirements

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC-2119].

Object links

It is expected that software will allow users to define object links using some kind of microsyntax, similar to @mention and #hashtag microsyntaxes. The exact way of defining object links may vary depending on the use case and is out of scope of this document.

If an object’s name, summary, or content has qualified links to other objects, that object SHOULD have the tag property, where each object link is represented as a Link object, as suggested by [Activity Vocabulary]. The properties of this Link object are:

  • type (REQUIRED): the type MUST be Link or a subtype.
  • mediaType (REQUIRED): the media type MUST be application/ld+json; profile="https://www.w3.org/ns/activitystreams". This specification only deals with ActivityPub objects but in practice the media type can be different and servers MAY accept object links which do not comply with the requirement.
  • href (REQUIRED): the href property MUST contain the URI of the referenced object.
  • name (OPTIONAL): the name SHOULD match the microsyntax used in object’s content.
  • rel (OPTIONAL): if relevant, the rel SHOULD specify how the link is related to the current resource. Using rel can provide additional purpose to object links by signaling specific intended use-cases.

Example:

{
    "type": "Note",
    "content": "This is a quote:<br>RE: https://example.com/objects/123",
    "tag": [
        {
            "type": "Link",
            "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
            "href": "https://example.com/objects/123",
            "name": "RE: https://example.com/objects/123"
        }
    ]
}

Note that the content includes the RE: <url> microsyntax but consuming implementations are not required to parse that in order to make the appropriate associations.

Implementations

TBD

References

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.

1 Like

Hello!

FEP-e232 is a proposal for representing text-based links to ActivityPub objects using tags. The immediate goal is standardization of quotes (related issues: Misskey, Pleroma, Mastodon), but in the future this specification can be re-used for other kinds of cross-references (e.g. in federated wikis).

3 Likes

Thank you @silverpill for this proposal. For convenience in getting the word out and generating discussion, here’s a post you can reshare.

2 Likes

The draft has been reviewed, and several minor changes were made.
There’s also a proposal to use rel attribute instead of mediaType to differentiate object links from other types of links:

{
    "type": "Note",
    "content: "This is a quote:<br>RE: https://example.com/objects/123",
    "tag": [
        {
            "type": "Link",
            "rel": "https://www.w3.org/ns/activitystreams",
            "href": "https://example.com/objects/123",
            "name": "RE: https://example.com/objects/123"
        }
    ]
}

If there are no objections, this change will be accepted into the draft as well.

1 Like

I think there is a problem of the rel registry. My original implementation of this (before the FEP was created) used both ‘via’ and ‘cite-as’. Neither is a perfect match but are the closest existing rel attributes I could find. If an unregistered url is a legal rel value and/or if it’s submitted to the ietf I’m quite happy to go with that.

My understanding is that you only have to register simple keyword values (i.e. one might register activitystreams); if not registered, it MUST be a URL to indicate that it is an extension: RFC 8288: Web Linking

Applications that don’t wish to register a relation type can use an extension relation type, which is a URI [RFC3986] that uniquely identifies the relation type. […] The URI used for an extension relation type SHOULD be under the control of the person or party defining it or be delegated to them. […] When extension relation types are compared, they MUST be compared as strings (after converting to URIs if serialised in a different format) in a case-insensitive fashion, character by character. Because of this, all-lowercase URIs SHOULD be used for extension relations.


Tangentially, it should be mentioned in this thread that the points of consideration on using rel vs mediaType is as follows:

  • rel is unambiguously the ActivityStreams namespace, whereas mediaType could equally be either application/activity+json or application/ld+json; profile="https://www.w3.org/ns/activitystreams".
    • If it is acceptable to have two possible values, then this isn’t a concern.
  • mediaType is semantically more clear, but it specifically indicates what might be returned, rather than providing instructions on how to fetch. In other words, the presence of mediaType does not imply that you should specify a header of Accept: <mediaType> when fetching.
    • If it is acceptable to have the only implication be this FEP, then this is less of a concern.

If these two concerns are deemed to not be worthwhile, I have no problem with using mediaType and dropping the proposed change.

I’m all for having a link relation. And thanks for clarifying the URI is allowed - I’m happy with the proposed changes.

I’m probably the only person who might use a different mediaType so I appreciate the consideration, but I have no desire to die on that hill either. As long as there’s a ‘rel’ attribute we’ll always check to see if the mediaType is something we can work with, and/or keep looking to see if there’s a better one for our needs.

I’d be interested to know what other mediaType values you may use or expect!

I should probably also say, this isn’t a hill for me to die on either – I’m strongly convinced about using tag over attachment for marking up substrings of name/summary/content, but I could really go either way on mediaType vs rel. And mediaType is technically the “more correct” solution, I suppose; there might be value in software e.g. marking up a Link as application/magic-envelope+xml for Diaspora (if fetchable Diaspora resources are even a thing) or otherwise leaving the door open for other vocabularies, so to speak.

Actually, while typing this, I think I might have convinced myself…

1 Like

I would probably disagree that mediaType is more correct. The way we usually parse arrays of links of any kind is to start with the link relation to find the “purpose” (is this thing an object we care about?) and then loop on on the mediaType to choose the most appropriate file format for our needs (json, xml, yaml, turtle, jpeg, gif, mp3, mp4, whatever) and finally (if there are still choices to be made) examine specific media attributes such as height, width, duration, etc. to find the most appropriate representation. This is a model and workflow that has served us well since the early days of MIME. Without the link relation, the code to examine these things quickly becomes spaghetti.

2 Likes

Ah, okay. For what it’s worth, Misskey’s PR is using code primarily like so:

"tag": [
  {
    "type": "Link",
    "href": "URI",
    "name": "RE: URI",
    "rel": "https://misskey-hub.net/ns#_misskey_quote",
  }
]

With the current state of the PR, they also attach mediaType like so:

"tag": [
  {
    "type": "Link",
    "href": "URI",
    "name": "RE: URI",
    "rel": "https://misskey-hub.net/ns#_misskey_quote",
    "mediaType": "application/activity+json"
  }
]

With the current state of the FEP proposal, it should be like so:

"tag": [
  {
    "type": "Link",
    "href": "URI",
    "name": "RE: URI",
    "rel": "https://misskey-hub.net/ns#_misskey_quote",
    "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
  }
]

With the alternate FEP proposal, it should be like so:

"tag": [
  {
    "type": "Link",
    "href": "URI",
    "name": "RE: URI",
    "rel": ["https://www.w3.org/ns/activitystreams", "https://misskey-hub.net/ns#_misskey_quote"]
  }
]

For the purposes of parsing a Misskey quote, it should be enough to check for the _misskey_quote URI in the rel array. That covers the purpose of the link:

if "https://misskey-hub.net/ns#_misskey_quote" in rel:
    # treat it like a misskey quote

What is a bit of an open question, or rather, what is the point of discussion right now, is how to best signal that a certain media type should be explicitly included in the Accept header. It’s essentially a consequence of HTTP content negotiation.

I’m unsure if rel is appropriate; I initially proposed it to remove the ambiguity of multiple potential media types. But I later realized that multiple media types is less of an issue – either you understand the type or you don’t.

Given that the purpose of the FEP is purely to signal that a Link’s href may be fetched as an ActivityPub object, it later made sense to me to go back to mediaType rather than rel, and keeping rel focused on the actual relation between the link and the current resource. So future extensions would use rel to signal their purpose similarly to _misskey_quote, and the FEP would remain focused on signaling how to fetch the Object given only the Link.

In other words, what I realized is that the FEP’s text should be enough guidance to tell implementations that if they see a mediaType for ActivityPub, they should use the Accept header as they are required to do so under the main ActivityPub spec. There is no inherent purpose in object links, and their presence in tag basically only gives metadata similar to subtypes like Mention or Hashtag.

I think some language in the FEP should be added to make this distinction clear, and to encourage purposeful object links to attach a rel-value that signals their purpose.

2 Likes

In the latest revision of the FEP the mediaType and rel properties have the following descriptions:

  • mediaType (REQUIRED): the media type MUST be application/ld+json; profile="https://www.w3.org/ns/activitystreams". This specification only deals with ActivityPub objects but in practice the media type can be different and servers MAY accept object links which do not comply with the requirement.
  • rel (OPTIONAL): if relevant, the rel SHOULD specify how the link is related to the current resource. Using rel can provide additional purpose to object links by signaling specific intended use-cases.

I just tested this from Friendica and sent test posts to Friendica, Pleroma and Mastodon. And it seems as if Pleroma has got a problem with this. My tests posts don’t come through. But since I don’t operate a Pleroma server, I cannot say where the problem lies.

1 Like

I think overall this is quite un-JSON-LD where typing ends up being shoved into a mostly internal type (like Object, Activity and Actor also are) with just some metadata to try to match things.
And mediaType seems to be treated as something on which you could simply string-match onto, expect MIME doesn’t really works that way so it wouldn’t be a surprise to end up with slightly different representations.
This quite reminds me how HTML4 ended up with <div id="content" role="main">, making parsing stuck to heuristics-based and often dependent on the source.

I think something like a dedicated object type would make more sense, say:

{
  "type": "Quote", // Required
  "object": "https://example.org/objects/42", // Required
  "name": "RE: Object Links" // Optional
}

And like for ChatMessage, it could be added into the LitePub namespace.

Yeah, the validator is quite (too?) strict, only specific objects are supported inside tag.

Link is a general type and I think it’s exactly what we need. Consider the following applications:

  1. Wiki
link to [[article]], link to [[another article]]
  1. Imageboard
>>6513 reply to post 
>>6516 another reply
  1. Issue tracker
Bug #13
Merge request #123

FEP-e232 can be used in each of these cases, but the type Quote doesn’t seem be appropriate here.

I’ve opened an issue: Allow "Note" to have "Link" tags (#2952) · Issues · Pleroma / pleroma · GitLab

1 Like

Echoing what silverpill said: the FEP covers more than just Misskey-style quotes. The rel of a Link expresses its purpose by signaling how the linked resource is related to the current resource. This is why the Misskey PR uses "rel": "https://misskey-hub.net/ns#_misskey_quote" – this way, Misskey-aware implementations can treat it as a quote, but generic implementations just treat it as a link, except with this FEP they know it’s referring to an ActivityPub object specifically and can add their own special handling (for example, resolving the object locally)

mediaType is functional so it will be a string; the FEP specifies a specific value that can be matched onto.

Now, sure, it could be "mediaType": "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" or it could be "mediaType": "application/activity+json", that much is true. But the former is recommended (and specified) because that is how you MUST fetch objects in ActivityPub.

1 Like

[2022-10-09 19:10:09+0000] silverpill via SocialHub:

Link is a general type and I think it’s exactly what we need. Consider the following applications:
[Wiki, Imageboard/email quotes, Issue Tracker]

You’re probably misunderstanding, this isn’t to handle formatting of quotes like your examples are doing, HTML already manages that.

Post quotes (take them as references if you want, “Quote” is more branding than technical here) have protocol implications, typically the backend should try to fetch a quoted post and potentially do things like increasing a “Quotes” counter similarly to Like and Announce.
Consider that mentions also have their own object type, while if they would be using Link + mediaType it would have prevented Quotes or at least created a massive heuristics-based mess.
Potentially similar kind of things for other AP-metadata like hashtags (loose link to a collection of objects).

I’ve opened an issue: Allow "Note" to have "Link" tags (#2952) · Issues · Pleroma / pleroma · GitLab

I’m aware and I won’t fix it other than potentially dropping Link objects from the tag field.

1 Like

FEP-e232 recommends using rel attribute to specify the link subtype, so it should be future-proof. But I’m not strongly against using dedicated type. If there’s a better name for a type that other contributors would agree with, I would update the spec.

The issue is only about providing forward compatibility. I’m not asking for FEP-e232 support (because the spec is still in a draft stage and may change).

Not necessarily? A Mention is a sub-type of Link, specifically implying that the linked resource is an Actor. You could totally get away with not having a Mention type, or having a rel=mention if you really cared to add special handling for it. Protocol-wise, the presence of a Mention doesn’t imply anything special aside from maybe affecting the addressing, but the addressing is explicitly defined with to/cc on activities anyway. “Who to include in a reply” is already a heuristic thing – you might or might not include the person you are replying to, your own followers, the audience property if present on the object, previous recipients in to/cc on the related activity you received, and so on. Having a Mention linked in tag is just one more heuristic, really – and I would argue that’s not even its primary purpose! The primary purpose of a Mention is for implementations to know that @username should be linked to https://domain.tld/users/id or whatever. Just like a Hashtag links to the url of some collection of posts, or an Emoji links to the url of the image resource that should replace the shortcode. In each of these cases, they inherit from Link and are simply specialized links that imply a microsyntax, the processing of which is optional.

This is entirely optional and not relevant in all cases. If that is the intention of a “quote”, then yes, a Quote type could be defined as a sub-type of Link, with the expressed semantics and side effects, but that would be better as a separate extension; that extension would effectively replace rel=_misskey_quote with the Quote type, and should probably be paired with some quotes Collection similar to likes and shares, sure. If you want to define a litepub:Quote type and litepub:quotes Collection then go ahead. But for the generic case, all that is needed and defined for this FEP-e232 is that the type is Link or a subtype, and that mediaType is the ActivityPub content-type. This should be enough for clients to know that they can fetch the resource at href using ActivityPub. If you wanted to be specific about the logic, you shouldn’t even be matching against type, you should be matching on the presence of href and on the value of mediaType (since these are properties of Links, and since the type may be an extension you are unaware of!)

For robustness, you should probably filter the tag array and only keep understood/supported types. Anything not understood should be ignored instead of breaking processing entirely.

I decided to support wiki-like syntax in my application:

[[https://example.org/objects/1]]

or

[[https://example.org/objects/2|this is my link]]

The fist one is rendered as a link with URL as a text and the second one is rendered as a link with “this is my link” text.
Other instances won’t see this syntax, only the resulting HTML, so I’m wondering what should the name attribute of the Link tag contain. FEP-e232 states:

name (OPTIONAL): the name SHOULD match the microsyntax used in object’s content.

I understand it as if there’s no microsyntax, the name attribute can be omitted.

possibly, but for best practices you should try to match it to whatever substring of the natural language properties (name, summary, content) it ends up being in plain-text.

in example 1, i’d say name = "https://example.org/objects/1" and in example 2, i’d say name = "this is my link".

1 Like