GoActivityPub library

GoActivityPub library

The GoActivityPub library contains the building blocks for adding ActivityPub functionality to both client and server applications for the Go programming language. It is a robust, “batteries included” product with a high level of interoperability with the rest of the Go ecosystem.

The focus is on high spec compliance, minimal external dependencies, and minimal computing requirements.

The library is divided into functionally independent modules connected through interfaces.

The high level view on the project offers the following distinct pieces:

Below you can find short descriptions of the individual modules.

Spec compliance chart.

The module layout

Processing

Defines most of the logic for handling the ActivityPub interactions for both Client to Server and Server to Server dissemination in the form of a state machine making decisions based on the Activity and Object types pairs.

This state machine is wrapped in standard HTTP Handlers that are compatible with the rest of the Go ecosystem.

It also contains additional handlers used to output individual objects and collections after retrieving them from storage.

Finally, it defines the interfaces that the storage backends need to implement to be able to work with the rest of the GoActivityPub modules.

Storage modules

We provide multiple implementations of storage backends which are compatible with the processing module.

The interfaces that they need to implement are defined in the processing module.

Currently we support:

Client

Represents a module that can be used as a base for a client that interacts with ActivityPub servers. It can be used with both client to server and server to server authorization mechanisms.

Auth

Defines the logic for verifying and generating HTTPSig and OAuth2 compatible authorization headers, for both Client to Server and Server to Server authorization.

Cache

A module to help with creating an in-memory cache for ActivityPub objects. It stores objects under the key of their ID.

It is used in various places where caching the objects makes sense, like the slower storage modules, or just as a memoisation mechanism.

Currently the module has no explicit method for cache invalidation or limiting the amount of space it uses, so please handle with care.

ActivityPub

Is the module that contains the default Activity Vocabulary data types as described by the ActivityStreams vocabulary, with additions made for presenting public keys for actors which are required by the server to server HTTP Signatures authorization mechanism.

The types in this module require the JSON-LD module for marshaling and unmarshaling to be compatible with other ActivityPub services, the standard library json module does not deal with the dynamic nature of the linked data properties in a correct fashion, see design decisions for details.

JSON-LD

A module to help with the JSON-LD marshaling and unmarshaling of the ActivityPub vocabulary objects. It provides the functionality to deal with the peculiarities of having dynamic values for the objects’ properties, and also adds a small convenience over dealing with the @context property that JSON-LD requires.

This module needs to be reimplemented or switched to something better.

Errors

Has wrappers for dealing with specific errors inside our library, mostly focused around dealing with HTTP errors.


Design decisions

The main problem of implementing the ActivityPub specification in the Go programming language stems from the very dynamic nature of JSON-LD, which is used as its data format, and the limited (in this respect) features of the Go type system.

This apparent incompatibility stems from the fact that an object property in the ActivityPub specification can have any of the following values:

Because the Go type system can’t express this union explicitly, we rely on implementing these four meta-types independently and giving them unifying behavoiur through the interface: ObjectOrLink. Basically if any type implements this interface[1], it can be used with the GoActivityPub modules.

To keep in line with Go’s interface guidelines, we have tried to keep the methods this interface exposed to a minimum, and this led to a very limited guaranteed API for the module.

To circumvent the fact that most of the library deals with instances of this interface, we had to create the convenience functions that allow a developer to assert them to actual useful ActivityPub structs.

They are the functions starting with OnXXX and ToXXX in the module and give the possibility of treating any struct that impelments ObjectOrIRI as an XXX ActivityPub object, where XXX can be an Activity, an Actor, Object, Link samd.

This is incidentally the mechanism through which we allow extending the default AP vocabulary by other modules. Other developers can create their own type YYY, implement the interface and add their OnYYY functionality[2].

The first caveat about this type of logic is that it relies heavily (at least currently) on the fact that the memory layout for each type needs to be identical for the properties which are common. The properties need to have the same order and the same type. Basically the existing ToXXX functions work as a cast does in plain C. It takes the pointer the interface holds, and converts it to the desired type (using the unsafe package) relying on the fact that the common properties of the Objects are at the same offsets from the pointer[3].

[1] The interface matches the ActivityStreams separation between Object compatible structs and Link compatible structs.

[2] This also requires overriding the default typer function, which is used to return the correct type based on the YYY.Type property.

[3] This behaviour is risky and it can probably change without warning if the Go dev team changes the language’s memory model at a later date. I hope I’ll find a cleaner way to implemnt this in the future, but for now it serves its purpose.

Dependency graph

github.com/go-ap/jsonld: - no dependencies

github.com/go-ap/errors:

github.com/go-ap/activitypub:

github.com/go-ap/cache:

github.com/go-ap/filters:

github.com/go-ap/client:

github.com/go-ap/auth:

github.com/go-ap/processing:

github.com/go-ap/storage-fs

github.com/go-ap/storage-sqlite

github.com/go-ap/storage-boltdb

github.com/go-ap/storage-badger: currently in a failing state

github.com/go-ap/storage-all

Pitfalls

I compiled a list of pitfalls when using the modules.