Object Graphs in ActivityPub

If I’m reading the Client Addressing (§6.1) and Outbox Delivery (§7.1.1) correctly, it seems as though the specification is written such that every Activity having to do with an Object (e.g., Like, or Document sent inReplyTo) is addressed to the recipients of the original message plus any who have later sent an activity addressed to those recipients about said Object.

If that’s the case (and all servers/networks are working), then every Actor’s server should have received every Activity associated with the Object. And if that’s the case, then servers should be able to index those messages and provide collections of e.g., /likes, /replies without online access to the server where the message originated. It’s kind of like email where everyone is expected to use Reply All on every message, except obviously the parent.

Is that correct? If so, I have follow-on questions:

  1. There seems to be a race condition. If a user U sends a public message A to their followers, and a user S not addressed in the original message (because they weren’t a follower) could Like object A but still be in the process of delivering A to all recipients (imagine U has thousands of followers). Before receiving S’s Like, another user T sends a Create activity containing a Note with A as the inReplyTo. T’s client will not include S as an addressee (since it doesn’t know about S’s Like yet).

  2. If the Followers collection changes, which followers should receive reactions to the original message? How are they addressed? Do S2S Activities contain the expansion of the Followers collection, or are they referenced by the collection ID (e.g., https://example.com/bob/followers?

Clients can choose to address activities to whatever audience they wish to. The SHOULD in 6.1 is intended to provide a “sensible default” for this addressing, but ultimately users can choose to amend or omit whatever actors they want.

I think there’s a misunderstanding here about what activities 6.1 is suggesting you recurse through. 6.1 calls out the object , target , inReplyTo and tag fields. In your first example, S’s Like is not attached to the original activity through any of these fields. It is not the object or the target of A, nor is A inReplyTo it, nor does A have it as a tag. It could be contained in A’s replies, if A chooses to include one, but there’s no requirement A do so.

I think this answers your question #1, but let me know if I missed something!

  1. If the Followers collection changes, which followers should receive reactions to the original message? How are they addressed? Do S2S Activities contain the expansion of the Followers collection, or are they referenced by the collection ID (e.g., https://example.com/bob/followers ?

So, in a strict reading of the ActivityPub spec, the expansion of the collection happens at delivery time. The responsibility for doing so is primarily on the delivering server, but may also be filled by the server that owns the Collection in question, following the rules in § 7.1.2 Forwarding from Inbox. In either case, the expansion happens, and then the activity is delivered to each actor’s inbox by POSTing it. From then on, that activity resides only in those actors’ inboxes, and the to, cc, and audience values are no longer used. In this way, the s2s activity does not need to contain the expansion of the Followers collection, but it still only gets delivered to the actors that are in the collection at the time of posting.

Now, there are two complications here between the spec and current practice, and they go hand in hand. The first is that the usage of sharedInbox moves the responsibility for targeting individual inboxes from the delivering server to the server delivered to. Accordingly, this means that receiving servers have to make the decision of which inboxes should get which messages on their own, based on the targeting information supplied. Since the ActivityPub spec didn’t provide explicit guidance for this, not all servers implemented the same internal delivery mechanism, and so for activities delivered to sharedInbox, not all servers follow the behavior outlined above strictly.

This is complicated by the second issue, which is that it’s a commonly desired feature in many social networking apps that following a user gives you access to that user’s previously followers only posts. (see Facebook, Twitter, Instagram, etc). So when servers were implementing ActivityPub, or adding it to existing apps, it seemed very natural to allow new followers, once Approved, to see the old private posts made by user followed, even if those messages weren’t “delivered” to them, in the strict sense.

Anyway, all that is a very long-winded way of saying “technically, only the followers at the time of delivery are supposed to see each message, (Meaning that if A is posted, and then U2 follows A’s actor, and then A2 is posted in reply to A, U2 would see A2 but not A). But many servers may give new followers access to old messages anyway, so it’s unclear whether this is an issue in practice.”

1 Like

Thanks for the interpretation!

I’m trying to draw interoperability conclusions where perhaps I should not. It sounds like I should not assume that, across platforms, the graph of objects associated to an Object is intended to be consistent for all viewers of the document. I suppose I could make that a condition for servers that want to interoperate with my platform, but there’s no reason to belive other servers will comply.

Yes, fundamentally this is impossible with the spec as it is—linked data graphs are open. I can create an object that references your object, and not tell you about it. Here’s an example:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "to": "https://example.com/my-friend",
  "content": "Get a load of this guy!",
  "type": "Announce",
  "target": "https://example2.com/someone-i-dislike/post/1"
}

Yes, fundamentally this is impossible with the spec as it is—linked data graphs are open.

Graphs can be open, and that doesn’t prevent a specification from indicating how implementations of that specification should behave for interoperability. ActivityPub does this all the time; current specifications fall short in defining how graph consistency should be maintained.

This isn’t a fatal flaw, but it should be directly addressed in a subsequent version of the specification. Until then, developers will piece together systems that work for their specific use case, which will result in different data being available to users on those platforms. Users won’t know except by trial and error what objects will be visible to others participating in a conversation, and that behavior will evolve over time. This puts an untoward mental burden on users and undermines the purpose of having an interoperability specification in the first place.

The AP Object model is fine for representing social interactions; it’s just that there should be a better behavior model dealing with how the graph is transmitted and how implementations should achieve graph consistency (even the idea that graph consistency is important is never mentioned in the specification, that I can see), otherwise there’s limited benefit to users.

With email, there’s a model that users understand: they can predict who will see a message, and have consistent control over that on any platform regardless of vendor. Likewise with competing platforms like Facebook and Twitter: it’s easy to understand, consistent, and predictable.

Again, it’s great that AP specifies what it does for how systems should inter operate. What I would like to see in future versions of the specification is what the benefits to users are that implementations are expected to provide. That will make it much easier for implementors to know how their systems should behave, and why.