A place to cache linked articles (think custom and personal wayback machine)
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

index.html 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>The Fediverse, And Custom Domains (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Documented, feel free to shoot an email. -->
  28. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  29. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  30. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  31. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  36. <script>
  37. function toggleTheme(themeName) {
  38. document.documentElement.classList.toggle(
  39. 'forced-dark',
  40. themeName === 'dark'
  41. )
  42. document.documentElement.classList.toggle(
  43. 'forced-light',
  44. themeName === 'light'
  45. )
  46. }
  47. const selectedTheme = localStorage.getItem('theme')
  48. if (selectedTheme !== 'undefined') {
  49. toggleTheme(selectedTheme)
  50. }
  51. </script>
  52. <meta name="robots" content="noindex, nofollow">
  53. <meta content="origin-when-cross-origin" name="referrer">
  54. <!-- Canonical URL for SEO purposes -->
  55. <link rel="canonical" href="https://aeracode.org/2022/11/01/fediverse-custom-domains/">
  56. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  57. <article>
  58. <header>
  59. <h1>The Fediverse, And Custom Domains</h1>
  60. </header>
  61. <nav>
  62. <p class="center">
  63. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  64. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  65. </svg> Accueil</a> •
  66. <a href="https://aeracode.org/2022/11/01/fediverse-custom-domains/" title="Lien vers le contenu original">Source originale</a>
  67. </p>
  68. </nav>
  69. <hr>
  70. <p>For reasons that are very relevant to the events of this past week, I have
  71. decided to finally get around to setting up a Mastodon / Fediverse account.</p>
  72. <p>I wanted to do it under my own domain, but last time I looked into this
  73. (three or so years ago), that was not possible if the domain was running
  74. something <em>other</em> than a Fediverse server - if you tried to follow
  75. <code>@andrew@aeracode.org</code>, it would come along to this website and try and do some
  76. ActivityPub on it.</p>
  77. <p>Faced with the idea of implementing proxy views for all the ActivityPub
  78. endpoints within my site, I instead opted to go back to the warm, stable
  79. embrace of Twitter (ahem), and come back and see how the fediverse had evolved in
  80. a few years' time.</p>
  81. <p>Well, here we are, and not only has the software and number of people improved,
  82. so has a few aspects of the underlying protocol (and look, ActivityPub is not
  83. great, but the only thing worse than implementing a flawed protocol is to try
  84. and invent yet another one to take its place).</p>
  85. <p>In particular, Fediverse servers now go looking for server and account (Actor)
  86. information via three URLs (which ones they want seems to differ by server):</p>
  87. <ul>
  88. <li><code>/.well-known/webfinger</code></li>
  89. <li><code>/.well-known/host-meta</code></li>
  90. <li><code>/.well-known/nodeinfo</code></li>
  91. </ul>
  92. <p>The last is static, and while the first two can be static if you just have one
  93. account on a server and never want to add any more, they're really meant to be
  94. dynamic and so should be proxied through to the fediverse server.</p>
  95. <p>So, faced with this much easier problem, that's what I did. This site still
  96. runs Django under the hood (though now powered by a sqlite database created on
  97. the fly - more on that in the future), and so doing this was a simple case of
  98. adding three views:</p>
  99. <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>
  100. <span class="k">def</span> <span class="nf">wellknown_webfinger</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
  101. <span class="n">remote_url</span> <span class="o">=</span> <span class="p">(</span>
  102. <span class="s2">"https://fedi.aeracode.org/.well-known/webfinger?"</span>
  103. <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>
  104. <span class="p">)</span>
  105. <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>
  106. <span class="k">def</span> <span class="nf">wellknown_hostmeta</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
  107. <span class="n">remote_url</span> <span class="o">=</span> <span class="p">(</span>
  108. <span class="s2">"https://fedi.aeracode.org/.well-known/host-meta?"</span>
  109. <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>
  110. <span class="p">)</span>
  111. <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>
  112. <span class="k">def</span> <span class="nf">wellknown_nodeinfo</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
  113. <span class="n">remote_url</span> <span class="o">=</span> <span class="s2">"https://fedi.aeracode.org/.well-known/nodeinfo"</span>
  114. <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>
  115. </div>
  116. <p>This uses the <code>django-proxy</code> package to provide the <code>proxy_view</code>. You might
  117. also want to put in a view that redirects <code>yourdomain.com/@username</code>:</p>
  118. <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>
  119. <span class="k">def</span> <span class="nf">username_redirect</span><span class="p">(</span><span class="n">request</span><span class="p">):</span>
  120. <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>
  121. </div>
  122. <p>Hooking these up to the right URLs is the only other thing that's needed:</p>
  123. <div class="language-python3"><div class="highlight"><pre><span></span><span class="n">urlpatterns</span> <span class="o">=</span> <span class="p">[</span>
  124. <span class="o">...</span>
  125. <span class="c1"># Fediverse</span>
  126. <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>
  127. <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>
  128. <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>
  129. <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>
  130. <span class="p">]</span></pre></div>
  131. </div>
  132. <p>That's what's up and running on this site right now, meaning you can type
  133. <code>@andrew@aeracode.org</code> into your favourite Fediverse client and it will
  134. understand that it's meant to actually go over to <code>fedi.aeracode.org</code> to ply
  135. its ActivityPub magic. If you're a CloudFlare Pages user, Jacob has
  136. <a href="https://jacobian.org/til/my-mastodon-instance/">a nice write-up</a> on how to do
  137. a similar trick over there.</p>
  138. <p>I'm happy, and crucially, I have avoided the temptation of writing my own
  139. ActivityPub/Mastodon-compatible server in Django. For now, at least.</p>
  140. <p>Incidentally, my server is hosted by <a href="https://masto.host">masto.host</a>, who
  141. I have nothing but good things to say about so far. I will happily pay $9 a
  142. month to avoid running yet another copy of all the various datastores that
  143. Mastodon needs, just for my own private instance.</p>
  144. <p><em>Note: Updated since first publication to add</em> <code>host-meta</code>
  145. <em>and the username redirect</em></p>
  146. </article>
  147. <hr>
  148. <footer>
  149. <p>
  150. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  151. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  152. </svg> Accueil</a> •
  153. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  154. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  155. </svg> Suivre</a> •
  156. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  157. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  158. </svg> Pro</a> •
  159. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  160. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  161. </svg> Email</a> •
  162. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  163. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  164. </svg> Légal</abbr>
  165. </p>
  166. <template id="theme-selector">
  167. <form>
  168. <fieldset>
  169. <legend><svg class="icon icon-brightness-contrast">
  170. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  171. </svg> Thème</legend>
  172. <label>
  173. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  174. </label>
  175. <label>
  176. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  177. </label>
  178. <label>
  179. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  180. </label>
  181. </fieldset>
  182. </form>
  183. </template>
  184. </footer>
  185. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  186. <script>
  187. function loadThemeForm(templateName) {
  188. const themeSelectorTemplate = document.querySelector(templateName)
  189. const form = themeSelectorTemplate.content.firstElementChild
  190. themeSelectorTemplate.replaceWith(form)
  191. form.addEventListener('change', (e) => {
  192. const chosenColorScheme = e.target.value
  193. localStorage.setItem('theme', chosenColorScheme)
  194. toggleTheme(chosenColorScheme)
  195. })
  196. const selectedTheme = localStorage.getItem('theme')
  197. if (selectedTheme && selectedTheme !== 'undefined') {
  198. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  199. }
  200. }
  201. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  202. window.addEventListener('load', () => {
  203. let hasDarkRules = false
  204. for (const styleSheet of Array.from(document.styleSheets)) {
  205. let mediaRules = []
  206. for (const cssRule of styleSheet.cssRules) {
  207. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  208. continue
  209. }
  210. // WARNING: Safari does not have/supports `conditionText`.
  211. if (cssRule.conditionText) {
  212. if (cssRule.conditionText !== prefersColorSchemeDark) {
  213. continue
  214. }
  215. } else {
  216. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  217. continue
  218. }
  219. }
  220. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  221. }
  222. // WARNING: do not try to insert a Rule to a styleSheet you are
  223. // currently iterating on, otherwise the browser will be stuck
  224. // in a infinite loop…
  225. for (const mediaRule of mediaRules) {
  226. styleSheet.insertRule(mediaRule.cssText)
  227. hasDarkRules = true
  228. }
  229. }
  230. if (hasDarkRules) {
  231. loadThemeForm('#theme-selector')
  232. }
  233. })
  234. </script>
  235. </body>
  236. </html>