title: The Fediverse, And Custom Domains
url: https://aeracode.org/2022/11/01/fediverse-custom-domains/
hash_url: 9a78ae9b6814326d9c3df1425006f8f4

<p>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.</p>
<p>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 <em>other</em> than a Fediverse server - if you tried to follow
<code>@andrew@aeracode.org</code>, it would come along to this website and try and do some
ActivityPub on it.</p>
<p>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.</p>
<p>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).</p>
<p>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):</p>
<ul>
<li><code>/.well-known/webfinger</code></li>
<li><code>/.well-known/host-meta</code></li>
<li><code>/.well-known/nodeinfo</code></li>
</ul>
<p>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.</p>
<p>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:</p>
<div class="language-python3"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">proxy.views</span> <span class="kn">import</span> <span class="n">proxy_view</span>

<span class="k">def</span> <span class="nf">wellknown_webfinger</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
    <span class="n">remote_url</span> <span class="o">=</span> <span class="p">(</span>
        <span class="s2">"https://fedi.aeracode.org/.well-known/webfinger?"</span>
        <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="n">META</span><span class="p">[</span><span class="s2">"QUERY_STRING"</span><span class="p">]</span>
    <span class="p">)</span>
    <span class="k">return</span> <span class="n">proxy_view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">remote_url</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">wellknown_hostmeta</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
    <span class="n">remote_url</span> <span class="o">=</span> <span class="p">(</span>
        <span class="s2">"https://fedi.aeracode.org/.well-known/host-meta?"</span>
        <span class="o">+</span> <span class="n">request</span><span class="o">.</span><span class="n">META</span><span class="p">[</span><span class="s2">"QUERY_STRING"</span><span class="p">]</span>
    <span class="p">)</span>
    <span class="k">return</span> <span class="n">proxy_view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">remote_url</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">wellknown_nodeinfo</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
    <span class="n">remote_url</span> <span class="o">=</span> <span class="s2">"https://fedi.aeracode.org/.well-known/nodeinfo"</span>
    <span class="k">return</span> <span class="n">proxy_view</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="n">remote_url</span><span class="p">)</span></pre></div>
</div>
<p>This uses the <code>django-proxy</code> package to provide the <code>proxy_view</code>. You might
also want to put in a view that redirects <code>yourdomain.com/@username</code>:</p>
<div class="language-python3"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.http</span> <span class="kn">import</span> <span class="n">HttpResponseRedirect</span>

<span class="k">def</span> <span class="nf">username_redirect</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">HttpResponseRedirect</span><span class="p">(</span><span class="s2">"https://fedi.aeracode.org/@andrew"</span><span class="p">)</span></pre></div>
</div>
<p>Hooking these up to the right URLs is the only other thing that's needed:</p>
<div class="language-python3"><div class="highlight"><pre><span></span><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
    <span class="o">...</span>
    <span class="c1"># Fediverse</span>
    <span class="n">path</span><span class="p">(</span><span class="s2">".well-known/webfinger"</span><span class="p">,</span> <span class="n">blog</span><span class="o">.</span><span class="n">wellknown_webfinger</span><span class="p">),</span>
    <span class="n">path</span><span class="p">(</span><span class="s2">".well-known/host-meta"</span><span class="p">,</span> <span class="n">blog</span><span class="o">.</span><span class="n">wellknown_hostmeta</span><span class="p">),</span>
    <span class="n">path</span><span class="p">(</span><span class="s2">".well-known/nodeinfo"</span><span class="p">,</span> <span class="n">blog</span><span class="o">.</span><span class="n">wellknown_nodeinfo</span><span class="p">),</span>
    <span class="n">path</span><span class="p">(</span><span class="s2">"@andrew"</span><span class="p">,</span> <span class="n">blog</span><span class="o">.</span><span class="n">username_redirect</span><span class="p">),</span>
<span class="p">]</span></pre></div>
</div>
<p>That's what's up and running on this site right now, meaning you can type
<code>@andrew@aeracode.org</code> into your favourite Fediverse client and it will
understand that it's meant to actually go over to <code>fedi.aeracode.org</code> to ply
its ActivityPub magic. If you're a CloudFlare Pages user, Jacob has
<a href="https://jacobian.org/til/my-mastodon-instance/">a nice write-up</a> on how to do
a similar trick over there.</p>
<p>I'm happy, and crucially, I have avoided the temptation of writing my own
ActivityPub/Mastodon-compatible server in Django. For now, at least.</p>
<p>Incidentally, my server is hosted by <a href="https://masto.host">masto.host</a>, 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.</p>
<p><em>Note: Updated since first publication to add</em> <code>host-meta</code>
<em>and the username redirect</em></p>
