Server-Sent Events For the ActivityPub API

This document describes an application of Server-Sent Events to the ActivityPub API.

This is an experimental specification and is undergoing regular revisions.

Introduction

The [[[!ActivityPub]]] API is a RESTful interface for social networking servers. It provides a readable interface for locally-hosted objects via HTTP GET to their id property URL, and for remotely-hosted objects via the proxyUrl endpoint. A writeable interface is provided with an HTTP POST to the user's outbox collection.

To get updated versions of an object or collection, the client application has to repeatedly fetch the object -- "polling" for updates at a given interval. This has three disadvantages:

One alternative to the polling model is server push. In this architecture, the client establishes a long-running connection to the server. The server then sends update notifications to the client, as close to the actual modification time as possible, so that the client can modify its local representation of the object.

Several server push technologies exist in the Web ecosystem. This document describes an application of [[[!eventsource]]] to the ActivityPub API. Server-Sent Events have the advantage of being well-supported in many browser environments, and easy to support in other contexts. Their downside, compared to some other technologies such as [[[websockets]]], is that they are one-way and read-only; the server can send updates to the client, but the client cannot use the same channel to send updates to the server. The POST interface through the user's outbox should suffice for most applications.

This specification uses a constrained subset of the [[[!activitystreams-vocabulary]]] to represent changes to ActivityPub objects. This has the advantage of not requiring a separate vocabulary or format for changes, which are already well represented in AS2. On the downside, it blurs the line between the data to be represented, and the changes to that data.

User Stories

The following user stories illustrate how this specification may be used:

Discovery

The publisher of an ActivityPub object can declare an endpoint to retrieve an event stream for events related to that object with the eventStream property.

Events in the event stream SHOULD have either the object, or one of its object properties, as the object or target of the event.

Authentication

The client application MAY use the Authorization header to pass an [[[rfc6749]]] access token to the server.

Because the EventSource interface does not enable passing custom headers, the client application MAY pass the access token as an URL parameter access_token.

Events

Events from the server are in the form of [[[!activitystreams-core]]] Activity objects, in [[[!json-ld]]] format.

Activity objects MAY include an actor property, and MAY include an id property.

Add

An Add event is sent when a new item is added to a collection.

Remove

A Remove event is sent when an existing item is removed from a collection.

Update

An Update event is sent when an object has been changed. The object property of the event should contain a full representation of the changed object.

Delete

The Delete activity is sent when an object has been deleted.

Proxy Event Stream

The client may want to be notified of changes to remote objects; that is, objects hosted on remote servers. Similar to the proxyUrl endpoint, a publisher can share a proxyEventStream property in the endpoints property of an actor to indicate a local proxy URL that can be used to be notified of events about remote objects.

The proxy event stream URL takes two arguments (in addition to any arguments defined in the URL):

Security Considerations

OAuth 2.0

Including the access_token in the URL for the EventSource can leak the access token; the token will be embedded when the URL is logged. Server software can mitigate this issue by obscuring the access token in server logs, for example, by replacing it with a string like "XXXXXXXX" or "********".

Implementation Notes

Client

Real-time server updates are easier to process with a model-view-controller or similar architecture. The event handler, for server events, can look up models by id and call the appropriate methods to change the models and, consequently, change all the views that depend on that model.

Server

This specification shifts complexity from the client to the server; responsibility for tracking which objects are related to which client is primarily on the side of the server.

One option for making this work is to keep a map of objects currently being observed with an event stream to the streams that are observing them. When a change is made to one of the objects, look up the relevant event streams, and send out an event on those streams. A reverse mapping (from streams to objects) can help with cleanup when the stream is torn down.

Server software that spans multiple processes will need to have a way to coordinate triggering event updates between processes. For example, if an activity is received from a remote server on a sharedInbox endpoint, the effects of that change should propagate to open connections, even if they're being handled by another process.