Skip to content

The actor layer

Note

bovine_herd will probably be adapted to fit this role. The big change will actually to remove stuff like storing activities … So this layer exists, except it currently does too much.

The cattle_grid layer checks remote identities. With the exception of signed objects, e.g. eddsa-jcs-2022, cattle_grid should be the one time public keys of remote actors are retrieved.

The goal of the next layer should be to enable to send messages. This is already sufficiently complicated to make a lot of stuff necessary. See jskitten for a simple implementation.

Plan

My current plan for the next layer are the following things:

  • The actor objects
  • The inbox endpoint
  • The ability to send messages
  • The followers collection (can be disabled)
  • The following collection (can be disabled)
  • Necessary parsing / validation / modifications

These things are connected and in some sense not separable.

graph LR

A[send messages] -->|needs| B[followers collection]
A -->|needs| C[private key]
C -->|defines| D[public key]
D -->|distributed by| E[actor object]
B -->|update from| F[Accept-Follow to Inbox]

This shows that in order to be able to send messages, we need most of the properties already mentioned. I believe that if we update the actor object, e.g. change the public key, it should be best practice to update at least the followers and following. So this makes tracking the following collection also necessary.

Note

If one doesn’t support the followers collection, everything becomes simpler. Unfortunately, it also becomes uninteresting, as the followers collection is one of the things that turn the Fediverse into a social network. More interesting would be to support other addressing collections.

Things that can be build with this

Without needing fancy storage:

  • Bots like jskitten that reply to a prompt
  • RSS to Fedi bots, e.g. fetch an RSS feed and on updates post them
  • Better lists like ayrshire
  • Tag reposters like tags

With fancy storage: anything. That’s not really the point, this reduces building something for the Fediverse to implementing logic to store your own content and make it accessible. You do not have to worry about handling POST requests, except by communicating with queues.

Actor object

The actor object contains the following properties, we are interested in

{
    "@context": [...],
    "id": "https://my_server.example/my_actor",
    "inbox": "https://my_server.example/my_inbox",
    "followers": "https://my_server.example/my_followers",
    "following": "https://my_server.example/my_followers",
    "publicKey": {
        "id": "https://my_server.example/my_actor#key",
        "owner":  "https://my_server.example/my_actor",
        "publicKeyPem": "..."
    }, ...
}

we probably also want to manage the preferredUsername and webfinger here, as the current Fediverse is unusable without it. Similarly, one might want to define an outbox, as people seem to expect it (it’s a MUST in ActivityPub for some reason).

If no outbox service is provided, one can just stub it to direct to the actor or something.

outbox service

This means one probably wants some server to play the role of outbox … One should consider the outbox service as a challenge of writing the simplest code. It just needs to subscribe to the send queue, filter out non-public things, then store ids (keep say the last 1000). Then vomit them out as an OrderedCollection on requests.

Sending messages

Sending messages involves several steps:

  1. Determine the recipients by dereferencing collections owned by the actor, i.e. the followers collection
  2. Remove bto and bcc, then enqueue delivery for each recipient
  3. Transform messages to ActivityPub format
  4. Lookup inbox for recipient
  5. Send signed post to recipient’s inbox with message sans bto, bcc

One should of course cache the looked up inboxes, and update them on an Update.

Due to Json-LD and ActivityPub being great, one needs to compact the activities against the context

[
    "https://www.w3.org/ns/activitystreams",
    {
        "Hashtag": "as:Hashtag"
    }
]

to be interoperable with a lot of software. More @context elements may be necessary. So one probably needs to cook up some way to transport this.

Followers / Following

One should introduce the following meaning to the absence of followers / following collections:

  • If no followers collection, the actor does not process Follow requests.
  • If no following collection, the actor does not send Follow requests.

Objects are added to these collections on Accept - Follow and removed on Undo - Follow, Reject - Follow, Block, Delete, Undo - Accept - Follow. Finally, modifications of the entry may occur on Update and Move.

Parsing / Validation / Modifications

The first step in parsing should be getting rid of external json-ld contexts. One can probably just use compacted against the empty context. Next, one should check that the ids match the authority associated with the requester. This falls under validation. If the authority does not match, one should check for signatures (keep the raw message), otherwise refetch the stuff.

By modifications, I mean stuff like handling Accepts. Finally, one needs to include inbox forwarding.