Article / 1st Nov 2022

The Fediverse, And Custom Domains

For reasons that are very relevant to the events of this past week, I have decided to finally get around to setting up a Mastodon / Fediverse account.

I wanted to do it under my own domain, but last time I looked into this (three or so years ago), that was not possible if the domain was running something other than a Fediverse server - if you tried to follow @andrew@aeracode.org, it would come along to this website and try and do some ActivityPub on it.

Faced with the idea of implementing proxy views for all the ActivityPub endpoints within my site, I instead opted to go back to the warm, stable embrace of Twitter (ahem), and come back and see how the fediverse had evolved in a few years' time.

Well, here we are, and not only has the software and number of people improved, so has a few aspects of the underlying protocol (and look, ActivityPub is not great, but the only thing worse than implementing a flawed protocol is to try and invent yet another one to take its place).

In particular, Fediverse servers now go looking for server and account (Actor) information via three URLs (which ones they want seems to differ by server):

The last is static, and while the first two can be static if you just have one account on a server and never want to add any more, they're really meant to be dynamic and so should be proxied through to the fediverse server.

So, faced with this much easier problem, that's what I did. This site still runs Django under the hood (though now powered by a sqlite database created on the fly - more on that in the future), and so doing this was a simple case of adding three views:

from proxy.views import proxy_view

def wellknown_webfinger(request):
    remote_url = (
        "https://fedi.aeracode.org/.well-known/webfinger?"
        + request.META["QUERY_STRING"]
    )
    return proxy_view(request, remote_url)


def wellknown_hostmeta(request):
    remote_url = (
        "https://fedi.aeracode.org/.well-known/host-meta?"
        + request.META["QUERY_STRING"]
    )
    return proxy_view(request, remote_url)


def wellknown_nodeinfo(request):
    remote_url = "https://fedi.aeracode.org/.well-known/nodeinfo"
    return proxy_view(request, remote_url)

This uses the django-proxy package to provide the proxy_view. You might also want to put in a view that redirects yourdomain.com/@username:

from django.http import HttpResponseRedirect

def username_redirect(request):
    return HttpResponseRedirect("https://fedi.aeracode.org/@andrew")

Hooking these up to the right URLs is the only other thing that's needed:

urlpatterns = [
    ...
    # Fediverse
    path(".well-known/webfinger", blog.wellknown_webfinger),
    path(".well-known/host-meta", blog.wellknown_hostmeta),
    path(".well-known/nodeinfo", blog.wellknown_nodeinfo),
    path("@andrew", blog.username_redirect),
]

That's what's up and running on this site right now, meaning you can type @andrew@aeracode.org into your favourite Fediverse client and it will understand that it's meant to actually go over to fedi.aeracode.org to ply its ActivityPub magic. If you're a CloudFlare Pages user, Jacob has a nice write-up on how to do a similar trick over there.

I'm happy, and crucially, I have avoided the temptation of writing my own ActivityPub/Mastodon-compatible server in Django. For now, at least.

Incidentally, my server is hosted by masto.host, who I have nothing but good things to say about so far. I will happily pay $9 a month to avoid running yet another copy of all the various datastores that Mastodon needs, just for my own private instance.

Note: Updated since first publication to add host-meta and the username redirect