flak rss random

the activity person examined

One of the basic objects in ActivityPub is the actor. Also known as a Person, although there’s no promise it’s a human. If you are building ActivityPub software, or curious how the network works, it’s a good place to start. The ActivityPub spec and underlying ActivityStreams vocabulary explain what could or should be here, but not necessarily what you’ll see in the wild.

I’ve collected a few sample actor objects from various implementations. Here’s a walkthrough with an eye towards exposing real world behaviors and some compatibility notes. Implementations gathered include Honk, Mastodon, Microblog, Peertube, Pixelfed, and Pleroma. All use ActivityPub for federation, although the services themselves are targeted at slightly different use cases. There are also several other implementations out there.

(I had a collection of objects, but I lost them.) I’ve also included some outbox objects, which are a natural next step if you’re manually poking around. I’ve pretty printed everything, although in the wild you often see whitespace stripped objects, with the fields in any order.

@context

Every actor starts with a @context field which identifies it as part of the ActivityStreams namespace. This could be as simple as a string or a rather complicated array.

  "@context": "https://www.w3.org/ns/activitystreams",

  "@context": [
    "https://www.w3.org/ns/activitystreams",
    "https://w3id.org/security/v1",
    ........

I’ll come back to this, but it’s mostly a distraction. The important thing is that "https://www.w3.org/ns/activitystreams" must appear in there somewhere.

Compatibility note: Some software ignores @context entirely. Other software checks for the magic activitystreams string.

id

The id is a self referential field. Every actor object should be fetchable via this URL. It’s how I collected all the samples.

At the network protocol layer, this is your identity. All other published activities will refer to this actor.

Compatibility note: this URL very often looks like example.com/u/username but that’s not required.

Compatibility note: misskey in particular uses a different scheme, though I don’t have a sample handy.

url

The url field is the human friendly, browser ready URL. For a lot of software, this will be the same as the id. Mastodon prefers example.com/@username URLs to example.com/users/username URLs, and will even redirect browsers.

Compatibility note: Most software differentiates between browsers and other ActivityPub servers by looking at the Accept header. Both the id and url fields (where different) will return the appropriate response, either json or html, but this is not guaranteed. Some servers will only return html for the url field and only return json for the id field. So they’re not interchangeable.

Compatibility note: In particular, copying a URL from a browser and pasting into ActivityPub software requires some care, since it may not be the actor id.

type

The type field is probably “Person”. Other types exist, like Service and Application, although it’s rare to see them and rarer to distinguish.

name

The name field is whatever you want it to be, readily changed. The preferredUsername is the user’s handle. It probably won’t change.

Compatibility note: Changing username is likely to cause trouble and break federation in exciting ways, but it’s theoretically possible. So don’t change it, but prepare for the possibility that others may change theirs, I guess.

microblog

The microblog.pub actor is an interesting case study.

  "id": "https://microblog.pub",
  "url": "https://microblog.pub"

Not even a trailing slash! But there’s still a username.

  "preferredUsername": "dev",

And thus @dev@microblog.pub would work for mentions.

summary

This is your biography. It’s probably HTML, but with only very limited tag support. Maybe. Some implementations understand more tags than just <a>. Some software may let you drop custom emoji in here as well. There are no rules, just varying levels of commitment to interoperability.

  "summary": "<p>Recent books:<br />tech: FreeBSD Mastery: Jails<br />mystery: git commit murder<br />SF: Hydrogen Sleets</p><p>:flan_writing: :flan_book:🥋:ratstand: :ratstand: :ratstand:</p>",

Compatibility note: Peertube uses markdown.

  "summary": "FLOSS advocate, programmer, sysadmin, language enthusiast, amateur astronomer, etc.\n\nEnglish/日本語/한국어/Español OK.\n\nAlso found at [@sir@cmpwn.com](https://cmpwn.com/@sir)",

While here, I’ll mention the attachment field. Mastodon puts extra bits of info in here. This is an extension.

    {
      "name": "Pronouns",
      "type": "PropertyValue",
      "value": "he/him/"Oi! You!""
    },
    {
      "name": "next business model",
      "type": "PropertyValue",
      "value": "tarpit as a service"
    }

Compatibility note: Other software may not display these. Mastodon may not display any attachments after four.

avatar

The tiny picture that goes next to your posts is the icon.

  "icon": {
    "mediaType": "image/png",
    "type": "icon",
    "url": "https://honk.tedunangst.com/a?a=https%3A%2F%2Fhonk.tedunangst.com%2Fu%2Ftedu"
  },

The background picture for your profile, where present or supported, is the image.

  "image": {
    "mediaType": "image/jpeg",
    "type": "Image",
    "url": "https://media.bsd.network/accounts/headers/000/000/218/original/809c58a5be67bba0.jpg"
  },

Compatibility note: Maybe here, maybe not. Maybe animated GIF for great fun.

Compatibility note: The mediaType may not be present. It may be a lie.

follows

There are two special collections in the actor object, followers and following. These should be URLs pointing to the actual collections. It’s not required that the collections be fetchable, or accurate. They can return 403, and empty collections.

Compatibility note: The existence of a followers collection is fairly important for some software. Even if the collection itself isn’t fetched or used, its presence is sometimes used to determine post scope, addressing, delivery, etc.

Compatibility note: Some software known to the author, ahem, ahem, cough, cough, assumes followers collections are named “/followers”. This is more expedient than correct.

outbox

The outbox property describes a URL from which one should be able to retrieve an actor’s recent activities. This is a useful endpoint to know about when doing manual testing, since it allows collection a variety of other object types. Actually finding the objects can require some effort, however.

The simplest outbox is simply a collection of objects. (I’ve trimmed all the actual objects out of my samples.)

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://honk.tedunangst.com/u/tedu/outbox",
  "orderedItems": [
    "a great many things"
  ],
  "totalItems": 103,
  "type": "OrderedCollection"
}

Some software will hide the objects inside a first label.

  "first": {
    "id": "https://kawen.space/users/lain/outbox?max_id=9ke3P9nFhKknOZtOOO",
    "next": "https://kawen.space/users/lain/outbox?max_id=9ke4ifOWWtyIrRC0WG",
    "orderedItems": [
      "a great many things"
    ],
    "partOf": "https://kawen.space/users/lain/outbox",
    "type": "OrderedCollectionPage"
  },

And Mastodon will hide everything behind another URL.

  "first": "https://bsd.network/users/mwlucas/outbox?page=true",

So there’s several cases that need to be handled.

Compatibility note: Pixelfed returns 404 for the outbox.

inbox

The inbox is where other servers POST messages to. There may also be a sharedInbox property to reduce interserver traffic, but it lives under the endpoints property. This is more useful for sending posts, not fetching.

publicKey

As a practical matter, federated message delivery depends on HTTP signatures. These are signed with keys that live in the actor object in the publicKey property.

  "publicKey": {
    "id": "https://honk.tedunangst.com/u/tedu#key",
    "owner": "https://honk.tedunangst.com/u/tedu",
    "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA593GZ9TYrvWgMaMKQ6k6\ngkItUapUgNnNXzU9J63GRtYZ7CE/Zi39Kgpsxu77hHBj34vwjr1Oc9AMrVDIMfu9\nEirW1RWxPvrjThBU56VgkpkAXVsieaffJo80BA00QzV4x69Jgat6OT7ox/HMvMxR\nyZ6CXNCPKQALYqQF6v1fX1kO9lhIA+mPd0JN/qMKvZfd1NXABEk9nORUneH7Audt\nIHNdJzKMHC6wPSQWC7SmXT0/nq6o5mR2SgvwTI/JUx6T5r8NDrwSaqB69e+EMJqR\nxKOh9N4A1ba/AQOQZbO/YkFyYY2VE4HWbvS9XpYL74yT9D6Fp4cUovJiXC+ziam0\nNwIDAQAB\n-----END PUBLIC KEY-----\n"
  },

Note that in addition to the key data itself, the key has an owner, which matches the actor id. The key also has its own id, using a URL fragment. This allows the key and actor to be retrieved in the same object.

Compatibility note: In theory there’s some flexibility here. The key could maybe be a different URL. But I’m pretty sure that would fail to interoperate.

Followup: I’ve seen implementations that store the key at a different URL. It caused interoperability trouble.

@context redux

The Peertube actor in particular has a very large @context, although Mastodon can be quite large as well. This is some sort of linked data schema thing, which supposedly lets the data describe itself in some fashion. If you want to know more about what it’s supposed to do, talk to somebody who believes in it. Full disclosure: that is not me.

As a purely practical matter, though, here are a few additional comments. The required “https://www.w3.org/ns/activitystreams” context must be present for some implementations to know they are dealing with an activity object. Otherwise they will drop the object without processing. However, none of the other schemas are actually processed.

For example, Mastodon includes the following to introduce its own namespace and objects.

      "Emoji": "toot:Emoji",
      "toot": "http://joinmastodon.org/ns#",

And then later includes emoji in the tag collection.

      "icon": {
        "mediaType": "image/png",
        "type": "Image",
        "url": "https://media.bsd.network/custom_emojis/images/000/011/827/original/flan_writing.png"
      },
      "id": "https://bsd.network/emojis/11827",
      "name": ":flan_writing:",
      "type": "Emoji",
      "updated": "2018-07-29T16:41:17Z"

All is well and proper. However, Mastodon (and other implementations) will merrily process Emoji in actor objects that do not reference the toot schema. In fact, if you create an alternate schema honk:Emoji with entirely different semantics, Mastodon will still just go ahead and process these objects as if they were toot:Emoji. None of the namespacing is actually checked.

Compatibility note: theory and practice.

conclusion

More to come. I hope to cover Note objects in a followup.

My thanks, condolences, and apologies to the people whose actor objects I snagged for samples.

Posted 08 Jul 2019 18:44 by tedu Updated: 02 May 2022 01:15
Tagged: activitypub web