Programming · free software · the open webSubscribe via RSS

ActivityPub has actor identity, object identity and delivery identity

ActivityPub uses actors to identify accounts, objects to identify content, and activities to describe actions delivered between inboxes. Keeping those identities separate is central to reliable handling of retries.

Three identifiers, three questions

A federated message can involve several identities that look similar in JSON but answer different questions. The actor identifies who performs an action. The object identifies the content or entity the action concerns. The activity identifies the event being distributed. An inbox is a delivery endpoint belonging to an actor, while an outbox is where that actor submits and publishes activities. They are URLs in an actor description, not alternate names for the actor itself. [1]

A receiver should preserve these distinctions in its data model. An activity record might point to an actor and contain or reference an object. A delivery record can then associate that activity with a destination inbox and a delivery attempt. If all three are collapsed into a single “post ID,” later operations such as retries, replies, and likes become difficult to interpret.

A Create wraps an object

Consider a client submitting a newly composed Note to its actor’s outbox. The server may wrap that non-activity object in a Create activity before distributing it. The activity has its own ID and actor; its object is the Note, which has a separate ID and attributed author. The Create answers “what happened?” and the Note answers “what content was created?” [1]

That difference matters when a Note is referenced elsewhere. A reply’s inReplyTo ordinarily points to the Note’s object ID. A Like is itself an activity and can identify the Note as its object. Neither operation should assume that the Note ID and the original Create ID are interchangeable. They identify related records, not the same record.

What the retry trace should do

Suppose a sender posts activity A to a recipient inbox, but the response is lost. The sender cannot tell whether the recipient stored A; it retries the same activity. On the first receipt, the recipient records A as processed and applies its effect. On the second receipt, it finds the same activity ID and avoids applying that effect again. This is why deduplication by activity ID is more useful than comparing content: two distinct Create activities can legitimately contain similar Notes, while a retry preserves the identity of the original activity. [1]

The receiver needs persistent deduplication state for this behavior to survive a restart. It also needs a transaction boundary: the processed-ID record and the effect must commit together. If the effect commits and the ID record does not, a retry can repeat the effect. If the ID record commits first and the effect fails, the retry may be discarded even though the effect never happened. A database transaction can make those local writes atomic; external side effects may need an outbox or another recovery design.

Keep delivery separate from processing

One activity may be addressed to multiple recipients, so delivery can involve several inboxes and independent failures. A sender can track attempts per destination while retaining one activity identity. Retrying to a failed inbox should not mint a new activity ID: doing so would turn a transport retry into a new event from the receiver’s perspective.

A small implementation can model actors, activities, objects, inbox deliveries, and processed activity IDs as separate tables. That is a useful starting point for reasoning, not a complete federation server. The ActivityPub specification covers more than this model, and its examples and object requirements should be read in context. The practical design question is narrower: when the same activity arrives again, what durable record tells the receiver whether its effects were already applied?

References

  1. W3C ActivityPub Recommendation

Suggest a correction · All posts