FEP-e229: Best practices for extensibility


This is a discussion thread for the proposed FEP-e229: Best practices for extensibility.
Please use this thread to discuss the proposed FEP and any potential problems
or improvements that can be addressed.


Current popular implementations of ActivityPub do not handle extensibility very well. This FEP seeks to highlight some basic requirements for extensibility, and offer suggested advice to implementers who wish to avoid compatibility issues, particularly for LD-unaware consumers.

Posting this reply to https://socialhub.activitypub.rocks/t/best-practices-for-ap-vocabulary-extensions/3162/32 here because I’m late to the party…

I think it would be good to also mention the versioned contexts of Activity Streams as an example use case.

Very minor nitpick, but doen’t JSON-LD spec also call a value of an entry like "type": "Collection" a “set”? I think the proper term for a plain-JSON syntactic sugar like that is an “array”.

Also, I think the guidance on the handling of sets is not specific to type, but also applicable to any non-functional terms, including other core Activity Streams terms. For example, the assumption that the value of the url term is always a single JSON string would be incompatible with FEP-fffd portable objects.

Another minor nitpick: This also expands the value of the attachment term to an array, which is in violation of the AS2 requirement IIUC.

(Personally speaking, I don’t like the fact that the spec requires compacting values of the attachment term that way, but that is entirely different problem.)

Also, the name term used by existing implementations refers to as:name, not sc:name, so you should never “expand” the term that way.

As for the use of expanded terms, I think the advisory should be “for new extensions only”, so that it won’t break existing extension terms. While that would leave existing extensions fragile to name collisions, we could at least bound the risk of name collisions to existing extension terms (and future addition to the normative context, if any). After all, the point of the FEP is to reduce interoperability issues, not to cause another churn to the ecosystem.

So the FEP has added a warning against blank node terms since the initial draft. But the description still sounds as if there are a few cases where they are justified, and I don’t think it has fully resolved the concern.

As Evan has pointed out, every ActivityPub document is meant to be shared. I think that not only means sharing across implementations, but also sharing among different documents produced by a same implementation.

IIUC, blank nodes from different datasets never denote a same entity, even if they share a lexically “same” blank node identifier in the surface syntax (cf. https://www.w3.org/TR/rdf11-concepts#h-note-3), which makes the properties almost nonsense (at least without any entailment regime? I don’t know.).

I guess the default @vocab is merely a “fool-proof” against accidental lost of JSON entries during JSON-LD processing algorithms and is not meant to be intentionally used, is it? After all, coining a disposable IRI isn’t particularly hard task (you don’t even need to own a domain in order to make a urn:uuid: or a tag: IRI), so there is no reason to avoid it, even for experimental purposes.

  • Those whose value is a node on the graph. In uncompacted form, these would use @id.

I feel this might introduce another problem that consumers would need to use different logics for the core Activity Streams terms (compacted) and extension properties (expanded) to determine if the value is a reference to an external node or an embedded node.

Instead, couldn’t we specify the @type for the property in the context without defining a shorthand, like the following?

  "@context": [
      "http://example.com/idProperty": {
        "@type": "@id"
  "http://example.com/valueProperty": "some string or number or boolean",
  "http://example.com/idProperty": "https://example.com/some-resource"

You might wonder if this is a valid JSON-LD document. Well, I’m not an expert of the matrer, but the Create Term Definition algorithm of JSON-LD 1.1 Processing Algorithms and API has the following step:

Otherwise, term is an IRI or blank node identifier. Set the IRI mapping of definition to term.

So I think a term can also be a (non-compact) IRI. At least, the JSON-LD Playground doesn’t complain to it:


By the way, the partially-expanded form should use the id term instead of the @id keyword, since the normative context aliases the former keyword to the latter term. Either way, however, I would argue that it is yet another source of ambiguity.

The following are random ideas of additional topics:

Prefer "@container": "@set" for non-functional property terms

Just the same reason as you described in the following issue:

The blank predicates are not serialized into RDF so there’s no dataset issue for those. For example, the following will result in an empty RDF graph.

  "@context": {
    "@vocab": "_:"
  "foo": "bar"

The AP @vocab protects against loss during JSON-LD expansion and compaction but doesn’t protect against loss when serializing to RDF. Like you mentioned, a non-blank prefix (e.g., urn:x-activitypub:) would be a more “fool-proof” @vocab prefix.


I’d like to see a reference to the SocialCG’s Extension Policy. It’d be good to note that there is a possibility of extensions becoming part of the main AS2 context, if certain easy conditions are met (as listed in that document).