Profile

User's avatar
npub1y66r...eusx
npub1y66r...eusx
## The Web Runs On Tolerance If you've ever tried to write a computer program, you'll know the dread of a syntax error. An errant space and your code won't compile. Miss a semi-colon and the world collapses. Don't close your brackets and watch how the computer recoils in distress. The modern web isn't like that. You can make your HTML as malformed as you like and the web-browser will do its best to display the page for you. I love the [todepond]( ) website, but the source-code makes me break out in a cold sweat. Yet it renders just fine. Sure, [occasionally there are weird artefacts](https://news.ycombinator.com/item?id=28052190 ). But the web works because browsers are tolerant. You can be *crap* at coding and the web still works. Yes, it takes an awful lot of effort from browser manufacturers to make "do what I mean, not what I say" a reality. But the world is better for it. That's the crucial mistake that XHTML made. It was an attempt to bring pure syntactic rigour to the web. It had an intolerant ideology. Every document had to precisely conform to the specification. If it didn't, the page was irrevocably broken. I don't mean broken like a weird layout glitch, I mean broken like this: https://example.com/test.xhtml Line Number 9, Column 5:" width="1800" height="600" class="aligncenter size-full wp-image-63925"> The user experience of XHTML was rubbish. The disrespect shown to anyone for deviating from the One True Path made it an unwelcoming and unfriendly place. Understandably, XHTML is now a mere footnote on the web. Sure, people are free to use it if they want, but its unforgiving nature makes it nobody's first choice. The beauty of the web as a platform is that it isn't a monoculture. That's why it baffles me that some prominent technologists embrace hateful ideologies. I'm not going to give them any SEO-juice by linking to them, but I cannot fathom how someone can look at the beautiful diversity of the web and then declare that only pure-blooded people should live in a particular city. How do you acknowledge that the father of the computer was a homosexual, brutally bullied by the state into suicide, and then fund groups that want to deny gay people fundamental human rights? The ARM processor which powers the modern world was co-designed by a trans woman. When you throw slurs and denigrate people's pronouns, your ignorance and hatred does a disservice to history and drives away the next generation of talent. History shows us that *all* progress comes from the meeting of diverse people, with different ideas, and different backgrounds. The notion that only a pure ethnostate can prosper is simply historically illiterate. This isn't an academic argument over big-endian or little-endian. It isn't an ideological battle about the superiority of your favourite text editor. There's no good-natured ribbing about which desktop environment has the better design philosophy. Denying rights to others is poison. Wishing violence on people because of their heritage is harmful to all of us. Do we want all computing to go through the snow-white purity of Apple Computer? Have them as the one and only arbiters of what is and isn't allowed? No. That's obviously terrible for our ecosystem. Do we want to segregate computer users so that an Android user can never connect their phone to a Windows machine, or make it impossible for Linux laptops to talk to Kodak cameras? That sort of isolation should be an anathema to us. Why then align with people who espouse isolationism? Why gleefully cheer the violent racists who terrorise our communities? Why demean people who merely wish to exist? The web runs on tolerance. Anyone who preaches the ideology of hate has no business here. #politics #web
## Now witness the power of this fully operational Fediverse! 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 which is just bots talking to each other, right? Earlier this year [I built a stats-counter for my blog]( ). 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. Until fairly recently, the Mastodon social network didn't send referer details. I thought that reduced the visibility of the network and [lobbied for it to change]( ). As various Mastodon servers upgrade, and admins opt-in, it is becoming more apparent just how much traffic originates from the Fediverse. Over the last few weeks, here's how many people have clicked *from* BlueSky and Mastodon *to* one of my blog posts.<thead><tr><th class="totals">Total</th><th>Source</th></tr></thead><tbody><tr><td class="stats-count">1,607</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=bsky.app"><a href="https://bsky.app">bsky.app</a></td></tr><tr><td class="stats-count">752</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.social"><a href="https://mastodon.social">mastodon.social</a></td></tr></tbody> At first glance, it doesn't look good for our elephantine friends, does it? The butterfly sends over twice the traffic. Game over! 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.<thead><tr><th class="totals">Total</th><th>Source</th></tr></thead><tbody><tr><td class="stats-count">193</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=phanpy.social"><a href="https://phanpy.social">phanpy.social</a></td></tr><tr><td class="stats-count">120</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=joinmastodon.org"> android-app://org.joinmastodon.android/</td></tr><tr><td class="stats-count">106</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=infosec.exchange"><a href="https://infosec.exchange">infosec.exchange</a></td></tr><tr><td class="stats-count">62</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mas.to"><a href="https://mas.to">mas.to</a></td></tr><tr><td class="stats-count">59</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mstdn.social"><a href="https://mstdn.social">mstdn.social</a></td></tr><tr><td class="stats-count">55</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=social.vivaldi.net"><a href="https://social.vivaldi.net">social.vivaldi.net</a></td></tr><tr><td class="stats-count">49</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=wandering.shop"><a href="https://wandering.shop">wandering.shop</a></td></tr><tr><td class="stats-count">48</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=fosstodon.org"><a href="https://fosstodon.org">fosstodon.org</a></td></tr><tr><td class="stats-count">33</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mathstodon.xyz"><a href="https://mathstodon.xyz">mathstodon.xyz</a></td></tr><tr><td class="stats-count">27</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.online"><a href="https://mastodon.online">mastodon.online</a></td></tr><tr><td class="stats-count">26</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.scot"><a href="https://mastodon.scot">mastodon.scot</a></td></tr><tr><td class="stats-count">24</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=app.wafrn.net"><a href="https://app.wafrn.net">app.wafrn.net</a></td></tr><tr><td class="stats-count">19</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=indieweb.social"><a href="https://indieweb.social">indieweb.social</a></td></tr><tr><td class="stats-count">18</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=social.lol"><a href="https://social.lol">social.lol</a></td></tr><tr><td class="stats-count">17</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=tech.lgbt"><a href="https://tech.lgbt">tech.lgbt</a></td></tr><tr><td class="stats-count">17</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=toot.wales"><a href="https://toot.wales">toot.wales</a></td></tr><tr><td class="stats-count">16</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=en.osm.town"><a href="https://en.osm.town">en.osm.town</a></td></tr><tr><td class="stats-count">16</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=feditrends.com"><a href="https://feditrends.com">feditrends.com</a></td></tr><tr><td class="stats-count">14</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mstdn.ca"><a href="https://mstdn.ca">mstdn.ca</a></td></tr><tr><td class="stats-count">14</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=piefed.social"><a href="https://piefed.social">piefed.social</a></td></tr><tr><td class="stats-count">12</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=wetdry.world"><a href="https://wetdry.world">wetdry.world</a></td></tr><tr><td class="stats-count">11</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=c.im"><a href="https://c.im">c.im</a></td></tr><tr><td class="stats-count">11</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.nl"><a href="https://mastodon.nl">mastodon.nl</a></td></tr><tr><td class="stats-count">51</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=mastodon.social"> Sites sending &lt; 10 clicks</td></tr></tbody> Ah! Add them all up and you get a grand total of **1,773 visitors from Mastodon-powered sites**. That's *more* than BlueSky. Now, there are some obvious caveats to the data:<li>I have a smaller follower count on BlueSky than I do on Mastodon.</li><li>My posts may appeal more to one demographic than another.</li><li>People may have strict privacy controls which suppress the true volume of visitors.</li><li>There's no way to measure how long someone spends reading my posts.</li><li>RSS and newsletter visitors aren't counted.</li><li>Clicks from apps may not always show a referer.</li><li>Some people may be on multiple services.</li><li>Fediverse users can follow the post directly, so don't need to visit the site to read it.</li> And yet… no matter how you slice it, Fediverse servers are sending as much traffic as BlueSky! 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. Just for completeness, this is how Reddit, Facebook, LinkedIn, Twitter, and Lemmy do over the same period:<thead><tr><th class="totals">Total</th><th>Source</th></tr></thead><tbody><tr><td class="stats-count">1,158</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=reddit.com"><a href="https://reddit.com">reddit.com</a></td></tr><tr><td class="stats-count">585</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=reddit.com"> android-app://com.reddit.frontpage/</td></tr><tr><td class="stats-count">76</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=facebook.com"><a href="https://facebook.com">facebook.com</a></td></tr><tr><td class="stats-count">76</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=old.reddit.com"><a href="https://old.reddit.com/r/programming/">https://old.reddit.com/r/programming/</a></td></tr><tr><td class="stats-count">56</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/programming/">https://www.reddit.com/r/programming/</a></td></tr><tr><td class="stats-count">52</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=youtube.com"><a href="https://youtube.com">youtube.com</a></td></tr><tr><td class="stats-count">41</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=t.co"><a href="https://t.co">t.co</a></td></tr><tr><td class="stats-count">38</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=old.reddit.com"><a href="https://old.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/">https://old.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/</a></td></tr><tr><td class="stats-count">31</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=linkedin.com"><a href="https://linkedin.com">linkedin.com</a></td></tr><tr><td class="stats-count">27</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.world"> android-app://io.syncapps.lemmy_sync/</td></tr><tr><td class="stats-count">27</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/">https://www.reddit.com/r/todayilearned/comments/1nsw7f4/til_in_mongolia_instead_of_a_street_address_a/</a></td></tr><tr><td class="stats-count">22</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=old.reddit.com"><a href="https://old.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/">https://old.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/</a></td></tr><tr><td class="stats-count">22</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.ca"><a href="https://lemmy.ca">lemmy.ca</a></td></tr><tr><td class="stats-count">17</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=linkedin.com"> android-app://com.linkedin.android/</td></tr><tr><td class="stats-count">16</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.dbzer0.com"><a href="https://lemmy.dbzer0.com">lemmy.dbzer0.com</a></td></tr><tr><td class="stats-count">14</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=feddit.org"><a href="https://feddit.org">feddit.org</a></td></tr><tr><td class="stats-count">11</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/">https://www.reddit.com/r/programming/comments/1n96ftn/40_years_later_are_bentleys_programming_pearls/</a></td></tr><tr><td class="stats-count">10</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=discuss.tchncs.de"><a href="https://discuss.tchncs.de">discuss.tchncs.de</a></td></tr><tr><td class="stats-count">10</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=l.instagram.com"><a href="https://l.instagram.com">l.instagram.com</a></td></tr><tr><td class="stats-count">8</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=lemmy.blahaj.zone"><a href="https://lemmy.blahaj.zone">lemmy.blahaj.zone</a></td></tr><tr><td class="stats-count">6</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=www.reddit.com"><a href="https://www.reddit.com/r/GrapheneOS/comments/1m2l84b/considering_making_the_switch_does_google_pay/">https://www.reddit.com/r/GrapheneOS/comments/1m2l84b/considering_making_the_switch_does_google_pay/</a></td></tr><tr><td class="stats-count">6</td><td><img class="pingback-favicon" src="https://shkspr.mobi/favicons/?domain=reddthat.com"><a href="https://reddthat.com">reddthat.com</a></td></tr></tbody> 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. Twitter is basically [the Dead Internet]( ). 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 *lot* - sometimes by accounts with huge followers - yet there are no comments or retweets and barely and clicks. 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.## [Final Thoughts](#final-thoughts )Firstly, my stats only represent my site. Your site might be very different. Secondly, I've ignored search engine traffic, big blogs, newsletters, and other sources. Thirdly, and most importantly, this *isn't* 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. I want a thousand sites, running a hundred different software stacks, some of which only serve a dozen people, or even a lone participant. Diversity is strength. #activitypub #bluesky #fediverse #mastodon #statistics
## Quick and dirty bar-charts using HTML's meter elementhttps://shkspr.mobi/blog/2025/10/quick-and-dirty-bar-charts-using-htmls-meter-element/ "If it's stupid but it works, it's not stupid." I want to draw some vertical bar charts. I don't want to use a 3rd party library, or bundle someone else's CSS, or learn how to build SVGs. HTML contains a &lt;meter&gt; element. It is used like this:<span class="tempest-highlight-language"><img src="https://shkspr.mobi/blog/wp-content/plugins/tempest-highlight//svg/html.svg" width="32" height="32" alt="" class="tempest-highlight-language-icon"><span itemprop="programmingLanguage"> HTML</span></span><code class="html" itemprop="text">&lt;<span style="color: #0000ff;">meter</span> <span style="color: #795E26;">min</span>="0" <span style="color: #795E26;">max</span>="4000" <span style="color: #795E26;">value</span>="1234"&gt;1234&lt;/<span style="color: #0000ff;">meter</span>&gt;</code> Which looks like this: 1234 There isn't *much* you can do to style it. Browser manufacturers seem to have forgotten it exists and the CSS standard kind of ignores it. It *is* possible to use CSS to rotate it using:<span class="tempest-highlight-language"><img src="https://shkspr.mobi/blog/wp-content/plugins/tempest-highlight//svg/css.svg" width="32" height="32" alt="" class="tempest-highlight-language-icon"><span itemprop="programmingLanguage"> CSS</span></span><code class="css" itemprop="text"><span style="color: #0000ff;">meter </span>{ <span style="color: #795E26;">transform</span>: <span style="color: #0000ff;">rotate</span>(-90deg);}</code> But then you have to mess about with origins and the box model gets a bit confused. See what 1234 I mean? You can hack your way around that with &lt;div&gt;s and bludgeoning your layout into submission. But that is a bit tedious. Luckily, there's another way. As suggested by [Marius Gundersen]( ), it's possible to set the [writing direction]( ) of the element to be vertical. That means you can have them "written" vertically, while having them laid out horizontally. Giving a nice(ish) bar-chart effect.1000200030004000 As well as the normal sort of CSS spacing, there is basic colour support for values which are inside a specific range:1000200030004000 The background colour can also be set.1000 I dare say they're slightly more accessible than a raster image - even with good alt text. They can be targetted with JS, if you want to do fancy things with them. Or, if you just want a quick and dirty bar-chart, they're basically fine. #css #HTML
## Getting started with Mastodon's Quote Posts - technical implementation details for servers Quoting posts on Mastodon is *slightly* complex. Because of the privacy conscious nature of the platform and its users, reposting isn'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've [read through the specification](📃.md ) and tried to simplify it. Quoting is a multi-step process:<li>The status <em>must</em> opt-in to being shared.</li><li>The quoter quotes the status.</li><li>The quoter's server sends a request to the status's server.</li><li>The status's server sends an accept message back to the quoter's server.</li><li>When other servers see the quote, they check with the status's server to see if it is allowed.</li> I'm going to walk you through each stage as best as I understand them.## [Opting In](#opting-in )An ActivityPub status message is JSON. In order to opt-in, it needs this additional field.<span class="tempest-highlight-language"><img src="https://shkspr.mobi/blog/wp-content/plugins/tempest-highlight//svg/json.svg" width="32" height="32" alt="" class="tempest-highlight-language-icon"><span itemprop="programmingLanguage"> JSON</span></span><code class="json" itemprop="text"><span style="color: #0000ff;">"interactionPolicy"</span>: <span style="color: #795E26;">{</span> <span style="color: #0000ff;">"canQuote"</span>: <span style="color: #795E26;">{</span> <span style="color: #0000ff;">"automaticApproval"</span>: <span style="color: #a31515;">"https://www.w3.org/ns/activitystreams#Public"</span> <span style="color: #795E26;">}</span><span style="color: #795E26;">}</span></code> 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.## [The QuoteRequest](#the-quoterequest )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.<span class="tempest-highlight-language"><img src="https://shkspr.mobi/blog/wp-content/plugins/tempest-highlight//svg/json.svg" width="32" height="32" alt="" class="tempest-highlight-language-icon"><span itemprop="programmingLanguage"> JSON</span></span><code class="json" itemprop="text"><span style="color: #795E26;">{</span> <span style="color: #a31515;">"@context"</span>: <span style="color: #795E26;">[</span> <span style="color: #a31515;">"https://www.w3.org/ns/activitystreams"</span>, <span style="color: #795E26;">{</span> <span style="color: #0000ff;">"QuoteRequest"</span>: <span style="color: #a31515;">"https://w3id.org/fep/044f#QuoteRequest"</span> <span style="color: #795E26;">}</span> <span style="color: #795E26;">]</span>, <span style="color: #0000ff;">"type"</span>: <span style="color: #a31515;">"QuoteRequest"</span>, <span style="color: #0000ff;">"id"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent/quote_requests/1234-5678-9101"</span>, <span style="color: #0000ff;">"actor"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent"</span>, <span style="color: #0000ff;">"object"</span>: <span style="color: #a31515;">"https://example.com/posts/987654321.json"</span>, <span style="color: #0000ff;">"instrument"</span>: <span style="color: #795E26;">{</span> <span style="color: #0000ff;">"id"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent/statuses/123456789"</span>, <span style="color: #0000ff;">"url"</span>: <span style="color: #a31515;">"https://mastodon.test/@Edent/123456789"</span>, <span style="color: #0000ff;">"attributedTo"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent"</span>, <span style="color: #0000ff;">"quote"</span>: <span style="color: #a31515;">"https://example.com/posts/987654321.json"</span>, <span style="color: #0000ff;">"_misskey_quote"</span>: <span style="color: #a31515;">"https://example.com/posts/987654321.json"</span>, <span style="color: #0000ff;">"quoteUri"</span>: <span style="color: #a31515;">"https://example.com/posts/987654321.json"</span> <span style="color: #795E26;">}</span><span style="color: #795E26;">}</span></code> All this says is "I would like permission to quote you."## [The Stamp](#the-stamp )The quoted server needs to approve this quote. First, it generates a "stamp". 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 [stamp's ID is requested the stamp *must* be returned]( ).<span class="tempest-highlight-language"><img src="https://shkspr.mobi/blog/wp-content/plugins/tempest-highlight//svg/json.svg" width="32" height="32" alt="" class="tempest-highlight-language-icon"><span itemprop="programmingLanguage"> JSON</span></span><code class="json" itemprop="text"><span style="color: #795E26;">{</span> <span style="color: #a31515;">"@context"</span>: <span style="color: #795E26;">[</span> <span style="color: #a31515;">"https://www.w3.org/ns/activitystreams"</span>, <span style="color: #795E26;">{</span> <span style="color: #0000ff;">"gts"</span>: <span style="color: #a31515;">"https://gotosocial.org/ns#"</span>, <span style="color: #0000ff;">"QuoteAuthorization"</span>: <span style="color: #795E26;">{</span> <span style="color: #a31515;">"@id"</span>: <span style="color: #a31515;">"https://w3id.org/fep/044f#QuoteAuthorization"</span>, <span style="color: #a31515;">"@type"</span>: <span style="color: #a31515;">"@id"</span> <span style="color: #795E26;">}</span>, <span style="color: #0000ff;">"interactingObject"</span>: <span style="color: #795E26;">{</span> <span style="color: #a31515;">"@id"</span>: <span style="color: #a31515;">"gts:interactingObject"</span> <span style="color: #795E26;">}</span>, <span style="color: #0000ff;">"interactionTarget"</span>: <span style="color: #795E26;">{</span> <span style="color: #a31515;">"@id"</span>: <span style="color: #a31515;">"gts:interactionTarget"</span> <span style="color: #795E26;">}</span> <span style="color: #795E26;">}</span> <span style="color: #795E26;">]</span>, <span style="color: #0000ff;">"type"</span>: <span style="color: #a31515;">"QuoteAuthorization"</span>, <span style="color: #0000ff;">"id"</span>: <span style="color: #a31515;">"https://example.com/quote-987654321.json"</span>, <span style="color: #0000ff;">"attributedTo"</span>: <span style="color: #a31515;">"https://example.com/users/username"</span>, <span style="color: #0000ff;">"interactionTarget"</span>: <span style="color: #a31515;">"https://example.com/posts/987654321.json"</span>, <span style="color: #0000ff;">"interactingObject"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent/statuses/123456789"</span><span style="color: #795E26;">}</span></code> If the quoted status is viewed from a different server, that server will query the stamp to make sure the share is allowed.## [The Accept](#the-accept )This is the message that the quoted server sends to the quoting server. It references the request and the stamp.<span class="tempest-highlight-language"><img src="https://shkspr.mobi/blog/wp-content/plugins/tempest-highlight//svg/json.svg" width="32" height="32" alt="" class="tempest-highlight-language-icon"><span itemprop="programmingLanguage"> JSON</span></span><code class="json" itemprop="text"><span style="color: #795E26;">{</span> <span style="color: #a31515;">"@context"</span>: <span style="color: #795E26;">[</span> <span style="color: #a31515;">"https://www.w3.org/ns/activitystreams"</span>, <span style="color: #795E26;">{</span> <span style="color: #0000ff;">"QuoteRequest"</span>: <span style="color: #a31515;">"https://w3id.org/fep/044f#QuoteRequest"</span> <span style="color: #795E26;">}</span> <span style="color: #795E26;">]</span>, <span style="color: #0000ff;">"type"</span>: <span style="color: #a31515;">"Accept"</span>, <span style="color: #0000ff;">"to"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent"</span>, <span style="color: #0000ff;">"id"</span>: <span style="color: #a31515;">"https://example.com/posts/987654321.json"</span>, <span style="color: #0000ff;">"actor"</span>: <span style="color: #a31515;">"https://example.com/account"</span>, <span style="color: #0000ff;">"object"</span>: <span style="color: #795E26;">{</span> <span style="color: #0000ff;">"type"</span>: <span style="color: #a31515;">"QuoteRequest"</span>, <span style="color: #0000ff;">"id"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent/quote_requests/1234-5678-9101"</span>, <span style="color: #0000ff;">"actor"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent"</span>, <span style="color: #0000ff;">"instrument"</span>: <span style="color: #a31515;">"https://mastodon.test/users/Edent/statuses/123456789"</span>, <span style="color: #0000ff;">"object"</span>: <span style="color: #a31515;">"https://example.com/posts/987654321.json"</span> <span style="color: #795E26;">}</span>, <span style="color: #0000ff;">"result"</span>: <span style="color: #a31515;">"https://example.com/quote-987654321.json"</span><span style="color: #795E26;">}</span></code> The "result" *must* be the same as the stamp's URl.## [And then?](#and-then )You can follow and quote @npub17ylm...xht6 on your favourite Fediverse platform. I've written an ActivityPub server in a single file which is designed to teach you have the protocol works. Have a play with [ActivityBot]( ). #ActivityPub #fediverse #mastodon #MastodonAPI
## 40 years later, are Bentley's "Programming Pearls" still relevant? In September 1985, Jon Bentley published [Programming Pearls](https://dl.acm.org/doi/10.1145/4284.315122 ). A collection of aphorisms designed to reveal truths about the field of programming. It's 40 years later - long enough to see several revolutions in the field - so surely these are obsolete, right? They belong in the same category as "always carry a bundle of hay for the horses" or "you won't always have a pocket calculator with you" or "tie an onion on your belt to stay stylish". Ah, my sweet summer child! *Plus ça change, plus c'est la même chose.* You'll find nearly everything in here depressingly relevant. Before we dive in, a word for Bentley on the provenance of this collection: [Programming Pearls.](📄.pdf ) > Although there is some truth in each saying in this column, all should be taken with a grain of salt. A word about credit. The name associated with a rule is usually the person who sent me the rule, even if they in fact attributed it to their Cousin Ralph (sorry, Ralph). In a few cases I have listed an earlier reference, together with the author’s current affiliation (to the best of my knowledge). I’m sure that I have slighted many people by denying them proper attribution, and to them I offer the condolence that Plagiarism is the sincerest form of flattery. Here we go! [](https://dl.acm.org/doi/10.1145/4284.315122 )## [Coding](#coding )> When in doubt, use brute force.Ken Thompson - Bell Labs Straight off the bat, a winner! Almost all problems are solvable through brute force. It may take time - but throw more resources at it! Once you know it *can* be done, then it is time to see *how* it can be done better. > Avoid arc-sine and arc-cosine functions - you can usually do better by applying a trig identity or computing a vector dot-product.Jim Conyngham - Arvin/Calspan Advanced Technology Center And then, just like that, something broadly irrelevant today. These sorts of mathematical functions have been optimised so far that it probably doesn't matter which way you calculate them. > Allocate four digits for the year part of a date: a new millenium is coming.David Martin - Norristown, Pennsylvania **weeps** Why didn't they listen to you, David? While I would hope any code written this side of Y2K uses ISO8601, it is amusing that you still occasionally encounter people who want to save two bytes *somewhere*. Handy in some small systems, but mostly just a recipe for disaster. Looking at you, [GPS](https://www.gps.gov/support/user/rollover/ )! > Avoid asymmetry.Andy Huber - Data General Corporation I'll be honest, I'm not sure what Andy is going on about here. I *assume* that he's talking about having the ability to go A->B without being able to go B->A. Equally, it could be about accepting data in one format and outputting it in a different format. [Some more discussion on the topic](https://news.ycombinator.com/item?id=33739184 ). > The sooner you start to code, the longer the program will take.Roy Carlson - University of Wisconsin *Bam!* Right in the truth. Much like [the woodsman who spends his time sharpening his axe](https://quoteinvestigator.com/2014/03/29/sharp-axe/ ), we know that diving into code is probably the least efficient way to create something. > If you can’t write it down in English, you can’t code it.Peter Halpern - Brooklyn, New York So many bugs come from us not understanding the requirements of the user / customer. > Details count.Peter Weinberger - Bell Labs Hard agree, Pete! It's very easy to go for the "big picture" view of the software. But unless all those sharp edges are filed down, the code isn't going to have a happy life. > If the code and the comments disagree, then both are probably wrong.Norm Schyer - Belt Labs Ah, the dream of self-documenting code will never be realised. Again, this goes back to our (in)ability to properly describe our requirements and our (in)adequacies at turning those comments into code. > A procedure should fit on a page.David Tribble - Arlington, Texas Famously, [Amazon has a "Two Pizza" rule]( ) which defines the maximum size of a team. The larger and more complex something is, the more likely it is to go wrong. Yes, there are limits to DRY and YAGNI - but we seem firmly in the paradigm that large procedures / functions are ruinous to one's health. > If you have too many special cases, you are doing it wrong.Craig Zerouni - Computer FX Ltd. London, EnglandIF/ELSE and CASE/SWITCH still really test our patience. Beautifully clean code which is ruined by special subroutines for rarely occurring situations. But it is hard to call them "wrong". Sometimes the world is complex and it is the job of computers to do the hard work for us. > Get your data structures correct first, and the rest of the program will write itself.David Jones. Assen, The Netherlands Dave is right. A well-defined data structure is *still* the essence of most CRUD systems.## [User Interfaces](#user-interfaces )> [The Principle of Least Astonishment] Make a user interface as consistent and as predictable as possible.Contributed by several readers **weeps** Why isn't this hammered into every programmer? Today's tools are filled with hidden UI gestures, random menus, and a complete disregard for the user's time. > A program designed for inputs from people is usually stressed beyond the breaking point by computer-generated inputs.Dennis Ritchie. Bell Labs I think this one is mostly irrelevant now. Humans can only type at a limited speed, but computers can generate massive amounts of data instantly. But our machines' abilities to ingest that data has also grown. I suppose the nearest thing is the DDoS - where a webserver designed for a few visitors is overwhelmed by a flood of automated and malicious requests. > Twenty percent of all input forms filled out by people contain bad data.Vic Vyssotsky. Bell Labs Ha! Vic didn't know that we'd have &lt;input type... validation in the 21st century! But, yeah, people write all sorts of crap into forms. > Eighty percent of all input forms ask questions they have no business asking.Mike Garey. Bell Labs Mike was sent from the future to warn the people of the past - but they paid him no heed. > Don't make the user provide information that the system already knows.Rick Lemons. Cardinal Data Systems I'm going to slightly disagree with Rick here. Asking for repeated information is a reasonable way to double-check you've got that information correct. It also helps to validate that the user is who they say they are. > For 80 percent of all data sets, 95 percent of the information can be seen in a good graph.William S. Cleveland. Bell Labs Those of us who have seen [Anscombe's quartet]( ) know how true this is.## [Debugging](#debugging )> Of all my programming bugs, 80 percent are syntax errors. Of the remaining 20 percent, 80 percent are trivial logical errors. Of the remaining 4 percent, 80 percent are pointer errors. And the remaining 0.8 percent are hard.Marc Donner. IBM T. J. Watson Research Center Syntax errors are rarer now that we have IDEs. And I hope visual programming languages will further reduce them. Logic errors still plague us. Pointer errors have been eradicated unless you're working at the very lowest levels. And I'd say the number of "hard" bugs is probably higher now due to the complex interaction of multiple libraries and systems. > It takes three times the effort to find and fix bugs in system test than when done by the developer. It takes ten times the effort to find and fix bugs in the field than when done in system test. Therefore, insist on unit tests by the developer.Larry Bernstein. Bell Communications Research We can quibble about the numbers and the ratios - but it is generally harder to fix in prod. That said, getting crash logs from the field has considerable shortened those ratio. > Don’t debug standing up. It cuts your patience in half, and you need all you can muster.Dave Storer. Cedar Rapids, Iowa I'm with Team-Standing-Desk! So I think Dave is wrong. > Don’t get suckered in by the comments - they can be terribly misleading. Debug only the code. Dave Storer. Cedar Rapids, Iowa Hmmm. Yes, this is probably correct. I'm not going to say code is self-documenting these days; but it certainly is a lot easier to read. > Testing can show the presence of bugs, but not their absence.Edsger W. Dijkstra. University of Texas Dare we disagree with Dijkstra?! Well, perhaps a little. With modern fuzzing tools we can show the absence of certain kinds of bugs. > Each new user of a new system uncovers a new class of bugs.Brian Kernighan. Bell Labs Yup! Our code would be bug-free if it weren't for those pesky users! > If it ain’t broke, don’t fix it.Ronald Reagan. Santa Barbara, California Amongst the many things about which to disagree with the former President, this is up there! Code needs maintenance. Some things aren't broke until all of a sudden they are. Sure, maybe don't change your app's layout because a manager wants a bonus; but things constantly need fixing. > [The Maintainer’s Motto] If we can’t fix it, it ain’t broke.Lieutenant Colonel Walt Weir. United States Army I believe in you. Self deprecation is fine, but self confidence is better. > The first step in fixing a broken program is getting it to fail repeatably.Tom Duff. Bell Labs Yes! Transient errors are the worst! And a huge source of the "it works for me" antipattern.## [Performance](#performance )> [The First Rule of Program Optimization] Don’t do it.[The Second Rule of Program Optimization - for experts only] Don't do it yet.Michael Jackson. Michael Jackson Systems Ltd. As true now as it ever was. > The fastest algorithm can frequently be replaced by one that is almost as fast and much easier to understand.Douglas W. Jones. University of Iowa I'm only *mostly* in agreement here. Many of the security bugs we see in modern code are due to "clever" tricks which turn out to have nasty strings attached. But, at the microcode level, performance is still everything. And a well-tested fast algorithm may be necessary. As part of the climate crisis we should all be thinking about the efficiency of our code. > On some machines indirection is slower with displacement, so the most-used member of a structure or a record should be first. Mike Morton. Boston, Massachusetts We live in an age of ridiculously fast SSD and RAM access times. Sequential reads are still slightly faster than random jumps, and structures like [B-Tree]( ) give us a good mix of the two. We don't need to align data to the physical tracks of a spinning disk any more. > In non-I/O-bound programs, a few percent of the source code typically accounts for over half the run time.Don Knuth. Stanford University I wonder how true this now is? Perhaps we could replace "I/O" with "Internet requests" and still be accurate? > Before optimizing, use a profiler to locate the “hot spots” of the program.Mike Morton. Boston, Massachusetts Mostly true. But you don't lose much by doing some manual optimisations that you know (from bitter experience) will make a difference. > [Conservation of Code Size] When you turn an ordinary page of code into just a handful of instructions for speed, expand the comments to keep the number of source lines, constant.Mike Morton. Boston, Massachusetts I don't think this is relevant these days. Perhaps it is useful to spend time explaining exactly what trickery you're pulling off with weird syntax. But our tools are now line-count agnostic. Mostly. > If the programmer can simulate a construct faster than the compiler can implement the construct itself, then the compiler writer has blown it badly.Guy L. Steele, Jr. Tartan Laboratories I think this is rather self-evident. But compilers are so ridiculously optimised that this scenario is increasingly rare. > To speed up an I/O-bound program, begin by accounting for all I/O. Eliminate that which is unnecessary or redundant, and make the remaining as fast as possible.David Martin. Norristown, Pennsylvania I think this can be generalised even further. I'm reminded of [NPM's progress bar slowdown issue]( ). There's a lot of redundancy which can be removed in many programs. > The fastest I/O is no I/O.Nils-Peter Nelson. Bell Labs Man! They were *obsessed* with I/O back in the day! At large volumes, it is still an issue. But perhaps now we can relax just a little? > The cheapest, fastest, and most reliable components of a computer system are those that aren’t there.Gordon Bell. Encore Computer Corporation A little unfair, I think. It's cheaper to have less RAM, but that doesn't make my laptop faster. > [Compiler Writer’s Motto-Optimization Pass] Making a wrong program worse is no sin.Bill McKeeman. Wang Znstitute Personally, I don't think it is the compiler's job to tell me I'm doing it wrong. > Electricity travels a foot in a nanosecond.Commodore Grace Murray Hopper. United States Navy And a nano-Century is Pi seconds! One of those pub-trivia facts which are irrelevant to modern computing. > LISP programmers know the value of everything but the cost of nothing.Alan Perlis. Yale University Nowadays LISP programmers are a protected species and shouldn't be subject to such harsh treatment. > [Little’s Formula] The average number of objects in a queue is the product of the entry rate and the average holding time.Richard E. Fairley. Wang Institute Another of those truisms which kinda don't matter in a world with infinite disk space. Speed is our greatest worry.## [Documentation](#documentation )> [The Test of Negation] Don’t include a sentence in documentation if its negation is obviously false.Bob Martin. AT&T Technologies I don't know if that's the same guy as [Uncle Bob]( ) - but it sounds like the sort of claptrap he'd come up with. What's obvious to you might not be obvious to others. Test your writing with your audience to see if they understand your meaning. > When explaining a command, or language feature, or hardware widget, first describe the problem it is designed to solve.David Martin. Norristown, Pennsylvania Agreed. It doesn't need to be an essay, but documentation needs context. > [One Page Principle] A (specification, design, procedure, test plan) that will not fit on one page of 8.5-by-11 inch paper cannot be understood.Mark Ardis. Wang Institute I do have some sympathy with this - see the Two-Pizza rule above - but I think this ignores the reality of modern systems. Yes, we should keep things simple, but we also have to recognise that complexity is unavoidable. > The job’s not over until the paperwork’s done.Anon Amen!## [Managing Software](#managing-software )> The structure of a system reflects the structure of the organization that built it.Richard E. Fairley. Wang Institute This is [Conway's Law]( ) and it is still fairly true. [Some studies show it is possible to break out of the paradigm](https://dl.acm.org/doi/10.1109/RESER.2013.14 ) but it holds remarkable power. > Don’t keep doing what doesn’t work.Anon If only we could tattoo this on the inside of our eyelids, eh? > [Rule of Credibility] The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time.Tom Cargill. Bell Labs Agile methodology has *somewhat* dimmed the potency of this prediction. I think people are *generally* better at estimating now. But it is hard to escape [Zeno's Paradox]( ). > Less than 10 percent of the code has to do with the ostensible purpose of the system; the rest deals with input-output, data validation, data structure maintenance, and other housekeeping.May Shaw. Carnegie-Mellon University How many times have you installed a simple program only to see it pull in every dependency under the sun? We need an awful lot of scaffolding to keep our houses standing. > Good judgment comes from experience, and experience comes from bad judgment.Fred Brooks. University of North Carolina I lean *slightly* towards this. I also strongly believe that you can pick up a lot of good judgement by listening to your users. > Don’t write a new program if one already does more or less what you want. And if you must write a program, use existing code to do as much of the work as possible.Richard Hill. Hewlett-Packard S.A. Geneva, Switzerland This is the open source way. Much easier to fork than start again. But at some point you'll run up against an unwanted design decision which will be load-bearing. Think carefully before you re-use. > Whenever possible, steal code.Tom Duff. Bell Labs ITYM "Respect the terms of an OSI approved Open Source licence" - don't you, Tom? > Good customer relations double productivity.Larry Bernstein. Bell Communications Research A lesson learned by Apple and ignored by Google. > Translating a working program to a new language or system takes 10 percent of the original development time or manpower or cost.Douglas W. Jones University of Iowa I honestly don't know how true that is any more. Automated tools must surely have improved that somewhat? > Don’t use the computer to do things that can be done efficiently by hand.Richard Hill. Hewlett-Packard S.A. Geneva, Switzerland A rare disagreement! Things can be efficiently done by hand *once or twice* but after that, go nuts! Even if it's something as simple as renaming a dozen files in a directory, you'll learn something interesting from automating it. > I’d rather write programs to write programs than write programs.Dick Sites. Digital Equipment Corporation There will always be people who love working on the meta-task. They're not wrong for doing so, but it can be an unhelpful distraction sometimes. > [Brooks’s Law of Prototypes] Plan to throw one away, you will anyhow.Fred Brooks. University of North Carolina I'd go further an suggest throwing out even more. It can be hard to sell that to management - but it is necessary. > If you plan to throw one away, you will throw away two.Craig Zerouni. Computer FX Ltd. London, England Craig with the double-tap! > Prototyping cuts the work to produce a system by 40 percent.Larry Bernstein. Bell Communications Research Minor disagreement. Prototyping *is* part of the work. And it should probably take a considerable amount of time. > [Thompson’s rule for first-time telescope makers] It is faster to make a four-inch mirror then a six-inch mirror than to make a six-inch mirror.Bill McKeeman. Wang Institute Yes. It is always tempting to go for the big win. But baby-steps! > Furious activity is no substitute for understanding.H. H. Williams. Oakland, California Goodness me, yes! It's always tempting to rush in pell-mell. But that's a poor use of time. > Always do the hard part first. If the hard part is impossible, why waste time on the easy part? Once the hard part is done, you’re home free.Always do the easy part first. What you think at first is the easy part often turns out to be the hard part. Once the easy part is done, you can concentrate all your efforts on the hard part.Al Schapira. Bell Labs Oh, Al! You card! Luckily, there are very few "basic" problems to be solved in modern computing. We know what most of the hard problems are. Perhaps Agile teaches us to always leave software in a working state, so we start with the easy parts? > If you lie to the computer, it will get you.Perry Farrar. Germantown, Maryland We shouldn't anthropomorphise computers; they don't like it. Actually, nowadays it's is quite common to "lie" to computers with dummy data and virtualised environments. It's fine. > If a system doesn’t have to be reliable, it can do anything else.H. H. Williams. Oakland, California Perhaps it is my imagination, but we seem less concerned with reliability these days. A Tesla car is a wonderful example of that. > One person’s constant is another person’s variable.Susan Gerhart. Microelectronics and Computer Technology Corp. I wonder about this one a lot. Scoped access to variables possibly makes this less of an issue in the 21st century? > One person’s data is another person’s program.Guy L. Steele, Jr. Tartan Laboratories I don't quite get this. Anyone care to explain? > Eschew clever rules.Joe Condon. Bell Labs The pearls end with this gem.## [What have we learned today?](#what-have-we-learned-today )The majority of my disagreements are minor quibbles. And while disk-bound I/O is rarely a problem, network latency has replaced it as the main cause of delays. We've managed to fix some things, but many seem irrevocably tied to the human condition. Which one was your favourite? #programming
## I'm never going back to Matrix I should love Matrix. It is a decentralised, privacy preserving, multi-platform chat tool. Goodbye Slack and your ridiculous free limits. Adiós Discord and your weird gamification. Suck it IRC with your obscure syntax and faint stench of BO. WhatsApp and Telegram can stick their heads in a bucket of lukewarm sick and sing sea shanties! Let's join the future! The problem is - Matrix is shit. Not just on a protocol level, but on an organisational level as well. I joined Matrix at FOSDEM - the largest gathering of open source nerds in Europe. We were all encouraged to use it - every talk had its own channel, all the official comms came from there, I was even invited to a top-secret private channel for speaker. This was going to be epic! Viva la rèvölūçïón, right? Wrong. It was dead. Even among the most seasoned geeks on the planet, most people preferred to use other services like Signal, Telegram, and Slack. Why? Because those other tools *actually* work. Matrix has two official Android apps - one of which is old and unsupported, the other is new and doesn't work with many of the basic chat features. I want to be absolutely clear about this - the company behind Matrix have put out an app which doesn't work with their own product! Lest you think I'm exaggerating, here's a typical view of the official FOSDEM speaker room, using the official Matrix app: It was **embarrassing**. People would pipe up in channels and say "this doesn't work" only to be told they were using the wrong app and should go back to the one marked unsupported. So they left, never to return. Even in the large talks, where people were encouraged to use the official Matrix chat, most of the conversation happened on other platforms. It was just too hard to use Matrix. A few thousands geeks, all used to recompiling their own kernels and participating in the Fediverse, and most thought that Matrix was too much of a faff. After FOSDEM, I kept the Matrix app on my phone. Occasionally receiving a ping from some long-forgotten channel. And then, one day, I got hit with the most vile spam. A dozen notifications suddenly appeared on my phone with abuse, torture, and transphobic slurs in them. [You can view the screenshot]( ) - but, fair warning, it is grim. This shouldn't be possible. It doesn't take an expensive team of moderators to add some keyword monitoring. It doesn't take a massive AI model to work out that a stranger shouldn't be able to bombard users with multiple notifications. You don't have to sacrifice your dream of a decentralised future - you just need to care about your users. This stuff is *basic*. I moaned about it on Mastdon and was surprised to receive a private reply from the *official* Matrix account. > Please do not encourage the spammer by giving them a platform and propagating their spam; you may want to consider deleting your post. This is classic victim blaming. It is my fault for giving the spammer attention. I am the one who needs to take responsibility and delete the evidence. I shouldn't warn people that Matrix is actively dangerous to use. Bullshit. Here's what I *expected* them to say: > "We're sorry you had such a bad experience on Matrix. Rest assured we're working hard to block these spammers - here's a link to show what we're doing. You can protect your account further by doing x, y and z. Once again, sorry and we hope we can win back your trust." I'm not saying scrappy open source projects have to hire anodyne corporate communications specialists; they just need to have a *little* empathy. But, no, just constant whining about how it isn't their fault and how **I** am the one who needs to change my behaviour. This is pretty typical behaviour from the team. Find any post complaining about some aspect of Matrix and you'll see their instant woe-is-me replies. So I deleted the app. I would have liked to have nuked my account [but apparently that's not possible]( ). I'm not the only one who feels like this. [Here's an epic post by Marius]( ), which concludes: > Between the slow performance, the increasing amount of spam, the miserable web client, and the unfinished state of Element X, the Matrix.org network is not something I am willing to continue to recommend, especially to non-technical users. Normal people are simply tolerating it to communicate with idealistic nerds like myself who insist(ed) on using it. Matrix just isn't focussed on users. I'm not talking about user-experience tweaks like which shade of cornflower blue to use - I mean basic user needs like apps that work and a way to combat spam. There's a [long list of ways the protocol contributes to a poor user experience]( ). It almost seems designed without regard for how it will actually be used. While the protocol may be conceptually interesting and their intentions noble, I'm not prepared to suffer abuse in the name of technical purity. Open Source and Open Standards nerds like me ought to know by now that the protocol is the *least* compelling thing about a service. Who cares if your home is built using only Stallman-blessed tools, when the walls are full of rats? #foss #Matrix #OpenSource #rant
## Grinding down open source maintainers with AI Early one morning I received an email notification about a bug report to one of my open source projects. I like to be helpful and I want people who use my stuff to have a good time, so I gave it my attention. Here's what it said:## > [> 😱 I Can't Use On This Day 😭](#%f0%9f%98%b1-i-cant-use-on-this-day-%f0%9f%98%ad )> Seriously, What’s Going On?! 🔍 > I’ve been trying to use the On This Day feature, but it’s just not working for me! 😩 > Every time I input my details, it says I have no posts for today, even though I know I’ve posted stuff! 🧐### > [> Here’s My Setup: ⚙️](#heres-my-setup-%e2%9a%99%ef%b8%8f )<li>Python 3.x 🐍</li><li>Access token fully generated (I triple-checked!) 🔑</li><li>Attempted on multiple instances but still nothing! 😩😩</li>### > [> Could It Be a Bug? 🤔](#could-it-be-a-bug-%f0%9f%a4%94 )> I’m really starting to doubt my posting history! 😳 > Is it supposed to show only specific types of posts? > I’ve made some pretty epic posts before! 💥💬### > [> Documentation Confusion 📚](#documentation-confusion-%f0%9f%93%9a )> The README says to register for an access token but doesn’t clarify if it factors into this feature! 🤔❓ > Did I miss something REALLY important?! > Help me figure this out, please!!! 😱### > [> Feature Suggestion 💭](#feature-suggestion-%f0%9f%92%ad )> If this is broken, can we at least have a debug mode to log what’s happening! 😬 > I need to know if it’s truly my fault or the code’s! 🔍🛠 > Thanks for looking into this TRAGIC situation!!! 😭💔 > P.S. My friends ARE posting on this day and their instances work!! 😤 > I feel so left out!! 😟 > Let’s get this sorted ASAP! ⚡ OK, that's a *lot* of Emoji - too much even for me! But if one of my users needs help, I'm there for them! As the feature works for me, I decided I'd ask for the output of the app. Maybe there'd be a clue in the minimal debugging output it had. I clicked on the link to the Codeberg repository and was hit be a 404! What? I clicked on the link to the user "simpleseaport2" but that was also broken. "Seriously, What’s Going On?! 🔍" It looks like Codeberg has been hit by a wave of spam bug reports. I read through the bug report again, slightly more awake, and saw just how content free it was. Yes, it is superficially well structured, the Emoji are a bit over-the-top but not the worst I've seen, and the emotional manipulation is quite insidious. A few weeks later, I got a bug report to a different repo. This one was also deleted before I could reply to it, see if you can spot that it is AI generated: > I've been trying to use the Threads tool to visualize some conversations but I'm running into a serious problem, and it's really frustrating! > When I input the URL for a post with a substantial number of replies, the script seems to hang indefinitely. I've waited more than 15 minutes on a couple of occasions, and nothing seems to happen. This is not what I expected, especially since the README mentions large conversations may take a long time, but doesn’t specify any limits or give guidance on what users should do if it doesn’t respond at all! > It's unclear what's actually happening here. Is the script failing silently? Is it the API timing out? Why isn’t there any sort of progress notification built into the tool? It feels like a complete dead end. > Can you please add some kind of error handling or logging feature to the Threads script? It would be helpful if it could at least inform the user when a timeout occurs or if the API response is simply taking too long. Additionally, could you clarify the maximum number of replies that can be handled? It’s really inconvenient to have no idea if the script is still processing or if it’s just broken. > Thanks for addressing this. I hope to see improvements soon.<li>The emotional manipulation starts in the first line - telling me how frustrated the user is.</li><li>It turns the blame on me for providing poor guidance.</li><li>Then the criticism of the tool.</li><li>Next, a request that I do work.</li><li>Finally some more emotional baggage for me to carry.</li> I'm not alone in getting these - [other people have also received similar spam]( ) To be fair to Codeberg, they are under attack and are trying to stop these specious complaints reaching maintainers. > > [> <path d="M74.7135 16.6043C73.6199 8.54587 66.5351 2.19527 58.1366 0.964691C56.7196 0.756754 51.351 0 38.9148 0H38.822C26.3824 0 23.7135 0.756754 22.2966 0.964691C14.1319 2.16118 6.67571 7.86752 4.86669 16.0214C3.99657 20.0369 3.90371 24.4888 4.06535 28.5726C4.29578 34.4289 4.34049 40.275 4.877 46.1075C5.24791 49.9817 5.89495 53.8251 6.81328 57.6088C8.53288 64.5968 15.4938 70.4122 22.3138 72.7848C29.6155 75.259 37.468 75.6697 44.9919 73.971C45.8196 73.7801 46.6381 73.5586 47.4475 73.3063C49.2737 72.7302 51.4164 72.086 52.9915 70.9542C53.0131 70.9384 53.0308 70.9178 53.0433 70.8942C53.0558 70.8706 53.0628 70.8445 53.0637 70.8179V65.1661C53.0634 65.1412 53.0574 65.1167 53.0462 65.0944C53.035 65.0721 53.0189 65.0525 52.9992 65.0371C52.9794 65.0218 52.9564 65.011 52.9318 65.0056C52.9073 65.0002 52.8819 65.0003 52.8574 65.0059C48.0369 66.1472 43.0971 66.7193 38.141 66.7103C29.6118 66.7103 27.3178 62.6981 26.6609 61.0278C26.1329 59.5842 25.7976 58.0784 25.6636 56.5486C25.6622 56.5229 25.667 56.4973 25.6775 56.4738C25.688 56.4502 25.7039 56.4295 25.724 56.4132C25.7441 56.397 25.7678 56.3856 25.7931 56.3801C25.8185 56.3746 25.8448 56.3751 25.8699 56.3816C30.6101 57.5151 35.4693 58.0873 40.3455 58.086C41.5183 58.086 42.6876 58.086 43.8604 58.0553C48.7647 57.919 53.9339 57.6701 58.7591 56.7361C58.8794 56.7123 58.9998 56.6918 59.103 56.6611C66.7139 55.2124 73.9569 50.665 74.6929 39.1501C74.7204 38.6967 74.7892 34.4016 74.7892 33.9312C74.7926 32.3325 75.3085 22.5901 74.7135 16.6043ZM62.9996 45.3371H54.9966V25.9069C54.9966 21.8163 53.277 19.7302 49.7793 19.7302C45.9343 19.7302 44.0083 22.1981 44.0083 27.0727V37.7082H36.0534V27.0727C36.0534 22.1981 34.124 19.7302 30.279 19.7302C26.8019 19.7302 25.0651 21.8163 25.0617 25.9069V45.3371H17.0656V25.3172C17.0656 21.2266 18.1191 17.9769 20.2262 15.568C22.3998 13.1648 25.2509 11.9308 28.7898 11.9308C32.8859 11.9308 35.9812 13.492 38.0447 16.6111L40.036 19.9245L42.0308 16.6111C44.0943 13.492 47.1896 11.9308 51.2788 11.9308C54.8143 11.9308 57.6654 13.1648 59.8459 15.568C61.9529 17.9746 63.0065 21.2243 63.0065 25.3172L62.9996 45.3371Z" fill="currentColor"></path>> ]( ) > [> Post by ]( )> [> @Codeberg]( ) > View on Mastodon > But, still, search the socials and you'll find a stream of frustrated developers. > Woke this morning to my first ever AI generated spam issue on a repo. Got it via email. When I went to check it out at Codeberg, it had already been moderated. Wonder how many others were affected.I immediately knew it was AI spam due to the overuse of emojis…🎉 > [> [image or embed]]( ) > — Jeff Sikes (> [> @bsky.box464.social]( )> ) > [> 24 April 2025 at 15:07]( )## [What's Going On⁉️](#whats-going-on%e2%81%89%ef%b8%8f )I can only think of a few possibilities - none of them particularly positive.<li>Attacking the viability of CodeBerg - make users abandon it for a different platform.</li><li>Attacking the attention of developers - make them unwilling to give attention where it is actually needed.</li><li>Attacking the integrity of users - make them less likely to receive help because they are mistaken for AI.</li><li>Maybe it is just a bored kid or an unethical researcher. Trying to find the limits of what a maintainer will recognise as spam?</li> Either way, AI bug reports like this are about as welcome as a haemorrhage in a jacuzzi. #AI #git #LLM #spam
## The NHS shouldn't outsource its QR codes QR codes are brilliant. They're a simple way to allow users to easily and quickly go to the right URl - no matter how complex. No more worrying about typing in long addresses or figuring out if that's a letter O or the number O. Scan and go! The best thing about QR codes is that they're free. It doesn't cost any money to generate one. They're an open standard with no middle-men. Users can go direct to your site! Except… Some people want to insert themselves into your conversation. Sometimes it is for malicious reasons, sometimes it is greed for user data, and sometimes it is just incompetence. Let's take this example - a health centre wants people to register. Scan the QR and get started. Fab! Photo shamelessly stolen from a LinkedIn contact. But what happens when you scan the QR code? Rather than taking you directly to an authoritative and trusted NHS.UK domain name, it sends you through https://register-with-gp.ht1.uk/.## [Who on earth are HT1.UK?](#who-on-earth-are-ht1-uk )According to [their website]( ), they're an automation company who are "on a mission to make the NHS the most advanced healthcare system in the world." Good for them. But what information are they collecting about users who traverse through their QR codes? If you take a look at [their privacy policy]( ) you won't find anything specific. Never mind, let's email their friendly privacy team. What's their email address? Of course, emailing that gets you back this error: Emoji! How fun!! So I emailed the new address to see what information they were collecting. Their response wasn't particularly informative. > because Healthtech-1 is a processor of information and the GP practice is the data controller any requests about how your data is handled should be made to the GP practice who can inform you of the information you requested. > … > I can confirm that there is no information stored about users who scan the QR codes and no cookies placed. But, of course, users have no way of verifying what this company is storing about them. There's simply no reason to use an untrusted 3rd party like this to provide either a QR code or an intermediary website.## [Why this is a problem](#why-this-is-a-problem )Trust is everything. People are *constantly* being scammed. One of the great things that GOV.UK did was to say "This here is our trusted brand. If you don't see GOV.UK in the URl bar - don't trust it!" The NHS should be doing the same. Every hospital, surgery, and clinic should have an NHS.UK domain name. When a user sees a link to a healthcare service which *doesn't* go through NHS.UK, they should feel suspicious and not click on it. There is no way as a regular user to know that HT1.UK is a trusted domain. What about HT1.biz? HT2.UK? NHS.info.ly? What happens if HT1 go bust or have their domain name hijacked? The NHS must stop the proliferation of these 3rd party domain names. They need to reinforce users' understanding that NHS.UK is the *only* trusted domain name for official NHS services. I'm sure HT1.UK aren't doing anything nefarious with the data of people who visit their QR codes. I'm sure they're not inserting tracking cookies or selling my data. But I shouldn't have to be sure. All users should be pointed *directly* to an NHS.UK domain without having to risk whether their details are going via a dodgy site. Here endeth the rant. #gdpr #nhs #privacy #qr
## Book Review: Throne of the Crescent Moon by Saladin Ahmed After reading [Saladin Ahmed's collection of short stories]( ), I was keen to read more. This book is fantastic! Fantasy books usually seem to be swords and dragons, set in a generic European country. Crescent Moon is scimitars and sorcery, and set in a mythical Middle-Eastern country. The writing is sublime. It feels like an ancient epic, translated a hundred years ago with archaic language left intact. It'll make good use of your eReader's dictionary to discover words like "ensorcelled". Amongst all the blood and magic, are literary gems like: > Zamia’s little laugh cut through him like a sword poisoned with pure happiness. But, perhaps the best thing about this, is that it reads like the *end* of a trilogy. The characters are all established, there's little exposition about the fantasy-word, the environment is richly textured. Above all, the characters are *tired*! It is a fast-paced, exciting, and entertaining book. Perfect for fantasy-lovers who fancy something a bit different from endless Game-of-Thrones rip-offs. #BookReview
## Towards a test-suite for TOTP codes Because I'm a massive nerd, I *actually try to read* specification documents. As I've ranted *ad nauseam* before, the current TOTP<a href="#fn:totp" class="footnote-ref" title="Time-based One Time Passwords. Not the TV show you remember from your youth, grandad." role="doc-noteref">0</a> spec is [irresponsibly obsolete]( ). The three major implementations of the spec - [Google]( ), [Apple]( ), and [Yubico]( ) - all subtly disagree on how it should be implemented. Every other MFA app has their own idiosyncratic variants. The [official RFC is infuriatingly vague]( ). That's no good for a security specification. Multiple implementations are great, multiple interpretations are not. So I've [built a nascent test suite]( ) - you can use it to see if your favourite app can correctly implement the TOTP standard. []( ) Please do contribute tests and / or feedback. Here's what the standard *actually* says - see if you can find apps which don't implement it correctly.## [Background](#background )Time-based One Time Passwords are based on HOTP - HMAC-Based One-Time Password. HOTP uses counters; a new password is regularly generated. TOTP uses time as the counter. At the time of writing this post, there have been about 1,740,800,000 seconds since the UNIX Epoc. So a TOTP with an period of 30 seconds is on counter (1,740,800,000 ➗ 30) = 58,026,666. Every 30 seconds, that counter increments by one.### [Number of digits](#number-of-digits )How many digits should your 2FA token have? Google says 6 or 8. YubiCo graciously allows 7. Why those limits? Who knows!? [The HOTP specification gives an *example* of 6 digits]( ). The example generates a code of 0x50ef7f19 which, in decimal, is 1357872921. It then takes the last 6 digits to produce the code 872921. The TOTP RFC say: > Basically, the output of the HMAC-SHA-1 calculation is truncated to obtain user-friendly values > [> 1.2. Background]( ) But doesn't say how far to truncate. There's nothing I can see in the spec that *prevents* an implementer using all 10. The HOTP spec, however, *does* place a minimum requirement - but no maximum: > Implementations MUST extract a 6-digit code at a minimum and possibly 7 and 8-digit code. Depending on security requirements, Digit = 7 or more SHOULD be considered in order to extract a longer HOTP value. > [> RFC 4226 - 5.3. Generating an HOTP Value]( ) (As a minor point, the first digit is restricted to 0-2, so being 10 digits long isn't significantly stronger than 9 digits.) Is a 4 digit code acceptable? The security might be weaker, but the usability is greater. Most apps will allow a *one* digit code to be returned. If no digits are specified, what should the default be?### [Algorithm](#algorithm )The given algorithm in the HOTP spec is SHA-1. > In order to create the HOTP value, we will use the HMAC-SHA-1 algorithm > [> RFC 4226 - 5.2. Description]( ) As we now know, SHA-1 has some fundamental weaknesses. The spec comments (perhaps somewhat naïvely) about SHA-1: > The new attacks on SHA-1 have no impact on the security of HMAC-SHA-1. > [> RFC 4226 - B.2. HMAC-SHA-1 Status]( ) I daresay that's accurate. But the TOTP authors disagree and allow a for some different algorithms to be used. The specification for HMAC says: > HMAC can be used with > *any*> iterative cryptographic hash function, e.g., MD5, SHA-1 [Emphasis added] > [> RFC 2104 - HMAC: Keyed-Hashing for Message Authentication]( ) So most TOTP implementation allow SHA-1, SHA-256, and SHA-512. > TOTP implementations MAY use HMAC-SHA-256 or HMAC-SHA-512 functions […] instead of the HMAC-SHA-1 function that has been specified for the HOTP computation > [> RFC 6238 - TOTP: Time-Based One-Time Password Algorithm]( ) But the HOTP spec goes on to say: > Current candidates for such hash functions include SHA-1, MD5, RIPEMD-128/160. These different realizations of HMAC will be denoted by HMAC-SHA1, HMAC-MD5, HMAC-RIPEMD > [> RFC 2104 - Introduction]( ) So, should your TOTP app be able to handle an MD5 HMAC, or even SHA3-384? Will it? If no algorithm is specified, what should the default be?### [Period](#period )As discussed, this is what increments the counter for HOTP. The [Google Spec]( ) says: > The period parameter defines a period that a TOTP code will be valid for, in seconds. The default value is 30. The TOTP RFC says: > We RECOMMEND a default time-step size of 30 seconds > [> 5.2. Validation and Time-Step Size]( ) It doesn't make sense to have a negative number of second. But what about one second? What about a thousand? Lots of apps artificially restrict TOTP codes to 15, 30, or 60 seconds. But there's no specification to define a maximum or minimum value. A user with mobility difficulties or on a high-latency connection probably wants a 5 minute validity period. Conversely, machine-to-machine communication can probably be done with a single-second (or lower) time period.### [Secret](#secret )Google says the secret is > an arbitrary key value encoded in Base32 according to RFC 3548. The padding specified in RFC 3548 section 2.2 is not required and should be omitted. Whereas Apple says it is: > An arbitrary key value encoded in Base32. Secrets should be at least 160 bits. Can a shared secret be a single character? What about a thousand? Will padding characters cause a secret to be rejected or can they be safely stripped?### [Label](#label )The label allows you to have multiple codes for the same service. For example Big Bank:Personal Account and Big Bank:Family Savings. The Google spec is slightly confusing: > The issuer prefix and account name should be separated by a literal or url-encoded colon, and optional spaces may precede the account name. Neither issuer nor account name may themselves contain a colon. What happens if they are *not* URl encoded? What about Matrix accounts which use a colon in their account name? Why are spaces allowed to precede the account name? Is there any practical limit to the length of these strings? If no label is specified, what should the default be?### [Issuer](#issuer )Google says this parameter is: > **Strongly Recommended**> The issuer parameter is a string value indicating the provider or service this account is associated with, URL-encoded according to RFC 3986. If the issuer parameter is absent, issuer information may be taken from the issuer prefix of the label. If both issuer parameter and issuer label prefix are present, they should be equal. Apple merely says: > The domain of the site or app. The password manager uses this field to suggest credentials when setting up a new code generator. Yubico equivocates with > The issuer parameter is recommended, but it can be absent. Also, the issuer parameter and issuer string in label should be equal. If it isn't a domain, will Apple reject it? What happens if the issuer and the label don't match?## [Next Steps](#next-steps )<li>If you're a user, <a href="https://codeberg.org/edent/TOTP_Test_Suite">please contribute tests</a> or give feedback.</li><li>If you're a developer, please check your app conforms to the specification.</li><li>If you're from Google, Apple, Yubico, or another security company - wanna help me write up a proper RFC so this doesn't cause issues in the future?</li><li id="fn:totp" role="doc-endnote"><p>Time-based One Time Passwords. Not the TV show you remember from your youth, grandad.&nbsp;<a href="#fnref:totp" class="footnote-backref" role="doc-backlink">↩︎</a></p></li> #2fa #CyberSecurity #HTOP #MFA #OpenSource #totp