Woot! More Drupal. I hadn’t got an watch on for the github project so hadn’t noticed you posting there. I’ll look through the issues added. But here I’ll just write my experiences implementing ActivityPub in Drupal 7 and what my thoughts were for doing it in Drupal 8.
I also want to be implementing it for D8, certainly in the first instance to push and pull from the D7 site. These are existing sites presently sharing data via a custom API, but can be better in the fediverse and hence communicate with the likes of Mobilizon and Friendica as well.
Drupal itself should handle serialization, routing incoming and making outgoing http requests. So most of the libraries have redundant code. In addition Drupal has baked in rdf and flexible field mapping so you can, and want to, make most of the objects (that will in ActivityPub be represented in the json-ld) with Drupal’s own entity/field system rather than duplicating (and worse constraining) them in library PHP code. The challenge this flexible approach leaves is the best way to map fields/properties.
Libraries are certainly helpful for signing, and signature verification. For Drupal 7 I just opted for dgwynne/php-http-signature. But for Drupal 8 99designs/http-signatures-php looks much more promising.
Before continuing further into the gritty detail of Drupal 7 implementation, the other set of code I’ve found helpful, in addition, or possibly more so, than pixelfed, for understanding all this is that for friendica.
Now my details from doing the Drupal 7. I’ve so far opted to create additional entities for ActivityPub Actor, Activity and Relation. They act mainly as glue to Drupal entities (with Entity References). So when represented in Activity Vocabulary they provide a wrapper and required additional data, an then the fields of the native Drupal entity, along with the namespace/s and properties. Let me explain:
Starting with the actor. It felt like it would be even nicer to directly use ‘user’ or ‘group’ entities as the ActivityPub actor. However as there needs to be a unique constrained ‘webfinger’ name, and a public and private key for the signature, I opted to make an actor entity with these fields and an entity reference to the Drupal entity.
The activitypub actor module then also provides the route, and the representation of the actor, in Activity Vocabulary, and the inbox and outbox of the actors, and intends to implement followers and would do following, but it’s not needed for my use case at the moment.
There is a separate webfinger module that does little other than provide the route and a hook which the activitypub actor module implements. It does the lookup on the machine name of the actor, which is also used as the webfinger name, and returns the appropriate data - key, inbox outbox…
To separate out the actors and allow for different mappings I make a different entity type for Actor Type https://www.w3.org/TR/activitystreams-vocabulary/#actor-types In this implementation just ‘Person’ <-> ‘user’ and ‘Group’ <-> (organic group) ‘node’.
The Activity Entity represents the https://www.w3.org/TR/activitystreams-vocabulary/#activity-types , bundles or types for each actually implemented. So when you ‘Create’ a node that is to be announced in the fediverse it makes a Activity Entity of bundle ‘Create’ with references at a minimum with fields ‘actor’ and ‘object’, but could have more (fieldable).
Depending on the entity referenced there is a mapping to Activity Vocabulary (actually plus other name spaces as discussed here How to represent places in an event. So basically everything in the
object: section there is provided by the Event Node itself and everything wrapping it is provided by the Activity entity.
The Outbox is then populated by a query of Activity entities - queried by reference to Actor.
I’m yet to get there (or need to implement this), but then, access control needs then also to be delegated to a (plugin?) that controls that ‘type’ of actor, and referenced entities. This works for the different 'Audience’s. Bonus of entity references here, the Action can remain with an reference to the removed entity. The delete action can use the entity reference still - only returning the entity reference uri; old actions referencing deleted entities return a Tombstone.
Finally the relationship entity is there for the actors ‘Follow’ and ‘Followers’. Again I initially thought this could be a multiple field on the actor, but it wants to have an acceptation workflow and can store, cache, additional fields - image, nick etc, about the remote following/follower. I’m coding away here still.