<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>fediverse &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/fediverse/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Tue, 07 Apr 2026 09:19:52 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>fediverse &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Now witness the power of this fully operational Fediverse!]]></title>
		<link>https://shkspr.mobi/blog/2025/11/now-witness-the-power-of-this-fully-operational-fediverse/</link>
					<comments>https://shkspr.mobi/blog/2025/11/now-witness-the-power-of-this-fully-operational-fediverse/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 23 Nov 2025 12:34:35 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[BlueSky]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[statistics]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=63716</guid>

					<description><![CDATA[How can you measure the popularity of a social network site? Perhaps by counting the number of active accounts, or the quality of the discourse, or even how many people reply to your witty memes.  Me? I prefer to look at how many people visit my blog from each site. It is an imperfect measure - and a vain one - but lets me know where I should be spending my time. No point posting on a network…]]></description>
										<content:encoded><![CDATA[<p>How can you measure the popularity of a social network site? Perhaps by counting the number of active accounts, or the quality of the discourse, or even how many people reply to your witty memes.</p>

<p>Me? I prefer to look at how many people visit my blog from each site. It is an imperfect measure - and a vain one - but lets me know where I should be spending my time. No point posting on a network which is just bots talking to each other, right?</p>

<p>Earlier this year <a href="https://shkspr.mobi/blog/2025/09/reasonably-accurate-privacy-conscious-cookieless-visitor-tracking-for-wordpress/">I built a stats-counter for my blog</a>. Every time someone clicks from a website which links to my blog, it records that visit in a database. I get to see which blog posts are doing numbers, and where those numbers came from.</p>

<p>Until fairly recently, the Mastodon social network didn't send referer details. I thought that reduced the visibility of the network and <a href="https://shkspr.mobi/blog/2024/12/mastodon-now-sends-referer-headers-hurrah/">lobbied for it to change</a>. As various Mastodon servers upgrade, and admins opt-in, it is becoming more apparent just how much traffic originates from the Fediverse.</p>

<p>Over the last few weeks, here's how many people have clicked <em>from</em> BlueSky and Mastodon <em>to</em> one of my blog posts.</p>

<table class="edent_stats_column"><thead><tr><th class="totals">Total</th><th>Source</th></tr></thead><tbody>
<tr><td class="stats-count">1,607</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=bsky.app"><a href="https://bsky.app">bsky.app</a></td></tr>
<tr><td class="stats-count">752</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.social"><a href="https://mastodon.social">mastodon.social</a></td></tr>
</tbody></table>

<p>At first glance, it doesn't look good for our elephantine friends, does it? The butterfly sends over twice the traffic. Game over!</p>

<p>But, of course, while Mastodon.social is the biggest instance - it is far from the only one. What happens if we slide down the long tail? Here's all the Mastodon-ish instances which sent me over 10 clicks.</p>

<table class="edent_stats_column"><thead><tr><th class="totals">Total</th><th>Source</th></tr></thead><tbody>
<tr><td class="stats-count">193</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=phanpy.social"><a href="https://phanpy.social">phanpy.social</a></td></tr>
<tr><td class="stats-count">120</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=joinmastodon.org"> android-app://org.joinmastodon.android/</td></tr>
<tr><td class="stats-count">106</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=infosec.exchange"><a href="https://infosec.exchange">infosec.exchange</a></td></tr>
<tr><td class="stats-count">62</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mas.to"><a href="https://mas.to">mas.to</a></td></tr>
<tr><td class="stats-count">59</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mstdn.social"><a href="https://mstdn.social">mstdn.social</a></td></tr>
<tr><td class="stats-count">55</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=social.vivaldi.net"><a href="https://social.vivaldi.net">social.vivaldi.net</a></td></tr>
<tr><td class="stats-count">49</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=wandering.shop"><a href="https://wandering.shop">wandering.shop</a></td></tr>
<tr><td class="stats-count">48</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=fosstodon.org"><a href="https://fosstodon.org">fosstodon.org</a></td></tr>
<tr><td class="stats-count">33</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mathstodon.xyz"><a href="https://mathstodon.xyz">mathstodon.xyz</a></td></tr>
<tr><td class="stats-count">27</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.online"><a href="https://mastodon.online">mastodon.online</a></td></tr>
<tr><td class="stats-count">26</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.scot"><a href="https://mastodon.scot">mastodon.scot</a></td></tr>
<tr><td class="stats-count">24</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=app.wafrn.net"><a href="https://app.wafrn.net">app.wafrn.net</a></td></tr>
<tr><td class="stats-count">19</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=indieweb.social"><a href="https://indieweb.social">indieweb.social</a></td></tr>
<tr><td class="stats-count">18</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=social.lol"><a href="https://social.lol">social.lol</a></td></tr>
<tr><td class="stats-count">17</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=tech.lgbt"><a href="https://tech.lgbt">tech.lgbt</a></td></tr>
<tr><td class="stats-count">17</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=toot.wales"><a href="https://toot.wales">toot.wales</a></td></tr>
<tr><td class="stats-count">16</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=en.osm.town"><a href="https://en.osm.town">en.osm.town</a></td></tr>
<tr><td class="stats-count">16</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=feditrends.com"><a href="https://feditrends.com">feditrends.com</a></td></tr>
<tr><td class="stats-count">14</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mstdn.ca"><a href="https://mstdn.ca">mstdn.ca</a></td></tr>
<tr><td class="stats-count">14</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=piefed.social"><a href="https://piefed.social">piefed.social</a></td></tr>
<tr><td class="stats-count">12</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=wetdry.world"><a href="https://wetdry.world">wetdry.world</a></td></tr>
<tr><td class="stats-count">11</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=c.im"><a href="https://c.im">c.im</a></td></tr>
<tr><td class="stats-count">11</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.nl"><a href="https://mastodon.nl">mastodon.nl</a></td></tr>
<tr><td class="stats-count">51</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.social"> Sites sending &lt; 10 clicks</td></tr>
</tbody></table>

<p>Ah! Add them all up and you get a grand total of <strong>1,773 visitors from Mastodon-powered sites</strong>.  That's <em>more</em> than BlueSky.</p>

<p>Now, there are some obvious caveats to the data:</p>

<ul>
<li>I have a smaller follower count on BlueSky than I do on Mastodon.</li>
<li>My posts may appeal more to one demographic than another.</li>
<li>People may have strict privacy controls which suppress the true volume of visitors.</li>
<li>There's no way to measure how long someone spends reading my posts.</li>
<li>RSS and newsletter visitors aren't counted.</li>
<li>Clicks from apps may not always show a referer.</li>
<li>Some people may be on multiple services.</li>
<li>Fediverse users can follow the post directly, so don't need to visit the site to read it.</li>
</ul>

<p>And yet… no matter how you slice it, Fediverse servers are sending as much traffic as BlueSky!</p>

<p>I think this is brilliant. Web services should be able to scale from small to big - and each ActivityPub-powered site helps power the open Internet.</p>

<p>Just for completeness, this is how Reddit, Facebook, LinkedIn, Twitter, and Lemmy do over the same period:</p>

<table class="edent_stats_column"><thead><tr><th class="totals">Total</th><th>Source</th></tr></thead><tbody>
<tr><td class="stats-count">1,158</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=reddit.com"><a href="https://reddit.com">reddit.com</a></td></tr>
<tr><td class="stats-count">585</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=reddit.com"> android-app://com.reddit.frontpage/</td></tr>
<tr><td class="stats-count">76</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=facebook.com"><a href="https://facebook.com">facebook.com</a></td></tr>
<tr><td class="stats-count">76</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=old.reddit.com"><a href="https://old.reddit.com/r/programming/">https://old.reddit.com/r/programming/</a></td></tr>
<tr><td class="stats-count">56</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/programming/">https://www.reddit.com/r/programming/</a></td></tr>
<tr><td class="stats-count">52</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=youtube.com"><a href="https://youtube.com">youtube.com</a></td></tr>
<tr><td class="stats-count">41</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=t.co"><a href="https://t.co">t.co</a></td></tr>
<tr><td class="stats-count">38</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=old.reddit.com"><a href="https://old.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/">https://old.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/</a></td></tr>
<tr><td class="stats-count">31</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=linkedin.com"><a href="https://linkedin.com">linkedin.com</a></td></tr>
<tr><td class="stats-count">27</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.world"> android-app://io.syncapps.lemmy_sync/</td></tr>
<tr><td class="stats-count">27</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/">https://www.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/</a></td></tr>
<tr><td class="stats-count">22</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=old.reddit.com"><a href="https://old.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/">https://old.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/</a></td></tr>
<tr><td class="stats-count">22</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.ca"><a href="https://lemmy.ca">lemmy.ca</a></td></tr>
<tr><td class="stats-count">17</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=linkedin.com"> android-app://com.linkedin.android/</td></tr>
<tr><td class="stats-count">16</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.dbzer0.com"><a href="https://lemmy.dbzer0.com">lemmy.dbzer0.com</a></td></tr>
<tr><td class="stats-count">14</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=feddit.org"><a href="https://feddit.org">feddit.org</a></td></tr>
<tr><td class="stats-count">11</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/">https://www.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/</a></td></tr>
<tr><td class="stats-count">10</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=discuss.tchncs.de"><a href="https://discuss.tchncs.de">discuss.tchncs.de</a></td></tr>
<tr><td class="stats-count">10</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=l.instagram.com"><a href="https://l.instagram.com">l.instagram.com</a></td></tr>
<tr><td class="stats-count">8</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.blahaj.zone"><a href="https://lemmy.blahaj.zone">lemmy.blahaj.zone</a></td></tr>
<tr><td class="stats-count">6</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/GrapheneOS/comments/1m2l84b/considering_making_the_switch_does_google_pay/">https://www.reddit.com/r/GrapheneOS/comments/1m2l84b/considering_making_the_switch_does_google_pay/</a></td></tr>
<tr><td class="stats-count">6</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=reddthat.com"><a href="https://reddthat.com">reddthat.com</a></td></tr>
</tbody></table>

<p>If you add up all the Lemmy instances, they send about as much traffic as Facebook and LinkedIn combined. That's not a huge surprise - those platforms hate anyone clicking away to the wider web.</p>

<p>Twitter is basically <a href="https://en.wikipedia.org/wiki/Dead_Internet_theory">the Dead Internet</a>. I'm no longer on there, but I do occasionally search it to see who is sharing my posts. The popular posts I write get shared a <em>lot</em> - sometimes by accounts with huge followers - yet there are no comments or retweets and barely and clicks.</p>

<p>I don't do Instagram or Threads, and that might be reflected in their low numbers. But I'm not active on YouTube either - yet people there occasionally link back to me.</p>

<h2 id="final-thoughts"><a href="https://shkspr.mobi/blog/2025/11/now-witness-the-power-of-this-fully-operational-fediverse/#final-thoughts">Final Thoughts</a></h2>

<p>Firstly, my stats only represent my site. Your site might be very different.</p>

<p>Secondly, I've ignored search engine traffic, big blogs, newsletters, and other sources.</p>

<p>Thirdly, and most importantly, this <em>isn't</em> a competition! The desire for a "winner-takes-all" service is dangerous and disturbing. An ecosystem is at its most vibrant when there are multiple participants each thriving in their own niche.</p>

<p>I want a thousand sites, running a hundred different software stacks, some of which only serve a dozen people, or even a lone participant.</p>

<p>Diversity is strength.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63716&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/11/now-witness-the-power-of-this-fully-operational-fediverse/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Getting started with Mastodon's Quote Posts - technical implementation details for servers]]></title>
		<link>https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/</link>
					<comments>https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 03 Oct 2025 11:34:27 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[MastodonAPI]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=63527</guid>

					<description><![CDATA[Quoting posts on Mastodon is slightly complex. Because of the privacy conscious nature of the platform and its users, reposting isn&#039;t merely a case of sharing a URl.  A user writes a status. The user can choose to make their statuses quotable or not. What happens when a quoter quotes that post?  I&#039;ve read through the specification and tried to simplify it.  Quoting is a multi-step process:   The…]]></description>
										<content:encoded><![CDATA[<p>Quoting posts on Mastodon is <em>slightly</em> complex. Because of the privacy conscious nature of the platform and its users, reposting isn't merely a case of sharing a URl.</p>

<p>A user writes a status. The user can choose to make their statuses quotable or not. What happens when a quoter quotes that post?</p>

<p>I've <a href="https://codeberg.org/fediverse/fep/src/branch/main/fep/044f/fep-044f.md">read through the specification</a> and tried to simplify it.  Quoting is a multi-step process:</p>

<ol>
<li>The status <em>must</em> opt-in to being shared.</li>
<li>The quoter quotes the status.</li>
<li>The quoter's server sends a request to the status's server.</li>
<li>The status's server sends an accept message back to the quoter's server.</li>
<li>When other servers see the quote, they check with the status's server to see if it is allowed.</li>
</ol>

<p>I'm going to walk you through each stage as best as I understand them.</p>

<h2 id="opting-in"><a href="https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/#opting-in">Opting In</a></h2>

<p>An ActivityPub status message is JSON. In order to opt-in, it needs this additional field.</p>

<pre><code class="language-JSON">"interactionPolicy": {
  "canQuote": {
    "automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
  }
}
</code></pre>

<p>That tells ActivityPub clients that anyone is allowed to quote this post. It is also possible to say that only specific users, or only followers, or no-one is allowed.</p>

<h2 id="the-quoterequest"><a href="https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/#the-quoterequest">The QuoteRequest</a></h2>

<p>Someone has hit the quote post button, typed their own message, and shared their wisdom. Their server sends the following message to the server which hosts the quoted status. This has been edited for brevity.</p>

<pre><code class="language-JSON">{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "QuoteRequest":   "https://w3id.org/fep/044f#QuoteRequest"
    }
  ],
  "type": "QuoteRequest",
  "id":     "https://mastodon.test/users/Edent/quote_requests/1234-5678-9101",
  "actor":  "https://mastodon.test/users/Edent",
  "object": "https://example.com/posts/987654321.json",
  "instrument": {
    "id":           "https://mastodon.test/users/Edent/statuses/123456789",
    "url":          "https://mastodon.test/@Edent/123456789",
    "attributedTo": "https://mastodon.test/users/Edent",
    "quote":          "https://example.com/posts/987654321.json",
    "_misskey_quote": "https://example.com/posts/987654321.json",
    "quoteUri":       "https://example.com/posts/987654321.json"
  }
}
</code></pre>

<p>All this says is "I would like permission to quote you."</p>

<h2 id="the-stamp"><a href="https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/#the-stamp">The Stamp</a></h2>

<p>The quoted server needs to approve this quote. First, it generates a "stamp".</p>

<p>This is a file which lives on the quoted server. It is proof that the quote is allowed. If it is deleted, the quote permission is revoked. When the <a href="https://socialhub.activitypub.rocks/t/quote-post-implementation-issues/8032/2?u=eden_t">stamp's ID is requested the stamp <em>must</em> be returned</a>.</p>

<pre><code class="language-JSON">{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "gts": "https://gotosocial.org/ns#",
      "QuoteAuthorization": {
        "@id": "https://w3id.org/fep/044f#QuoteAuthorization",
        "@type": "@id"
      },
      "interactingObject": {
        "@id": "gts:interactingObject"
      },
      "interactionTarget": {
        "@id": "gts:interactionTarget"
      }
    }
  ],
  "type": "QuoteAuthorization",
  "id":                "https://example.com/quote-987654321.json",
  "attributedTo":      "https://example.com/users/username",
  "interactionTarget": "https://example.com/posts/987654321.json",
  "interactingObject": "https://mastodon.test/users/Edent/statuses/123456789"
}
</code></pre>

<p>If the quoted status is viewed from a different server, that server will query the stamp to make sure the share is allowed.</p>

<h2 id="the-accept"><a href="https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/#the-accept">The Accept</a></h2>

<p>This is the message that the quoted server sends to the quoting server. It references the request and the stamp.</p>

<pre><code class="language-JSON">{
  "@context": [
    "https://www.w3.org/ns/activitystreams",
    {
      "QuoteRequest": "https://w3id.org/fep/044f#QuoteRequest"
    }
  ],
  "type": "Accept",
  "to":    "https://mastodon.test/users/Edent",
  "id":    "https://example.com/posts/987654321.json",
  "actor": "https://example.com/account",
  "object": {
    "type": "QuoteRequest",
    "id":         "https://mastodon.test/users/Edent/quote_requests/1234-5678-9101",
    "actor":      "https://mastodon.test/users/Edent",
    "instrument": "https://mastodon.test/users/Edent/statuses/123456789",
    "object":     "https://example.com/posts/987654321.json"
  },
  "result": "https://example.com/quote-987654321.json"
}
</code></pre>

<p>The "result" <em>must</em> be the same as the stamp's URl.</p>

<h2 id="and-then"><a href="https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/#and-then">And then?</a></h2>

<p>You can follow and quote <a href="https://colours.bots.edent.tel/">@colours@colours.bots.edent.tel</a> on your favourite Fediverse platform.</p>

<p>I've written an ActivityPub server in a single file which is designed to teach you have the protocol works. Have a play with <a href="https://gitlab.com/edent/activity-bot">ActivityBot</a>.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63527&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/10/getting-started-with-mastodons-quote-posts-technical-implementation-details-for-servers/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Meeting my Fedifriends AFK]]></title>
		<link>https://shkspr.mobi/blog/2025/06/meeting-my-fedifriends-afk/</link>
					<comments>https://shkspr.mobi/blog/2025/06/meeting-my-fedifriends-afk/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 14 Jun 2025 11:34:11 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[fediverse]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=61215</guid>

					<description><![CDATA[There&#039;s a lovely moment in the documentary about The Pirate Bay where Peter Sunde is being interviewed in a District Court:  Prosecutor 1: When was the first time you met IRL?  brokep: We don&#039;t use the expression IRL. We say AFK. But that&#039;s another issue.  Prosecutor 2: Got to know each other IRL? What is that?  Prosecutor 1: In Real Life.  brokep: We don&#039;t like that expression. We say AFK - Away …]]></description>
										<content:encoded><![CDATA[<p>There's a lovely moment in <a href="https://www.imdb.com/title/tt2608732/">the documentary about The Pirate Bay</a> where Peter Sunde is being interviewed in a District Court:</p>

<blockquote><p>Prosecutor 1: When was the first time you met IRL?</p>

<p>brokep: We don't use the expression IRL. We say AFK. But that's another issue.</p>

<p>Prosecutor 2: Got to know each other IRL? What is that?</p>

<p>Prosecutor 1: In Real Life.</p>

<p>brokep: We don't like that expression. We say AFK - Away From Keyboard. <strong>We think that the Internet is for real.</strong></p></blockquote>

<p>Isn't that great? Why do some people insist that online relationships are somehow less real than physical relationships?</p>

<p>As part of our recent <a href="https://shkspr.mobi/blog/2025/06/5025-km-21-journeys-and-10-countries-in-30-days-an-interrailing-adventure/">grand Interrail journey</a>, I wanted to see how many people from social networks I could meet AFK.  In the glory days of Twitter, I'm sure I'd've found a friend in every one-horse town. But the fractured nature of networking made it a bit of challenge.</p>

<p>Just before arriving in a new country, I sent out messages saying "Hey, I'm arriving in XYZ tomorrow. Anyone want to meet for a beer?" And, to my surprise and delight, many people did!</p>

<p>In half of the countries we went to, we met new friends. Sometimes for a quick drink, sometimes for dinner, and sometimes for a little exploring of a city. We even got invited to a local geek meet-up.  It was <em>lovely</em>. We got tips on how to use the public transport system, which restaurants were tourist traps, and introduced to new beers.</p>

<p>It is nice to put faces to names. It's fun to meet a random friend and chat nonsense with them. And it is reassuring to know that there are people around the world who'll take a risk and meet a weary traveller.</p>

<p>Now, there are some obvious caveats to this story. We only met in well populated areas. I'm a tall bloke with a loud voice. Some light background stalking made sure the people we met weren't <em>too</em> crazy. We didn't get drunk. If you do this - I suggest taking all the normal precautions when meeting strangers. But, in the end, everything was fine.</p>

<p>Sometimes you want to go where at least one person knows your name.</p>

<p>Cheers!</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2025/06/crop.webp" alt="Me with a big mug of beer." width="1280" height="1280" class="aligncenter size-full wp-image-61222">
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=61215&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/06/meeting-my-fedifriends-afk/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Mastodon Now Sends Referer Headers! Hurrah!]]></title>
		<link>https://shkspr.mobi/blog/2024/12/mastodon-now-sends-referer-headers-hurrah/</link>
					<comments>https://shkspr.mobi/blog/2024/12/mastodon-now-sends-referer-headers-hurrah/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 14 Dec 2024 12:34:25 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[mastodon]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=54457</guid>

					<description><![CDATA[Back in 2022, I wrote this rather grumpy post on Mastodon, the federated social media platform.  @Edent@mastodon.socialTerence EdenMastodon enforces a &#34;noreferrer&#34; on all external links.I have mixed feelings about that.As a blogger, I want to see *where* visitors are coming from. I also like to see (and sometimes join in) with the conversations they&#039;re having.But, I get that people want privacy…]]></description>
										<content:encoded><![CDATA[<p>Back in 2022, I wrote this rather grumpy post on Mastodon, the federated social media platform.</p>

<blockquote class="social-embed" id="social-embed-109323917419768019" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://mastodon.social/@Edent" class="social-embed-user" itemprop="url"><img class="social-embed-avatar" src="https://files.mastodon.social/accounts/avatars/000/007/112/original/37df032a5951b96c.jpg" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">@Edent@mastodon.social</p>Terence Eden</div></a><img class="social-embed-logo" alt="Mastodon" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-label='Mastodon' role='img' viewBox='0 0 512 512' fill='%23fff'%3E%3Cpath d='m0 0H512V512H0'/%3E%3ClinearGradient id='a' y2='1'%3E%3Cstop offset='0' stop-color='%236364ff'/%3E%3Cstop offset='1' stop-color='%23563acc'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23a)' d='M317 381q-124 28-123-39 69 15 149 2 67-13 72-80 3-101-3-116-19-49-72-58-98-10-162 0-56 10-75 58-12 31-3 147 3 32 9 53 13 46 70 69 83 23 138-9'/%3E%3Cpath d='M360 293h-36v-93q-1-26-29-23-20 3-20 34v47h-36v-47q0-31-20-34-30-3-30 28v88h-36v-91q1-51 44-60 33-5 51 21l9 15 9-15q16-26 51-21 43 9 43 60'/%3E%3C/svg%3E"></header><section class="social-embed-text" itemprop="articleBody"><p>Mastodon enforces a "noreferrer" on all external links.</p><p>I have mixed feelings about that.</p><p>As a blogger, I want to see *where* visitors are coming from. I also like to see (and sometimes join in) with the conversations they're having.</p><p>But, I get that people want privacy and don't want to "leak" where they're visiting from.</p><p>Is it such a bad thing to tell a website "I was referred from this specific server"?</p><div class="social-embed-media-grid"></div></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://mastodon.social/@Edent/109323917419768019"><span aria-label="61 likes" class="social-embed-meta">❤️ 61</span><span aria-label="16 replies" class="social-embed-meta">💬 16</span><span aria-label="29 reposts" class="social-embed-meta">🔁 29</span><time datetime="2022-11-11T07:09:55.396Z" itemprop="datePublished">07:09 - Fri 11 November 2022</time></a></footer></blockquote>

<p>When you click on this link - <a href="https://www.bbc.co.uk/news">https://www.bbc.co.uk/news</a> - your browser says "Hey! BBC! Please can I have your <code>/news</code> page? BTW, I was referred here by <code>shkspr.mobi</code>. THANKS!"  This is called the "<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer">Referer</a>" and, yes, it is <a href="https://en.wikipedia.org/wiki/HTTP_referer#Etymology">mispelt</a>.</p>

<p>One the one hand, sending the referer is good; it lets the linked-to server know who is linking to it. That allows them to see where traffic is coming from. On the other hand, this <em>could</em> be bad for much the same reason.</p>

<p>If you run a server <code>anarcho_terrorists.biz</code>, you probably don't want the FBI knowing that your members are sharing links to their pages. If you run a small personal server, you may not want anyone knowing that you personally linked to them. If you run a server for a marginalised community, you may not want a hate-site to know your members are linking to you.</p>

<p>But if you're a large-ish, general purpose, non-private site - like Mastodon.social - where's the harm in allowing referer headers?</p>

<p>Anyway, for historic reasons, Mastodon blocked the referer header. This, I believe, was sensible for smaller servers but a miss-step for larger servers.  As I pointed out last week:</p>

<blockquote class="social-embed" id="social-embed-113611619218784737" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://mastodon.social/@Edent" class="social-embed-user" itemprop="url"><img class="social-embed-avatar" src="https://files.mastodon.social/accounts/avatars/000/007/112/original/37df032a5951b96c.jpg" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">@Edent@mastodon.social</p>Terence Eden</div></a><img class="social-embed-logo" alt="Mastodon" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-label='Mastodon' role='img' viewBox='0 0 512 512' fill='%23fff'%3E%3Cpath d='m0 0H512V512H0'/%3E%3ClinearGradient id='a' y2='1'%3E%3Cstop offset='0' stop-color='%236364ff'/%3E%3Cstop offset='1' stop-color='%23563acc'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23a)' d='M317 381q-124 28-123-39 69 15 149 2 67-13 72-80 3-101-3-116-19-49-72-58-98-10-162 0-56 10-75 58-12 31-3 147 3 32 9 53 13 46 70 69 83 23 138-9'/%3E%3Cpath d='M360 293h-36v-93q-1-26-29-23-20 3-20 34v47h-36v-47q0-31-20-34-30-3-30 28v88h-36v-91q1-51 44-60 33-5 51 21l9 15 9-15q16-26 51-21 43 9 43 60'/%3E%3C/svg%3E"></header><section class="social-embed-text" itemprop="articleBody"><p>Two years later.</p><p>Want to know one of the major reasons Mastodon didn't catch on with journalists and large website owners?</p><p>It is *invisible* in referrer statistics.</p><p>Here's my blog from the last month.</p><p>BlueSky now sends me more traffic than Bing.</p><p>How much traffic does Mastodon send? It is impossible to know due to the "noreferrer" header in all links.</p><p>(I'm not saying your privacy isn't important. But you can't grow a community if no-one knows you exist.)</p><div class="social-embed-media-grid"><a href="https://files.mastodon.social/media_attachments/files/113/611/599/519/383/213/original/f32f26cb4a0f015a.png" class="social-embed-media-link"><img class="social-embed-media" alt="1 google.com 10,957 12,1112 news.ycombinator.com 1,681 1,7633 duckduckgo.com 415 4584 css-tricks.com 353 3875 reddit.com 317 3736 yandex.ru 352 3567 google.co.uk 280 3058 bsky.app 252 2969 bing.com 254 282" src="data:image/webp;base64,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"></a></div></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://mastodon.social/@Edent/113611619218784737"><span aria-label="305 likes" class="social-embed-meta">❤️ 305</span><span aria-label="57 replies" class="social-embed-meta">💬 57</span><span aria-label="248 reposts" class="social-embed-meta">🔁 248</span><time datetime="2024-12-07T12:48:52.705Z" itemprop="datePublished">12:48 - Sat 07 December 2024</time></a></footer></blockquote>

<p>I'm not the only one to make this point - it has been a popular complaint for some time.</p>

<p>A few days ago, <a href="https://github.com/mastodon/mastodon/pull/33214">Mastodon changed to allow this to be configurable</a>.</p>

<p>This is <em>excellent</em> news. Website owners will be able to (somewhat) accurately see how much traffic Mastodon sends them.  That way they can determine if there is a suitably large audience to engage with on the Fediverse.</p>

<p>It is, of course, slightly more complicated than that!</p>

<ul>
<li>Instance owners can opt-in to allowing Referer headers (it is off by default).</li>
<li>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#directives">policy</a> means that only the domain name is sent; not the full page.</li>
<li>Mastodon is federated and there are thousands of sites. Even if they all opted-in, their statistics will be fragmented.</li>
<li>Apps can set their own Referer header - leading to more fragmentation.</li>
<li>Even if they do opt-in, users can set their browsers not to send Referer headers.</li>
</ul>

<p>Nevertheless, I'm delighted with this change. Hopefully it will allow the Fediverse to grow and attract more users.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=54457&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/12/mastodon-now-sends-referer-headers-hurrah/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Self Hosting is an Unhelpful Term]]></title>
		<link>https://shkspr.mobi/blog/2024/11/self-hosting-is-an-unhelpful-term/</link>
					<comments>https://shkspr.mobi/blog/2024/11/self-hosting-is-an-unhelpful-term/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 24 Nov 2024 12:34:05 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[ReDeCentralize]]></category>
		<category><![CDATA[Social Networks]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=54146</guid>

					<description><![CDATA[Mathew Duggan has a brilliant post called &#34;Self-Hosting Isn&#039;t a Solution; It&#039;s A Patch&#34;. In it, he (correctly and convincingly) argues that compelling people to run their own computer services is a complex and distracting crutch for the current problems we face.  It&#039;s expensive to self-host, there are moderation problems, and the difficulty level is too high for most people.  But, in my opinion,…]]></description>
										<content:encoded><![CDATA[<p>Mathew Duggan has a brilliant post called "<a href="https://matduggan.com/self-hosting-isnt-a-solution-its-a-patch/">Self-Hosting Isn't a Solution; It's A Patch</a>". In it, he (correctly and convincingly) argues that compelling people to run their own computer services is a complex and distracting crutch for the current problems we face.</p>

<p>It's expensive to self-host, there are moderation problems, and the difficulty level is too high for most people.</p>

<p>But, in my opinion, I think he misunderstands something about self-hosting because, as a term, it is both misleading and unhelpful.  When people say "Defund The Police" what they mean is "<a href="https://www.brookings.edu/articles/7-myths-about-defunding-the-police-debunked/">Move funds away from miliary style policing and give it to trained mental health professionals</a>" - what people <em>hear</em> is "Abolish the police and let anarchy reign".</p>

<p>The ability to "Self Host" doesn't <em>just</em> mean "run this on a Raspberry Pi in your cupboard and be responsible for constant maintenance".  Yes, you <em>can</em> do that if you're a masochist, but it isn't <em>restricted</em> to that.</p>

<p>To me, "Self-Hosting" means "I am in control of where I host something". I currently pay a company to host this blog. It has previously been hosted on Blogger, WordPress, my own VPS, and a variety of other services.  Tomorrow I could decide to host it with a big company, or I could run it from my phone. I get to choose.  That's what "Self-Hosting" is - a choice in where to host.</p>

<p>Similarly, Mastodon allows me self-host my account. I can have my content on one of the big servers and let them do moderation, storage, and maintenance for me - or I can move my account anywhere I choose. To a server in my cupboard and back again.</p>

<p>Email is similar. I know people who've gone from CompuServe, to HoTMaiL, to Gmail, to their own domain, then to OutLook. Their address-book moves with them. Forwarding rules ensure incoming email is routed correctly. They can choose to actively moderate spam, or outsource it. They can pay a company to host, keep backups in their basement, or watch adverts in return for services.</p>

<p>I agree with <a href="https://matduggan.com/self-hosting-isnt-a-solution-its-a-patch/">nearly everything Mathew says in his post</a>. It is absurdly privileged to think that running your own services is something normal people want to do and are capable of doing. Strong regulation helps everyone, people want simplicity, and ecosystems can be fragile.</p>

<p>But witness all the people moving over from Twitter to new networks. Do they care where their data is hosted and how it is maintained? No! But they want to move their social graph with them. And when BlueSky and Mastodon collapse, people will want to move again.</p>

<p>In the UK, I have the ability to move my phone number between hundreds of providers. If I'm particularly techy, I can even run my own infrastructure and route the number there. People <em>love</em> the fact that they can leave crappy service providers and move somewhere cheaper or with with better customer service or whatever it is they value.  I think that's a form of self-hosting; I get to choose who provides my services.</p>

<p>Similarly, I believe people have a desire for "self-hosting" which is difficult for them to articulate. They want to move their data around - be it old photos, a social graph, or a username. Most of them don't really care about the underlying technology (and why should they?) but they do care about continuity of service and being able to escape crappy service providers.</p>

<p>So, that's my reckons. Self-Hosting means you can choose where to host, and I think most people can find value in that.</p>

<p>What do you think?</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=54146&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/11/self-hosting-is-an-unhelpful-term/feed/</wfw:commentRss>
			<slash:comments>20</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Set your domain name as your handle for a BlueSky bot which is bridged from the Fediverse]]></title>
		<link>https://shkspr.mobi/blog/2024/11/set-your-domain-name-as-your-handle-for-a-bluesky-bot-which-is-bridged-from-the-fediverse/</link>
					<comments>https://shkspr.mobi/blog/2024/11/set-your-domain-name-as-your-handle-for-a-bluesky-bot-which-is-bridged-from-the-fediverse/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Thu, 14 Nov 2024 12:34:32 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[BlueSky]]></category>
		<category><![CDATA[bot]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=53849</guid>

					<description><![CDATA[If you&#039;ve found this page, it&#039;s because you are me in the future and want to remember these instructions!   Create an account on the Fediverse using a domain you control   For example @user@bots.example.com  Follow the Fediverse-ATProto bridge @bsky.brid.gy@bsky.brid.gy   Your account will need to be over 2 weeks old and have a name, profile picture, etc.  You now have an account on BSky! Its…]]></description>
										<content:encoded><![CDATA[<p>If you've found this page, it's because you are me in the future and want to remember these instructions!</p>

<ol start="0">
<li>Create an account on the Fediverse using a domain you control

<ul>
<li>For example <code>@user@bots.example.com</code></li>
</ul></li>
<li><a href="https://fed.brid.gy/docs#fediverse-get-started">Follow the Fediverse-ATProto bridge</a> <code>@bsky.brid.gy@bsky.brid.gy</code>

<ul>
<li><a href="https://fed.brid.gy/docs#troubleshooting">Your account will need to be over 2 weeks old and have a name, profile picture, etc</a>.</li>
</ul></li>
<li>You now have an account on BSky! Its name will be something like <code>user.bots.example.com.ap.brid.gy</code></li>
<li>Get the DID of your account

<ul>
<li><code>https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=user.bots.example.com.ap.brid.gy</code></li>
<li>Or <code>https://fed.brid.gy/ap/@user@bots.example.com</code></li>
</ul></li>
<li><a href="https://atproto.com/specs/handle#handle-resolution">Add the DID to your domain</a>

<ul>
<li>I think the easiest way is sticking it in a plain text file at <code>bots.example.com/.well-known/atproto-did</code></li>
</ul></li>
<li>Use the <a href="https://bsky-debug.app/handle?handle=user.bots.example.com.ap.brid.gy">BSky Debugger</a> to make sure it was successful.</li>
<li>Send a Direct Message from the Fediverse to <code>@bsky.brid.gy@bsky.brid.gy</code>. The message must only contain <code>username bots.example.com</code>.

<ul>
<li>That's <em>literally</em> the word <code>username</code>. It isn't your account's username.</li>
</ul></li>
<li>Wait a few moments.</li>
<li>Your bot will now be on BSky as <code>https://bsky.app/profile/bots.example.com</code>!</li>
</ol>

<p>You can see that <a href="https://bot.viii.fi/bot">https://bot.viii.fi/bot</a> is also available at <a href="https://bsky.app/profile/bot.viii.fi">https://bsky.app/profile/bot.viii.fi</a></p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53849&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/11/set-your-domain-name-as-your-handle-for-a-bluesky-bot-which-is-bridged-from-the-fediverse/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Introducing ActivityBot - the simplest way to build Mastodon Bots]]></title>
		<link>https://shkspr.mobi/blog/2024/11/introducing-activitybot-the-simplest-way-to-build-mastodon-bots/</link>
					<comments>https://shkspr.mobi/blog/2024/11/introducing-activitybot-the-simplest-way-to-build-mastodon-bots/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 10 Nov 2024 12:34:00 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[bot]]></category>
		<category><![CDATA[fediverse]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=53728</guid>

					<description><![CDATA[As you may have read, BotsIn.Space is closing down, I have lots of automated bot accounts living on the Fediverse - and I want them to continue posting.  Installing and maintaining an entire Mastodon instance sounds like hard work. Paying people to host my stuff feels like putting my fate in someone else&#039;s hands.  Say… didn&#039;t I write my own ActivityPub server? Why, yes! Yes I did!  I took the c…]]></description>
										<content:encoded><![CDATA[<p>As you may have read, <a href="https://muffinlabs.com/posts/2024/10/29/10-29-rip-botsin-space/">BotsIn.Space is closing down</a>, I have lots of automated bot accounts living on the Fediverse - and I want them to continue posting.  Installing and maintaining an entire Mastodon instance sounds like hard work. Paying people to host my stuff feels like putting my fate in someone else's hands.</p>

<p>Say… didn't I write <a href="https://shkspr.mobi/blog/2024/03/updates-to-activitypub-in-a-single-php-file/">my own ActivityPub server</a>? Why, yes! Yes I did!</p>

<p>I took the code and stripped it down to the bare essentials. All you need to do is upload two files<sup id="fnref:env"><a href="https://shkspr.mobi/blog/2024/11/introducing-activitybot-the-simplest-way-to-build-mastodon-bots/#fn:env" class="footnote-ref" title="You can also upload a .env file for your configuration if you want." role="doc-noteref">0</a></sup> - <code>index.php</code> and <code>.htaccess</code> - fill in your details, and you're done.</p>

<p><a href="https://gitlab.com/edent/activity-bot/">Get the ActivityBot source code on GitLab</a>.</p>

<p>There's no database, no containers, no caching. It is as simple as I could make it</p>

<p>This bot can do the following:</p>

<ul>
<li>🔍 Be discovered on the Fediverse</li>
<li>👉 Be followed by other accounts</li>
<li>🚫 Be unfollowed by accounts</li>
<li>📩 Send messages to the Fediverse</li>
<li>💌 Send direct messages to users</li>
<li>🖼️ Attach an image &amp; alt text to a message</li>
<li>🕸️ Autolink URls, hashtags, and @ mentions</li>
<li>🚚 Move followers from an old account</li>
<li>🔏 Verify cryptographic signatures</li>
<li>🪵 Log sent messages and error.</li>
</ul>

<p>That's it! Here's what it <em>doesn't</em> do:</p>

<ul>
<li>❌ Receive messages (other than follows and unfollows)</li>
<li>❌ Thread replies</li>
<li>❌ Delete or update a post</li>
<li>❌ Create Polls</li>
<li>❌ Attach multiple images</li>
<li>❌ Set focus point for images</li>
<li>❌ Set sensitivity for images / blur</li>
<li>❌ Set "Content Warning"</li>
<li>❌ Accurate support for converting user's text to HTML</li>
<li>❌ Cannot be discovered by Lemmy instances</li>
</ul>

<p>Grab a subdomain (don't buy a whole new domain name!) and <a href="https://gitlab.com/edent/activity-bot/">stick this code on it</a>. You'll have an ActivityPub bot running in minutes.</p>

<p>You can follow one of my bots <code>@colours@colours.bots.edent.tel</code></p>

<p>Feedback very much welcome.</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:env">
<p>You can also upload a <code>.env</code> file for your configuration if you want.&nbsp;<a href="https://shkspr.mobi/blog/2024/11/introducing-activitybot-the-simplest-way-to-build-mastodon-bots/#fnref:env" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53728&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/11/introducing-activitybot-the-simplest-way-to-build-mastodon-bots/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[No, ActivityPub votes aren't anonymous]]></title>
		<link>https://shkspr.mobi/blog/2024/09/no-activitypub-isnt-anonymous/</link>
					<comments>https://shkspr.mobi/blog/2024/09/no-activitypub-isnt-anonymous/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 09 Sep 2024 11:34:07 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[privacy]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=52999</guid>

					<description><![CDATA[Several years ago, I posted this poll on Twitter.  Terence Eden is on Mastodon@edentIf the recent Twitter hack had exposed they way you voted on every Twitter poll, how would you feel?(There is no suggestion that this has happened, I&#039;m just curious about people&#039;s relationships to voting and privacy.)Meh. So what?: (167)167Hmph. That&#039;s annoying.: (68)68Umm… This could be bad!: (32)32Delete account …]]></description>
										<content:encoded><![CDATA[<p>Several years ago, I posted this poll on Twitter.</p>

<blockquote class="social-embed" id="social-embed-1286178187937042432" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://twitter.com/edent" class="social-embed-user" itemprop="url"><img class="social-embed-avatar social-embed-avatar-circle" src="data:image/webp;base64,UklGRkgBAABXRUJQVlA4IDwBAACQCACdASowADAAPrVQn0ynJCKiJyto4BaJaQAIIsx4Au9dhDqVA1i1RoRTO7nbdyy03nM5FhvV62goUj37tuxqpfpPeTBZvrJ78w0qAAD+/hVyFHvYXIrMCjny0z7wqsB9/QE08xls/AQdXJFX0adG9lISsm6kV96J5FINBFXzHwfzMCr4N6r3z5/Aa/wfEoVGX3H976she3jyS8RqJv7Jw7bOxoTSPlu4gNbfXYZ9TnbdQ0MNnMObyaRQLIu556jIj03zfJrVgqRM8GPwRoWb1M9AfzFe6Mtg13uEIqrTHmiuBpH+bTVB5EEQ3uby0C//XOAPJOFv4QV8RZDPQd517Khyba8Jlr97j2kIBJD9K3mbOHSHiQDasj6Y3forATbIg4QZHxWnCeqqMkVYfUAivuL0L/68mMnagAAA" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">Terence Eden is on Mastodon</p>@edent</div></a><img class="social-embed-logo" alt="Twitter" src="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%0Aaria-label%3D%22Twitter%22%20role%3D%22img%22%0AviewBox%3D%220%200%20512%20512%22%3E%3Cpath%0Ad%3D%22m0%200H512V512H0%22%0Afill%3D%22%23fff%22%2F%3E%3Cpath%20fill%3D%22%231d9bf0%22%20d%3D%22m458%20140q-23%2010-45%2012%2025-15%2034-43-24%2014-50%2019a79%2079%200%2000-135%2072q-101-7-163-83a80%2080%200%200024%20106q-17%200-36-10s-3%2062%2064%2079q-19%205-36%201s15%2053%2074%2055q-50%2040-117%2033a224%20224%200%2000346-200q23-16%2040-41%22%2F%3E%3C%2Fsvg%3E"></header><section class="social-embed-text" itemprop="articleBody">If the recent Twitter hack had exposed they way you voted on every Twitter poll, how would you feel?<br><br>(There is no suggestion that this has happened, I'm just curious about people's relationships to voting and privacy.)<hr class="social-embed-hr"><label for="poll_1_count">Meh. So what?: (167)</label><br><meter class="social-embed-meter" id="poll_1_count" min="0" max="100" low="33" high="66" value="60.7">167</meter><br><label for="poll_2_count">Hmph. That's annoying.: (68)</label><br><meter class="social-embed-meter" id="poll_2_count" min="0" max="100" low="33" high="66" value="24.7">68</meter><br><label for="poll_3_count">Umm… This could be bad!: (32)</label><br><meter class="social-embed-meter" id="poll_3_count" min="0" max="100" low="33" high="66" value="11.6">32</meter><br><label for="poll_4_count">Delete account &amp; run away: (8)</label><br><meter class="social-embed-meter" id="poll_4_count" min="0" max="100" low="33" high="66" value="2.9">8</meter></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://twitter.com/edent/status/1286178187937042432"><span aria-label="0 likes" class="social-embed-meta">❤️ 0</span><span aria-label="8 replies" class="social-embed-meta">💬 8</span><span aria-label="0 reposts" class="social-embed-meta">🔁 0</span><time datetime="2020-07-23T05:55:50.000Z" itemprop="datePublished">05:55 - Thu 23 July 2020</time></a></footer></blockquote>

<p>Most of the tech world that I interact with has moved to Mastodon and other ActivityPub-based social networks.  Decentralised social media is <em>great</em>. It allows you to be fully in control of what you post, what you see, and how you interact with others.</p>

<p>Of course, there are downsides. No centralised authorities means verification is difficult. Abuse (of all sorts) can only be dealt with in a piecemeal fashion. And anonymity takes a bit of a nosedive.</p>

<p>When you block or mute someone, that information <a href="https://shkspr.mobi/blog/2023/07/fediverse-account-portability-and-blocking/">might leak to the offending user</a>. By its nature, you need to send a message to someone else's server in order to interact with them.</p>

<p>So what about polls on the Fediverse?  This poll, for example, is gathering sensitive personal information.</p>

<blockquote class="social-embed" id="social-embed-113079948257450773" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"><header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"><a href="https://blackrock.city/@farooqkz" class="social-embed-user" itemprop="url"><img class="social-embed-avatar" src="https://files.mastodon.social/cache/accounts/avatars/110/487/620/471/518/769/original/8e8c3e9b9b7b122e.png" alt="" itemprop="image"><div class="social-embed-user-names"><p class="social-embed-user-names-name" itemprop="name">@farooqkz@blackrock.city</p>Farooq Karimi Zadeh</div></a><img class="social-embed-logo" alt="Mastodon" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' aria-label='Mastodon' role='img' viewBox='0 0 512 512' fill='%23fff'%3E%3Cpath d='m0 0H512V512H0'/%3E%3ClinearGradient id='a' y2='1'%3E%3Cstop offset='0' stop-color='%236364ff'/%3E%3Cstop offset='1' stop-color='%23563acc'/%3E%3C/linearGradient%3E%3Cpath fill='url(%23a)' d='M317 381q-124 28-123-39 69 15 149 2 67-13 72-80 3-101-3-116-19-49-72-58-98-10-162 0-56 10-75 58-12 31-3 147 3 32 9 53 13 46 70 69 83 23 138-9'/%3E%3Cpath d='M360 293h-36v-93q-1-26-29-23-20 3-20 34v47h-36v-47q0-31-20-34-30-3-30 28v88h-36v-91q1-51 44-60 33-5 51 21l9 15 9-15q16-26 51-21 43 9 43 60'/%3E%3C/svg%3E"></header><section class="social-embed-text" itemprop="articleBody"><p>Let's see how many Muslims are out there on Fediverse. Are you a <a href="https://blackrock.city/tags/muslim" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank">#<span>muslim</span></a>? </p><p><a href="https://blackrock.city/tags/Islam" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank">#<span>Islam</span></a> <a href="https://blackrock.city/tags/Religion" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank">#<span>Religion</span></a> <a href="https://blackrock.city/tags/God" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank">#<span>God</span></a> </p><p>Please boost it so we can have more accurate statistics.</p><div class="social-embed-media-grid"></div><hr class="social-embed-hr"><label for="poll_0">I am a Muslim: (62)</label><br><meter class="social-embed-meter" id="poll_0" min="0" max="100" low="33" high="66" value="1.6">62</meter><br><label for="poll_1">Not a Muslim: (3,696)</label><br><meter class="social-embed-meter" id="poll_1" min="0" max="100" low="33" high="66" value="98.4">3696</meter><br></section><hr class="social-embed-hr"><footer class="social-embed-footer"><a href="https://blackrock.city/@farooqkz/113079948160094406"><span aria-label="11 likes" class="social-embed-meta">❤️ 11</span><span aria-label="30 replies" class="social-embed-meta">💬 30</span><span aria-label="874 reposts" class="social-embed-meta">🔁 874</span><time datetime="2024-09-04T15:17:56.000Z" itemprop="datePublished">15:17 - Wed 04 September 2024</time></a></footer></blockquote>

<p>In order to vote on the poll, your server sends a message to the poll's server saying "I am user @someone@example.com. I wish to vote for option X. Here is an HTTP signature confirming my message."</p>

<p>Does the receiving server abide by GDPR? Who knows!</p>

<p>The <a href="https://www.w3.org/TR/activitystreams-vocabulary/#questions">specification around questions</a> is a little ill-defined and the <a href="https://docs.joinmastodon.org/methods/polls/#vote">Mastodon documentation</a> is also a bit vague. Neither of them discuss privacy.</p>

<p>There is an <a href="https://humberto.io/blog/mastodon_poll_in_activitypub/">excellent blog post by Humberto Rocha looking at Mastodon Poll in ActivityPub</a>. It shows quite clearly that a vote is just a normal message which is passed onto the receiving server.</p>

<p>Services like Mastodon won't let the poll's author see who voted for which option. But that's by convention. There's nothing technical to stop them. Indeed, I understand that <a href="https://outerheaven.club/notice/Aln6q1bVGpyToIx7J2">the Akkoma social network <em>does</em> show users how users voted</a>.</p>

<p>Of course, on a centralised service like Facebook or Twitter your vote is still recorded somewhere. It can be subpoenaed or looked at by unscrupulous engineers.</p>

<p>Privacy is, of course, a social construct. In some communities it might be sensible to have all votes on the public record. In others, it could be deadly.  Some countries have laws mandating strong privacy protections, others less so.</p>

<p>Conduct yourself with that in mind!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=52999&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/09/no-activitypub-isnt-anonymous/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[The Limits of Organic Growth for Startups and Social Networks]]></title>
		<link>https://shkspr.mobi/blog/2024/08/the-limits-of-organic-growth-for-startups/</link>
					<comments>https://shkspr.mobi/blog/2024/08/the-limits-of-organic-growth-for-startups/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 05 Aug 2024 11:34:01 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[capitalism]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[startups]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=51194</guid>

					<description><![CDATA[Many years ago, when I was younger and more foolish, I worked for an advertising startup. Things seemed to be going pretty well! The office was expanding, the sales team was screaming into phones, the budget for servers was rising. Growth had been healthy, but now looked to be plateauing.  One day we were summoned into a large conference room. Our CEO was on the speakerphone (I told you this was…]]></description>
										<content:encoded><![CDATA[<p>Many years ago, when I was younger and more foolish, I worked for an advertising startup. Things seemed to be going pretty well! The office was expanding, the sales team was screaming into phones, the budget for servers was rising. Growth had been healthy, but now looked to be plateauing.</p>

<p>One day we were summoned into a large conference room. Our CEO was on the speakerphone (I told you this was a long time ago) with an important update on our financial situation.</p>

<p>I think every arsehole in the room puckered. Were we about to be made redundant? No! Far from it. The business had just taken on a <em>massive</em> amount of investment from a prominent Venture Capital fund.  The champagne flowed! With this money we could turbo-charge our hiring, build new products, and accelerate our growth.</p>

<p>At least, that's what I thought. In my naïveté, I congratulated our head of sales on the growth of his empire. More money meant more sales staff, yes?</p>

<p>He was glum. "That's not how it works," he explained.</p>

<p>Our large institutional investors had a vast stable of other companies. Each covered a different bit of the tech ecosystem and our advertising business was about to become part of an incestuous web.  Every app that the VCs had invested in would now be "encouraged" to use our advertising solution. You don't need a sales team when your customers are your cousins in an arranged marriage.</p>

<p>Similarly, our approved corporate chat tool would be replaced by one from another stable-mate.  Who, in turn, would buy advertising from us.</p>

<p>Over the next few months our growth rocketed and almost none of it was organic.</p>

<p>Eventually I left for pastures marginally more ethical than advertising. But I continue to see the same pattern repeat itself.</p>

<p>When you wonder why one social network grows and another doesn't - look at the investors.</p>

<p>For example, I'm sure a lot of Twitter's early growth was organic. But once rich and powerful companies can direct their investments to sign up and route all their customer service / product announcements through there, it exploded.  When you have a financial investment at stake, finding ways to boost growth is a priority - and a little mutually reinforced reciprocation goes a long way.</p>

<p>I don't know if Mastodon (and other ActivityPub services) have reached their maximum organic growth. I suspect they have. To be clear, I'm not calling on Mastodon to take on a billion dollars of VC funding. But without sales teams and without a bunch of associated organisations forced to use it, I worry that the Fediverse will hit a natural limit.</p>

<p>And, again, I don't know if that's a bad thing <i lang="la">per se</i> - but if you're expecting big companies, governments, and your favourite VC-adjacent celebrities to join, you're likely to be disappointed.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=51194&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/08/the-limits-of-organic-growth-for-startups/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[The Fediverse of Things]]></title>
		<link>https://shkspr.mobi/blog/2024/04/the-fediverse-of-things/</link>
					<comments>https://shkspr.mobi/blog/2024/04/the-fediverse-of-things/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 07 Apr 2024 11:34:31 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[IoT]]></category>
		<category><![CDATA[Social Networks]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=50085</guid>

					<description><![CDATA[One of the most frustrating things in modern technology is the effort spent trying to artificially restrict abundance.  Take, for example, this tale from museum-worker Aaron Cope:  I was out with a friend who worked for Twitter and I asked them whether it would be possible for the museum to “create 200,000 Twitter accounts, one for each object in the Cooper Hewitt’s collection”. My friend looked a…]]></description>
										<content:encoded><![CDATA[<p>One of the most frustrating things in modern technology is the effort spent trying to artificially restrict abundance.</p>

<p>Take, for example, <a href="https://millsfield.sfomuseum.org/blog/2024/03/12/activitypub/">this tale from museum-worker Aaron Cope</a>:</p>

<blockquote><p>I was out with a friend who worked for Twitter and I asked them whether it would be possible for the museum to “create 200,000 Twitter accounts, one for each object in the Cooper Hewitt’s collection”. My friend looked at me for a moment, laughed, and then simply said: No.
</p></blockquote>

<p>In that blog post, Aaron reveals that the San Francisco International Airport Museum is using ActivityPub to create automated social-media bot accounts for all its exhibits and, possibly, every object it hold.</p>

<p>And why not! That would be close to impossible to do on a centralised service. But on a decentralised service under your own control, it is relatively simple.  Perhaps I only want to follow the museum's canteen, or I just want to engage with a specific artefact. The Fediverse makes that possible.</p>

<p>This reminds me of the <a href="https://www.bbc.co.uk/news/magazine-33560182">Melbourne "treemail" phenomenon</a>. Every tree in the city had an email address, ostensibly so residents could email maintenance issues for a specific tree. Instead, people started interacting with the trees and sending them little love notes!</p>

<blockquote><p>Dearest Golden Elm Tree, I finally found you! As in I see you everyday on my way to uni, but I had no idea of what kind of tree you are. You are the most beautiful tree in the city and I love you
</p></blockquote>

<p>A few weeks ago, I read about <a href="https://bensmith.blog/posts/i-invented-tweeting-trains-probably">Ben Smith inventing Tweeting trains</a>. With a bit of code, every train line in the UK was suddenly represented on the web in a convenient format. Well… Convenient if you were on Twitter.</p>

<p>Museums, trees, and trains naturally brings me on to the Internet of Things. I think it is fair to say that IoT is in a bit of an odd place right now. <a href="https://csa-iot.org/all-solutions/matter/">Matter</a> is a confusing mishmash of standards.  Security and privacy issues dog the simplest devices. Many people don't even <em>want</em> their toaster online!</p>

<p>For the majority of domestic uses, people want an <em>Intranet</em> of Things. There's little need to have your light-bulbs controlled when you're outside of WiFi range.  Similarly, it is probably a <em>really</em> bad idea to have your <a href="https://www.politico.com/news/2023/11/28/federal-government-investigating-multiple-hacks-of-us-water-utilities-00128977">hydroelectric dam connected to the Internet</a>.</p>

<p>Which brings me back to the Fediverse.</p>

<p>On the one hand, it would be nice to be able to follow <code>@Yellow_Line@Transit_Authority.gov</code> - or even <code>@Bus_Stop_1234@bus_company.biz</code> - that would allow for hyperfocused data getting to the right people.  It seems feasible that <em>every</em> civic object could have a Fediverse account. From the individual streetlights to the municipal sewerage system.  Perhaps people won't send love letters to overflowing drains - but a social-dashboard of your civic environment could be both practical and delightful.</p>

<p>And, as for your <em>domestic</em> gadgets? Why not give every room, or every light-bulb, in your home a <em>private</em> Fediverse account? You could send a message like:</p>

<blockquote><p>Hey @thermostat@My_Home.example.com, please set the temperature to 19°C. Thanks!
</p></blockquote>

<p>That might be a bit much! But I like the idea of a <em>private</em> social network which consists of all my IoT gadgets talking to me and each other.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=50085&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/04/the-fediverse-of-things/feed/</wfw:commentRss>
			<slash:comments>15</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[HTTP Signature Infinite Loop?]]></title>
		<link>https://shkspr.mobi/blog/2024/02/http-signature-infinite-loop/</link>
					<comments>https://shkspr.mobi/blog/2024/02/http-signature-infinite-loop/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 26 Feb 2024 12:34:28 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[CyberSecurity]]></category>
		<category><![CDATA[encryption]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[http]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49728</guid>

					<description><![CDATA[I&#039;m trying to get my head round HTTP Signatures as they&#039;re used extensively in the Fediverse.  Conceptually, they&#039;re relatively straightforward.  You send me a normal HTTP request. For example, you want to POST something to https://example.com/data  You send me these headers:  POST /data Host: example.com Date: Sat, 24 Feb 2024 14:43:48 GMT Accept-Encoding: gzip Digest:…]]></description>
										<content:encoded><![CDATA[<p>I'm trying to get my head round <a href="https://www.rfc-editor.org/rfc/rfc9421.html">HTTP Signatures</a> as they're used extensively in the Fediverse.</p>

<p>Conceptually, they're relatively straightforward.</p>

<p>You send me a normal HTTP request. For example, you want to POST something to <code>https://example.com/data</code></p>

<p>You send me these headers:</p>

<pre><code class="language-_">POST /data
Host: example.com
Date: Sat, 24 Feb 2024 14:43:48 GMT
Accept-Encoding: gzip
Digest: SHA-256=aaC57TDzM0Wq+50We2TkCsdMDvdqON92edg7KI+Hk8M=
Content-Type: application/activity+json
Signature: keyId="https://your_website.biz/publicKey",algorithm="rsa-sha256",headers="(request-target) host date digest content-type",signature="JGQ53kEoIiMWRp9By9jajVGCOCu4n7XBeiA1uY5xLcnAxL2Y1GIgU/...=="
Connection: Keep-Alive
Content-Length: 751
</code></pre>

<p>In order to verify the contents of the message, I need to do three things:</p>

<ol>
<li>Check the SHA-256 hash of the message matches the content of the "Digest" header.</li>
<li>Check the timestamp is somewhat fresh.</li>
<li>Check the signature matches.</li>
</ol>

<p>The first is simple: <code>base64_encode( hash( "sha256", $request_body, true ) )</code>.</p>

<p>The second is a matter of opinion. I might be happy to receive messages from the distant past or far in the future. For the sake of a little clock drift, let's allow 60 seconds either way.</p>

<p>The third gets <em>complicated</em>.</p>

<p>First, I need to get the public key published at <code>keyId="https://your_website.biz/publicKey"</code>.</p>

<p>Next, I need to know which algorithm is being used to sign the headers: <code>algorithm="rsa-sha256"</code></p>

<p>Then, I need to know which headers - and in what order - are being signed: <code>headers="(request-target) host date digest content-type"</code></p>

<p>So I create a string using the received details which matches those headers in that specific order:</p>

<pre><code class="language-_">(request-target) POST /data
Host: example.com
Date: Sat, 24 Feb 2024 14:43:48 GMT
Digest: SHA-256=aaC57TDzM0Wq+50We2TkCsdMDvdqON92edg7KI+Hk8M=
Content-Type: application/activity+json
</code></pre>

<p>I can verify if the signature - <code>signature="JGQ53kEoIiMWRp9By9jajVGCOCu4n7XBeiA1uY5xLcnAxL2Y1GIgU/...=="</code> matches by:</p>

<pre><code class="language-php">openssl_verify(
    $headersString, 
    $signature, 
    $publicKey, 
    $algorithm
);
</code></pre>

<p>If that's <code>TRUE</code> then all is well.</p>

<p>But can you spot the implicit problem?</p>

<p><em>How</em> do I get your server's public key?</p>

<p>I just GET <code>https://your_website.biz/publicKey</code> - but if your server uses something like <a href="https://docs.joinmastodon.org/admin/config/#authorized_fetch">Authorised Fetch</a> then I have to sign my request to you.</p>

<p>Which means your server will need to validate my signature by obtaining my public key. Which it will get by signing a request and sending it to me. Which, before I return my public key, I will need to validate your signature by obtaining your public key. Which I will get by signing a request... and so on.</p>

<p>This <a href="https://www.w3.org/wiki/SocialCG/ActivityPub/Authentication_Authorization#Instance_actors">deadlock loop</a> is documented. The usual way around it is either for the <em>sending</em> server to <a href="https://socialhub.activitypub.rocks/t/authorized-fetch-and-the-instance-actor/3868">use an instance-specific signature</a> which can be retrieved by an unsigned request, or to allow any unsigned request to access a user's public key.</p>

<p>I get why things happen this way - I just wish it were easier to implement!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49728&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/02/http-signature-infinite-loop/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Internationalise The Fediverse]]></title>
		<link>https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/</link>
					<comments>https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 17 Feb 2024 12:34:58 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[unicode]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49643</guid>

					<description><![CDATA[We live in the future now. It is OK to use Unicode everywhere.  It seems bizarre to me that modern Internet services sometimes &#34;forget&#34; that there&#039;s a world outside the Anglosphere. Some people have the temerity to speak foreign languages! And some of those languages have accents on their letters!! Even worse, some don&#039;t use English letters at all!!!  A decade ago, I was miffed that GitHub only…]]></description>
										<content:encoded><![CDATA[<p>We live in the future now. It is OK to use Unicode everywhere.</p>

<p>It seems bizarre to me that modern Internet services sometimes "forget" that there's a world outside the Anglosphere. Some people have the temerity to speak <em>foreign</em> languages! And some of those languages have accents on their letters!! Even worse, some don't use English letters <em>at all!!!</em></p>

<p>A decade ago, I was miffed that <a href="https://shkspr.mobi/blog/2013/06/is-github-racist/">GitHub only supported some ASCII characters</a> in its project names. There's no <em>technical</em> reason why your repo can't be called "ഹലോ വേൾഡ്".</p>

<p>Similarly, I'm frustrated that Mastodon (the largest ActivityPub service) <a href="https://github.com/mastodon/mastodon/issues/8417">doesn't allow Unicode usernames</a> and has <a href="https://jam.xwx.moe/notice/AdXsJF6Q5oYHJBEAiG">resisted efforts to change</a>.</p>

<p>So I built a small ActivityPub server which publishes content from an Actor called <a href="https://i18n.viii.fi/.well-known/webfinger"><code>@你好@i18n.viii.fi</code></a> - it is only a demo account, but it works!</p>

<p>Some ActivityPub clients report that they are able to follow it and receive messages from it. Others - like Mastodon - simply can't see anything from it.  Take a look <a href="https://mastodon.social/@Edent/111920759100955860">at the replies on Mastodon</a> to see which services work.  You can also <a href="https://fed.xnor.in/users/$Aet3ViWYORXdinGChM">see some of its posts on the Fediverse</a>.</p>

<h2 id="what-does-the-fox-spec-say"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#what-does-the-fox-spec-say">What Does The <del>Fox</del> Spec Say?</a></h2>

<p>The ActivityPub specification says:</p>

<blockquote><p>Building an international base of users is important in a federated network.
<a href="https://www.w3.org/TR/activitypub/#i18n-concerns">Internationalization</a></p></blockquote>

<p>I can't find anything in the specifications which limits what languages a username can be written in. But there are a few clues scattered about.</p>

<p>The user's <code>@</code> name is defined by <code>preferredUsername</code> which is:</p>

<blockquote><p>A short username which may be used to refer to the actor, with no uniqueness guarantees. 
<a href="https://www.w3.org/TR/activitypub/#preferredUsername">4.1 Actor objects</a></p></blockquote>

<p>There's nothing in there about what scripts it can contain. However, later on, the spec says:</p>

<blockquote><p>Properties containing natural language values, such as <code>name</code>, <code>preferredUsername</code>, or <code>summary</code>, make use of <a href="https://www.w3.org/TR/activitystreams-core/#naturalLanguageValues">natural language support defined in ActivityStreams</a>.
<a href="https://www.w3.org/TR/activitypub/#h-note-2">4. Actors</a></p></blockquote>

<p>So it is expected that a preferred username could be written in multiple scripts. Which implies that the default need not be limited to A-Z0-9.</p>

<p>The <a href="https://www.w3.org/TR/activitystreams-core/#marking-up-language">ActivityStreams specification talks about language mapping</a>.</p>

<p>Finally, the <a href="https://www.w3.org/TR/activitypub/#liked-property">ActivityPub specification has some examples on non-Latin text</a> in names.</p>

<p>So, I think that it is acceptable for usernames to be written in a variety of non-Latin scripts.</p>

<h2 id="but-what-about"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#but-what-about">But What About...?</a></h2>

<p>There are usually a few objections to "Unicode Everywhere" zealots like me. I'd like to forestall any arguments.</p>

<h3 id="what-about-homograph-attacks"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#what-about-homograph-attacks">What about homograph attacks?</a></h3>

<p>Well, what about them? ASCII has plenty of similar looking characters. I doubt most people would notice when a capital i is replaced by a lower L - and vice-versa. Similarly the kerning issue of an r and n looking like an m is well known. Are mixed language homographs more dangerous? I don't think so.</p>

<h3 id="what-if-people-make-names-that-cant-be-typed"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#what-if-people-make-names-that-cant-be-typed">What if people make names that can't be typed?</a></h3>

<p>Well, what if they do? Maybe not being found by people who can't type your language is a feature, not a bug.  But, anyway, clients can let users search for other people, or copy and paste their names.</p>

<h3 id="what-about-weird-zalgo-text"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#what-about-weird-zalgo-text">What about weird "Zalgo" text?</a></h3>

<p>It is up to a client to decide how they want to render text input. The "problems" of strange Unicode combinations are well known. This is not a hard computer-science problem.</p>

<h3 id="what-about-bi-directional-text"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#what-about-bi-directional-text">What about bi-directional text?</a></h3>

<p><a href="https://www.w3.org/TR/activitystreams-core/#h-biditext">The spec makes clear this is allowed</a>.</p>

<h3 id="do-people-even-want-a-username-in-their-own-script"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#do-people-even-want-a-username-in-their-own-script">Do people even want a username in their own script?</a></h3>

<p>I have no evidence for this. But I bet you'd get pretty frustrated if you had to switch keyboard just to type your own name, wouldn't you? In any case, why can't I have a username of <code>@😉</code></p>

<h2 id="whats-next"><a href="https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/#whats-next">What's Next?</a></h2>

<p>If you build ActivityPub software, give some thought to the billions of people who don't have names which easily fit into ASCII.</p>

<p>If your software can see <a href="https://i18n.viii.fi/.well-known/webfinger"><code>@你好@i18n.viii.fi</code></a> and its posts, please let me know.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49643&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/02/internationalise-the-fediverse/feed/</wfw:commentRss>
			<slash:comments>36</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[A (tiny, incomplete, single user, write-only) ActivityPub server in PHP]]></title>
		<link>https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/</link>
					<comments>https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 03 Feb 2024 12:34:51 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[Symfony]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49490</guid>

					<description><![CDATA[I&#039;ve written an ActivityPub server which only allows you to post messages to your followers.  That&#039;s all it does.  It won&#039;t record favourites or reposts. There&#039;s no support for following other accounts or receiving replies.  It cannot delete or update posts nor can it verify signatures. It doesn&#039;t have a database or any storage beyond flat files.  But it will happily send messages and allow…]]></description>
										<content:encoded><![CDATA[<p>I've written <a href="https://github.com/edent/location-activitypub-symfony/">an ActivityPub server which <em>only</em> allows you to post messages to your followers</a>.  That's all it does.  It won't record favourites or reposts. There's no support for following other accounts or receiving replies.  It cannot delete or update posts nor can it verify signatures. It doesn't have a database or any storage beyond flat files.</p>

<p>But it will happily send messages and allow itself to be followed.</p>

<p>This shows that it is <em>totally</em> possible to broadcast fully-featured ActivityPub messages to the Fediverse with minimal coding skills and modest resources.</p>

<h2 id="why"><a href="https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#why">Why</a></h2>

<p>I wanted to create <a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/">a service a bit like FourSquare</a>.  For this, I needed an ActivityPub server which allows posting geotagged locations to the Fediverse.</p>

<p>I didn't want to install a fully-featured server with lots of complex parts. So I (foolishly) decided to write my own. I had a lot of trouble with HTTP Signatures.  Because they are cursed and I cannot read documentation. But mostly the cursed thing.</p>

<h2 id="how"><a href="https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#how">How</a></h2>

<p>Creating a minimum viable Mastodon instance can be done with <a href="https://justingarrison.com/blog/2022-12-06-mastodon-files-instance/">half a dozen static files</a>. That gets you an account that people can see. They can't follow it or receive any posts though.</p>

<p>I wanted to use PHP to build an interactive server. PHP is supported everywhere and is simple to deploy.  Luckily, <a href="https://rknight.me/blog/building-an-activitypub-server/">Robb Knight has written an excellent tutorial</a>, so I ripped off his code and rewrote it for Symfony.</p>

<p>The structure is relatively straightforward.</p>

<ul>
<li><code>/.well-known/webfinger</code> is a static file which gives information about where to find details of the account.</li>
<li><code>/[username]</code> is a static file which has the user's metadata, public key, and links to avatar images.</li>
<li><code>/following</code> and <code>/followers</code> are also static files which say how many users are being followed / are following.</li>
<li><code>/posts/[GUID]</code> a directory with JSON files saved to disk - each ones contains the published ActivityPub note.</li>
<li><code>/photos/</code> is a directory with any uploaded media in it.</li>
<li><code>/outbox</code> is a list of all the posts which have been published.</li>
<li><code>/inbox</code> is an <em>external</em> API endpoint. An ActivityPub server sends it a follow request, the endpoint then POSTs a cryptographically signed Accept message to the follower's inbox. The follower's inbox address is saved to disk.</li>
<li><code>/logs</code> is a listing of all the messages received by the inbox.</li>
<li><code>/new</code> is a password protected page which lets you write a message. This is then sent to...</li>
<li><code>/send</code> is an <em>internal</em> API endpoint. It constructs an ActivityPub note, with attached location metadata, and POSTs it to each follower's inbox with a cryptographic signature.</li>
</ul>

<p>That's it.</p>

<p>The front-end grabs my phone's geolocation and shows the 25 nearest places within 100 metres. One click and the page posts to the <code>/send</code> endpoint which then publishes a message saying I'm checked in.  It is also possible to attach to the post a short message and a single photo with alt text.</p>

<p>There's no database. Posts are saved as JSON documents. Images are uploaded to a directory. It is single-user, so there is no account management.</p>

<h2 id="what-works"><a href="https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#what-works">What Works</a></h2>

<ul>
<li style="list-style-type: '✅ ';">Users can find the account.</li>
<li style="list-style-type: '✅ ';">Users can follow the account and receive updates.</li>
<li style="list-style-type: '✅ ';">Posts contain geotag metadata.</li>
<li style="list-style-type: '✅ ';">Posts contain a description of the place.</li>
<li style="list-style-type: '✅ ';">Posts contain an OSM link to the place.</li>
<li style="list-style-type: '✅ ';">Posts contain a custom message.</li>
<li style="list-style-type: '✅ ';">Posts autolink #Hashtags (sort of).</li>
<li style="list-style-type: '✅ ';">Posts can have an image attached to them.</li>
<li style="list-style-type: '✅ ';">Messages to the inbox are recorded (but not yet integrated).</li>
</ul>

<h2 id="todo"><a href="https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#todo">ToDo</a></h2>

<ul>
<li>My account only has a few dozen followers, some of whom share the same sever. Even with cURL multi handle, it takes time to post to several servers.</li>
<li>It posts plain text. It doesn't autolink websites</li>
<li>Hashtags are linked when viewed remotely, but they don't go anywhere locally.</li>
<li>There's no language selection - it is hard-coded to English.</li>
<li>The outbox isn't paginated.</li>
<li>The UI looks crap - but it is only me using it.</li>
<li>There's only a basic front-page showing a map of all my check-ins.</li>
<li>Replies are logged, but there's no easy way to see them.</li>
<li>Doesn't show any metadata about the place being checked-in to. It could use the item's website (if any) or hashtags for the type of amenity it is.</li>
<li>No way to handle being unfollowed.</li>
<li>No way to remove servers which have died.</li>
<li>Probably lots more.</li>
</ul>

<h2 id="other-resources"><a href="https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#other-resources">Other Resources</a></h2>

<p>I found these resources helpful while creating this project:</p>

<ul>
<li><a href="https://tinysubversions.com/notes/activitypub-tool/">MVP ActivityPub in Python on Glitch</a></li>
<li><a href="https://seb.jambor.dev/posts/understanding-activitypub/">Understanding ActivityPub Part 1: Protocol Fundamentals</a></li>
<li><a href="https://flak.tedunangst.com/post/activity-notes">Activity Notes</a></li>
<li><a href="https://docs.gotosocial.org/en/latest/federation/">Federating with GoToSocial</a></li>
</ul>

<h2 id="whats-next"><a href="https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#whats-next">What's Next?</a></h2>

<p>I've <a href="https://github.com/mastodon/mastodon/issues/29002">raised an issue on Mastodon</a> to see if they can support showing locations in posts. Hopefully, one day, they'll allow adding locations and then I can shut this down.</p>

<p>The code needs tidying up - it is very much a scratch-my-own-itch development. Probably riddled with bugs and security holes.</p>

<p>World domination?</p>

<h2 id="where"><a href="https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/#where">Where</a></h2>

<p>You can <a href="https://github.com/edent/location-activitypub-symfony/">laugh at my code on GitHub</a>.</p>

<p>You can <a href="https://location.edent.tel/">look at my check-ins on a map</a>.</p>

<p>You can follow my location on the Fediverse at <code>@edent_location@location.edent.tel</code></p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49490&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/02/a-tiny-incomplete-single-user-write-only-activitypub-server-in-php/feed/</wfw:commentRss>
			<slash:comments>10</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Rebuilding FourSquare for ActivityPub using OpenStreetMap]]></title>
		<link>https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/</link>
					<comments>https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 27 Jan 2024 12:34:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[FourSquare]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[OpenStreetMap]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49432</guid>

					<description><![CDATA[I used to like the original FourSquare. The &#34;mayor&#34; stuff was a bit silly, and my friends never left that many reviews, but I loved being able to signal to my friends &#34;I am at this cool museum&#34; or &#34;We&#039;re at this pub if you want to meet&#34; or &#34;Spending the day at the park&#34;.  So, is there a way to recreate that early Web 2.0 experience with open data and ActivityPub? Let&#039;s find out!  This quest is…]]></description>
										<content:encoded><![CDATA[<p>I used to like the original FourSquare. The "mayor" stuff was a bit silly, and my friends never left that many reviews, but I loved being able to signal to my friends "I am at this cool museum" or "We're at this pub if you want to meet" or "Spending the day at the park".</p>

<p>So, is there a way to recreate that early Web 2.0 experience with open data and ActivityPub? Let's find out!</p>

<p>This quest is divided into two parts.</p>

<ol>
<li>Get nearby "Points of Interest" (POI) from OpenStreetMap.</li>
<li>Share a location on the Fediverse.</li>
</ol>

<h2 id="openstreetmap-api"><a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#openstreetmap-api">OpenStreetMap API</a></h2>

<p>OpenStreetMap is the Wikipedia of maps. It is a freely available resource which anyone can edit (if they're skilled enough).</p>

<p>It also comes with a pretty decent API for querying things.  For example, <a href="https://overpass-turbo.eu/s/1GaE"><code>nw["amenity"]({{bbox}});</code> finds all "amenities" near a specific location</a>.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/01/amenities-fs8.png" alt="Map of a part of London. Some parts are highlighted." width="826" height="504" class="aligncenter size-full wp-image-49441">

<p>As you can see, it has highlighted <em>some</em> useful areas - a pharmacy and a pub. But it has <em>ignored</em> other useful locations - the train station and the park. It has also included some things that we may not want - bike parking and a taxi rank.</p>

<p>What API call is needed to get <em>useful</em> locations of of OverPass?</p>

<p>It's possible to specify the <em>type</em> of thing to find using <code>nw["amenity"="restaurant"];</code> - but adding every single type of thing would quickly end up with a very large query containing hundreds of types.</p>

<p>It is also possible to <em>exclude</em> specific types of places. This retrieves all amenities except for fast food joints:</p>

<pre><code class="language-_">nw["amenity"]({{bbox}});
-
nw["amenity"="fast_food"]({{bbox}});
</code></pre>

<p>Again, that would be complex.</p>

<p>Perhaps one solution is just to return <em>everything</em> and let the user decide if they want to check in to a telephone kiosk or a fire hydrant?  That's a bit user-hostile.</p>

<p>Instead, <a href="https://overpass-turbo.eu/s/1GaP">this query returns everything which has a name</a> <code>nw["name"]({{bbox}});</code></p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/01/names-fs8.png" alt="Map of London with several bits highlighted." width="826" height="504" class="aligncenter size-full wp-image-49442">

<p>That cuts out any unnamed things - like park benches and car-sharing spots. But it does add named roads and train lines.</p>

<p>It is possible to <a href="https://osm-queries.ldodds.com/tutorial/05-nodes-combined-filter.osm.html">use filters to exclude results from OverPass</a>. The best that I can come up with is: <a href="https://overpass-turbo.eu/s/1GaR"><code>nw["name"][!"highway"][!"railway"][!"waterway"][!"power"]({{bbox}});</code></a></p>

<p>That gets everything which has a name, but isn't a highway or railway or waterway or powerline. It isn't perfect - but it will do!</p>

<p>This is the query which will retrieve the 25 nearest things within 100 metres of a specific latitude and longitude. It includes the name and any other tags, the location, and the OSM ID.</p>

<p><a href="https://overpass-api.de/api/interpreter?data=[out:json];nw%5B%22name%22%5D%5B%21%22highway%22%5D%5B%21%22railway%22%5D%5B%21%22waterway%22%5D%5B%21%22power%22%5D(around:100,51.5202,-0.1040);out%20center%20qt%2025;"><code>overpass-api.de/api/interpreter?data=[out:json];nw["name"][!"highway"][!"railway"][!"waterway"][!"power"](around:100,51.5202,-0.1040);out center qt 25;</code></a></p>

<h2 id="activitypub"><a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#activitypub">ActivityPub</a></h2>

<p>There's good news and bad news here. Firstly, ActivityStreams (which are subscribed to in ActivityPub) <a href="https://www.w3.org/TR/activitystreams-vocabulary/#places">supports the concept of "Place"</a> and <a href="https://www.w3.org/ns/activitystreams#location">"Location"</a>.</p>

<p>Once the user has a latitude and longitude, the can share it - along with a message, photo, or anything else.</p>

<p>Something like:</p>

<pre><code class="language-json">{
    "@context": "https://www.w3.org/ns/activitystreams",
    "type": "Note",
    "content": "Here in NYC! &lt;a href=\"https://www.openstreetmap.org/way/958999496\"&gt;John Lennon's Imagine Mosaic&lt;/a&gt;.",
    "attachment": [
        {
            "type": "Image",
            "mediaType": "image\/jpeg",
            "url": "https:\/\/fastly.4sqi.net\/img\/general\/590x786\/56367_9pxuZJD7d1hgPdaMFcFq1pipvTTMynBJsYcpHH-b8mU.jpg",
            "name": "A photo of a mosaic which says 'Imagine'."
        }
    ],
    "location": {
        "name": "John Lennon's Imagine",
        "type": "Place",
        "longitude": 40.77563,
        "latitude": -73.97474
    }
}
</code></pre>

<p>For example, here's <a href="https://pixelfed.social/p/dansup/75783646421848064?fs=1">a PixelFed post with an attached location</a> - and this is the <a href="https://pixelfed.social/p/dansup/75783646421848064.json">JSON representation</a>. That status can be reposted into other social networks.</p>

<p>It is worth noting that Mastodon doesn't (natively) support location - if you <a href="https://mastodon.social/@Edent/100581317169164202">view my repost of that PixelFed post</a> you'll see there's no location metadata attached. That's OK! It just means that the status needs to include human-readable data.</p>

<p>Similarly, Mastodon doesn't support the <a href="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-arrive"><code>arrive</code></a> vocabulary. So this will be limited to a message with a location attached.</p>

<p><a href="https://benjojo.co.uk/u/benjojo/h/5j4yjq8JbrKY6Vp79K">Other ActivityPub services <em>do</em> support location</a>.</p>

<h2 id="putting-it-all-together"><a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#putting-it-all-together">Putting it all together</a></h2>

<p>Well… that's a job for next week. Probably!</p>

<ul>
<li>Building a web site which gets the user's location is easy.</li>
<li>Getting the data from OverPass should be straightforward.</li>
<li>Creating an ActivityPub server which can post geotagged notes into the Fediverse might be a little beyond my skillset!  Some testing with <a href="https://tinysubversions.com/notes/activitypub-tool/">Darius Kazemi's AP Glitch</a> suggests this should work.</li>
</ul>

<p>If you'd like to help, please leave a comment.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49432&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/feed/</wfw:commentRss>
			<slash:comments>30</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Federation is pretty cool, but kinda confusing, and maybe a little scary]]></title>
		<link>https://shkspr.mobi/blog/2023/06/federation-is-cool-but-kinda-confusing-and-maybe-a-little-scary/</link>
					<comments>https://shkspr.mobi/blog/2023/06/federation-is-cool-but-kinda-confusing-and-maybe-a-little-scary/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 18 Jun 2023 11:34:11 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=46115</guid>

					<description><![CDATA[Last week, this strange mention appeared on my Mastodon feed.    After a bit of clicking around, I figured out what had happened. A user on the Kbin social network had linked to my Mastodon profile. Thanks to the magic of the ActivityPub protocol, it filtered into my mentions - even though I&#039;ve never even heard of Kbin. That&#039;s pretty cool! A user on one social network can mention a user on a…]]></description>
										<content:encoded><![CDATA[<p>Last week, <a href="https://kbin.social/m/asklemmy@lemmy.ml/t/33475/-/comment/138013">this strange mention appeared on my Mastodon feed</a>.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2023/06/kbin-mention-fs8.png" alt="Otome-chan says: &quot;See here. you can see this mastodon user's post (which to them looks like a regular tweet on twitter does) ends up in our random microblogs section. We can also view their profile directly as well as follow them to have their posts appear in our microblogs (as well as threads if they go out of their way to make one). It seems kbin microblogs appear as threads/comments to you on lemmy. so I have to imagine mastodon posts might be similar?&quot;" width="425" height="635" class="aligncenter size-full wp-image-46116">

<p>After a bit of clicking around, I figured out what had happened. A user on the Kbin social network had linked to my Mastodon profile. Thanks to the magic of the ActivityPub protocol, it filtered into my mentions - even though I've never even <em>heard</em> of Kbin. That's pretty cool! A user on one social network can mention a user on a different social network - neither needs to be registered on the other.</p>

<p>And that is where things get a little confusing and, perhaps, a bit scary.</p>

<p>At the moment, users on TikTok and Facebook are completely segregated.  If my TikTok video gets shared on Facebook, I have no way of knowing about it.  But ActivityPub breaks down those walls.</p>

<p>If my cat photo on Mastodon goes viral, I can start receiving comments from PixelFed telling me how cute my cat is. Lovely!</p>

<p>If I post something on Lemmy saying "I don't think that Trump fellow is entirely my cup of tea", I can start receiving vitriolic comments from a dozen different networks which sprang up in the last week and will vanish tomorrow. Not lovely.</p>

<p>In many ways, this isn't a new problem. Users have always been at risk of abusive people - but that's usually confined to the users of a single network. And most users understand the norms of their social network community. And most networks have ways to combat harassment and limit post visibility.</p>

<p>Because most social networks are walled gardens, the easiest way to share content is via a screenshot. That makes it harder for a user on Facebook to find the Twitter user whose screenshot so offended them. But with ActivityPub there is zero friction. There's no need to share a screenshot when you can embed a post from Network A into Network B with no fuss. Which means with a single click a disgruntled user can slake their ire.</p>

<p>I don't think federation is a <em>bad</em> thing. But I think it's a new way of thinking about your social networking. I <em>like</em> having a different personality and different audience of Facebook and LinkedIn and Reddit and MySpace. And, just sometimes, I want a little bit of friction to stop angry idiots from shouting at me.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=46115&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2023/06/federation-is-cool-but-kinda-confusing-and-maybe-a-little-scary/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Naming things is hard - DNS for the Federated Web]]></title>
		<link>https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/</link>
					<comments>https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 27 Dec 2022 12:34:27 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[dns]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=44324</guid>

					<description><![CDATA[How should I design my personal DNS for all the cool new Federated Services and IndieWeb protocols?  Way back in the early 2000s, I started this website - shkspr.mobi. A few years later, I added a blog.  I could have used the main domain, or created a subdomain like blog.shkspr.mobi. In the end, I chose a subdirectory of shkspr.mobi/blog  I don&#039;t know if that was the right choice back then, but…]]></description>
										<content:encoded><![CDATA[<p>How should I design my personal DNS for all the cool new Federated Services and IndieWeb protocols?</p>

<p>Way back in the early 2000s, I started this website - <code>shkspr.mobi</code>. A few years later, I added a blog.  I could have used the main domain, or created a subdomain like <code>blog.shkspr.mobi</code>. In the end, I chose a subdirectory of <code>shkspr.mobi/blog</code></p>

<p>I don't know if that was the right choice back then, but it is looking like the wrong choice now.</p>

<p>I want to be a "first class" citizen of the Fediverse. I want a dozen different apps installed on my little slice of the Internet.  I want a fairly consistent online identity. What's the best way to do that?</p>

<h2 id="buy-a-new-domain-for-every-app"><a href="https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/#buy-a-new-domain-for-every-app">Buy a new domain for every app!</a></h2>

<p>No.  This is impractical for two reasons.</p>

<ol>
<li>It's expensive.</li>
<li>Nothing ties together <code>my_awesome_photos.biz</code> to <code>read_my_blog.com</code>.</li>
</ol>

<h2 id="subdirectories"><a href="https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/#subdirectories">Subdirectories</a></h2>

<p>I currently have <code>/blog</code>.  Should I also have <code>/mastodon</code> and <code>/pixelfed</code> and <code>/yet_another_cool_service</code> and...</p>

<p>Maybe? The problem is, most of these new services assume that they're going to be on their own domain.  Usernames are based on domains - so I guess I'd end up with <code>@edent-mastodon@shkspr.mobi</code> and <code>@edent-pixelfed@shkspr.mobi</code>? That just looks ugly.</p>

<h2 id="subdomains"><a href="https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/#subdomains">Subdomains</a></h2>

<p>Adding sub-domains is free and easy.</p>

<p><code>mastodon.shkspr.mobi</code> and <code>pixelfed.shkspr.mobi</code> - done!</p>

<p>But there are a couple of issues.</p>

<ol>
<li>Do I want to name them after the app, or something more generic in case I switch later? Perhaps <code>posts.shkspr.mobi</code> and <code>pictures.shkspr.mobi</code>?</li>
<li>Do users understand that they need to follow <code>@edent@location.shkspr.mobi</code> for my Foursquare-style check-ins and <code>@edent@beer.shkspr.mobi</code> for my beer reviews?</li>
</ol>

<h2 id="one-domain-to-rule-them-all"><a href="https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/#one-domain-to-rule-them-all">One Domain To Rule Them All</a></h2>

<p>Perhaps I'll just have everything on my main domain?  That <em>also</em> comes with a few problems! I'll need to install the apps <em>somewhere</em> and then work out how to redirect users to the correct app based on... what? And it still doesn't resolve the username issue.</p>

<h2 id="just-treat-everything-as-a-single-activitypub-feed"><a href="https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/#just-treat-everything-as-a-single-activitypub-feed">Just treat everything as a single ActivityPub feed</a></h2>

<p>I <em>think</em> this is where I'm heading.</p>

<p>I've written before about <a href="https://shkspr.mobi/blog/2020/04/how-id-redesign-twitter-and-why-it-wont-work/">my perfect social network</a>.</p>

<ul>
<li>I post items tagged <code>work</code>, <code>tech</code>, <code>sport</code>, <code>politics</code>, etc.</li>
<li>You decide which of those channels you want to subscribe to.</li>
</ul>

<p>If you <em>only</em> want to read my sport punditry, subscribe to that channel. If you want everything <em>except</em> my political views, ignore that specific channel.</p>

<p>I could publish everything on a single feed. It is then up to you or your client to work out how to filter that.</p>

<p>I'm still not sure <em>how</em> that would work! Perhaps clients will be smart enough to ignore statuses which don't fit their model? Perhaps users will manually choose what to follow?</p>

<p><a href="https://aaronparecki.com/2018/04/20/46/indieweb-reader-my-new-home-on-the-internet">That's sort of how Aaron Parecki's unified view works</a>.</p>

<h2 id="what-would-you-do"><a href="https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/#what-would-you-do">What would you do?</a></h2>

<p>If you've done this successfully - or have particularly strong opinions - please let me know!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=44324&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/12/naming-things-is-hard-dns-for-the-federated-web/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Snowflake IDs in Mastodon (and Unique IDs in the Fediverse more generally)]]></title>
		<link>https://shkspr.mobi/blog/2022/12/snowflake-ids-in-mastodon-and-unique-ids-in-the-fediverse-more-generally/</link>
					<comments>https://shkspr.mobi/blog/2022/12/snowflake-ids-in-mastodon-and-unique-ids-in-the-fediverse-more-generally/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Mon, 12 Dec 2022 12:34:06 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[MastodonAPI]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=44114</guid>

					<description><![CDATA[Computer Science has two canonical &#34;hard problems&#34;:   cache invalidation naming things off-by-one errors   Let&#039;s talk about how we name unique items in Federated services - for example, posts on a social media service.  If you have only one service, it&#039;s pretty easy.  Every time a new entry is created in a database, give it a sequential number.  This becomes a problem at scale. If you have…]]></description>
										<content:encoded><![CDATA[<p>Computer Science has two canonical "<a href="https://joncalder.co.za/2017-12-04-naming-things-is-hard/">hard problems</a>":</p>

<ol>
<li>cache invalidation</li>
<li>naming things</li>
<li>off-by-one errors</li>
</ol>

<p>Let's talk about how we name unique items in Federated services - for example, posts on a social media service.</p>

<p>If you have only one service, it's pretty easy.  Every time a new entry is created in a database, give it a sequential number.</p>

<p>This becomes a problem at scale. If you have millions of users on hundreds of different shards of a database, eventually you'll get a clash of IDs.  To that end, <a href="https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake">Twitter invented Snowflake IDs</a>.</p>

<p>Snowflakes are pretty clever.  They are a 64 bit ID. The first part of the ID is a timestamp, and the second part is some information about the server which generated the ID.  This means that IDs can be sorted by time, and will globally unique.</p>

<p>So, does the Fediverse use Snowflake? Yes. <strong>AND NO!</strong></p>

<p>The Mastodon service uses <a href="https://github.com/mastodon/mastodon/blob/main/lib/mastodon/snowflake.rb">Snowflake for its IDs</a>.  For example, one of my posts has the ID <a href="https://mastodon.social/@Edent/109347703064222520"><code>109347703064222520</code></a>.  If you bitshift that into a 64 bit number, you'll see it is a UNIX timestamp with a few bits of extra data.</p>

<p>But here comes the problem.  On a Federated service, there's <em>no guarantee</em> of uniqueness. A naughty server could deliberately generate a duplicate Snowflake ID.  You can't trust anyone on the Internet!</p>

<p>So here's how Mastodon - and the wider Fediverse - deals with unique IDs.  It cheats.</p>

<ul>
<li>When I make a post, my server gives it a Snowflake ID which is unique <em>to my server</em> - e.g. <code>123456</code></li>
<li>This generates a URl which is <em>globally</em> unique - e.g. <code>https:// my_server.xyz/@username/123456</code></li>
<li>My post is delivered to your server.</li>
<li>Your server gives it an ID which is unique to <em>your</em> server - e.g. <code>4d7b70c7</code></li>
<li>Your server turns that into a URl - e.g. <code>https:// your_server.lol/incoming/from/@username@my_server.xyz/4d7b70c7</code></li>
</ul>

<p>For example, here's the JSON response I get when I look up the Snowflake ID of a reply someone sent me. My server replies with:</p>

<pre><code class="language-json">{
 'created_at': datetime.datetime(2022, 11, 13, 0, 52, 37, tzinfo=tzutc()),
 'id': 109347716491680514,
 ...
 'uri': 'https://queer.af/@erincandescent/109347716173491502',
}
</code></pre>

<p>The <em>original</em> ID is <code>109347716173491502</code> on the sender's server.  But the unique ID on my server is <code>109347716491680514</code>.</p>

<p>Their unique ID could be a GUID, a series of emoji, or a sequential number. It doesn't matter. To prevent clashes, the receiving server generates its own ID.</p>

<p>This could all be solved a lot more easily if every server minted each post using a Proof of Work transaction against a centralised BlockCha… <em>*sound of gunshot*</em></p>

<p>Ahem.</p>

<p>It is a pretty reasonable solution. A remote server may or may not have a globally unique ID. It doesn't matter. Once you see a post, it is given its own <em>locally</em> unique ID.  And that's good enough.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=44114&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/12/snowflake-ids-in-mastodon-and-unique-ids-in-the-fediverse-more-generally/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[How much decentralisation is too much?]]></title>
		<link>https://shkspr.mobi/blog/2022/12/how-much-decentralisation-is-too-much/</link>
					<comments>https://shkspr.mobi/blog/2022/12/how-much-decentralisation-is-too-much/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sun, 11 Dec 2022 12:34:02 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[ReDeCentralize]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=44047</guid>

					<description><![CDATA[Twitter&#039;s over, my dudes! And now everyone is on Mastodon! But Mastodon isn&#039;t a site, it is a federated network running an interoperable protocol! Yay for ActivityPub!  Anyway, that means there isn&#039;t one Mastodon website. There are many.  There is only one Twitter. There is only one Facebook.  There is only one Instagram.  If you want to interact with Twitter/FB/Insta then you have to do it on…]]></description>
										<content:encoded><![CDATA[<p>Twitter's over, my dudes! And now everyone is on Mastodon! But Mastodon isn't a site, it is a <em>federated network running an interoperable protocol</em>! Yay for ActivityPub<sup id="fnref:ActivityPub"><a href="https://shkspr.mobi/blog/2022/12/how-much-decentralisation-is-too-much/#fn:ActivityPub" class="footnote-ref" title="I’d just like to interject for a moment. What you're referring to as Mastodon, is in fact, ActivityPub/Mastodon, or as I’ve recently taken to calling it, ActivityPub plus Mastodon. Mastodon is not an…" role="doc-noteref">0</a></sup>!</p>

<p>Anyway, that means there isn't <em>one</em> Mastodon website. There are many.  There is only one Twitter. There is only one Facebook.  There is only one Instagram.  If you want to interact with Twitter/FB/Insta then you have to do it on those websites, or via the official apps.</p>

<p>Mastodon is decentralised. I am on Mastodon.Social, and you are on Mastodon.me.uk.  Those are different servers.  But we can still see each others' messages.</p>

<p>All of those different servers have multiple users. Some of the big servers have hundreds of thousands of accounts, some of the smaller ones only a few dozen. We're all apart, even when we're together. This is the joy - and the sorrow - of decentralisation.</p>

<p>What's the optimal size of a server? Bigger ones might be slower, or they may benefit from efficiencies of scale. Smaller ones might be more nimble, but may not have the resources to keep the server going.</p>

<p>Let's take a look at PixelFed - a federated version of Flickr / Instagram.  There are about <a href="https://fedidb.org/software/pixelfed">280 different PixelFed instances you could join</a>.  Here's a graph showing the number of users on the top 25 instances:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/11/chart.png" alt="The top server has over 60,000 users, the next under 20,000, the rest tail off into double digits." width="600" height="371" class="aligncenter size-full wp-image-44068">

<p>Pretty classic "long-tail" there. A dominant main server. A credible second place. A distant third. And then all the rest quickly descend under 100 users.</p>

<p>I want to be very clear - I don't think that's necessarily a <em>bad</em> thing.  It is entirely possible to have a sustainable server which only has one user.</p>

<p>For example, the Raspberry Pi foundation has an instance at aspberrypi.social. It only has a single account you can follow - <a href="https://web.archive.org/web/20240623060954/https://raspberrypi.social/@Raspberry_Pi">@Raspberry_Pi@raspberrypi.social</a>.</p>

<iframe src="https://someone.elses.computer/@laurence/109335661569754444/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" height="400" allowfullscreen="allowfullscreen"></iframe>

<p>Is that the future?</p>

<p>What happens if <em>everyone</em> has their own instance? Would managing spam and blocklists on the Fediverse be as difficult as managing them for email?</p>

<h2 id="inside-of-you-there-are-two-extremes"><a href="https://shkspr.mobi/blog/2022/12/how-much-decentralisation-is-too-much/#inside-of-you-there-are-two-extremes">Inside of you there are two extremes</a></h2>

<p>Consider email.  Almost everyone is on GMail, O365, or one of a limited number of big email providers.  Even if you're a digital extremist and have your own domain name, you almost certainly don't run your own email server.  It's just too complicated and no one wants a full-time unpaid job keeping up with the admin of configuring, patching, and defending their mail.</p>

<p>Similarly, I doubt anyone wants to spend much more than a couple of hours per year setting up and maintaining their federated social network app.</p>

<p>Those of us vain / paranoid enough to want to be <code>@MyName@my-domain.xyz</code> will almost certainly just rent computing power and space from one of the big Mastodon providers.</p>

<p>What about big businesses?</p>

<iframe src="https://cloudisland.nz/@aurynn/109332454233821775/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="400" height="300" allowfullscreen="allowfullscreen"></iframe>

<p>I love that idea. But businesses don't run servers any more.  If you're a small business, you just outsource everything to Google or Microsoft.  If you're a big business... the same!</p>

<p>It's easier and cheaper to pay dedicated professionals to run specialist appliances than it is to bring them in-house.</p>

<h2 id="predictions"><a href="https://shkspr.mobi/blog/2022/12/how-much-decentralisation-is-too-much/#predictions">Predictions</a></h2>

<p>Making predictions is a fool's game.  So here are my predictions for the future of Federated Services:</p>

<ul>
<li>Die-hard geeks like me will still run our family's Fediverse apps from a Raspberry Pi tucked in a cupboard.</li>
<li>Small sellers will offer Fediverse packages just the same way as they do one-click WordPress installs. But they'll mostly all be running on AWS anyway.</li>
<li>Twitter - or someone like them - will start offering "Fediverse-as-a-Service".  You can be <code>@Bob@Mastodon.Your_Domain.com</code> - but it'll be Twitter under the hood.  Just like GMail for domains.</li>
<li>People will have <em>multiple</em> instances they need to be on. Work and personal, for example. Just as some of us have a family email which is different from the friends one.  But they'll all come through to a single app.</li>
<li>Facebook - or someone like them - will gradually add "enhancements" to their service which will cause it not to work other Federated systems. The first time this happens it will be excused as a mistake. But, just like how Google killed off XMPP, it will eventually become a silo. Not many people will notice, because the majority of their friends are all on the same instance.</li>
<li>Someone will invent a server which runs on your Android phone. It would revolutionise decentralisation and federation. But it chews up battery and Apple bans the iOS version for no good reason. So it never takes off except with a few people prepared to root their devices and carry an extra battery.</li>
<li>A nation-state will insist that every citizen and resident <em>must</em> have an account on the national Mastodon. Perhaps in order to listen to the thoughts of Dear Leader™.  Perhaps for some sinister monitoring purpose.  If you want to talk to your buddies in that region, your server may have to Federate with something running old, outdated, or hostile software.</li>
<li>Geeks like me will rage that this all could been avoided if everyone bought their own Raspberry Pi and learned half-a-dozen simple Linux commands.</li>
</ul>

<p>Perhaps that's pessimistic of me? I think the pendulum will swing a few more times at least.</p>

<p>There might be a dozen different phone networks in your country.  Yes, they Federate with each other, but they all happen to be running close-to-identical networking gear made by Nokia or Huawei.  Yes, you can put your SIM in any device, but chances are it is either Android or iOS.</p>

<p>We'll be stuck with the <em>illusion</em> of Federation.</p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:ActivityPub">
<p>I’d just like to interject for a moment. What you're referring to as Mastodon, is in fact, ActivityPub/Mastodon, or as I’ve recently taken to calling it, ActivityPub plus Mastodon. Mastodon is not an service unto itself, but rather another free component of a fully functioning Fediverse made useful by the W3C core protocols and other vital components etc. etc.&nbsp;<a href="https://shkspr.mobi/blog/2022/12/how-much-decentralisation-is-too-much/#fnref:ActivityPub" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=44047&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/12/how-much-decentralisation-is-too-much/feed/</wfw:commentRss>
			<slash:comments>22</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[The ethics of syndicating comments using WebMentions]]></title>
		<link>https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/</link>
					<comments>https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 03 Dec 2022 12:34:17 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[blogging]]></category>
		<category><![CDATA[ethics]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[WebMention]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=43980</guid>

					<description><![CDATA[This blog uses WebMention technology.  If you write an article on your website and mention one of my blog posts, I get a notification. That notification can then be published as a comment.  It usually looks something like this:    This means readers of my post can see where it has been mentioned around the web.  They can read your article after reading mine. Nice!  I&#039;ve also set up a &#34;bridge&#34;…]]></description>
										<content:encoded><![CDATA[<p>This blog uses <a href="https://indieweb.org/Webmention">WebMention</a> technology.  If you write an article on your website and mention one of my blog posts, I get a notification. That notification can then be published as a comment.  It usually looks something like this:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/11/Screenshot-2022-11-07-at-17-17-02-Have-I-reached-the-Douglas-Adams-Inflection-point-or-is-modern-tech-just-a-bit-rubbish.png" alt="Screenshot of a comment showing that someone mentioned my post on their blog." width="1170" height="151" class="aligncenter size-full wp-image-43988">

<p>This means readers of my post can see where it has been mentioned around the web.  They can read your article after reading mine. Nice!</p>

<p>I've also set up a <a href="https://brid.gy/">"bridge" service which looks for people posting comments about my work on social media</a>.  For example, if you post a link to my blog on Twitter - or reply to someone who has shared a link - I get a notification.  That means if I think it is an interesting comment, I can publish it in the comment section. It usually looks something like this:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/11/Screenshot-2022-11-07-at-13-50-46-Is-Open-Graph-Protocol-dead.png" alt="Screenshot showing some comments. One has the Mastodon Logo, the other has the Twitter Logo." width="1180" height="431" class="aligncenter size-full wp-image-43984">

<p>This means readers of my post can see your Twitter or Mastodon comments. They're identified by the logo. Users can go to Twitter or Mastodon to reply to you. Nice!</p>

<p>Everyone is one big happy family, no matter where on the web you are.</p>

<p>Or so I thought.  There are a few drawbacks with this system.</p>

<h2 id="i-didnt-write-that"><a href="https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/#i-didnt-write-that">I didn't write that!</a></h2>

<p>I had one reader complain that someone else was impersonating them in the comments of my posts. It wasn't immediately clear to them that I'd syndicated their comment and reposted it. After that, I added the Twitter logo to make it a bit more obvious.  But many people still find it unintuitive that content can be replicated outside of its original publication.</p>

<h2 id="i-deleted-that"><a href="https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/#i-deleted-that">I deleted that!</a></h2>

<p>I had another reader who automatically deletes their Tweets every month. My blog retains a copy of their Tweets because it doesn't check for deletions. This might be against the user's wishes - especially if they had posted something inappropriate.  Twitter doesn't send a "deleted" notification to services which have stored Tweets - and it would be impractical to periodically check every single Tweet I have stored.   The reader was ambivalent about whether they should be kept on my blog.</p>

<h2 id="im-not-that-person-any-more"><a href="https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/#im-not-that-person-any-more">I'm not that person any more!</a></h2>

<p>One of the comments was a person who had changed their name &amp; Twitter avatar.  Understandably, they weren't happy about still being referred to by their <a href="https://www.pinknews.co.uk/2018/12/04/deadname-deadnaming/">deadname</a> on my site.  Again, neither Twitter nor the bridging service notifies me when a user changes their name or avatar. Naturally I deleted the comments when they contacted me.</p>

<h2 id="i-have-the-copyright-to-that"><a href="https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/#i-have-the-copyright-to-that">I have the copyright to that!</a></h2>

<p>An overly aggressive person was furious that I'd copied their © content onto my blog without permission. Personally, I thought that adding their 7 word reply was covered by fair-dealing, but I didn't fancy pissing them off. So I deleted it.  If I'd embedded directly from Twitter it would have been fair game - but some people feel there's a material difference between embedding and copying.</p>

<h2 id="now-what"><a href="https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/#now-what">Now what?</a></h2>

<p>This is a complicated problem. I want to see what people are writing in public about my posts.  I also want to direct people to the conversations which are happening elsewhere on the web. But people - quite rightly - might not want their content permanently stored by my site.</p>

<p>So I think I have a few options.</p>

<ol start="0">
<li>Do nothing. My site; my rules. If you don't want me to grab your hot takes, don't post them in public. (Feels a bit rude, TBQH.)</li>
<li>Be reactive. If someone asks me to remove their content, do so. (But, of course, how will they know I've made a copy?)</li>
<li>Stop syndicating comments. (I don't wanna!)</li>
<li>Replace the verbatim comments with a link saying "Fred mentioned this article on Twitter" . (A bit of a disruptive experience for readers.)</li>
<li>Use oEmbed to capture the user's comment and dynamically load it from the 3rd party site. That would update automatically if the user changes their name or deleted the comment. (A massive faff to set up.)</li>
</ol>

<p>What do you think I should do?</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=43980&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/12/the-ethics-of-syndicating-comments-using-webmentions/feed/</wfw:commentRss>
			<slash:comments>23</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Create a "Share To Mastodon" Button for WordPress]]></title>
		<link>https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/</link>
					<comments>https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Fri, 17 Jun 2022 11:34:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[mastodon]]></category>
		<category><![CDATA[WordPress]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=42718</guid>

					<description><![CDATA[Everyone is decamping from Twitter to Mastodon!  The great thing about the Federated Internet (hereafter the &#34;Fediverse&#34;) is that it is distributed. The bad thing is… it is distributed!  What do I mean by that? Here&#039;s an example of the problems with decentralised systems.  If I want to create a link on a website which will share text to Twitter, I just create a URl which points to:  t…]]></description>
										<content:encoded><![CDATA[<p>Everyone<sup id="fnref:everyone"><a href="https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/#fn:everyone" class="footnote-ref" title="OK - just the nerds" role="doc-noteref">0</a></sup> is decamping from Twitter to <a href="https://joinmastodon.org/">Mastodon</a>!  The great thing about the Federated Internet (hereafter the "Fediverse") is that it is distributed.
The <em>bad</em> thing is… it is distributed!</p>

<p>What do I mean by that? Here's an example of the problems with decentralised systems.  If I want to create a link on a website which will share text to Twitter, I just create a URl which points to:</p>

<p><a href="https://twitter.com/intent/tweet?&amp;text=https%3A%2F%2Fexample.com"><code>twitter.com/intent/tweet?text=https%3A%2F%2Fexample.com</code></a></p>

<p>The same is broadly true of any centralised system. Sure, the syntax differs, but you can use a URl like that to post to Facebook, WhatsApp, LinkedIn, etc.</p>

<p>But the Fediverse is different. I use the <code>mastodon.social</code> instance, but you might be on <code>fediverse.rocks</code> or <code>lolcats.chat</code>.  So we can't use a <em>single</em> URl to target all of them.</p>

<p>On the mobile web, the answer is simple. Just use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share"><code>navigator.share()</code></a> Javascript function (and hope the user has an app installed). But for the desktop web, that just isn't available.</p>

<p>So, we need to use an intermediary. For this, I'm using <a href="https://www.kytta.dev/">Nikita Karamov</a>'s <a href="https://toot.kytta.dev/">Toot Proxy</a>.</p>

<p>A URl like: <a href="https://toot.kytta.dev/?text=Check%20out%20https://example.com/"><code>toot.kytta.dev/?text=Check%20out%20https%3A%2F%2Fexample.com</code></a></p>

<p>Will open a page like this:
<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/05/Toot-Sharing.png" alt="Screenshot of a website with the URL text already filled. A prompt asks the user to enter their fediverse destination." width="751" height="552" class="aligncenter size-full wp-image-42720">
The user can then type in their preferred Mastodon address - for example <code>mastodon.social</code> - and get redirected to:<a href="https://mastodon.social/share?text=Check+out+https%3A%2F%2Fexample.com%2F"><code>mastodon.social/share?text=Check+out+https%3A%2F%2Fexample.com%2F</code></a></p>

<p>Magic!</p>

<h2 id="setting-it-up-in-wordpress"><a href="https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/#setting-it-up-in-wordpress">Setting it up in WordPress</a></h2>

<p>The WordPress Jetpack plugin defines a bunch of standard sharing buttons.  You can add your own at <code>your-blog.whatever/wp-admin/options-general.php?page=sharing</code></p>

<p>Clicking on "Add a new service" will bring up a prompt. You can fill it in like this:</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2022/05/Social-Sharing-Text.png" alt="A WordPress Popup." width="750" height="493" class="aligncenter size-full wp-image-42721">

<ul>
<li>Service name  <code>Mastodon</code></li>
<li>Sharing URL <code>//toot.kytta.dev/?text=%post_title%%20%post_full_url%</code></li>
<li>Icon URL <code>//edent.github.io/SuperTinyIcons/images/svg/mastodon.svg</code></li>
</ul>

<p>You can choose your own icon, and edit the text you share.</p>

<h2 id="is-this-a-problem"><a href="https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/#is-this-a-problem">Is this a problem?</a></h2>

<p>The Fediverse is a complicated concept and, for better or worse, its structure doesn't lend itself to easily sharing content. Users have to remember <em>which</em> instance of each service they're on. That introduces cognitive overhead which just isn't present when choosing to share to the singular Facebook.</p>

<p>Is there a straightforward solution to this?<sup id="fnref:no"><a href="https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/#fn:no" class="footnote-ref" title="No." role="doc-noteref">1</a></sup></p>

<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes">
<ol start="0">

<li id="fn:everyone">
<p>OK - just the nerds&nbsp;<a href="https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/#fnref:everyone" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

<li id="fn:no">
<p>No.&nbsp;<a href="https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/#fnref:no" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>

</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=42718&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2022/06/create-a-share-to-mastodon-button-for-wordpress/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
	</channel>
</rss>
