Q: Extensibility (how to determine whether an arbitrary AP object is a subclass of another class?)

This question is a slight generalization of an AP issue closed a few days ago:

One of the recommended techniques is to:

    1. Parse the context as JSON-LD, and identify if the type is a subtype of AS2:Activity. This is the “right way”.

I’m not an expert on JSON-LD but how is this done? In the existing AP JSON-LD context, I see no subtype information. For example, there doesn’t appear to be anything that could be used to know that a Create is a subtype of Activity. How would a new subtype of Activity, for example, be represented in a context extension?

i’m not entirely sure what evan meant by that, but option #3 (check for actor) is what works best in practice. this question has been posed on the w3c mailing list as well, as there seems to be no mechanism for subtyping in JSON-LD @context.

Thanks. Option #3 makes sense to me for Activities. For other subtyping relationships, it would be interesting to investigate if the same technique would work reliably (I’ve seen you suggest that “inbox” and “outbox” are hints that something is an Actor, for example).

I’ve looked at several AP implementations. So far, all of them use the “type” value to determine the type of an object (and typically have a hard-coded set of types they will process). For example, if an implementation extends OrderedCollection most implementations would ignore it as an unknown type (or treat it as an error) even though it is fully AP/AS2-compliant and included in the JSON-LD context (and could be reasonably processed by the implementation that only understands the base OrderedCollection).

This is an extensibility and interoperability challenge, but also a challenge for automated test suites. The spec says an OrderedCollection must be used for an Actor inbox. My understanding is that a subtype of OrderedCollection satisfies that requirement. However, there’s no way for testing software to determine, based on the JSON-LD context, that the type returned by the server is actually an OrderedCollection subtype (other than inferring it based on the content or being pre-configured for that case).

There’s at least one example in ActivityStreams-Core where Option #3 would fail (unless that example is a bug). The item collection in the example contains an activity without actor property:

    {
      "id": "http://example.org/activity/20150101000059",
      "type": [ "Update", "prov:Activity", "oa:Annotation" ],
      "summary": "Eric edited a note.",
      "dcterms:created": "2015-01-01T00:00:59Z",
      "dcterms:creator": { "@id": "http://example.org/#eric" },
      "oa:hasBody": {
        "id": "http://example.org/entry/20150101000059",
        "type": [ "Note", "prov:Entity" ],
        "content": "Remember... all I'm offering is the truth. Nothing more.",
        "prov:wasAttributedTo": { "@id": "http://example.org/#eric" },
        "prov:wasRevisionOf": { "@id": "http://example.org/entry/20150101000000" }
      },
      "oa:hasTarget": { "@id": "http://example.org/entry/20150101000000" },
      "oa:motivatedBy": { "@id": "oa:editing" },
      "prov:generated": { "@id": "http://example.org/entry/20150101000059" },
      "prov:wasInformedBy": { "@id": "http://example.org/activity/20150101000000" }
    }

Btw, this also is a nice example of where type is a json set/array.

1 Like

Yeah, the reason i keep saying it is because that’s what a “type” or “class” is, mathematically speaking – the collection of sets which all share some property. The end result of type-checking is usually to use those shared properties, right? So why not just check for that property directly?

By this reasoning, the following property-based class definitions make sense to me:

  • Links have href. A link that doesn’t reference anything makes no sense.
  • Activities have actor. Things don’t just happen on their own, and all the defined side effects are expressed in terms of actor, object, and target.
  • In ActivityPub, anything can be an actor as long as it has an inbox and/or outbox. This is because an actor needs to be able to send and/or receive messages. ActivityPub requires both properties, but it would theoretically be possible to only have one of the two. Say we had an actor that could only receive messages, and thus it would only need an inbox. Or, say we had an actor that generated messages but didn’t process any messages for itself, and so it only needs an outbox.
  • A Collection has items (or orderedItems, which is an alias for items expressed as a JSON-LD ordered list via @container: @list)

However, some types carry semantic information, as this is another thing that types can do:

  • OrderedCollection is specified to be in reverse chronological order
  • Activity subtypes such as Create, Add, Remove, Update, Delete, Follow, Accept, Reject, Like, Announce have specified side-effects according to ActivityPub

Aside from that, most types are just hints and carry no real information:

  • Consider Document versus Image, Video, Audio, etc. – what is the real difference? Nothing, actually! These subclasses carry no additional properties, and barely any semantic information, either. So they can only be used as processing hints. You might use the name, you probably are interested in the url, and there’s all the properties of an Object that can be used as additional metadata if desired. For processing, you would generally look at the URL and see if it’s either a Link with mediaType, or you might just have to dereference the document and check the MIME type. (Or you might do heuristics like checking the file extension, but this is a bad idea and should only be done when unavoidable, such as when there’s no other type-hint and you need to know the type before dereferencing.)

In any case, it seems like the wiki page has been updated by Evan to clarify that this subtyping is only possible with an OWL definition, of which AS2 currently does not have a normative OWL definition, only a normative JSON-LD context.

Yes, and this behavior is wrong. It is brittle, fragile, prone to break, built to fail, et cetera. It even violates the specs, such as the language in ActivityPub about any object being a possible actor.

A similar widespread mistake is treating type like a singular value instead of as the unordered set that it actually is. It’s one of those annoying things about JSON-LD, where by default, if an array has a single value it can be coerced out of being an array.

The way to override this default and force an array is to define the property’s @container as either an unordered @set or an ordered @list. Unfortunately, the AS2 context neglects to do this. Adding these @container: @set or @container: list definitions would constitute a minor breaking change, because it would mean that old AS2 documents that are currently “valid” would then become “invalid”, if they do this coercion to a single value (as they are required to do, in order to be consistent with the JSON-LD compacted form that enables processing as plain JSON).

Such a change would ultimately reduce confusion, however; “plain JSON” implementations would no longer have to worry about the equivalence of single values vs. single-value arrays. If it can be an array, should be expressed as an array. See JSON-LD 1.1 (4.3.1 Lists and 4.3.2 Sets) for more.

As explained above, OrderedCollection is an exception because it is a semantic type rather than a property class. If you were to subtype OrderedCollection for some reason, then you would use a type array that includes at least OrderedCollection. This is a MUST because it indicates reverse-chronological sorting as a requirement has been fulfilled.

This use of the “core type” is expressed multiple times in AS2-Core:

When an implementation uses an extension type that overlaps with a core vocabulary type, the implementation MUST also specify the core vocabulary type.

When an implementation uses an extension type that overlaps with a core vocabulary type, the implementation MUST also specify the core vocabulary type.

When an implementation uses an extension type that overlaps with a core vocabulary type, the implementation MUST also specify the core vocabulary type.

These excerpts can be found near the end of each section on the type modeling (Object, Actor, Activity).

This example is non-normative. I’m not sure how you would process such an Update activity, as an Update is required to have actor and object via ActivityPub:

The Update activity is used when updating an already existing object. The side effect of this is that the object MUST be modified to reflect the new structure as defined in the update activity, assuming the actor has permission to update this object.

The receiving server MUST take care to be sure that the Update is authorized to modify its object. At minimum, this may be done by ensuring that the Update and its object are of same origin.

Yes, the updates are an improvement. Of the four options, I still think the property-based inference (duck typing) that you suggest is the only effective one for extensibility. Option 1 seems to require that something else made the typing decision (previously decided something was an Activity and then put it in the inbox/outbox). Option2 is checking the “type” property against a hard-coded known set from the specifications. And, as you wrote, Option 4 is not applicable since there is no normative OWL ontology (I’d have thought he knew that).

It appears to me that much of AS2 was based on the non-normative OWL ontology and then adapted to JSON-LD later. However, the AS2 specification is written as if there is a normative ontology. I think that causes much confusion about extensibility.

Yeah, the reason i keep saying it is because that’s what a “type” or “class” is, mathematically speaking – the collection of sets which all share some property. The end result of type-checking is usually to use those shared properties, right? So why not just check for that property directly?

I agree that without an ontology, this may be the best that can be done and that the “type” property has limited usefulness. That said, I’d guess most implementors are going to use the heuristic, “the document type is what’s specified in the ‘type’ property” since the specification strongly hints (in my opinion) that this is the proper way to process AP/AS2 types.

If you haven’t already done it, I think your suggestions, explanations, and examples would be a great blog article or a “best practices” kind of FEP.

Maybe I’m in a gloomy mood today, but I feel like there’s never going to be effective extensibility and interoperability with the current AP/AS2 specifications (beyond ad-hoc developer coordination) .

Is it accurate that Mastodon doesn’t support list-valued “type” properties? It looks that way in the code, but I may be missing something.

i found equals_or_includes_any? which was added in Support Actors/Statuses with multiple types by ekiru · Pull Request #7305 · mastodon/mastodon · GitHub and this seems to indicate support for type arrays, but i’m not sure how much coverage that has throughout the codebase, and certainly many other implementations make this mistake even if mastodon itself does not.

the specifications are fine imo… it’s the implementations that have the issues. although this probably stems from lack of guidance on how to implement the specifications.

my take is that extensibility and interop can still happen, but it will probably leave “legacy” implementations behind. either they adapt, or they break. so it’s not really any worse than the already-broken situation, which largely exists because there isn’t any pressing reason to fix anything. it would probably end up being a good thing to break them earlier.

i might. i’ve posted these thoughts all over the place, so it would probably be a good idea to have a central place to refer back to…

Agree with this.

The “already-broken” situation means that fixing something for some will break it for others. But in the longer run gentle upgrades will have more things working, interoperable, and extensible for more people.

If the decision is taken to make “breaking fixes”, the sooner it’s done the better, as there will be less to change later on.

I agree, in theory. I think that this could happen except for the fact that 95%+ of the AP microblogging instances are running on one software platform (and ~85% of the AP usage is currently for microblogging). If the other < 5% decide to leave Mastodon behind, I’m skeptical it will make much (positive) difference unless they have some very compelling new features.

It may be more likely that another protocol will emerge that has better support for extensibility and interop (maybe AT or something similar?). If a large number of users are on platforms using that protocol, I could see Mastodon potentially implementing support for that and then phasing out ActivityPub (like they did when transitioning to AP from OStatus).

and likewise in turn, those compelling new features will likely not be possible without extensibility. or, at the very least, by doing something completely different to the very narrow focus of the twitter-like softwares. there’s no guarantee the “social media” paradigm will stick around; it might end up just being a phase, and more effective paradigms might well surpass it. who can say?

i don’t see why it has to be a different protocol, when it could be instead a different interpretation of an existing protocol. for all the things that ATP et al are doing, we can do those with activitypub. it’s just a matter of architecture. the transition from OStatus to AP was primarily about a move from pull-based feeds to push-based messages. it was motivated by the need for private addressing. the primitive building blocks and conceptual ideas are the same, regardless of the protocol. we’re not really doing anything new that hasn’t already been done by SMTP, XMPP, IRC, and so on. all that’s changed is the technologies and philosophies. in the case of activitypub, it’s HTTP, JSON-LD, and the Linked Data Web. over in xmpp land it would be TCP, XML, and messaging/presence. and so on and so forth. it’s a shame that ATP/Nostr seem to have a a strong “not invented here” syndrome, where they have a “placeholder DID server” and a “Nostr well-known endpoint” instead of reusing something like Webfinger, for example.

1 Like

It may be more likely that another protocol will emerge that has better support for extensibility

Possible. Linked data already has this support, which is the point I made in the SWWG. Fedi community just needs to decide whether or not they want to use that feature

(maybe AT or something similar?)

Definitely not this.

It’s understandable that you might see ATP/Nostr as having a “not invented here” attitude, but there are some key points that demonstrate a different perspective.

First, Nostr doesn’t rely on well-known endpoints in its core protocol. As a WebSocket protocol, which is an upgrade of HTTP, nostr core itself, doesnt have strict or mandatory requirements for endpoint locations. Instead, common patterns evolve as bridges between systems. Most of the base HTTP part of nostr is unused or not yet developed, as it works primarily over websockets.

Second, it’s important to remember that Webfinger has its own well-known endpoint with a custom JSON format. Webfinger causes a lot of issues, a well known end point that gave back the same JSON as AP would have been much better. Custom JSON formats can create issues, as seen in ActivityPub, and their use could have been avoided. In contrast, Nostr ha a bridge to HTTP, which verifies users and domains similarly to Twitter’s blue checkmark, proving its usefulness.

Moreover, Nostr has a bridge to Mastodon and supports bidirectional proofs, providing increased flexibility and adaptability. The Fediverse could adopt a similar approach, using nostr keys as nomadic identity, to avoid DNS lock-in. This would also enable client to server prtocols and apps for free, and ability to reuse all the apps in the nostr ecosystem.

Finally, while the ATP protocol does have more of a “not invented here” mentality, creating everything from scratch, Nostr should not be grouped with ATP in this context. They are very different things. At is a company with closed source, nostr is a grass roots open source protocol. IMHO, Nostr has demonstrated a greater willingness to adapt and leverage existing technologies.

In summary, it’s crucial to differentiate between ATP and Nostr. Nostr has aimed to be more adaptable and open to existing technologies, rather than strictly adhering to a “not invented here” philosophy.

AP diverged from Linked Data around certain points that didnt give it any particular advantage, but introduced come pain points around extensibility. 6 years later, it’s possible to learn from the different systems around. But I think consensus from mastodon and other big players are needed in terms of what they want to achieve and what they are willing to change.

1 Like

If by “different interpretation of an existing protocol” you mean an ActivityPub Version 2 specification, I’d agree that it doesn’t need to be a completely different protocol. If you literally mean a different interpretation of the existing AP specification/protocol, I’m less inclined to agree. In general, I believe an interoperable specification that allows multiple interpretations is a bit of an oxymoron.

I see several reasons why it’s not likely there will be an AP V2 any time soon. I’ve seen claims that the current spec is “good enough”. However, I’m not sure I’ve seen this claimed by many (anybody?) that have tried to implement non-trivial, interoperable software with it. There’s also no funding at the moment and, in my opinion, a lack of a common vision. All of these will give competing protocols an opportunity to innovate faster and more effectively.

In the case of ATP, there will be large quantities of cash and engineering talent focused on the effort. However, I’m not suggesting that ATP will replace ActivityPub, just that it’s a possibility. And even though BlueSky will be closed-source, if the protocol is open, then I’d expect FOSS support and new implementations to quickly emerge.

One doesn’t make breaking changes to a protocol experiencing explosive growth. Let’s not even bring up the idea of breaking changes.

1 Like

i meant specifically “interpretation” as in “architecture”, i.e., we currently have implementations of the spec that assume a certain architecture (the “instance” model), whereas you might have other implementations that don’t make the same assumptions. after all, ActivityPub is on the whole much simpler than most people think: it’s basically just POST to inbox (and outbox, for C2S). the semantics of the payload are really not defined beyond a few activity types. this doesn’t need an “ActivityPub Version 2”, but it probably could use additional specs or conformance profiles that build on top of ActivityPub (which is “good enough” in the sense that it doesn’t need to be changed per se, it just needs clarification and guidance in addition to the aforementioned profiles)

to illustrate, consider the following:

  • the “Mastodon profile” specifies additional side effects, such as for when the object is a Note vs some other type. it can be said that when an activity like Create Note lands in some inbox or sharedInbox, it gets converted to a status on the “instance”. however, we might alternatively describe a system where actors are sending messages to each other, and they use the same Create Note semantics to do so. the side effects of receiving such a Create Note in the actor inbox is that it is rendered as a message directly by the user’s Client, with no transformation. these would be two different interpretations of the same payload.
  • conversely, consider the distribution model where you follow an actor that is specifically a Person, and your “instance” records this in a database for later fan-out… vs. a model where you follow a Collection and then the Collection forwards any activities targeting the Collection to its followers. these would be two different interpretations of what an “actor” is.
  • further still: consider how Mastodon et al currently use inReplyTo for constructing a “thread” context, as opposed to using explicit context and then having inReplyTo simply be metadata within that context. these are two different interpretations of the viewing model.

we could go on. there are many different abstractions, design decisions, etc. that are not specified by the protocol, but instead exist at the application layer, and are driven by application-specific logic rather than by interoperability or extensibility. i would argue that most implementations tend not to think about the generic semantics of what they are producing, and so they end up with something very specific to the application and its worldview. that’s a recipe for chaos. you can’t interoperate if you don’t share a worldview. the more specific your worldview, the worse your interoperability. this is why it’s important to keep the data model generic; you can then present that generic data in specific ways.

i understand the sentiment here, but which “protocol” is experiencing growth? the current situation regarding activitypub is more like saying the C Programming Language is experiencing growth because of a popular application written in C. that still leaves a lot of hard work for two applications that wish to be compatible with each other. in the absence of any changes or general activity or movement, what you’ll end up with is Mastodon becoming a de facto standard protocol, and Mastodon gGmbH its steward instead of the W3C or SocialCG/SWICG. until maybe some bigger player comes along and implements activitypub in a different way. and so we end up with a situation in which you have many different networks that are incompatible with each other despite all using ActivityPub. which is actually not too far from what we already currently have, is it? we have many different small networks that only somewhat overlap. the biggest of which might well be referred to as the Mastodon Network, currently defined by its use of Create Note, WebFinger, and RSA-SHA256-based HTTP Signatures (Cavage draft 8)… among other limitations, dictated by whatever mastodon is currently doing.

1 Like

Thanks for the additional explanation. I mostly agree with what you’re proposing. It’s possible that many of the gaps in the current spec could be addressed with additional related specifications. I definitely support the idea of conformance profiles or even just domain-specific implementation guides.

I’m not convinced this is all that is needed, but it would definitely be a major improvement over the current situation. I’d love to see a draft of a Mastodon conformance profile. Much of the information already exists in the online Mastodon documentation, but it’s a bit scattered.

I’ll think some more about your examples. For the Collections followers example would the actor profile look something like…

{
    ...
   "type": ["Service", "Collection"],
   "inbox" "...",
   "outbox": "...",
   "items": "...", (or collection paging properties)
    ...
}

(BTW, based on your previous information, I checked and Mastodon handles multivalued-type fields in most places except for inbox Activity types, which is consistent with what you thought might be the case.)

i might be convinced to write one, if i thought it was a good idea. i just am not entirely sure it is a good idea, because it involves entrenching mastodon’s power over the ecosystem even more.

or, well, you could argue that i already wrote such a thing at ActivityPub - Mastodon documentation i suppose?

yeah, mostly. although the types don’t need to specifically be Service and Collection. they can just be Collection, or there might be some extension type in there. in activitypub, any type can be an actor. it doesn’t have to be a Person / Group / Organization / Application / Service. (although again, most current implementations expect actors to be exactly one of these types…)

there’s more discussion of this at Unresolved issues surrounding Follow activities (from 2019!)

1 Like