A quick survey of PHP projects in the ActivityPub space, plus comments

As part of working towards ActivityPub integration for a couple of PHP projects I’ve been looking into PHP ActivityPub-related projects and recently posted a summary in this issue:

As a new user on this SocialHub instance I can’t (yet) repost the info here (too many links) but in that issue see the section “Potential models and building blocks” where I’ve listed the various efforts I found.

The other PHP project I’m contributing to is a module for Drupal:

Some of my early impressions:

  • There are various projects that are drafts towards general-use PHP packages for ActivityPub integration, including pterotype-project/activitypub-php, landrok/activitypub, dansup/php-activitypub, patbator/activitystreams, and assemblee-virtuelle/ActivityPubBundle (this last one for Synfony 4).
  • Of these, landrok/activitypub is probably the farthest along in terms of general applicability and comes with some great documentation. That said, it still has a ways to go and - inevitably - includes some architectural decisions that may conflict with those of specific ActivityPub applications.
  • Pixelfed is far and away the furthest developed specific implementation that I found and provides a solid example to work from in terms of grokking what’s involved and how the different pieces interact, including ones that are needed but not in the ActivityPub spec–webfinger, nodeinfo.

As usual, in theory we’d all stand to benefit from standardising on a set of packages we could collaborate on rather than maintaining a different implementation for each application. But there are challenges in getting there. What level of abstraction is enough to achieve broad applicability but not so much that it conflicts with specific architectures?

As a fairly arbitrary example, Drupal uses Symfony’s serializer component and accompanying normalizers/denormalizers. So in Drupal this is the API we would probably use for transforming ActivityPub objects to and from Drupal entities. But other implementations naturally will have their own distinct approaches.

Personally, some of my first steps will be:

  • Study Pixelfed–well worth the time.
  • See if I can directly use at least parts of, and/or contribute to, landrok/activitypub and delirehberi/webfinger.

Other tips and ideas very welcome!

1 Like

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.

1 Like

@ekes Wow, great to know you’re actively working in this space and to hear your insights and experience. And thanks for your extensive comments in the ActivityPub for Drupal issue queue. See you there!