LitePub (Objects)

Living Standard — June 26, 2019

Architectural Introduction

This section is non-normative.

LitePub and other applications of ActivityStreams are built on top of an object-oriented architecture. Unlike previous specifications concerning this protocol, we present the object model in a flat way, where all objects are derived from the base Object type. It is our hope that this presentation is more easily understood by the reader as well as more reflective of reality.

In production, we have learned that maintaining an artificial partition between the notion of regular objects (which reflect data) and activities (which reflect mutations on data) significantly increases the complexity involved in walking a graph of activities. Accordingly, we have come to see the artificial partition as more harmful than helpful, especially when storing graphs in normalized form inside so-called “NoSQL” and traditional SQL databases.

Handling of Objects

LitePub implementations process ActivityStreams objects and store them and/or interpret them to perform mutations on other objects. To facilitate this, LitePub defines additional vocabulary on top of the ActivityStreams core vocabulary.

Information about handling the full LitePub vocabulary is in available in the LitePub vocabulary section.

Processing of Referenced Objects

LitePub objects reference other objects. Objects are referenced by IRI (RFC3987). Every URI (RFC3986) is also usable as an IRI.

Relative IRI references MUST NOT be used inside a LitePub object.

Servers MUST validate the content they receive to avoid content spoofing attacks as well as to verify that processing the object does not induce any mutations which would be disallowed by the server’s configured policy. There are two security models specified by this specification in the security considerations section.

Object Requirements

All LitePub objects MUST have the following fields:

Object Identifiers and Transience

All LitePub objects MUST have unique global identifiers. If an object is received without an identifier, the receiver MAY assign the object an internally generated identifier.

Servers MAY respond with an HTTP 410 response code when fetched by their global identifier to indicate their transcience.

Object Deletion

Servers SHOULD replace the deleted object with a Tombstone to prevent re-use of the identifier or possible refetching of remote objects.

Servers MUST respond with an HTTP 404 response code when a Tombstone object is encountered.

Servers MUST NOT serve the contents of the Tombstone object.

Servers SHOULD delete any local copies of an object if refetching the object results in either a 404 or 410 response code being returned.

Note: ActivityPub implementations are allowed to respond with either 404 or 410 as well as serving the underlying Tombstone object. This behaviour is flawed because it results in metadata leakage – namely, it leaks that an object did at one time exist with the given identifier.

Object Delivery

Servers SHOULD track which peers they have successfully delivered a given object to.

Servers SHOULD broadcast Delete messages to all peers they successfully delivered the message to.

Object Serving and Fetching

Servers MAY require authorization to serve any object, including objects addressed to https://www.w3.org/ns/activitystreams#Public.

Servers MUST require authorization to serve any object not addressed to https://www.w3.org/ns/activitystreams#Public.

Servers SHOULD treat authorized object fetch requests as deliveries to the authorized peer and record them as a successful delivery.

Description of Object Processing Algorithm

This section is non-normative.

A possible algorithm for processing objects according to the above defined rules would be:

  1. Verify the object’s id is unique and not yet stored in the object graph.

  2. Verify that the object’s type is processable by the LitePub implementation.

  3. Iterate over all properties in the object looking for potential children (any property typed as an id or any included child objects).

  4. For each child object, replace the object with an authoritative local copy of the object. Fetch new authoritative copies of any missing objects.

  5. For all children, return to step 2 with the child object as the processing context.

  6. If authoritative versions of any children are missing, reject the root object if transitive child objects are not allowed for the root object’s type.

  7. Apply any required side effects or mutations prescribed by the root object’s type.

  8. Store the root object in the object graph using it’s id.