Having your Website Visible on the Fediverse
Published on
Updated on
ActivityPub is the backbone of the Fediverse. Implementing this protocol will allow one to interact with users on Mastodon, Pixelfed, Friendica, and others. Unfortunately for a static website, this protocol uses a publish-subscribe pattern. A service would need to be written in order to handle subscribers, pushing messages of published items, as well as receiving mentions. At the current state of my website, I am uninterested in creating such a service. However, I do wish to document the parts of the ActivityPub protocol I was able to implement so far using only a static website. This post extends off of Eugen Rochko’s work which I encourage you to check out.
Webfinger
When someone looks up a handle on Mastodon, it performs a Webfinger
lookup. For example if I am looking up the user brozek@brandonrozek.com
,
then the service will perform a GET request at
https://brandonrozek.com/.well-known/webfinger?resource=acct:brozek@brandonrozek.com
.
A Webfinger request should return the handle of the user (the subject
) as well as
a link to its actor
.
{
"subject": "acct:brozek@brandonrozek.com",
"links": [
{
"rel": "self",
"type": "application/activity+json",
"href": "https://brandonrozek.com/brozek.json"
}
]
}
Now for a static site with one user, you can hardcode the Webfinger directly
at .well-known/webfinger
. The only issue is that many webservers determine the
mime-type of a file by its extension. In this case, we’ll have to let the webserver
(mine is nginx) know how to serve it.
location /.well-known/webfinger {
default_type application/jrd+json;
add_header Access-Control-Allow-Origin "*";
}
Actor
An actor defines how to interact with a user as well as its metadata.
This file also needs to be served with the application/jrd+json
header.
Below is an example of what I have on my website as well as how it looks
on Mastodon.
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"id": "https://brandonrozek.com/brozek.json",
"type": "Person",
"inbox": "https://brandonrozek.com/inbox",
"outbox": "https://granary.io/url?input=rss&output=as2&url=http%3A%2F%2Fbrandonrozek.com%2Fblog%2Findex.xml",
"preferredUsername": "brozek",
"name": "Brandon Rozek",
"summary": "<p>Writings from <a href='https://brandonrozek.com'>https://brandonrozek.com</p></a><p>Social profile <a href='https://fosstodon.org/@brozek'>@brozek@fosstodon.org</a></p>",
"url": "https://brandonrozek.com",
"manuallyApprovesFollowers": false,
"discoverable": true,
"published": "2022-05-17T00:00:00Z",
"icon": {
"type": "Image",
"mediaType": "image/jpeg",
"url": "https://brandonrozek.com/img/avatar.jpg"
},
"publicKey": {
"id": "https://brandonrozek.com/brozek.json#main-key",
"owner": "https://brandonrozek.com.com/brozek.json",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----"
}
}
Limitations and Conclusion
Notice in the previous screenshot that it says “Cancel Follow Request”. This is because my static website is not equipped to handle a follow request. Standard ActivityPub servers keep an inventory of all the users that follow their users and who each user on the server is following.
The outbox defines items that the user has published. I have it pointed to an activity JSON feed generated directly from my RSS using granary.io. Mastodon correctly displays the number of blog posts I have, but sadly it does not show any of them. I’m not entirely sure why, though my hypothesis is that the posts need to be pushed to the Mastodon server when its created. It could also be that the actor isn’t defined properly in the activity JSON feed. If you happen to know please get in touch.