Being inspired by The Delete Activity And It’s Misconceptions, I’d like to write a bit on the topic of the Update Activity, and my experience with it on both the C2S and S2S sides.
Following the cache model that kaniini lays out in the article, the Update ActivityStreams type can be thought of as a cache-refetch signal to federated peers. As I will show later, this is not the only thing Update does: it also allows federated peers to have as detailed a historical system of record they want without relying on the granularity of the originating peer.
The Update Activity
The specification, like all other Activity types, lays out an open-ended recommendation for how to handle Update activities. No one has to follow the guidance’s "should"s, but at that point a person is relying on a convention outside the specification and not the guidance of the specification. It means they won’t be complying with the only suggested guidance in the spec. What is that guidance? Why should we care what the specification lays out?
Spec Guidance
In the Client-To-Server part of the specification, it actually lays out both the C2S and S2S suggestions. This is an instance where only reading the ActivityPub spec piecemeal leads to pain points.
C2S
The C2S says that only the top-level fields inside of an object
in an Update are wholly replaced in the Server’s model of the Activity. As an example, this Note
{
"type": "Note",
"content": "Hello",
"image": {
"url": "https://example.com/img1",
"name": "picture of the word hello"
}
}
Plus this C2S Update
{
"type": "Update",
"object": {
"type": "Note",
"image": "image": {
"url": "https://example.com/img2"
}
}
}
Would yield a final Note as:
{
"type": "Note",
"content": "Hello",
"image": {
"url": "https://example.com/img2"
}
}
A big bummer of this recommendation is its reliance on JSON’s null
to delete top-level properties, as certain programming languages don’t handle JSON’s null
well (cough Go).
S2S
On the other side, the ActivityPub specification recommends sending a federated peer the whole object
so that they can replace their internal representation with it. Thus, after the C2S Update example above, if that server then federated it over S2S, it would federate:
{
"type": "Update",
"object": {
"type": "Note",
"content": "Hello",
"image": {
"url": "https://example.com/img2"
}
}
}
This is the cache-updating part of the Update activity.
However! This guidance also contains another property: edits are broken out into their smallest units and federated. The federating server doesn’t wait, accumulate N Updates activities, and send out 1 representing all N updates. This may be an important property for the Fediverse to preserve at large, so that federated peers may have an accurate record for edit history.
In this way, the guidance by the ActivityPub spec makes Update activities more than just the cache-validation/invalidation that Delete activities are. It also ensures that federated peers have the capability of building historical records as edits and changes occur over time, without needing to rely on the originating server to do that for them.
Future
I forked this off of the conversation Notifying remote servers that they should refetch an object because the idea of cache-validation is well known, but I feel the property of historical record is not.
Complying with the ActivityPub spec’s suggestion has let the entire ecosystem let any other application (existing or not) be able to compute their own view of the edit history for an object, empowering users as much or as little as possible. It’s a property wholly implicit in the specification. So it comes down to us as a community whether it is a property we value and want to preserve when we build software that does not comply with this guidance.