<?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>Sun, 13 Sep 2026 06:08:41 +0000</lastBuildDate>
	<language>en-GB</language>
	<copyright>© Terence Eden. 🄯 CC BY-SA. See https://shkspr.mobi/blog/copyright-and-copyleft/</copyright>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</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[ActivityPub - How to send an updated user profile to Mastodon and the Fediverse]]></title>
		<link>https://shkspr.mobi/blog/2026/09/activitypub-how-to-send-an-updated-user-profile-to-mastodon-and-the-fediverse/</link>
					<comments>https://shkspr.mobi/blog/2026/09/activitypub-how-to-send-an-updated-user-profile-to-mastodon-and-the-fediverse/#respond</comments>
				<dc:creator><![CDATA[Terence Eden]]></dc:creator>
		<pubDate>Sat, 12 Sep 2026 11:34:02 +0000</pubDate>
		<category><![CDATA[ActivityBot]]></category><category><![CDATA[ActivityPub]]></category><category><![CDATA[fediverse]]></category><category><![CDATA[mastodon]]></category>		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=74470</guid>

					<description><![CDATA[Let&#039;s suppose you&#039;ve updated the description of your ActivityPub account from &#34;World&#039;s Number 1 Taylor Swift Fan&#34; to &#34;This account is now a Nickleback Truther&#34;. How do you let the rest of the Fediverse know that you&#039;ve changed your allegiance?  By default, most Mastodon instances won&#039;t periodically poll your account information just to see if you&#039;ve updated it. So how does the information get…]]></description>
										<content:encoded>&lt;p&gt;Let's suppose you've updated the description of your ActivityPub account from "World's Number 1 Taylor Swift Fan" to "This account is now a Nickleback Truther". How do you let the rest of the Fediverse know that you've changed your allegiance?&lt;/p&gt;

&lt;p&gt;By default, most Mastodon instances won't periodically poll your account information just to see if you've updated it. So how does the information get from your server to your followers' servers?&lt;/p&gt;

&lt;p&gt;This wasn't immediately obvious to me, but I got a clue from reading &lt;a href="https://shkspr.mobi/blog/2026/09/book-review-activitypub-by-evan-prodromou/"&gt;Evan Prodromou's book on ActivityPub&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The Update activity type is for updating the properties of an object represented by the object property.&lt;/p&gt;

&lt;p&gt;The most common types of objects that can be updated are content objects, like Note or Image. Actor types (like Person) and Question activity types can also be updated.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Aha!&lt;/p&gt;

&lt;p&gt;You need to craft an &lt;code&gt;Update&lt;/code&gt; message which has as its object the &lt;em&gt;new&lt;/em&gt; user information. That needs to be sent to the inbox of all your followers.&lt;/p&gt;

&lt;p&gt;Something like this:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-json"&gt;{
    "@context": "https://www.w3.org/ns/activitystreams",
    "actor": "https://example.com/user",
    "id": "6a9162a6-a8e5-ca0f-9c08-8e6b814acef8",
    "published": "2026-08-31T12:34:56+01:00",
    "to": "https://www.w3.org/ns/activitystreams#Public",
    "type": "Update",
    "object": {
        "@context": [
            "https://www.w3.org/ns/activitystreams",
            "https://w3id.org/security/v1"
        ],
        "id": "https://example.com/user",
        "name": "My new name",
        "summary": "A brand new description!",
        …
    },
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Obviously the &lt;em&gt;full&lt;/em&gt; user information is a bit more than that - it will include inbox details, public keys, avatars, etc. The &lt;code&gt;published&lt;/code&gt; property in the Update activity should be when you changed your details - not when the account was created.&lt;/p&gt;

&lt;p&gt;Once that was sent, Mastodon immediately reflected the changes.&lt;/p&gt;

&lt;h2 id="thanks-to-nlnet"&gt;&lt;a href="https://shkspr.mobi/blog/2026/09/activitypub-how-to-send-an-updated-user-profile-to-mastodon-and-the-fediverse/#thanks-to-nlnet"&gt;Thanks to NLnet&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;This blog post was funded in part by the work I'm doing for my NLnet NGI0 grant to develop &lt;a href="https://gitlab.com/edent/activity-bot"&gt;ActivityBot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nlnet.nl/project/ActivityBot/"&gt;&lt;img src="https://shkspr.mobi/blog/wp-content/uploads/2026/08/NGI-logos.webp" alt="NLnet logo." width="900" height="200" class="aligncenter"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=74470&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/09/activitypub-how-to-send-an-updated-user-profile-to-mastodon-and-the-fediverse/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[ActivityBot is the recipient of an NLnet grant!]]></title>
		<link>https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/</link>
					<comments>https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#comments</comments>
				<dc:creator><![CDATA[Terence Eden]]></dc:creator>
		<pubDate>Sun, 30 Aug 2026 11:34:55 +0000</pubDate>
		<category><![CDATA[ActivityBot]]></category><category><![CDATA[ActivityPub]]></category><category><![CDATA[fediverse]]></category><category><![CDATA[NLnet]]></category>		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=74406</guid>

					<description><![CDATA[Back in February, I applied for NLnet&#039;s Next Generation Zero grant. They were looking for Fediverse projects to help rewild the social media landscape. Or, as they describe it:  Reclaim the public nature of the internet  Small and medium-sized R&#38;D grants between 5.000 and 50.000 euro, with the possibility to scale up.  I run ActivityBot - it is a single-file ActivityPub server suitable for…]]></description>
										<content:encoded>&lt;p&gt;Back in February, I applied for &lt;a href="https://nlnet.nl/NGI0/"&gt;NLnet's Next Generation Zero grant&lt;/a&gt;. They were looking for Fediverse projects to help rewild the social media landscape. Or, as they describe it:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Reclaim the public nature of the internet&lt;/p&gt;

&lt;p&gt;Small and medium-sized R&amp;amp;D grants between 5.000 and 50.000 euro, with the possibility to scale up.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;I run &lt;a href="https://gitlab.com/edent/activity-bot"&gt;ActivityBot&lt;/a&gt; - it is a single-file ActivityPub server suitable for launching automated accounts and designed as a learning tool for those who want to understand how the protocol works. Several people have told me how useful it is, but I haven't had the time to make it better. So I decided to stick in a last-minute application to the fund.&lt;/p&gt;

&lt;p&gt;I really didn't know how much to apply for - or even if my project would be suitable for funding - so I cheekily asked for €10,000. After a few months of back-and-forth, I'm delighted to announce that I was successful!&lt;/p&gt;

&lt;p&gt;In the spirit of openness, this blog post details how the NLnet grant process worked for me and what I'll be using the money for.&lt;/p&gt;

&lt;img src="https://shkspr.mobi/blog/wp-content/uploads/2026/08/NGI-logos.webp" alt="NLnet logo." width="900" height="200" class="aligncenter"&gt;

&lt;h2 id="the-process"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#the-process"&gt;The Process&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The application was delightfully simple. Here's what it asked for, along with my answers. If you apply, please don't copy these verbatim; use your own words.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Abstract    : A single file server for ActivityPub. Designed for write-only bots. Allows any project to quickly and easily start publishing automated content to the Fediverse. Uses PHP, no other dependencies.
&lt;/li&gt;&lt;li&gt;Experience  : I am the sole developer of Single File ActivityPub - https://gitlab.com/edent/activitypub-single-php-file&lt;br&gt;I was formerly the UK Government's representative to the W3C and have contributed to various ActivityPub projects and specifications.
&lt;/li&gt;&lt;li&gt;Amount      : € 10000
&lt;/li&gt;&lt;li&gt;Use         : The fund will be used for development, testing, promotional activity (including conference travel).&lt;br&gt;I anticipate this will fund 6 months of development. I have funded all previous development.&lt;br&gt;
&lt;/li&gt;&lt;li&gt;Comparison  : Most ActivityPub services are complex. They implement a full specification and are designed for multi-user environments. Other projects allow reading and writing. ActivityBot is deliberately designed to be as simple as possible. A single file to upload, one user, publish only.&lt;br&gt;This will enable more projects to be able to instantly start publishing with low development cost and close to zero hosting cost.
&lt;/li&gt;&lt;li&gt;Challenges  : Formal spec verification and a security audit will be the main technical challenges. The ActivityBot software has been running well for over a year. The funding will allow for better compatibility and security.
&lt;/li&gt;&lt;li&gt;Ecosystem   : The project has mostly targeted individuals who want to run small bots. After further development, the project will engage with IoT providers, smaller publishers, open source projects who wish to publish updates, and other relevant parties.
&lt;/li&gt;&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was told there was intense competition. After a couple of months, I received word that I'd made it to the 2nd round.&lt;/p&gt;

&lt;p&gt;What then followed was a &lt;em&gt;very&lt;/em&gt; polite interrogation about my ideas, how I would develop the project, what I would use the money for, and what my AI usage policy was.  They also wanted a breakdown of the main tasks - with the understanding that this would be a provisional document subject to change.&lt;/p&gt;

&lt;p&gt;I was on &lt;a href="https://shkspr.mobi/blog/2026/07/another-ridiculous-interrail-holiday-6379km-and-13-countries-over-7-weeks/"&gt;a train through Europe&lt;/a&gt; when I wrote this. I don't claim it to be a brilliant document - but it got the job done!&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;1. User Research

&lt;/p&gt;&lt;p&gt;Recruit 2 - 5 potential users. Offer an incentive (approx £20ea) to participate in a user research session. Study design will take 1 - 2 days. Each interview and write up to take 1 day. Consolidation and report 2 days.

&lt;/p&gt;&lt;p&gt;Total effort 3 - 4 weeks.

&lt;/p&gt;&lt;p&gt;2. Standards Research

&lt;/p&gt;&lt;p&gt;Participate in ActivityPub user communities and standardisation groups. Attend virtual conferences (or any local to the UK). Approx 1 day per week for 6 months.

&lt;/p&gt;&lt;p&gt;3. Test Driven Development

&lt;/p&gt;&lt;p&gt;Create modern test harness, write test suite, iterate design based on tests. Anticipated effort 2 days per week for approx 3 months.

&lt;/p&gt;&lt;p&gt;4. Security Testing

&lt;/p&gt;&lt;p&gt;Work with the community and security professionals to test the resultant code. This will use human testers and normal fuzzers - this will not use AI tools. Anticipated effort 2 days per week for approx 2 months.

&lt;/p&gt;&lt;p&gt;5. User Acceptance Testing

&lt;/p&gt;&lt;p&gt;Recruit 2 - 5 potential users (ideally different to the research participants). Offer an incentive (approx £20ea) to participate in a user acceptance session. Study design will take 1 - 2 days. Each interview and write up to take 1 day. Consolidation and report 2 days.

&lt;/p&gt;&lt;p&gt;Total effort 3 - 4 weeks.

&lt;/p&gt;&lt;p&gt;6. Updates Based on Research, Testing, and Security

&lt;/p&gt;&lt;p&gt;While it would be lovely to anticipate getting everything right first time, the reality is that changes will need to be made based on the findings of the above. This will take up the remainder of the allocated time.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Again, there was a little more back and forth. But a few weeks later I was informed that I was at the final stage, pending review. And, a few weeks after that, I was told my project had been given the green light.&lt;/p&gt;

&lt;p&gt;I was invited to a group call where the very friendly team discussed the practicalities of the grant, what it could and couldn't fund. I also met a bunch of other people who'd also won.&lt;/p&gt;

&lt;p&gt;The final stage was writing a proper Memorandum of Understanding. With the help of one of the team (thanks Victoria!) I was able to turn my scrappy plan into something a bit more formal. The project tool NLnet uses made it easy to build up a plan and put € amounts by each task.&lt;/p&gt;

&lt;p&gt;The idea is that I will invoice against the grant whenever I have completed a task or sub-task. Obviously I don't want to leave invoicing until the end of the project, but I also need to be mindful of the foreign exchange fees charged by my bank for receiving Euro payments.&lt;/p&gt;

&lt;h2 id="final-project-plan"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#final-project-plan"&gt;Final Project Plan&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;This is the plan I submitted. It represents what I hope to accomplish and how I'll draw down on the grant. I suspect this will change as the months go on.&lt;/p&gt;

&lt;hr&gt;

&lt;p&gt;ActivityBot is an Open Source project which aims to develop, maintain, and improve a minimum viable ActivityPub server in a single PHP file.&lt;/p&gt;

&lt;p&gt;The project is run by Terence Eden (trading as @edent); a developer residing in England.&lt;/p&gt;

&lt;p&gt;This project is expected to run for approximately 6 months. All of the deliverables will be openly licenced using either an OSI approved software licence or a Creative Commons licence.&lt;/p&gt;

&lt;p&gt;The high-level aims of the project are for ActivityBot to be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;A fully compliant ActivityPub server, running in a single PHP file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A teaching tool to help developers understand the practical aspects of creating an ActivityPub server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A practical method of publishing automated messages to the Fediverse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A promotional tool to show how simple and easy ActivityPub development can be.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A secure and usable tool written in modern PHP.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Written by humans, with no AI/LLM generated code.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In light of NLnet's non-profit status, costs assume a discounted rate of €330 per day (£280). Incidentals such as hardware, software, travel, or sundries will be charged at cost with receipts provided.&lt;/p&gt;

&lt;h2 id="prepare-for-initial-release"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#prepare-for-initial-release"&gt;Prepare for initial release&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Ensure that the project is in a suitable state for initial release and future development.&lt;/p&gt;

&lt;p&gt;Deliverable: Updates published to GitLab.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;€495 Prepare initial release. Clarify licencing, solicit community engagement, include example usage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€495 Standards Research. Collation of standards websites. Ensure code comments refer to specific standards. Publish blog post(s) about findings for others to reference.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="user-research"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#user-research"&gt;User Research&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Recruit up to 10 participants for a user-research study. Participants should represent the diversity of the Fediverse.&lt;/p&gt;

&lt;p&gt;Investigate what participants want from a tool like ActivityPub. The project plan may be adapted following the results of this study.&lt;/p&gt;

&lt;p&gt;Deliverable: Study plan and results will be published and given a Creative Commons licence. Changes based on the results will be pushed to GitLab.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;€660 Study Design and recruitment of participants (blog post published as deliverable).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€990 Two days of user interviews, write up and publish results as blog post.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="test-driven-development"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#test-driven-development"&gt;Test Driven Development&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Create a modern test harness, write test suite, iterate design based on tests.&lt;/p&gt;

&lt;p&gt;Deliverable: Tests published to GitLab. Blog posts published about the process and results.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;€330 Set up test suite&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Write tests&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Fixes based on test results&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="security-testing"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#security-testing"&gt;Security Testing&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Working with NLnet's security offering, ensure that the project meets modern security requirements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;€720 Work with security team to assess security risks and possible mitigations. Fixes based on security team feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deliverable: Updates published to GitLab. Blogs published about the process and results.&lt;/p&gt;

&lt;h2 id="user-acceptance-testing"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#user-acceptance-testing"&gt;User Acceptance Testing&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Recruit up to 10 participants for a user-acceptance study. Participants should represent the diversity of the Fediverse.&lt;/p&gt;

&lt;p&gt;Investigate whether participants are able to use ActivityBot. See which aspects need improvement. The project plan may be adapted following the results of this study.&lt;/p&gt;

&lt;p&gt;Deliverable: Study plan and results will be published and given a Creative Commons licence. Changes based on feedback will be published to GitLab.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;€660 Study Design and recruitment of participants (blog post published as deliverable).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€990 Two days of user interviews, write up and publish results as blog post.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="conferences-and-standards-work"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#conferences-and-standards-work"&gt;Conferences and Standards Work&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Open Source participation often depends on attending conferences, either in person or virtually. Getting involved in the standardisation process ensures that future versions of ActivityPub and associated standards will be suitable for the community.&lt;/p&gt;

&lt;p&gt;Deliverables: Presentations material (slideware), speaking at conferences (may be published as video), conference outputs. Where possible, these will be available under a suitable Creative Commons licence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;€700 Travel and accommodation to one EU conference&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Publishing blog posts about ActivityPub standards work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Publishing blog posts about ActivityPub standards work.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="final-release"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#final-release"&gt;Final release&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Creating a final release for this phase of the ActivityBot project. This will involve incorporating all feedback received so far, improving documentation, and publishing code.&lt;/p&gt;

&lt;p&gt;Deliverable: Updates published to GitLab. Blog post written. Release announcements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;€330 Phase 1: Process and implement feedback from users&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Phase 2: Bug fixes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Phase 3: Features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Phase 4: Bug fixes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Phase 5: Features&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Phase 6: Remedial work&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Process and implement feedback from accessibility scan&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;€330 Final release&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="next-steps"&gt;&lt;a href="https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/#next-steps"&gt;Next Steps&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;I've already begun work on updating the code. If you'd like to get involved, or have suggestions or bug reports - please &lt;a href="https://gitlab.com/edent/activity-bot"&gt;take a look at ActivityBot on GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll be putting out a call for user-research participants once I've had a chance to catch my breath 😆&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=74406&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/08/activitybot-is-the-recipient-of-an-nlnet-grant/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Scattered thoughts on social geolocation]]></title>
		<link>https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/</link>
					<comments>https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/#comments</comments>
				<dc:creator><![CDATA[Terence Eden]]></dc:creator>
		<pubDate>Wed, 22 Jul 2026 11:34:12 +0000</pubDate>
		<category><![CDATA[ActivityPub]]></category><category><![CDATA[BlueSky]]></category><category><![CDATA[fediverse]]></category><category><![CDATA[FourSquare]]></category><category><![CDATA[GeoLocation]]></category><category><![CDATA[JSON]]></category><category><![CDATA[Open Standards]]></category><category><![CDATA[OpenStreetMap]]></category>		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=73050</guid>

					<description><![CDATA[I want to be able to share my location with my friends on social media. Twitter (RIP) had a way to attach an optional location to a post. Facebook still lets me check in to venues. But neither ActivityPub (Mastodon) nor AT Protocol (BlueSky) allow me to do that.  I&#039;ve been banging on about this for a while so was delighted to be invited to chat with the GeoSocial Task Force on their monthly call. …]]></description>
										<content:encoded>&lt;p&gt;I want to be able to &lt;a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/"&gt;share my location with my friends&lt;/a&gt; on social media. Twitter (RIP) had a way to attach an optional location to a post. Facebook still lets me check in to venues. But neither ActivityPub (Mastodon) nor AT Protocol (BlueSky) allow me to do that.&lt;/p&gt;

&lt;p&gt;I've been &lt;a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/"&gt;banging on about this for a while&lt;/a&gt; so was delighted to be invited to chat with the &lt;a href="https://github.com/swicg/geosocial"&gt;GeoSocial Task Force&lt;/a&gt; on &lt;a href="https://www.w3.org/events/meetings/ed630a3d-7581-4053-9978-75949ad42f2a/"&gt;their monthly call&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But, before I start, please remember that these proposals are not mandatory. If you don't want to share your location, then please don't share your location. This is an optional feature which shouldn't be used by people with a strong need for privacy.&lt;/p&gt;

&lt;p&gt;We were primarily discussing the nascent &lt;a href="https://swicg.github.io/geosocial/microsyntax.html"&gt;Geographical Microsyntax proposals&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Firstly, I &lt;em&gt;love&lt;/em&gt; the idea of being able to share my location. I want my friends to know I'm in town for a gig. I want people to think I'm cool because I'm seeing some experimental theatre. I want to virtue signal that I'm giving blood.&lt;/p&gt;

&lt;p&gt;Similarly, I want to be able to find social media posts by location. What's happening right now in Paris? Did anyone else hear that sonic boom in London? Which cool people are also at this gig with me?&lt;/p&gt;

&lt;p&gt;But, I do think these proposals have some issues which need to be addressed.&lt;/p&gt;

&lt;h2 id="user-research"&gt;&lt;a href="https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/#user-research"&gt;User Research&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The primary thing missing from the proposal is any form of understanding what users want to do with a geotagged post.&lt;/p&gt;

&lt;p&gt;You see a post like:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Hanging out with friends. I just checked in to &lt;u&gt;Leicester Square&lt;/u&gt;.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;What do you expect to happen when you click that link?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See who is at the location?&lt;/li&gt;
&lt;li&gt;Read more information about the place being discussed?&lt;/li&gt;
&lt;li&gt;Find all posts at that specific address?&lt;/li&gt;
&lt;li&gt;Discover posts which are &lt;em&gt;close&lt;/em&gt; to there?&lt;/li&gt;
&lt;li&gt;Something else?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What other things do people want to do with geotagged posts?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find all posts within 5Km of me?&lt;/li&gt;
&lt;li&gt;Discover users who have posted near me?&lt;/li&gt;
&lt;li&gt;Get emergency alerts for the city I'm in?&lt;/li&gt;
&lt;li&gt;See what's trending in a specific country?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without knowing what people might want to do with this information, it's rather hard to design a service which meets their needs.&lt;/p&gt;

&lt;h2 id="to-micro-or-not-to-micro"&gt;&lt;a href="https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/#to-micro-or-not-to-micro"&gt;To micro or not to micro?&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Where I disagree with the proposals is the idea of using inline text as a form of microsyntax.&lt;/p&gt;

&lt;p&gt;Proposals like "I am in /London/ eating ice-cream" or "Checked in to Leicester Square L:N123456" fail for a couple of reasons.&lt;/p&gt;

&lt;p&gt;Things like #hashtags and @mentions developed organically on social networks before they were eventually adopted by the platforms themselves. It is rare that a top-down diktat can be used to tell people how they &lt;em&gt;should&lt;/em&gt; be formatting their posts.&lt;/p&gt;

&lt;p&gt;More importantly, people are &lt;em&gt;crap&lt;/em&gt; at formatting things consistently! Every event I go to has people using &lt;code&gt;#Event&lt;/code&gt; alongside &lt;code&gt;#Event2026&lt;/code&gt; or &lt;code&gt;#Event26&lt;/code&gt; or a hundred variations.&lt;/p&gt;

&lt;p&gt;Even if users could format it consistently, names are ambiguous. Is &lt;code&gt;🌐:Paris&lt;/code&gt; the one in France or &lt;a href="https://en.wikipedia.org/wiki/Paris,_Texas_(film)"&gt;Texas&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;I'm strongly of the opinion that text is for text, not syntax.&lt;/p&gt;

&lt;h2 id="openness-%f0%9f%86%9a-centralisation"&gt;&lt;a href="https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/#openness-%f0%9f%86%9a-centralisation"&gt;Openness 🆚 Centralisation&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Saying that you're on a specific point on the globe doesn't always provide useful information. Even with altitude, it isn't always possible to work out if your in the Starbucks or the Costa next door. Do you want to say you're in a train station, or a distinct platform, or even a specific train journey?&lt;/p&gt;

&lt;p&gt;There are various services which offer unique IDs per loosely-defined place.&lt;/p&gt;

&lt;p&gt;Google Maps provides a &lt;a href="https://developers.google.com/maps/documentation/places/web-service/place-id"&gt;Place ID&lt;/a&gt; for every thing that it knows about. There's a similar service from &lt;a href="https://foursquare.com/products/places-api/"&gt;FourSquare&lt;/a&gt;.  But both of those are closed and proprietary systems - they can only be updated by the company that creates them.&lt;/p&gt;

&lt;p&gt;Wikipedia's Wikidata has &lt;a href="https://www.wikidata.org/wiki/Q676755"&gt;IDs for lots of places&lt;/a&gt;, similarly OpenStreetMap has &lt;a href="https://wiki.openstreetmap.org/wiki/Key:place"&gt;nodes with names&lt;/a&gt; which can be used to identify locations.&lt;/p&gt;

&lt;p&gt;But all of these suffer from the same flaw; they are centralised.&lt;/p&gt;

&lt;p&gt;If &lt;a href="https://killedbygoogle.com/"&gt;Google kills its service&lt;/a&gt; then all those Place IDs die.&lt;/p&gt;

&lt;p&gt;This has happened before. Yahoo used to run the &lt;a href="https://en.wikipedia.org/wiki/WOEID"&gt;Where On Earth IDentifier&lt;/a&gt; service but discontinued it.&lt;/p&gt;

&lt;p&gt;While OSM and Wikipedia are great projects, they're still centralised. If you're banned from OSM, you can't create a new node.&lt;/p&gt;

&lt;p&gt;So, to my mind, the &lt;em&gt;primary&lt;/em&gt; way of identifying a place has to use a fully open, globally agreed, and unrestricted standard.&lt;/p&gt;

&lt;h2 id="location-plus-plus"&gt;&lt;a href="https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/#location-plus-plus"&gt;Location Plus Plus&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;The obvious choice is latitude and longitude as defined by &lt;a href="https://www.linz.govt.nz/guidance/geodetic-system/coordinate-systems-used-new-zealand/geodetic-datums/world-geodetic-system-1984-wgs84"&gt;WGS 84&lt;/a&gt;. It allows for arbitrary precision anywhere on the planet.&lt;/p&gt;

&lt;p&gt;No one can stop you issuing lat/long just because a country is under sanctions. The standards community can't revoke your access to it. The co-ordinates are well understood by almost all software.&lt;/p&gt;

&lt;p&gt;There are some alternatives. Google's &lt;a href="https://maps.google.com/pluscodes/"&gt;Plus.Codes&lt;/a&gt; build on top of lat/long to make something more human readable. But, crucially, human readability is &lt;em&gt;not&lt;/em&gt; the issue here. This is metadata which should never be shown to a human. If a user interface wants to format a lat/long they can do so as a Plus.Code or any other format.&lt;/p&gt;

&lt;h2 id="geojson"&gt;&lt;a href="https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/#geojson"&gt;GeoJSON&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;There are dozens of &lt;a href="https://wiki.ietf.org/en/group/geopriv/LocationFormats"&gt;competing ways to mark up a location&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It seems sensible to me that, given most major social media protocols use JSON, adding &lt;a href="https://geojson.org/"&gt;GeoJSON&lt;/a&gt; would be the simplest way to add geographic information to a post's metadata.&lt;/p&gt;

&lt;p&gt;At its most basic, a single "Point" can be shared like so:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-json"&gt;{
    "type": "Feature",
    "geometry": {
        "type": "Point",
        "coordinates": [-0.13005,51.5103]
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It can have as much or as little precision as needed.&lt;/p&gt;

&lt;p&gt;If the place being checked into has a name, it can also be added - this could be used for display purposes within the message&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-json"&gt;{
    "type": "Feature",
    "geometry": {
        "type": "Point",
        "coordinates": [-0.13005,51.5103]
    },
    "properties": {
        "name": "Leicester Square"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;properties&lt;/code&gt; can be as complex as the sender wants. For example it could add Wikidata references, OpenStreetMap IDs, telephone numbers, or some cool new thing which hasn't been invented yet.&lt;/p&gt;

&lt;p&gt;Here's a maximalist example of checking in to a park:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-json"&gt;{
    "type": "Feature",
    "geometry": {
        "type": "Polygon",
        "coordinates": [
        [
                [-0.1303791,51.5099976],
                [-0.1295305,51.5100444],
                [-0.1297077,51.510947 ],
                [-0.1308786,51.5105859],
                [-0.1308893,51.5105793],
                [-0.1303791,51.5099976]
            ]
        ]
    },
    "properties": {
        "name": "Leicester Square",
        "WikiData": "Q848912",
        "OSM": "4082589",
        "GooglePlaceID": "ChIJPcTFENIEdkgR94E58rgB69o",
        "PlusCode": "9C3XGV69+4X",
        "Quinfrob": "🌐89451_𰻝",
        "address": {
            "road": "Leicester Square",
            "neighbourhood": "St. James's",
            "quarter": "East Marylebone",
            "suburb": "Covent Garden",
            "city": "City of Westminster",
            "ISO3166-2-lvl8": "GB-WSM",
            "state": "England",
            "ISO3166-2-lvl4": "GB-ENG",
            "postcode": "WC2H 7NA",
            "country": "United Kingdom",
            "country_code": "gb"
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="whats-next"&gt;&lt;a href="https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/#whats-next"&gt;What's Next&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;My personal opinion is that the following needs to happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Talk to some users - find out what they might use this for.&lt;/li&gt;
&lt;li&gt;Work with privacy advocates to reduce possible harms (for example, being able to redact a location from a previously shared post).&lt;/li&gt;
&lt;li&gt;Discuss with developers about how they'd work with this metadata.&lt;/li&gt;
&lt;li&gt;Clients should start adding GeoJSON metadata to their posts, even if it can't be displayed yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can &lt;a href="https://hedgedoc.socialweb.coop/hNLDxDqoQPaFlOf4aJ4E8g?view"&gt;read the minutes of the meeting&lt;/a&gt; and I'd encourage you to &lt;a href="https://www.w3.org/events/meetings/ed630a3d-7581-4053-9978-75949ad42f2a/"&gt;join the next call&lt;/a&gt;.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=73050&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/07/scattered-thoughts-on-social-geolocation/feed/</wfw:commentRss>
			<slash:comments>15</slash:comments>
		
		
			</item>
		<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[Terence Eden]]></dc:creator>
		<pubDate>Sun, 23 Nov 2025 12:34:35 +0000</pubDate>
		<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>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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?&lt;/p&gt;

&lt;p&gt;Earlier this year &lt;a href="https://shkspr.mobi/blog/2025/09/reasonably-accurate-privacy-conscious-cookieless-visitor-tracking-for-wordpress/"&gt;I built a stats-counter for my blog&lt;/a&gt;. 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.&lt;/p&gt;

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

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

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

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

&lt;p&gt;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.&lt;/p&gt;

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

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

&lt;p&gt;Now, there are some obvious caveats to the data:&lt;/p&gt;

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

&lt;p&gt;And yet… no matter how you slice it, Fediverse servers are sending as much traffic as BlueSky!&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Just for completeness, this is how Reddit, Facebook, LinkedIn, Twitter, and Lemmy do over the same period:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Twitter is basically &lt;a href="https://en.wikipedia.org/wiki/Dead_Internet_theory"&gt;the Dead Internet&lt;/a&gt;. 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 &lt;em&gt;lot&lt;/em&gt; - sometimes by accounts with huge followers - yet there are no comments or retweets and barely and clicks.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;Firstly, my stats only represent my site. Your site might be very different.&lt;/p&gt;

&lt;p&gt;Secondly, I've ignored search engine traffic, big blogs, newsletters, and other sources.&lt;/p&gt;

&lt;p&gt;Thirdly, and most importantly, this &lt;em&gt;isn't&lt;/em&gt; 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.&lt;/p&gt;

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

&lt;p&gt;Diversity is strength.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63716&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2025/11/now-witness-the-power-of-this-fully-operational-fediverse/feed/</wfw:commentRss>
			<slash:comments>11</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[Terence Eden]]></dc:creator>
		<pubDate>Fri, 03 Oct 2025 11:34:27 +0000</pubDate>
		<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>&lt;p&gt;Quoting posts on Mastodon is &lt;em&gt;slightly&lt;/em&gt; complex. Because of the privacy conscious nature of the platform and its users, reposting isn't merely a case of sharing a URl.&lt;/p&gt;

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

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

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

&lt;p&gt;I'm going to walk you through each stage as best as I understand them.&lt;/p&gt;

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

&lt;p&gt;An ActivityPub status message is JSON. In order to opt-in, it needs this additional field.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-JSON"&gt;{
  "@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"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;All this says is "I would like permission to quote you."&lt;/p&gt;

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

&lt;p&gt;The quoted server needs to approve this quote. First, it generates a "stamp".&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://socialhub.activitypub.rocks/t/quote-post-implementation-issues/8032/2?u=eden_t"&gt;stamp's ID is requested the stamp &lt;em&gt;must&lt;/em&gt; be returned&lt;/a&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-JSON"&gt;{
  "@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"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If the quoted status is viewed from a different server, that server will query the stamp to make sure the share is allowed.&lt;/p&gt;

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

&lt;p&gt;This is the message that the quoted server sends to the quoting server. It references the request and the stamp.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-JSON"&gt;{
  "@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"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The "result" &lt;em&gt;must&lt;/em&gt; be the same as the stamp's URl.&lt;/p&gt;

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

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

&lt;p&gt;I've written an ActivityPub server in a single file which is designed to teach you have the protocol works. Have a play with &lt;a href="https://gitlab.com/edent/activity-bot"&gt;ActivityBot&lt;/a&gt;.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63527&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Sat, 14 Jun 2025 11:34:11 +0000</pubDate>
		<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>&lt;p&gt;There's a lovely moment in &lt;a href="https://www.imdb.com/title/tt2608732/"&gt;the documentary about The Pirate Bay&lt;/a&gt; where Peter Sunde is being interviewed in a District Court:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;Prosecutor 1: When was the first time you met IRL?&lt;/p&gt;

&lt;p&gt;brokep: We don't use the expression IRL. We say AFK. But that's another issue.&lt;/p&gt;

&lt;p&gt;Prosecutor 2: Got to know each other IRL? What is that?&lt;/p&gt;

&lt;p&gt;Prosecutor 1: In Real Life.&lt;/p&gt;

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

&lt;p&gt;Isn't that great? Why do some people insist that online relationships are somehow less real than physical relationships?&lt;/p&gt;

&lt;p&gt;As part of our recent &lt;a href="https://shkspr.mobi/blog/2025/06/5025-km-21-journeys-and-10-countries-in-30-days-an-interrailing-adventure/"&gt;grand Interrail journey&lt;/a&gt;, 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.&lt;/p&gt;

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;lovely&lt;/em&gt;. We got tips on how to use the public transport system, which restaurants were tourist traps, and introduced to new beers.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;too&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;Sometimes you want to go where at least one person knows your name.&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

&lt;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"&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=61215&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Sat, 14 Dec 2024 12:34:25 +0000</pubDate>
		<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>&lt;p&gt;Back in 2022, I wrote this rather grumpy post on Mastodon, the federated social media platform.&lt;/p&gt;

&lt;blockquote class="social-embed" id="social-embed-109323917419768019" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"&gt;&lt;header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"&gt;&lt;a href="https://mastodon.social/@Edent" class="social-embed-user" itemprop="url"&gt;&lt;img class="social-embed-avatar" src="https://files.mastodon.social/accounts/avatars/000/007/112/original/37df032a5951b96c.jpg" alt="" itemprop="image"&gt;&lt;div class="social-embed-user-names"&gt;&lt;p class="social-embed-user-names-name" itemprop="name"&gt;@Edent@mastodon.social&lt;/p&gt;Terence Eden&lt;/div&gt;&lt;/a&gt;&lt;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"&gt;&lt;/header&gt;&lt;section class="social-embed-text" itemprop="articleBody"&gt;&lt;p&gt;Mastodon enforces a "noreferrer" on all external links.&lt;/p&gt;&lt;p&gt;I have mixed feelings about that.&lt;/p&gt;&lt;p&gt;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.&lt;/p&gt;&lt;p&gt;But, I get that people want privacy and don't want to "leak" where they're visiting from.&lt;/p&gt;&lt;p&gt;Is it such a bad thing to tell a website "I was referred from this specific server"?&lt;/p&gt;&lt;div class="social-embed-media-grid"&gt;&lt;/div&gt;&lt;/section&gt;&lt;hr class="social-embed-hr"&gt;&lt;footer class="social-embed-footer"&gt;&lt;a href="https://mastodon.social/@Edent/109323917419768019"&gt;&lt;span aria-label="61 likes" class="social-embed-meta"&gt;❤️ 61&lt;/span&gt;&lt;span aria-label="16 replies" class="social-embed-meta"&gt;💬 16&lt;/span&gt;&lt;span aria-label="29 reposts" class="social-embed-meta"&gt;🔁 29&lt;/span&gt;&lt;time datetime="2022-11-11T07:09:55.396Z" itemprop="datePublished"&gt;07:09 - Fri 11 November 2022&lt;/time&gt;&lt;/a&gt;&lt;/footer&gt;&lt;/blockquote&gt;

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

&lt;p&gt;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 &lt;em&gt;could&lt;/em&gt; be bad for much the same reason.&lt;/p&gt;

&lt;p&gt;If you run a server &lt;code&gt;anarcho_terrorists.biz&lt;/code&gt;, 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.&lt;/p&gt;

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

&lt;p&gt;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:&lt;/p&gt;

&lt;blockquote class="social-embed" id="social-embed-113611619218784737" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"&gt;&lt;header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"&gt;&lt;a href="https://mastodon.social/@Edent" class="social-embed-user" itemprop="url"&gt;&lt;img class="social-embed-avatar" src="https://files.mastodon.social/accounts/avatars/000/007/112/original/37df032a5951b96c.jpg" alt="" itemprop="image"&gt;&lt;div class="social-embed-user-names"&gt;&lt;p class="social-embed-user-names-name" itemprop="name"&gt;@Edent@mastodon.social&lt;/p&gt;Terence Eden&lt;/div&gt;&lt;/a&gt;&lt;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"&gt;&lt;/header&gt;&lt;section class="social-embed-text" itemprop="articleBody"&gt;&lt;p&gt;Two years later.&lt;/p&gt;&lt;p&gt;Want to know one of the major reasons Mastodon didn't catch on with journalists and large website owners?&lt;/p&gt;&lt;p&gt;It is *invisible* in referrer statistics.&lt;/p&gt;&lt;p&gt;Here's my blog from the last month.&lt;/p&gt;&lt;p&gt;BlueSky now sends me more traffic than Bing.&lt;/p&gt;&lt;p&gt;How much traffic does Mastodon send? It is impossible to know due to the "noreferrer" header in all links.&lt;/p&gt;&lt;p&gt;(I'm not saying your privacy isn't important. But you can't grow a community if no-one knows you exist.)&lt;/p&gt;&lt;div class="social-embed-media-grid"&gt;&lt;a href="https://files.mastodon.social/media_attachments/files/113/611/599/519/383/213/original/f32f26cb4a0f015a.png" class="social-embed-media-link"&gt;&lt;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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"&gt;&lt;/a&gt;&lt;/div&gt;&lt;/section&gt;&lt;hr class="social-embed-hr"&gt;&lt;footer class="social-embed-footer"&gt;&lt;a href="https://mastodon.social/@Edent/113611619218784737"&gt;&lt;span aria-label="305 likes" class="social-embed-meta"&gt;❤️ 305&lt;/span&gt;&lt;span aria-label="57 replies" class="social-embed-meta"&gt;💬 57&lt;/span&gt;&lt;span aria-label="248 reposts" class="social-embed-meta"&gt;🔁 248&lt;/span&gt;&lt;time datetime="2024-12-07T12:48:52.705Z" itemprop="datePublished"&gt;12:48 - Sat 07 December 2024&lt;/time&gt;&lt;/a&gt;&lt;/footer&gt;&lt;/blockquote&gt;

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

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

&lt;p&gt;This is &lt;em&gt;excellent&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;It is, of course, slightly more complicated than that!&lt;/p&gt;

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

&lt;p&gt;Nevertheless, I'm delighted with this change. Hopefully it will allow the Fediverse to grow and attract more users.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=54457&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/12/mastodon-now-sends-referer-headers-hurrah/feed/</wfw:commentRss>
			<slash:comments>5</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[Terence Eden]]></dc:creator>
		<pubDate>Sun, 24 Nov 2024 12:34:05 +0000</pubDate>
		<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>&lt;p&gt;Mathew Duggan has a brilliant post called "&lt;a href="https://matduggan.com/self-hosting-isnt-a-solution-its-a-patch/"&gt;Self-Hosting Isn't a Solution; It's A Patch&lt;/a&gt;". 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.&lt;/p&gt;

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

&lt;p&gt;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 "&lt;a href="https://www.brookings.edu/articles/7-myths-about-defunding-the-police-debunked/"&gt;Move funds away from miliary style policing and give it to trained mental health professionals&lt;/a&gt;" - what people &lt;em&gt;hear&lt;/em&gt; is "Abolish the police and let anarchy reign".&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I agree with &lt;a href="https://matduggan.com/self-hosting-isnt-a-solution-its-a-patch/"&gt;nearly everything Mathew says in his post&lt;/a&gt;. 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;love&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;What do you think?&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=54146&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Thu, 14 Nov 2024 12:34:32 +0000</pubDate>
		<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>&lt;p&gt;If you've found this page, it's because you are me in the future and want to remember these instructions!&lt;/p&gt;

&lt;ol start="0"&gt;
&lt;li&gt;Create an account on the Fediverse using a domain you control

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

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

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

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

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

&lt;p&gt;You can see that &lt;a href="https://bot.viii.fi/bot"&gt;https://bot.viii.fi/bot&lt;/a&gt; is also available at &lt;a href="https://bsky.app/profile/bot.viii.fi"&gt;https://bsky.app/profile/bot.viii.fi&lt;/a&gt;&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53849&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Sun, 10 Nov 2024 12:34:00 +0000</pubDate>
		<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>&lt;p&gt;As you may have read, &lt;a href="https://muffinlabs.com/posts/2024/10/29/10-29-rip-botsin-space/"&gt;BotsIn.Space is closing down&lt;/a&gt;, 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.&lt;/p&gt;

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

&lt;p&gt;I took the code and stripped it down to the bare essentials. All you need to do is upload two files&lt;sup id="fnref:env"&gt;&lt;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"&gt;0&lt;/a&gt;&lt;/sup&gt; - &lt;code&gt;index.php&lt;/code&gt; and &lt;code&gt;.htaccess&lt;/code&gt; - fill in your details, and you're done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/edent/activity-bot/"&gt;Get the ActivityBot source code on GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There's no database, no containers, no caching. It is as simple as I could make it&lt;/p&gt;

&lt;p&gt;This bot can do the following:&lt;/p&gt;

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

&lt;p&gt;That's it! Here's what it &lt;em&gt;doesn't&lt;/em&gt; do:&lt;/p&gt;

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

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

&lt;p&gt;You can follow one of my bots &lt;code&gt;@colours@colours.bots.edent.tel&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Feedback very much welcome.&lt;/p&gt;

&lt;div id="footnotes" role="doc-endnotes"&gt;
&lt;hr aria-label="Footnotes"&gt;
&lt;ol start="0"&gt;

&lt;li id="fn:env"&gt;
&lt;p&gt;You can also upload a &lt;code&gt;.env&lt;/code&gt; file for your configuration if you want.&amp;nbsp;&lt;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"&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;
&lt;/div&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=53728&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Mon, 09 Sep 2024 11:34:07 +0000</pubDate>
		<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>&lt;p&gt;Several years ago, I posted this poll on Twitter.&lt;/p&gt;

&lt;blockquote class="social-embed" id="social-embed-1286178187937042432" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"&gt;&lt;header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"&gt;&lt;a href="https://twitter.com/edent" class="social-embed-user" itemprop="url"&gt;&lt;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"&gt;&lt;div class="social-embed-user-names"&gt;&lt;p class="social-embed-user-names-name" itemprop="name"&gt;Terence Eden is on Mastodon&lt;/p&gt;@edent&lt;/div&gt;&lt;/a&gt;&lt;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"&gt;&lt;/header&gt;&lt;section class="social-embed-text" itemprop="articleBody"&gt;If the recent Twitter hack had exposed they way you voted on every Twitter poll, how would you feel?&lt;br&gt;&lt;br&gt;(There is no suggestion that this has happened, I'm just curious about people's relationships to voting and privacy.)&lt;hr class="social-embed-hr"&gt;&lt;label for="poll_1_count"&gt;Meh. So what?: (167)&lt;/label&gt;&lt;br&gt;&lt;meter class="social-embed-meter" id="poll_1_count" min="0" max="100" low="33" high="66" value="60.7"&gt;167&lt;/meter&gt;&lt;br&gt;&lt;label for="poll_2_count"&gt;Hmph. That's annoying.: (68)&lt;/label&gt;&lt;br&gt;&lt;meter class="social-embed-meter" id="poll_2_count" min="0" max="100" low="33" high="66" value="24.7"&gt;68&lt;/meter&gt;&lt;br&gt;&lt;label for="poll_3_count"&gt;Umm… This could be bad!: (32)&lt;/label&gt;&lt;br&gt;&lt;meter class="social-embed-meter" id="poll_3_count" min="0" max="100" low="33" high="66" value="11.6"&gt;32&lt;/meter&gt;&lt;br&gt;&lt;label for="poll_4_count"&gt;Delete account &amp;amp; run away: (8)&lt;/label&gt;&lt;br&gt;&lt;meter class="social-embed-meter" id="poll_4_count" min="0" max="100" low="33" high="66" value="2.9"&gt;8&lt;/meter&gt;&lt;/section&gt;&lt;hr class="social-embed-hr"&gt;&lt;footer class="social-embed-footer"&gt;&lt;a href="https://twitter.com/edent/status/1286178187937042432"&gt;&lt;span aria-label="0 likes" class="social-embed-meta"&gt;❤️ 0&lt;/span&gt;&lt;span aria-label="8 replies" class="social-embed-meta"&gt;💬 8&lt;/span&gt;&lt;span aria-label="0 reposts" class="social-embed-meta"&gt;🔁 0&lt;/span&gt;&lt;time datetime="2020-07-23T05:55:50.000Z" itemprop="datePublished"&gt;05:55 - Thu 23 July 2020&lt;/time&gt;&lt;/a&gt;&lt;/footer&gt;&lt;/blockquote&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;So what about polls on the Fediverse?  This poll, for example, is gathering sensitive personal information.&lt;/p&gt;

&lt;blockquote class="social-embed" id="social-embed-113079948257450773" lang="en" itemscope="" itemtype="https://schema.org/SocialMediaPosting"&gt;&lt;header class="social-embed-header" itemprop="author" itemscope="" itemtype="https://schema.org/Person"&gt;&lt;a href="https://blackrock.city/@farooqkz" class="social-embed-user" itemprop="url"&gt;&lt;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"&gt;&lt;div class="social-embed-user-names"&gt;&lt;p class="social-embed-user-names-name" itemprop="name"&gt;@farooqkz@blackrock.city&lt;/p&gt;Farooq Karimi Zadeh&lt;/div&gt;&lt;/a&gt;&lt;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"&gt;&lt;/header&gt;&lt;section class="social-embed-text" itemprop="articleBody"&gt;&lt;p&gt;Let's see how many Muslims are out there on Fediverse. Are you a &lt;a href="https://blackrock.city/tags/muslim" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank"&gt;#&lt;span&gt;muslim&lt;/span&gt;&lt;/a&gt;? &lt;/p&gt;&lt;p&gt;&lt;a href="https://blackrock.city/tags/Islam" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank"&gt;#&lt;span&gt;Islam&lt;/span&gt;&lt;/a&gt; &lt;a href="https://blackrock.city/tags/Religion" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank"&gt;#&lt;span&gt;Religion&lt;/span&gt;&lt;/a&gt; &lt;a href="https://blackrock.city/tags/God" class="mention hashtag" rel="nofollow noopener noreferrer" target="_blank"&gt;#&lt;span&gt;God&lt;/span&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Please boost it so we can have more accurate statistics.&lt;/p&gt;&lt;div class="social-embed-media-grid"&gt;&lt;/div&gt;&lt;hr class="social-embed-hr"&gt;&lt;label for="poll_0"&gt;I am a Muslim: (62)&lt;/label&gt;&lt;br&gt;&lt;meter class="social-embed-meter" id="poll_0" min="0" max="100" low="33" high="66" value="1.6"&gt;62&lt;/meter&gt;&lt;br&gt;&lt;label for="poll_1"&gt;Not a Muslim: (3,696)&lt;/label&gt;&lt;br&gt;&lt;meter class="social-embed-meter" id="poll_1" min="0" max="100" low="33" high="66" value="98.4"&gt;3696&lt;/meter&gt;&lt;br&gt;&lt;/section&gt;&lt;hr class="social-embed-hr"&gt;&lt;footer class="social-embed-footer"&gt;&lt;a href="https://blackrock.city/@farooqkz/113079948160094406"&gt;&lt;span aria-label="11 likes" class="social-embed-meta"&gt;❤️ 11&lt;/span&gt;&lt;span aria-label="30 replies" class="social-embed-meta"&gt;💬 30&lt;/span&gt;&lt;span aria-label="874 reposts" class="social-embed-meta"&gt;🔁 874&lt;/span&gt;&lt;time datetime="2024-09-04T15:17:56.000Z" itemprop="datePublished"&gt;15:17 - Wed 04 September 2024&lt;/time&gt;&lt;/a&gt;&lt;/footer&gt;&lt;/blockquote&gt;

&lt;p&gt;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."&lt;/p&gt;

&lt;p&gt;Does the receiving server abide by GDPR? Who knows!&lt;/p&gt;

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

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

&lt;p&gt;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 &lt;a href="https://outerheaven.club/notice/Aln6q1bVGpyToIx7J2"&gt;the Akkoma social network &lt;em&gt;does&lt;/em&gt; show users how users voted&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Conduct yourself with that in mind!&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=52999&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Mon, 05 Aug 2024 11:34:01 +0000</pubDate>
		<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>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;massive&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;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?&lt;/p&gt;

&lt;p&gt;He was glum. "That's not how it works," he explained.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;Over the next few months our growth rocketed and almost none of it was organic.&lt;/p&gt;

&lt;p&gt;Eventually I left for pastures marginally more ethical than advertising. But I continue to see the same pattern repeat itself.&lt;/p&gt;

&lt;p&gt;When you wonder why one social network grows and another doesn't - look at the investors.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;And, again, I don't know if that's a bad thing &lt;i lang="la"&gt;per se&lt;/i&gt; - but if you're expecting big companies, governments, and your favourite VC-adjacent celebrities to join, you're likely to be disappointed.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=51194&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Sun, 07 Apr 2024 11:34:31 +0000</pubDate>
		<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>&lt;p&gt;One of the most frustrating things in modern technology is the effort spent trying to artificially restrict abundance.&lt;/p&gt;

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

&lt;blockquote&gt;&lt;p&gt;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.
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;This reminds me of the &lt;a href="https://www.bbc.co.uk/news/magazine-33560182"&gt;Melbourne "treemail" phenomenon&lt;/a&gt;. 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!&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;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
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;A few weeks ago, I read about &lt;a href="https://bensmith.blog/posts/i-invented-tweeting-trains-probably"&gt;Ben Smith inventing Tweeting trains&lt;/a&gt;. 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.&lt;/p&gt;

&lt;p&gt;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. &lt;a href="https://csa-iot.org/all-solutions/matter/"&gt;Matter&lt;/a&gt; is a confusing mishmash of standards.  Security and privacy issues dog the simplest devices. Many people don't even &lt;em&gt;want&lt;/em&gt; their toaster online!&lt;/p&gt;

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

&lt;p&gt;Which brings me back to the Fediverse.&lt;/p&gt;

&lt;p&gt;On the one hand, it would be nice to be able to follow &lt;code&gt;@Yellow_Line@Transit_Authority.gov&lt;/code&gt; - or even &lt;code&gt;@Bus_Stop_1234@bus_company.biz&lt;/code&gt; - that would allow for hyperfocused data getting to the right people.  It seems feasible that &lt;em&gt;every&lt;/em&gt; 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.&lt;/p&gt;

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

&lt;blockquote&gt;&lt;p&gt;Hey @thermostat@My_Home.example.com, please set the temperature to 19°C. Thanks!
&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;That might be a bit much! But I like the idea of a &lt;em&gt;private&lt;/em&gt; social network which consists of all my IoT gadgets talking to me and each other.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=50085&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Mon, 26 Feb 2024 12:34:28 +0000</pubDate>
		<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>&lt;p&gt;I'm trying to get my head round &lt;a href="https://www.rfc-editor.org/rfc/rfc9421.html"&gt;HTTP Signatures&lt;/a&gt; as they're used extensively in the Fediverse.&lt;/p&gt;

&lt;p&gt;Conceptually, they're relatively straightforward.&lt;/p&gt;

&lt;p&gt;You send me a normal HTTP request. For example, you want to POST something to &lt;code&gt;https://example.com/data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You send me these headers:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-_"&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In order to verify the contents of the message, I need to do three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the SHA-256 hash of the message matches the content of the "Digest" header.&lt;/li&gt;
&lt;li&gt;Check the timestamp is somewhat fresh.&lt;/li&gt;
&lt;li&gt;Check the signature matches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first is simple: &lt;code&gt;base64_encode( hash( "sha256", $request_body, true ) )&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The third gets &lt;em&gt;complicated&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First, I need to get the public key published at &lt;code&gt;keyId="https://your_website.biz/publicKey"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, I need to know which algorithm is being used to sign the headers: &lt;code&gt;algorithm="rsa-sha256"&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;So I create a string using the received details which matches those headers in that specific order:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-_"&gt;(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
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I can verify if the signature - &lt;code&gt;signature="JGQ53kEoIiMWRp9By9jajVGCOCu4n7XBeiA1uY5xLcnAxL2Y1GIgU/...=="&lt;/code&gt; matches by:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-php"&gt;openssl_verify(
    $headersString, 
    $signature, 
    $publicKey, 
    $algorithm
);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If that's &lt;code&gt;TRUE&lt;/code&gt; then all is well.&lt;/p&gt;

&lt;p&gt;But can you spot the implicit problem?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How&lt;/em&gt; do I get your server's public key?&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;I get why things happen this way - I just wish it were easier to implement!&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49728&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Sat, 17 Feb 2024 12:34:58 +0000</pubDate>
		<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>&lt;p&gt;We live in the future now. It is OK to use Unicode everywhere.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;foreign&lt;/em&gt; languages! And some of those languages have accents on their letters!! Even worse, some don't use English letters &lt;em&gt;at all!!!&lt;/em&gt;&lt;/p&gt;

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

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

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

&lt;p&gt;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 &lt;a href="https://mastodon.social/@Edent/111920759100955860"&gt;at the replies on Mastodon&lt;/a&gt; to see which services work.  You can also &lt;a href="https://fed.xnor.in/users/$Aet3ViWYORXdinGChM"&gt;see some of its posts on the Fediverse&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;The ActivityPub specification says:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The user's &lt;code&gt;@&lt;/code&gt; name is defined by &lt;code&gt;preferredUsername&lt;/code&gt; which is:&lt;/p&gt;

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

&lt;p&gt;There's nothing in there about what scripts it can contain. However, later on, the spec says:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

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

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

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

&lt;p&gt;There are usually a few objections to "Unicode Everywhere" zealots like me. I'd like to forestall any arguments.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

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

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

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

&lt;p&gt;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 &lt;code&gt;@😉&lt;/code&gt;&lt;/p&gt;

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

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

&lt;p&gt;If your software can see &lt;a href="https://i18n.viii.fi/.well-known/webfinger"&gt;&lt;code&gt;@你好@i18n.viii.fi&lt;/code&gt;&lt;/a&gt; and its posts, please let me know.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49643&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Sat, 03 Feb 2024 12:34:51 +0000</pubDate>
		<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>&lt;p&gt;I've written &lt;a href="https://github.com/edent/location-activitypub-symfony/"&gt;an ActivityPub server which &lt;em&gt;only&lt;/em&gt; allows you to post messages to your followers&lt;/a&gt;.  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.&lt;/p&gt;

&lt;p&gt;But it will happily send messages and allow itself to be followed.&lt;/p&gt;

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

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

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

&lt;p&gt;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.&lt;/p&gt;

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

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

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

&lt;p&gt;The structure is relatively straightforward.&lt;/p&gt;

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

&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;/send&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

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

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

&lt;ul&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;It posts plain text. It doesn't autolink websites&lt;/li&gt;
&lt;li&gt;Hashtags are linked when viewed remotely, but they don't go anywhere locally.&lt;/li&gt;
&lt;li&gt;There's no language selection - it is hard-coded to English.&lt;/li&gt;
&lt;li&gt;The outbox isn't paginated.&lt;/li&gt;
&lt;li&gt;The UI looks crap - but it is only me using it.&lt;/li&gt;
&lt;li&gt;There's only a basic front-page showing a map of all my check-ins.&lt;/li&gt;
&lt;li&gt;Replies are logged, but there's no easy way to see them.&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;li&gt;No way to handle being unfollowed.&lt;/li&gt;
&lt;li&gt;No way to remove servers which have died.&lt;/li&gt;
&lt;li&gt;Probably lots more.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I found these resources helpful while creating this project:&lt;/p&gt;

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

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

&lt;p&gt;I've &lt;a href="https://github.com/mastodon/mastodon/issues/29002"&gt;raised an issue on Mastodon&lt;/a&gt; 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.&lt;/p&gt;

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

&lt;p&gt;World domination?&lt;/p&gt;

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

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

&lt;p&gt;You can &lt;a href="https://location.edent.tel/"&gt;look at my check-ins on a map&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can follow my location on the Fediverse at &lt;code&gt;@edent_location@location.edent.tel&lt;/code&gt;&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49490&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Sat, 27 Jan 2024 12:34:55 +0000</pubDate>
		<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>&lt;p&gt;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".&lt;/p&gt;

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

&lt;p&gt;This quest is divided into two parts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get nearby "Points of Interest" (POI) from OpenStreetMap.&lt;/li&gt;
&lt;li&gt;Share a location on the Fediverse.&lt;/li&gt;
&lt;/ol&gt;

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

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

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

&lt;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"&gt;

&lt;p&gt;As you can see, it has highlighted &lt;em&gt;some&lt;/em&gt; useful areas - a pharmacy and a pub. But it has &lt;em&gt;ignored&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;What API call is needed to get &lt;em&gt;useful&lt;/em&gt; locations of of OverPass?&lt;/p&gt;

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

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

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

&lt;p&gt;Again, that would be complex.&lt;/p&gt;

&lt;p&gt;Perhaps one solution is just to return &lt;em&gt;everything&lt;/em&gt; 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.&lt;/p&gt;

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

&lt;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"&gt;

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

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

&lt;p&gt;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!&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;&lt;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;"&gt;&lt;code&gt;overpass-api.de/api/interpreter?data=[out:json];nw["name"][!"highway"][!"railway"][!"waterway"][!"power"](around:100,51.5202,-0.1040);out center qt 25;&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

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

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

&lt;p&gt;Once the user has a latitude and longitude, the can share it - along with a message, photo, or anything else.&lt;/p&gt;

&lt;p&gt;Something like:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-json"&gt;{
    "@context": "https://www.w3.org/ns/activitystreams",
    "type": "Note",
    "content": "Here in NYC! &amp;lt;a href=\"https://www.openstreetmap.org/way/958999496\"&amp;gt;John Lennon's Imagine Mosaic&amp;lt;/a&amp;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
    }
}
&lt;/code&gt;&lt;/pre&gt;

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

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

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

&lt;p&gt;&lt;a href="https://benjojo.co.uk/u/benjojo/h/5j4yjq8JbrKY6Vp79K"&gt;Other ActivityPub services &lt;em&gt;do&lt;/em&gt; support location&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Well… that's a job for next week. Probably!&lt;/p&gt;

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

&lt;p&gt;If you'd like to help, please leave a comment.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49432&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/feed/</wfw:commentRss>
			<slash:comments>31</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[Terence Eden]]></dc:creator>
		<pubDate>Sun, 18 Jun 2023 11:34:11 +0000</pubDate>
		<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>&lt;p&gt;Last week, &lt;a href="https://kbin.social/m/asklemmy@lemmy.ml/t/33475/-/comment/138013"&gt;this strange mention appeared on my Mastodon feed&lt;/a&gt;.&lt;/p&gt;

&lt;img src="https://shkspr.mobi/blog/wp-content/uploads/2023/06/kbin-mention-fs8.png" alt="Otome-chan says: &amp;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?&amp;quot;" width="425" height="635" class="aligncenter size-full wp-image-46116"&gt;

&lt;p&gt;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 &lt;em&gt;heard&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;And that is where things get a little confusing and, perhaps, a bit scary.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;I don't think federation is a &lt;em&gt;bad&lt;/em&gt; thing. But I think it's a new way of thinking about your social networking. I &lt;em&gt;like&lt;/em&gt; 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.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=46115&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Tue, 27 Dec 2022 12:34:27 +0000</pubDate>
		<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>&lt;p&gt;How should I design my personal DNS for all the cool new Federated Services and IndieWeb protocols?&lt;/p&gt;

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

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

&lt;p&gt;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?&lt;/p&gt;

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

&lt;p&gt;No.  This is impractical for two reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It's expensive.&lt;/li&gt;
&lt;li&gt;Nothing ties together &lt;code&gt;my_awesome_photos.biz&lt;/code&gt; to &lt;code&gt;read_my_blog.com&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;I currently have &lt;code&gt;/blog&lt;/code&gt;.  Should I also have &lt;code&gt;/mastodon&lt;/code&gt; and &lt;code&gt;/pixelfed&lt;/code&gt; and &lt;code&gt;/yet_another_cool_service&lt;/code&gt; and...&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;@edent-mastodon@shkspr.mobi&lt;/code&gt; and &lt;code&gt;@edent-pixelfed@shkspr.mobi&lt;/code&gt;? That just looks ugly.&lt;/p&gt;

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

&lt;p&gt;Adding sub-domains is free and easy.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mastodon.shkspr.mobi&lt;/code&gt; and &lt;code&gt;pixelfed.shkspr.mobi&lt;/code&gt; - done!&lt;/p&gt;

&lt;p&gt;But there are a couple of issues.&lt;/p&gt;

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

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

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

&lt;h2 id="just-treat-everything-as-a-single-activitypub-feed"&gt;&lt;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"&gt;Just treat everything as a single ActivityPub feed&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;I &lt;em&gt;think&lt;/em&gt; this is where I'm heading.&lt;/p&gt;

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

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

&lt;p&gt;If you &lt;em&gt;only&lt;/em&gt; want to read my sport punditry, subscribe to that channel. If you want everything &lt;em&gt;except&lt;/em&gt; my political views, ignore that specific channel.&lt;/p&gt;

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

&lt;p&gt;I'm still not sure &lt;em&gt;how&lt;/em&gt; 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?&lt;/p&gt;

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

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

&lt;p&gt;If you've done this successfully - or have particularly strong opinions - please let me know!&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=44324&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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[Terence Eden]]></dc:creator>
		<pubDate>Mon, 12 Dec 2022 12:34:06 +0000</pubDate>
		<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>&lt;p&gt;Computer Science has two canonical "&lt;a href="https://joncalder.co.za/2017-12-04-naming-things-is-hard/"&gt;hard problems&lt;/a&gt;":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;cache invalidation&lt;/li&gt;
&lt;li&gt;naming things&lt;/li&gt;
&lt;li&gt;off-by-one errors&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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, &lt;a href="https://blog.twitter.com/engineering/en_us/a/2010/announcing-snowflake"&gt;Twitter invented Snowflake IDs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;So, does the Fediverse use Snowflake? Yes. &lt;strong&gt;AND NO!&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;p&gt;So here's how Mastodon - and the wider Fediverse - deals with unique IDs.  It cheats.&lt;/p&gt;

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

&lt;p&gt;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:&lt;/p&gt;

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

&lt;p&gt;The &lt;em&gt;original&lt;/em&gt; ID is &lt;code&gt;109347716173491502&lt;/code&gt; on the sender's server.  But the unique ID on my server is &lt;code&gt;109347716491680514&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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… &lt;em&gt;*sound of gunshot*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ahem.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;locally&lt;/em&gt; unique ID.  And that's good enough.&lt;/p&gt;
&lt;img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=44114&amp;amp;HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager"&gt;</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>
	</channel>
</rss>
