Shellsharks Blogroll - BlogFlock2026-05-07T08:47:03.457ZBlogFlockAdepts of 0xCC, destructured, fLaMEd, Trail of Bits Blog, Aaron Parecki, Westenberg, James' Coffee Blog, gynvael.coldwind//vx.log (pl), joelchrono, Evan Boehs, Kev Quirk, cool-as-heck, Posts feed, Sophie Koonin, <span>Songs</span> on the Security of Networks, cmdr-nova@internet:~$, Johnny.Decimal, Werd I/O, Robb Knight, Molly White, Hey, it's Jason!, Terence Eden’s BlogThe war between fast and legitimate is here - Westenberg69fbe42dccfa0c0001fd80f32026-05-07T01:18:04.000Z<img src="https://www.joanwestenberg.com/content/images/2026/05/photo-1727434032773-af3cd98375ba.jpeg" alt="The war between fast and legitimate is here"><p>The European Union spent four years drafting the AI Act, with OpenAI shipping GPT-4 to a hundred million users in two months. By the time Brussels finalised its definitions of "high-risk" systems, the systems in question had moved twice and grown various new appendages. The regulators were neither stupid, nor incompetent; they were doing what regulators are supposed to do. They consulted, they ran impact assessments, they debated wording, they translated everything into twenty-four languages, they voted in committee, and voted again, and harmonised national positions, and produced something defensible.</p><p><em>The process took the time it took.</em></p><p>This is the whole problem, and - to my mind - one of the central tensions of the decade. The institutions best able to move at the speed of the real world are the institutions we trust the least; while the institutions we trust the most are too slow, and too cumbersome to matter.</p><p>I'm not here to mount a defence of the idiotic spate of DOGE inspired initiatives; I want to argue instead for a degree of dispassionate realism about where we are, and where we're either doomed or blessed to go next. </p><p>Legitimacy is a slow technology, built of procedure, of precedent, of deliberation, and the gradual accumulation of trust across cycles of failure and correction, across generations, across years. You can't accelerate it without breaking it, because the whole point of due process is that it slows you down - it must slow you down. The whole point of peer review is that someone qualified gets to object and point out the things that should not be broken. The whole point of constitutional limits is that the people in charge can't just do whatever the hell they want, whenever the hell they feel like it.</p><p>When you strip those constraints out, you get speed - I'll grant. </p><p>It's the inevitable outcome of authority concentrating, accountability loosening, feedback loops collapsing.</p><p>The story of the twentieth century was, in part, the story of the slowest institutions racing to catch up to the fast. Markets ran ahead, regulators followed; inventors invented, courts adjudicated; technology disrupted, and culture absorbed - one way or another. The catching-up was painful and often violent, but it happened on a timescale that human institutions could survive.</p><p>I doubt this is still true // possible.</p><p>We're too far apart and drifting. </p><p>Facebook reached a billion users before any major democracy had a coherent policy position on what it was. By the time the policy machinery wound itself up, Facebook had already restructured politics in dozens of countries, undermined several elections, and pivoted into something else entirely. Whatever the regulators eventually produced was a response to a previous version of the company, but the current version had moved on.</p><p>The FDA's approval process is designed to be slow because the cost of a fast-tracked failure is, not to be ghoulish, literal bodies in a literal morgue. But the gene-editing tools available to a competent graduate student in 2026 would have required a fortified national laboratory in 1996. The technologically possible has outstripped the institutionally permissible - to the point that whole industries are migrating to jurisdictions with looser rules. The regulatory tortoise is still doing its job, but it's not the only animal in the race - not anymore.</p><p>We build legitimate institutions around legitimate problems - but the world changes. The institution remains optimised for the old problem, and gradually becomes ceremonial. Like the British monarchy. Or the United Nations. Or the academic peer review system.</p><p>The fast institutions I'm describing are rarely more competent. They're frequently, catastrophically worse. Theranos was fast. FTX was fast. WeWork outran its own ability to function. The history of speed without legitimacy is a history of fraud and human wreckage and a great many self-justifying memoirs published with the gift and grift of hindsight. Every time someone tells you that move-fast-and-break-things is a good strategy, you should ask what got broken and whose problem it's going to be to fix it.</p><p>But the answer to "fast institutions sometimes blow up" can't be that the slow ones are therefore vindicated; slow institutions blow up too. They just blow up in slow motion. The 2008 financial crisis was a slow blow-up. The opioid epidemic was a slow blow-up. The housing crisis in every major Anglophone city is a slow blow-up that's been unfolding for two decades while the relevant planning bodies follow procedure with admirable rigour. A failing institution can fail for a generation before anyone is willing to admit that the failure is structural rather than a rough patch. I've known marriages in that vein. I've known states in that vein. I've known companies, etc. </p><p>The new compact will involve some level of negotiated settlement between the two species. And I don't have a clear picture for what that settlement looks like - yet. My optimism leads me to believe (or at least, hope) that fast institutions adopt enough procedural integrity to earn the trust they lack, and slow institutions adopt enough adaptive capacity to remain relevant.</p><p>The pessimist in me (of whom I remain rather less fond) is convinced that the divergence only accelerates from here, and there's a betting chance we end up with a two-tier civilisation. The fast tier governs through algorithms, contracts, and platform policy; the slow tier governs through statute, precedent + parliamentary procedure. The two tiers nominally coexist but operate in different timeframes and address different populations. The fast tier handles anyone who is rich, technical, mobile, or willing to live within the rules of private platforms. The slow tier handles everyone else, in the residual physical world of borders, courts, parliaments, and the postal system. This is, broadly, what is already happening.</p><p>I'm wary of declensionist takes that romanticise the slow tier as "the last fortress of human dignity." There is, after all, nothing inherently dignified about waiting twelve years for a permission slip, or in the way the British NHS treats its waiting lists, or the American immigration "system" its most vulnerable applicants.</p><p>Procedure can and frequently does ossify into the basest of inhumane cruelty. Slow institutions aren't virtuous because they're slow; they're virtuous if and when their slowness produces the legitimacy it was designed to produce. </p><p>When slowness becomes a substitute for legitimacy, you have a Soviet-era clusterfuck.</p><p>But can legitimacy can be rebuilt at speed?</p><p>Can you construct an institution that is both accountable and reasonably fast?</p><p>Actual legitimacy seems to require a patience of movements and monuments that competitive markets and accelerating tech does not // will not allow. You can't do the equivalent of British common law in five years. You can't do peer review at the speed of preprint. You can't do constitutional design at the speed of a Slack thread.</p><p>What you can dov - possibly - is accept the trade-off honestly. Build fast institutions for things where speed is the binding constraint and slow institutions for things where trust is the binding constraint, and stop pretending that the same body can do both. The current confusion comes from expecting our slow institutions to keep up with the news cycle, and from expecting our fast institutions to behave with the gravitas of a constitutional court.</p><p>Neither of those expectations is ever going to be satisfied.</p><p>In the late medieval period, the Catholic Church was still the central legitimacy-conferring institution - but it had already stopped being operationally dominant. New money, new printing, new science, new political forms grew up alongside the old hierarchy and (eventually) displaced it. The displacement took two centuries and several wars, and it was far from orderly, but it happened all the same. The thing that came out the other side, the modern nation-state with a codified law and a standing armies and a civil service and a bureaucracy , eventually achieved some synthesis of speed + legitimacy that none of the contesting parties had managed alone.</p><p>We are probably at the starting point of an analogous process.</p><p>There are 2 things about that period worth flagging:</p><ol><li>The first is that the new institutions didn't announce themselves as such. The Medici were a bank before they were a political force; and the Dutch East India Company was a trading concern before it was effectively a state. The legitimacy came afterwards, retrofitted to whatever the speed had already built.</li><li>The second is that the Church didn't vanish. It kept performing its older functions for a population that wanted older things from it, while the operational running of European civilisation passed to bodies that didn't yet have the moral authority but were already doing the governing.</li></ol><p>How much of the actual coordination of modern life is now happening inside corporate platforms and private networks that have no constitutional standing whatsoever?</p><p>The practical advice is to know which game you're in. If you're running a startup, you're in the speed game, and pretending you're running a regulatory agency is a category error. If you're running a regulatory agency, you're in the legitimacy game, and it's something of a vapid conceit to pretend to be running a startup. Most of the dysfunction in contemporary institutions comes from this same category confusion. The legislators who tweet like influencers vs the CEOs who issue manifestos like political leaders. The universities who try to brand themselves like consumer products vs the journalists who behave like activists and then complain that no one trusts them anymore. Each of these is an institution trying to play a game for which it was neither designed nor built, and losing the legitimacy of its native game without acquiring the speed of its aspiration.</p><p>Pick a side and commit. Find a functional substitute for the legitimacy you lack, and find it before the next scandal makes your shortcomings impossible to ignore; or find a way to remain relevant despite your pace, and stop confusing the pomp of authority with its substance.</p><p>The hybrids will struggle. The pretenders - the institutions that perform speed without being fast or perform legitimacy without being legitimate - will be eaten first.</p><p>I'm not certain anyone "wins" this in any way the word "win" is usually applied. But in a war between institutions, the folks on the losing side are usually the last to figure out they're at war in the first place.</p>Looking for a bike - Joel's Log Fileshttps://joelchrono.xyz/blog/bike2026-05-06T21:10:00.000Z<p>For a couple weeks now, I had the itch to get into cycling again. Not as a sport or a hobby, but as a tool, to commute and go to places nearby.</p>
<p>Around 10 years ago I used to have a pretty neat bike, it was a bit big for me but I could use it no problem. It was gifted to me by my uncle and I really liked it. However, we moved, and my dad told me he had to sell it to a friend.</p>
<p>I was very, very upset, but we couldn’t bring it with us, and eventually had to come to terms with it.</p>
<p>A couple years later, my dad gave me another bike, but I kind of neglected it for many years, I don’t know if it felt like betrayal or what but I only really used it twice and it never felt right. The bike was smaller too, with 24” wheels, and I was already (barely) an adult, so I felt too slow with it.</p>
<p>When I had the day off on Monday I decided I’d try and get that old bike up and running. It didn’t work out.</p>
<p>The tires were deflated and had some cracks, so I carried it with me and walked to a workshop to see if they could fix them—it was closed. I walked back and left the bike home, and took a bus to Walmart to buy a and get there myself—it didn’t work. The bike was in a garage/storage room, a hand-made one with an aluminum ceiling and plenty of gaps, it was easy for moisture, dust and other elements to set in, so there’s that.</p>
<p>Anyway, I figured it wasn’t worth it, and decided that it was too small for me, so I’d get a used bike from a workshop instead.</p>
<p>Yesterday I went back to work, but after my shift was over I went to a bike shop nearby and saw a few options.</p>
<p>Unfortunately I was feeling kinda introverted and could only ask something like “a bike with different speeds that can go uphill and through dirt roads and stuff”, which was as generic as it gets. Anyway I was still offerend a few options, some were way out of my budget (which is around 450 bucks), but some others seemed pretty decent.</p>
<p>I didn’t take note of the brands for this post, I could only remember a <em>Giant</em> which was in reparations and would cost around 290 USD once fixed. I may return today once work is over and try and make some more questions. I literally walked to four other bike workshops from there all over the city—maybe just walking is fine—but most were closed or had a very small catalog.</p>
<p>Once I returned home I checked online for some reviews of affordable bikes in my country. I saw a few interesting options from brands such as <a href="https://alubike.com.mx">Alubike</a>, <a href="htttps://gravel.mx">Gravel</a> and <a href="https://bicicletasmercurio.com.mx">Mercurio</a> that seem to be popular in México and with prices to match the economy here. I’ve been looking at the <em>Gravel Everest</em> and the <em>Mercurio Ranger</em>, for the most part, with the <em>Alubike Sierra</em> as a more expensive option which may be worth the extra.</p>
<p>There are some pros and cons to consider here, I made a list with some more random thoughts:</p>
<ul>
<li>Buying online will require me to some assemble the bike, <em>but</em> learning how to do it might be fun.</li>
<li>I could still assemble it wrong and ruin some part, <em>but</em> I am pretty confident in myself to figure it out.</li>
<li>For the price of a new budget-friendly bike, I could buy a used one that’s higher quality.</li>
<li>I am not super sure the bikes available will be higher quality, but it may not matter that much anyway.</li>
<li>Buying used on a bike shop will let me try it immediately and check how it performs too.</li>
<li>I could also buy and assemble a new and more known brand bike online, get it serviced, and call it a day.</li>
<li>A bike shop will guarantee that it’s well assembled and maintained, I can get more equipment there.</li>
<li>Supporting a small business that brings old bikes to life sounds kinda cool.</li>
<li>I should still fix my older bike, maybe I could resell it after all.</li>
</ul>
<p>Anyway, I am still on the fence on all of this, I am trying to figure out where does a bike fits in my current lifestyle. I don’t know if I’d use it to commute to work, since I need to go through a highway with a lot of trailers and trucks and it just seems super scary. By the time I get home it’s already kind of dark too.</p>
<p>So would I only use it during the weekends? Or I should learn how to be safe on the bigger roads? I also really enjoy just taking the bus and having an hour to spend there without worries.</p>
<p>Decisions must be made once again…</p>
<p>This is day 62 of <a href="https://100daystooffload.com">#100DaysToOffload</a>.</p>
<p>
<a href="mailto:me@joelchrono.xyz?subject=Looking for a bike">Reply to this post via email</a> |
<a href="https://fosstodon.org/@joel/116529709826115710">Reply on Fediverse</a>
</p>MGK At The Spark Arena - The Weblog of fLaMEdhttps://flamedfury.com/posts/mgk-at-the-spark-arena/2026-05-06T20:29:28.000Z<p>What’s going on, Internet? Last night, me, my sister-n-law and our friend went into town to see the MGK gig as he brought his Lost Americana tour to Auckland for his only New Zealand show.</p>
<p>MGK, aka Machine Gun Kelly, aka Colson Baker is one of those artists where it’s probably good to separate the art from the artist as he seems to be a ball bag in real life.</p>
<p>I never paid any attention to him while he did hip hop records, but as soon as I saw the <a href="https://www.youtube.com/watch?v=wSdT-SArM2Q" rel="noopener">Bloody Valentine</a> I was hooked. The album, <a href="https://flamedfury.com/recordshelf/records/tickets-to-my-downfall/"><strong>Tickets To My Downfall</strong></a> was the exact type of nostalgia I needed for early 2000s pop punk in 2020.</p>
<p>I skimmed through <strong>Mainstream Sellout</strong> when it released and never came back to it. We got <strong>Lost Americana</strong> last year which was a step up from the second record and I listened to it a bunch. But we also got <strong>Tickets To My Downfall All Access</strong> last year, the 5th anniversary reissue. Original tracklist, the bonus tracks from the <strong>SOLD OUT Deluxe</strong>, plus 5 new unreleased tracks. Whew. It was good to hear some more tracks from that era.</p>
<p>We managed to grab reseller tickets, paid less for the three of us combined than a single ticket at face value, and the seats were pretty decent for where we ended up. Sweet as.</p>
<p>Anyway, the show was good. It kicked off on time, it was loud, there were guitars and drums, only a couple throwbacks to the rap days and one or two songs from Sellout. It didn’t take long to get right into the <strong>Tickets To My Downfall</strong> songs and that was all I needed to hear.</p>
<p>The stage was on theme too. A model of the Statue of Liberty’s head looming above with a cigarette hanging out her mouth, and his mic stand was a giant cigarette to match. Lost Americana indeed.</p>
<p>The crowd around us were all there for the same reasons. Singing along with strangers who love the same songs is one of the best bits of a gig, especially the Tickets ones. <em>Title Track</em>, <em>Drunk Face</em>, <em>Forget Me Too</em>, <em>Concert For Aliens</em>, <em>Jawbreaker</em>, <em>Nothing Inside</em>, all hit. The cover of Paramore’s <em>Misery Business</em> was expected, and rocked. My absolute highlight was belting out <em>Bloody Valentine</em> word for word with everyone around me. <em>My Ex’s Best Friend</em> my second favourite on the album, still can’t get that one out of my head. We had a great time, a fantastic night out.</p>
<p>Damn, what a show. I’ll see it again without hesitation.</p>
<p>Hey, thanks for reading this post in your feed reader! Want to chat? <a href="mailto:hello@flamedfury.com?subject=RE: MGK At The Spark Arena">Reply by email</a> or add me on <a href="xmpp:flamed@omg.lol">XMPP</a>, or send a <a href="https://flamedfury.com/posts/mgk-at-the-spark-arena/#webmention">webmention</a>. Check out the <a href="https://flamedfury.com/posts/">posts archive</a> on the website.</p>
Lamy Safari 2026 Neon Pink and Neon Yellow - Robb Knight • Posts • Atom Feedhttps://rknight.me/blog/lamy-safari-2026-neon-pink-and-neon-yellow/2026-05-06T20:11:34.000Z<figure><img src="https://cdn.rknight.me/site/2026/lamy-safari-neon-yellow-neon-pink-open.jpg" alt="Two fountain pens, one yellow one pink, on a green cutting mat with their caps off." /></figure>
<p>Today my 2026 Lamy Safaris arrived even though Lamy themselves still haven't actually announced them. Thank you to the fine folks at <a href="https://fontoplumo.nl">Fontoplumo</a> for putting these on sale early so I can get my grubby hands on them.</p>
<p>The yellow is striking in person and is exactly as bright as you think it will be. The pink doesn't jump out at me quite as much but the colour is still great. I <em>think</em> I would prefer the pink to have the black hardware because on the yellow it looks fantastic.</p>
<figure><img src="https://cdn.rknight.me/site/2026/lamy-safari-neon-yellow-neon-pink.jpg" alt="Two fountain pens, one yellow one pink, on a green cutting mat with their caps on" /></figure>
<p>I'm not going to "review" these - they're glossy Safaris, you know what you're getting and if you don't, smarter people than me <a href="https://www.penaddict.com/blog/2008/11/24/review-lamy-safari.html">have reviewed them before</a>.</p>
<p>Here's the new neon pink at the bottom compared to a pink Balloon, the AL Star Fiery, and the standard Safari pink. When it's next to the standard pink it's more obvious how bright it is.</p>
<figure><img src="https://cdn.rknight.me/site/2026/lamy-safari-neon-pink-comparison.jpg" alt="Four fountain pens on a green cutting board, of various shades of pink" /></figure>Hot Cross Buns - The Weblog of fLaMEdhttps://flamedfury.com/posts/hot-cross-buns/2026-05-06T14:58:18.000Z<p>What’s going on, Internet? I’ve been eating hot cross buns since January. Can you believe it? Growing up I remember that seasonal treats would come out a week or two before the associated holiday. As you might have noticed yourself, these days the bakeries, and supermarkets are pushing them out months before the event. For hot cross buns though, I’m not complaining.</p>
<p>I’m not a big fan of raisins, or orange spice, and whatever else they use to make hot cross buns. But when they’re combined and baked and the end result is a fresh hot cross bun, I’m right there for all of it.</p>
<p>While Easter is over now and my stash of hot cross buns has dried up, I’m both saddened and relieved. Since January I’ve been having at least one hot cross bun every morning with my hot chocolate (I gave up caffeine, and coffee with it, years ago). My favourite method of heating these delicious buns these days is in the air fryer. Cut in half and 170°C on the bake function for 4 minutes does the trick. Then a layer of butter. I’m not talking about a measly spread of butter. Nope. I’m talking about a slice of butter as thick as a slice of cheese. Then the goal is to eat it as quickly, but not too quickly before the butter melts and drips everywhere.</p>
<p>Damn they’re so good.</p>
<p><strong>Daily Bread</strong> were the standout, but at the price I only sprung for them once. They use an Italian sourdough starter, multiple awards every year, you can taste why. Daily Bread also bakes a collab bun for <strong>Farro Fresh</strong>, sold right alongside the originals — slightly bigger, half the price, probably not the same starter but still super good. <strong>Bakers Delight</strong> ran a little drier than those two, though the size meant I could load them up with even more butter.</p>
<p>During our road trip down to Martinborough I got to try a few. <strong>The Stables</strong> in Greytown had a hot cross doughnutm the dough was good but the sugar coating wasn’t really my thing. I would have preferred a standard bun than the doughnut hybrid. <strong>The French Baker</strong>, also in Greytown, delivered the real deal. But the surprise was <strong>Jean’s</strong>, a small bakery in Upper Hutt that my wife loves to visit. Crazy delicious like all of their baked goods. Next year I’m getting a box shipped up to Auckland the moment they’re available. I was a bit gutted when I demolished the last one the other day.</p>
<p>I’m relieved I get a break until next year. If they were around all year I’d be in real trouble. But who am I kidding? I’m already counting down to next year’s batch. 🤙</p>
<p>Hey, thanks for reading this post in your feed reader! Want to chat? <a href="mailto:hello@flamedfury.com?subject=RE: Hot Cross Buns">Reply by email</a> or add me on <a href="xmpp:flamed@omg.lol">XMPP</a>, or send a <a href="https://flamedfury.com/posts/hot-cross-buns/#webmention">webmention</a>. Check out the <a href="https://flamedfury.com/posts/">posts archive</a> on the website.</p>
My Inital Thoughts On Thunderbird Pro - Kev Quirkhttps://kevquirk.com/my-inital-thoughts-on-thundermail2026-05-06T14:01:00.000Z
<p>Yesterday I received an email from the Thunderbird team inviting me to join a preview of their new hosted email service, <a href="https://www.tb.pro">Thunderbird Pro</a>. I <em>love</em> email, so was very keep to sign up and test it out.</p>
<p class="notice">Before we get into this, I want to say that Thunderbird Pro is still under active development, please bear that in mind. Also, these are just my opinions, please don't get butthurt.</p>
<h2>What is Thunderbird Pro?</h2>
<p><a href="https://kevquirk.com/stop-explaining-what-things-are">I hate it when people explain what things are</a> in a blog post, but I think it's warranted here since Thunderbird Pro (TB Pro) is a new product, so people may not know what it is.</p>
<p>With that in mind, TB Pro is a hosted email service by the <a href="https://thunderbird.net">Thunderbird</a> team that includes email, contacts, calendar, secure file sending, and an appointment system that lets people book time with you.</p>
<p>It costs $6/month (paid yearly) and for that you get:</p>
<ul>
<li>30 GB of mail storage</li>
<li>60 GB of Send storage</li>
<li>15 Email aliases</li>
<li>3 custom domains</li>
</ul>
<h2>My initial thoughts</h2>
<p>So here's my thoughts - of which I have many, so I'll just list them out, then pick a few to talk about in more detail. Otherwise this will be a very long post.</p>
<ul>
<li>No webmail, <a href="https://ideas.tb.pro/p/webmail-for-thundermail">it's being worked on though</a>.</li>
<li>Was easy to setup on the Thunderbird app - just had to login (my Zoho mail account auto-detects server settings, so not much harder though).</li>
<li>Doesn't configure aliases automatically in Thunderbird.</li>
<li>Prompts to add calendar and contacts via a single click when setting up in Thunderbird. That was a nice touch.</li>
<li>No way to export all DNS records as a zone file when adding a custom domain.</li>
<li>I think the 15 alias/3 domain limit is arbitrary and pointless.</li>
<li>If you setup a catch-all for a custom domain, you can send from <code>[anything]@</code> which negates the 15 alias limitation.</li>
<li>Appointments app is weird.</li>
<li>Couldn't work out how to setup Send in Thunderbird.</li>
<li>Admin UI is clunky and has a number of UI issues.</li>
<li>No option to add additional mailboxes (understandable as this is a preview).</li>
<li>30GB is way too much storage for me. I'd like to see smaller, cheaper tiers.</li>
</ul>
<p>I think the lack of webmail is a huge miss. Every email hosting service I can think of comes with webmail - many people access their mail on desktop via the browser, so I'd have liked to see that up front.</p>
<p>Having said that, maybe that's not the market Thunderbird are going for with this service. If so, maybe a lack of webmail is fine. I'd prefer to have the flexibility to check my mail from anywhere though.</p>
<p>I don't understand the 15 alias and 3 domain limitation. They cost nothing - they're just a line in a config file. Plus, adding a catch-all allows you to both send and receive email to/from <code>[anything]@yourdomain.com</code>, which renders the alias limit even more pointless.</p>
<p>I'd like to see these limitations removed.</p>
<h3>Appointment service</h3>
<p>The Appointment feature lets people book time with you directly. Think <a href="https://calendly.com/">Calendly</a>, baked into your email service. If you're a freelancer or consultant who lives and dies by booking links, that's probably a nice convenience. For everyone else, it's likely redundant.</p>
<p>Those who need it probably have a solution already, and those who don't will just ignore it. I'm in the latter camp, so there's no value for me.</p>
<p><img src="https://kevquirk.com/content/images/my-inital-thoughts-on-thundermail/appointments.webp" alt="Appointments" />
<em>Thundermail Appointments</em></p>
<h3>Send service</h3>
<p>Unfortunately I couldn't test the Send service. On the dashboard it says:</p>
<blockquote>
<p>To use Send, you must enable it in Thunderbird Desktop. Download the app and sign in to Thunderbird Pro from the Thunderbird menu.</p>
</blockquote>
<p>For the life of me I couldn't find an option for Send within Thunderbird, so I couldn't test. Shame.</p>
<p>I'm using the Flatpak, which is currently on v140.10.1, and I see v150 is out, so that may be why. But the Flatpak is maintained by the Thunderbird team, so I would have expected this to all be sorted before the allowed paying customers to get their hands on <em>Pro</em>.</p>
<p>There is a support card on the Send dashboard, with an option to get help. Clicking that opens the Thunderbird docs in a new tab, showing nothing but a notice box containing <code>$ thunderbird --version=pro</code>. So something is broken.</p>
<p>Speaking of broken things, there were a number of other ugly UI notices and warning elements that displayed while getting set up. It just lacks polish, which I would have expected to be ironed out by the time consumers are getting their hands on it.</p>
<h2>Final thoughts</h2>
<p>If I'm honest, my first impressions are underwhelming. I get that this is an early preview but for the price, services like <a href="https://mail.zoho.eu">Zoho</a> and <a href="https://fastmail.com">Fastmail</a> are better services, and better value for money.</p>
<p>I don't regret signing up though - it's important to support open source services, and as Thunderbird Pro matures, it will hopefully evolve into a service that can contend with the OG's in this space.</p>
<p>If it does, I'll consider moving over fully. But for now, I'm considering my subscription a donation to Thunderbird, as I'm a very happy user of their email app.</p> <div class="email-hidden">
<hr />
<p>Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️</p>
<p>You can <a href="mailto:19gy@qrk.one?subject=My%20Inital%20Thoughts%20On%20Thunderbird%20Pro">reply to this post by email</a>, or <a href="https://kevquirk.com/my-inital-thoughts-on-thundermail#comments">leave a comment</a>.</p>
</div>
Emotional regulation is a dying art. - Westenberg69fa8961ccfa0c0001fd807f2026-05-06T00:21:50.000Z<img src="https://www.joanwestenberg.com/content/images/2026/05/photo-1617209597807-5528a9767db7.jpeg" alt="Emotional regulation is a dying art."><p>There was a time when adults could feel something without screaming at you about it. We could disagree, hard, in a meeting and walk out with our faces still attached. Bad news arrived at the dinner table and the meal finished anyway. Call it discipline: the capacity to feel a thing in full and still choose what to do next.</p><p>That capacity is going the way of the Buffalo. You can watch it disappear in real time on any platform that rewards reaction; the faster the feedback loop, the worse the regulation. People are unleashing their feelings, unbounded and uninhibited, before they’ve finished having them, which means they aren’t really having them at all. They’re skipping the inner step where a person sits with a sensation and decides whether or not it deserves to leave the body.</p><p>The new orthodoxy says suppressing emotion is harmful. This might be true, but outside of a therapist’s office, it’s trivial. Suppression and regulation are different animals. Suppression is shoving the feeling into a closet and pretending it isn’t there until it crawls out twenty years later as an autoimmune disease; regulation is letting yourself feel the feeling, in full, while keeping your hands on the wheel of the car. We’ve collapsed the distinction.</p><p>Look at how grown people now speak about minor friction. Someone disagrees with them at work and they describe it as “harm.” Someone fails to text back within an acceptable window and they say their boundary has been violated. The vocabulary of clinical psychology has been borrowed wholesale and applied to ordinary life, where it has begun to function as a permission slip. If every discomfort is trauma, then every reaction is justified, and the work of metabolizing your experience becomes either optional or the domain of the privileged.</p><p>This is downstream of a cultural shift that confuses authenticity with reactivity. The assumption is that whatever you feel first, raw and unmediated, is the real you, and anything else is a performance. In my experience, the opposite is closer to the truth. The real self is the part of you who survives the first reaction; the part that can be angry and still be kind, scared and still steady. The reactive self is a child throwing food.</p><p>Calling the food-throwing “brave” is a mistake.</p><p>Actual children, watching this, are absorbing the model with terrifying efficiency, and they’re growing up in homes where the adults rage-text their bosses and stage public meltdowns in airport terminals while filming themselves. The lesson they’ll enact is that feelings are emergencies, and emergencies require an audience. Walk into any third-grade classroom now and you’ll find children who can name fourteen emotions and regulate none of them. They already have the vocabulary of a therapist but they maintain the impulse control of a puppy.</p><p>Some of this is technological. Phones reward a specific kind of nervous system, twitching first and thinking later. The dopamine architecture that hooks you on slot machines hooks you on outrage, and the platforms have figured out that a regulated person is a bad customer. The regulated close the app, but the dysregulated person scroll until four in the morning, bleeding cortisol and efficiently monetized.</p><p>But laming the phone lets too many people off the hook. Phones inherited the tantrum and scaled it; the deeper rot is philosophical. Several decades of therapeutic culture, well-meaning and badly executed, have taught generations that the goal of inner life is to express and never to contain. Containment has been rebranded as toxic, and composure rebranded as being cold.</p><p>For all my critiques of the philosophy’s Reddit-bound adherents, the Stoics weren’t automatons; Marcus Aurelius wept for his son, and Epictetus had been a slave whose owner crippled him for sport. They knew exactly how much the world hurt, and they wrote about it unapologetically. Their “innovation” was the claim that our hurt <em>is not the last word</em>. Between stimulus and response there exists a space, and in that space a person with agency can choose, and be responsible for that choice. A human being, however battered, retains a small and sovereign workshop where they make and remake and rebuild and mend themselves. That workshop is the only piece of territory that can’t be confiscated by circumstance - lose access to it and you lose yourself.</p><p>A good many people now are locked out of their own workshops. They feel a thing and the thing feels them right back, and there’s no daylight between the two.</p><p>The art of emotional regulation is dying because the conditions that taught it have been removed. We’ve lost slow time and private time; we’ve lost the time when no one asked what you thought before you’d finished thinking it. A whole generation of children has watched the adults around them treat every passing affect as a press release, and they’re learning to do the same. But you can’t regulate what you’ve already broadcast - and you can’t reclaim a workshop you’ve turned into a stage.</p>How I use my phone - James' Coffee Bloghttps://jamesg.blog/2026/05/06/how-i-use-my-phone2026-05-06T00:00:00.000Z
<p>In my last two blog posts, I discussed how I use <a href="https://jamesg.blog/2026/05/04/using-greyscale">greyscale mode on my phone</a> and <a href="https://jamesg.blog/2026/05/05/night-shift">night shift on my computer</a>. These blog posts were inspired by discussions happening in the IndieWeb community about greyscale, and now, more generally, how we use our phones.</p><p>My phone, an iPhone, is an invaluable tool to me. Last year, though, I realised that there are some things that I really prefer doing on desktop devices. I prefer watching videos on computers. I prefer creating and updating pages on my website with my computer, although I liberally take notes for blog posts on my phone. Meanwhile, I use my phone for things I need to do on the go: taking notes of observations that I may want to develop in my writing, consulting maps, messaging, among other things.</p><p>Indeed, many of my blog posts start off as notes that I have written while out and about in the world; having a tool to hand to capture notes quickly is invaluable to me. I could use a notebook, but I prefer using a phone.</p><h2 id="applications">Applications</h2><p>Generally, my approach to using my phone is that I like to have as few applications installed as possible – only those that I actively use. For example, I have apps I need for travel like booking train tickets and maps, messaging apps, a music and podcast app, and apps for banking. I don’t have any social media on my phone. I don’t use social media any more, except for my occasional reply I write on this site and send to a Mastodon post using <a href="https://fed.brid.gy/" rel="noreferrer">Bridgy Fed</a>. <sup class="footnote-reference" id="f-1"><a href="https://jamesg.blog/longform-feed#1">1</a></sup> <sup class="footnote-reference" id="f-2"><a href="https://jamesg.blog/longform-feed#2">2</a></sup>. I only download email on my phone if I am going on a trip and will likely need some emails for reference (i.e. tickets, confirmations).</p><p>If I need an application for one context, such as for a trip away somewhere, I will install the application before I go and delete it when I get back home. This active gardening reduces the number of icons on my home screen, which I like to do. Fewer icons makes the home screen calmer.</p><h2 id="conversations">Conversations</h2><p>If I am in conversation with someone in real life, I don’t use my phone unless I'm finding information to help advance our conversation. My philosophy is that if I have the pleasure of someone’s company, I shouldn’t be on my phone. I occasionally slip, but I proactively avoid my phone when I am with others.</p><h2 id="privacy-and-notifications">Privacy and notifications</h2><p>I have my phone configured with as many privacy-respecting settings set up as possible. Location Services is turned off for almost every application. Almost no applications can access my camera. I have Personalised Ads turned off in the Privacy & Security settings.</p><p>I have Do Not Disturb set at all times, although phone calls get through as normal. Do Not Disturb mode helpfully removes all the (1) or red dot icons from applications. Indeed, generally, I don’t like notifications; I have them turned off on my computer too, except I do allow icon colour changes in apps where notifications are essential for me to read.</p><p>The way I have Do Not Disturb set up allows notifications to appear on the home screen, but my phone doesn't buzz and the screen doesn't turn on. This means I can check my notifications when I am ready.</p><p>The only notifications I like on my phone are messages from family and friends – the kind of notification you open up that makes you smile. In the messaging apps I use, I proactively mute group chats where I am not an active participant so that I am only notified of individual people messaging me.</p><h2 id="browsing">Browsing</h2><p>I use Firefox on both desktop and mobile. I have been using Firefox for years and love the software. Of note, I prefer the interface of Firefox on iPhone to Safari on iPhone. I find managing tabs much easier on Firefox.</p><h2 id="layering-colour-customisations">Layering colour customisations</h2><p>Last night, I learned that I having greyscale mode and night mode enabled on my phone simultaneously has an effect. The greyscale mode is warmer with night shift mode on. I tried turning night shift mode on while keeping greyscale on too and I quickly turned night shift back on again – the combination of both modes works really well for me.</p><h2 id="my-relationship-with-my-phone">My relationship with my phone</h2><p>Reducing the number of apps on my phone was difficult, but ultimately worth it. To the extent possible, I want my phone to be a creative tool for me as a writer, a reference tool for my every day world – whether that means consulting maps, Wikipedia, or searching something up – and a way to stay in touch with friends. I think my phone does all those three things right now.</p><p>My phone feels relatively calm. Do I use it too much? Probably; I still take out my phone when I am anxious while out and about and need a distraction. But I don’t really worry about my phone any more since it feels more like a tool than a destination. Will I change how I use my phone at some point? Probably; change is part of life. This post describes how things are right now.</p><p>This post has been more of a “stream of consciousness” than others. I have been writing things as they come to mind. Part of me worries whether this comes across as me having everything figured out in terms of how I use computers, or as presenting a recommended path. I don’t have everything figured out, nor is the way I use my phone how you may want to use yours. <sup class="footnote-reference" id="f-3"><a href="https://jamesg.blog/longform-feed#3">3</a></sup> <sup class="footnote-reference" id="f-4"><a href="https://jamesg.blog/longform-feed#4">4</a></sup> I think the point I want to make in this post is that you can make your phone yours. Indeed, your technology should be for you <sup class="footnote-reference" id="f-5"><a href="https://jamesg.blog/longform-feed#5">5</a></sup>.</p>
<div class="footnote-definition" id="1"><sup class="footnote-definition-label" id="f-6">1</sup>
<p>My personal website makes me feel infinitely more creative than social media ever did. I feel like I can be myself here.</p>
<a href="https://jamesg.blog/longform-feed#f-1">[↩]</a></div>
<div class="footnote-definition" id="2"><sup class="footnote-definition-label" id="f-7">2</sup>
<p>I installed Instagram temporarily earlier this year to participate in a group chat and I quickly realised just how much I didn’t like the experience, so I deleted the application. I also left the experience having great empathy for the difficulty people feel about leaving social platforms. I am perhaps lucky that I am in communities that don’t use social media as much but I still feel the void of there being people on the periphery of my life that I can’t contact because I only knew them through social media. This led me to think just how important open platforms are, where you should have easy portability of identity. Now, whenever someone wants to contact me, I recommend my website, email, Signal, or SMS.</p>
<a href="https://jamesg.blog/longform-feed#f-2">[↩]</a></div>
<div class="footnote-definition" id="3"><sup class="footnote-definition-label" id="f-8">3</sup>
<p>On a slight tangent, part of what still motivates me to think as much as I do about technology is that I think things can be better. I don’t know what our digital experiences will look like in the decades to come, but if I can have a small hand in holding the flame for technology that is really for you – even just through writing blog posts – I would be most delighted.</p>
<a href="https://jamesg.blog/longform-feed#f-3">[↩]</a></div>
<div class="footnote-definition" id="4"><sup class="footnote-definition-label" id="f-9">4</sup>
<p>Speaking of not having everything figured out, I do have one tip: don't put your laptop next to your bed at night. I have been doing this lately and so, instinctively, I keep picking up my laptop in the morning to read messages and notifications. I know I should put my laptop in another room at night. My relationship with technology is indeed ever changing.</p>
<a href="https://jamesg.blog/longform-feed#f-4">[↩]</a></div>
<div class="footnote-definition" id="5"><sup class="footnote-definition-label" id="f-10">5</sup>
<p> By which I mean really for you, unlike, ironically, a “for you page”.</p>
<a href="https://jamesg.blog/longform-feed#f-5">[↩]</a></div>
<a class="tag" href="https://fed.brid.gy/">Bridgy Fed</a>
<a class="tag" href="https://jamesg.blog/2026/05/04/using-greyscale">greyscale mode on my phone</a>
<a class="tag" href="https://jamesg.blog/2026/05/05/night-shift">night shift on my computer</a>
<a class="tag" href="https://jamesg.blog/longform-feed#1">1</a>
<a class="tag" href="https://jamesg.blog/longform-feed#2">2</a>
<a class="tag" href="https://jamesg.blog/longform-feed#3">3</a>
<a class="tag" href="https://jamesg.blog/longform-feed#4">4</a>
<a class="tag" href="https://jamesg.blog/longform-feed#5">5</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-1">[↩]</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-2">[↩]</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-3">[↩]</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-4">[↩]</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-5">[↩]</a>
Building the future - Posts feedhttps://www.coryd.dev/posts/2026/building-the-future2026-05-05T19:45:00.000Z<p>Watching the tech hype train while working in the industry has always felt a bit surreal. There's this nascent feeling that what I'm working on, while it pays my bills, either won't amount to much or is potentially a net negative. I'll admit that I'm predisposed to cynicism and will freely cast off jokes at the expense of what I observe. It's easy to do.</p>
RSS Feeds Send Me More Traffic Than Google - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=702382026-05-05T11:34:12.000Z<p>Yeah yeah, I know, data-point of 1.</p>
<p>I recently read <a href="https://susam.net/from-rss-to-atom.html">Susam's blog post</a> where they said that "most of the traffic to my personal website still comes from web feeds" - I wondered if that was true for my site.</p>
<p>I've been writing this blog for a while. I've never much bothered with "aggressive" <abbr title="Search Engine Optimisation">SEO</abbr> - I have a fairly <a href="https://shkspr.mobi/blog/2026/01/maximally-semantic-structure-for-a-blog-post/">semantic layout</a>, all my <a href="https://shkspr.mobi/blog/2023/01/restaurant-review-metadata/">reviews have metadata</a>, and stuff like that - but I'm not cramming in keywords, using AMP, or whatever other chickens Google requires to be sacrificed for a higher ranking. Nevertheless, I do OK.</p>
<p>Last year, I added a bit of <a href="https://shkspr.mobi/blog/2025/09/reasonably-accurate-privacy-conscious-cookieless-visitor-tracking-for-wordpress/">local-only, lightweight statistics-gathering</a> to my blog. I can see which sites people click on to reach mine. Google is right up the top, DuckDuckGo is surprisingly high, Bing is lucky to crack the top 20 on any day. Similarly, I can see <a href="https://shkspr.mobi/blog/2025/11/now-witness-the-power-of-this-fully-operational-fediverse/">how much traffic I get from the Fediverse</a> and BlueSky (Twitter has all but vanished).</p>
<p>A few weeks ago I added RSS and Newsletter tracking. These data are <em>very</em> lossy. If someone is subscribed to my RSS feed <em>and</em> opens a post <em>and</em> their client downloads a lazy-loaded image at the end of the post, I get a hit. For email it's broadly the same. If an email is opened and the tracker image is loaded, I get a hit (although <a href="https://gmail.googleblog.com/2013/12/images-now-showing.html">Gmail does obfuscate that somewhat</a>).</p>
<p>I'm not looking for super-accurate numbers (although I do block as many AI crawlers and bots as possible). I'm not creepily following people around the web nor am I trying to sell them anything. I just want a rough idea of where people find me.</p>
<p>Here are my blog's views for the last 28 days.</p>
<img src="https://shkspr.mobi/blog/wp-content/uploads/2026/05/page-views.webp" alt="Atom 13774. Google 10833. RSS 10419. DuckDuckGo 2302. Email 2123." width="553" height="395" class="aligncenter size-full wp-image-71208 no-border-radius"/>
<p>Some months I get a surge of hits from link aggregators like HN or Reddit. Sometimes I'm linked to from a popular site or <a href="https://shkspr.mobi/blog/citations">cited in academic work</a>. But most of the time I bumble along getting hits from here, there, and everywhere. Nevertheless, it's lovely to see so many people choosing to subscribe<sup id="fnref:rss"><a href="https://shkspr.mobi/blog/2026/05/rss-feeds-send-me-more-traffic-than-google/#fn:rss" class="footnote-ref" title="For historic reasons, I have separate Atom and RSS feeds. Perhaps I should consider merging them? But it doesn't take much effort to publish in two subtly different formats." role="doc-noteref">0</a></sup> (for free!) and astonishing that they provide more traffic than a major search engine.</p>
<p>Obviously, these are two <em>very</em> different types of traffic. People who are searching for a specific thing and stumble upon my blog are different from those who decide to like and subscribe.</p>
<p>But, yeah, about 25% of my traffic comes from people who have chosen to subscribe.</p>
<p>I'm just delighted that so many people read my random thoughts.</p>
<div id="footnotes" role="doc-endnotes">
<hr aria-label="Footnotes"/>
<ol start="0">
<li id="fn:rss">
<p>For historic reasons, I have separate Atom and RSS feeds. Perhaps I should consider merging them? But it doesn't take much effort to publish in two subtly different formats. <a href="https://shkspr.mobi/blog/2026/05/rss-feeds-send-me-more-traffic-than-google/#fnref:rss" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=70238&HTTP_REFERER=Atom" alt="" width="1" height="1" loading="eager"/>C/C++ checklist challenges, solved - Trail of Bits Bloghttps://blog.trailofbits.com/2026/05/05/c/c-checklist-challenges-solved/2026-05-05T11:00:00.000Z<p>We recently added a <a href="https://appsec.guide/docs/languages/c-cpp/">C/C++ security checklist</a> to the Testing Handbook and <a href="https://blog.trailofbits.com/2026/04/09/master-c-and-c-with-our-new-testing-handbook-chapter/">challenged readers to spot the bugs in two code samples</a>: a deceptively simple Linux ping program and a Windows driver registry handler. If you found the <code>inet_ntoa</code> global buffer gotcha or the missing <code>RTL_QUERY_REGISTRY_TYPECHECK</code> flag, nice work. If not, here’s a full walkthrough of both challenges, plus a deep dive into how the Windows registry type confusion escalates from a local denial of service to a kernel write primitive.</p>
<p>Since we first released the new C/C++ security checklist, we also developed a new Claude skill, <a href="https://github.com/trailofbits/skills/tree/main/plugins/c-review">c-review</a>. It turns the checklist into bug-finding prompts that an LLM can run against a codebase. It’s also platform and threat-model aware. Run these commands to install the skill:</p>
<pre>
claude skills add-marketplace https://github.com/trailofbits/skills
claude skills enable c-review --marketplace trailofbits/skills
</pre>
<h2 id="the-linux-ping-program-challenge">The Linux ping program challenge</h2>
<p>The Linux warmup challenge we showed you in the last blog post has an obvious command injection issue.</p>
<figure class="highlight">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><stdio.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><stdlib.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><string.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf"><arpa/inet.h></span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="cp">#define ALLOWED_IP "127.3.3.1"
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="n">ip_addr</span><span class="p">[</span><span class="mi">128</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="k">struct</span> <span class="n">in_addr</span> <span class="n">to_ping_host</span><span class="p">,</span> <span class="n">trusted_host</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// get address
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">fgets</span><span class="p">(</span><span class="n">ip_addr</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">ip_addr</span><span class="p">),</span> <span class="n">stdin</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">ip_addr</span><span class="p">[</span><span class="nf">strcspn</span><span class="p">(</span><span class="n">ip_addr</span><span class="p">,</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)]</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// verify address
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">inet_aton</span><span class="p">(</span><span class="n">ip_addr</span><span class="p">,</span> <span class="o">&</span><span class="n">to_ping_host</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="o">*</span><span class="n">ip_addr_resolved</span> <span class="o">=</span> <span class="nf">inet_ntoa</span><span class="p">(</span><span class="n">to_ping_host</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// prevent SSRF
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">((</span><span class="nf">ntohl</span><span class="p">(</span><span class="n">to_ping_host</span><span class="p">.</span><span class="n">s_addr</span><span class="p">)</span> <span class="o">>></span> <span class="mi">24</span><span class="p">)</span> <span class="o">==</span> <span class="mi">127</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// only allowed
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">inet_aton</span><span class="p">(</span><span class="n">ALLOWED_IP</span><span class="p">,</span> <span class="o">&</span><span class="n">trusted_host</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">char</span> <span class="o">*</span><span class="n">trusted_resolved</span> <span class="o">=</span> <span class="nf">inet_ntoa</span><span class="p">(</span><span class="n">trusted_host</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nf">strcmp</span><span class="p">(</span><span class="n">ip_addr_resolved</span><span class="p">,</span> <span class="n">trusted_resolved</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// ping
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="kt">char</span> <span class="n">cmd</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="nf">snprintf</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">cmd</span><span class="p">),</span> <span class="s">"ping '%s'"</span><span class="p">,</span> <span class="n">ip_addr</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="nf">system</span><span class="p">(</span><span class="n">cmd</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
</figure>
<p>There are three validations that have to be bypassed before the <code>system</code> call can be reached with malicious inputs:</p>
<ol>
<li>The <a href="https://man7.org/linux/man-pages/man3/inet.3.html"><code>inet_aton</code> function</a> “converts the Internet host address from the IPv4 numbers-and-dots notation into binary form” and “returns nonzero if the address is valid, zero if not.” Theoretically, if we provide an invalid IPv4 string as input, then the program should return early.</li>
<li>The <code>ntohl</code> call aims to prevent server-side request forgery (SSRF) attacks by disallowing addresses in 127.0.0.0/8 range.</li>
<li>The parsed IP address is normalized with an <code>inet_ntoa</code> call and compared against the <code>ALLOWED_IP</code>. We are only allowed to ping localhost, which should not be possible given the SSRF check (making the code effectively broken with this configuration).</li>
</ol>
<p>The issue with the <code>inet_aton</code> function is that it <a href="https://sourceware.org/bugzilla/show_bug.cgi?id=20018">accepts trailing garbage</a>. This behavior is not documented on its man page, making it a likely source of vulnerabilities. In our challenge, one can simply send “127.0.0.1 ‘; anything #” as valid input.</p>
<p>The gotcha with <code>inet_ntoa</code> is that it returns a pointer to a global buffer. Therefore, subsequent calls to the function overwrite previous outputs. In the challenge, <code>ip_addr_resolved</code> and <code>trusted_resolved</code> are the same pointer. When we provide “1.2.3.4” as input, <code>ip_addr_resolved</code> points to the string “1.2.3.4”, the SSRF check passes, the second call to <code>inet_ntoa</code> makes the <code>ip_addr_resolved</code> pointer point to “127.3.3.1”, and so the <code>strcmp</code> check passes too.</p>
<p>There are a few more functions that return pointers to static buffers; these are documented in the new C/C++ Testing Handbook chapter.</p>
<h2 id="the-windows-driver-registry-challenge">The Windows driver registry challenge</h2>
<p>We showed you this Windows Driver Framework (WDF) request handler from a Windows driver and asked you to spot the bugs.</p>
<figure class="highlight">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">NTSTATUS</span>
</span></span><span class="line"><span class="cl"><span class="nf">InitServiceCallback</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">_In_</span> <span class="n">WDFREQUEST</span> <span class="n">Request</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">NTSTATUS</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">PWCHAR</span> <span class="n">regPath</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">size_t</span> <span class="n">bufferLength</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// fetch the product registry path from the request
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">status</span> <span class="o">=</span> <span class="nf">WdfRequestRetrieveInputBuffer</span><span class="p">(</span><span class="n">Request</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="o">&</span><span class="n">regPath</span><span class="p">,</span> <span class="o">&</span><span class="n">bufferLength</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">NT_SUCCESS</span><span class="p">(</span><span class="n">status</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">TraceEvents</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_LEVEL_ERROR</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_QUEUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s">"%!FUNC! Failed to retrieve input buffer. Status: %d"</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">status</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="cm">/* check that the buffer size is a null-terminated
</span></span></span><span class="line"><span class="cl"><span class="cm"> Unicode (UTF-16) string of a sensible size */</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">bufferLength</span> <span class="o"><</span> <span class="mi">4</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl"> <span class="n">bufferLength</span> <span class="o">></span> <span class="mi">512</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">bufferLength</span> <span class="o">%</span> <span class="mi">2</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span> <span class="o">||</span>
</span></span><span class="line"><span class="cl"> <span class="n">regPath</span><span class="p">[(</span><span class="n">bufferLength</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">!=</span> <span class="sa">L</span><span class="sc">'\0'</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">TraceEvents</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_LEVEL_ERROR</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_QUEUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s">"%!FUNC! Buffer length %d was incorrect."</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">bufferLength</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">STATUS_INVALID_PARAMETER</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">ProductVersionInfo</span> <span class="n">version</span> <span class="o">=</span> <span class="p">{</span> <span class="mi">0</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"> <span class="n">HandlerCallback</span> <span class="n">handlerCallback</span> <span class="o">=</span> <span class="n">NewCallback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">readValue</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// read the major version from the registry
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">RTL_QUERY_REGISTRY_TABLE</span> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="nf">RtlZeroMemory</span><span class="p">(</span><span class="n">regQueryTable</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">RTL_QUERY_REGISTRY_TABLE</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Name</span> <span class="o">=</span> <span class="sa">L</span><span class="s">"MajorVersion"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">EntryContext</span> <span class="o">=</span> <span class="o">&</span><span class="n">readValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">RTL_QUERY_REGISTRY_DIRECT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">QueryRoutine</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">status</span> <span class="o">=</span> <span class="nf">RtlQueryRegistryValues</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">RTL_REGISTRY_ABSOLUTE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regPath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">NT_SUCCESS</span><span class="p">(</span><span class="n">status</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">TraceEvents</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_LEVEL_ERROR</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_QUEUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s">"%!FUNC! Failed to query registry. Status: %d"</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">status</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nf">TraceEvents</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_LEVEL_INFORMATION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_QUEUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s">"%!FUNC! Major version is %d"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">readValue</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">version</span><span class="p">.</span><span class="n">Major</span> <span class="o">=</span> <span class="n">readValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">version</span><span class="p">.</span><span class="n">Major</span> <span class="o"><</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// versions prior to 3.0 need an additional check
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">RtlZeroMemory</span><span class="p">(</span><span class="n">regQueryTable</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">RTL_QUERY_REGISTRY_TABLE</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Name</span> <span class="o">=</span> <span class="sa">L</span><span class="s">"MinorVersion"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">EntryContext</span> <span class="o">=</span> <span class="o">&</span><span class="n">readValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">RTL_QUERY_REGISTRY_DIRECT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">QueryRoutine</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">status</span> <span class="o">=</span> <span class="nf">RtlQueryRegistryValues</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">RTL_REGISTRY_ABSOLUTE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regPath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">NT_SUCCESS</span><span class="p">(</span><span class="n">status</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">TraceEvents</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_LEVEL_ERROR</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_QUEUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s">"%!FUNC! Failed to query registry. Status: %d"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">status</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">status</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nf">TraceEvents</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_LEVEL_INFORMATION</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">TRACE_QUEUE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s">"%!FUNC! Minor version is %d"</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">readValue</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">version</span><span class="p">.</span><span class="n">Minor</span> <span class="o">=</span> <span class="n">readValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nf">DoesVersionSupportNewCallback</span><span class="p">(</span><span class="n">version</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">handlerCallback</span> <span class="o">=</span> <span class="n">OldCallback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nf">SetGlobalHandlerCallback</span><span class="p">(</span><span class="n">handlerCallback</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
</figure>
<p>The intended behavior of the code is to read some software version information from the registry using the <code>RtlQueryRegistryValues</code> API, then select one of two possible callback functions depending on that version information.</p>
<h3 id="an-attacker-controlled-registry-path">An attacker-controlled registry path</h3>
<p>The first bug is that the path to the registry key is provided in the request, without validating the path string or checking that the caller is authorized to access the specified registry key. This means that anyone who can call into this handler can pick which registry key gets read, even if they ordinarily wouldn’t have access to that key. How this path string is interpreted depends on the <code>RelativeTo</code> parameter of the <code>RtlQueryRegistryValues</code> call. In this case, <code>RelativeTo</code> is set to <code>RTL_REGISTRY_ABSOLUTE</code>, which means that the path will be treated as an absolute path to a registry key object (e.g., <code>\Registry\User\CurrentUser</code>). There are two main reasons why this is a potential security issue.</p>
<p>First, if an attacker can control which registry key is being read, then they can point it at a registry key they control the contents of, allowing them to further manipulate the driver behavior. This may lead to logical inconsistencies (e.g., the wrong callback being set) or, as we will see shortly, enable exploitation of security issues elsewhere in the code.</p>
<p>Second, this enables a confused deputy attack that can be used to leak registry information that would normally be inaccessible to the user due to access controls. For example, a registry key might have a DACL applied that prevents normal users from enumerating its subkeys or reading any of the values inside those keys. Since the handler doesn’t check whether the call has sufficient rights to read the key, and the code emits a trace message and passes back the status code from <code>RtlQueryRegistryValues</code>, it can be used as an oracle to check for the existence of any registry key. It can also be used to leak any registry value named <code>MajorVersion</code> (and sometimes also <code>MinorVersion</code>) anywhere in the registry, but this is unlikely to be particularly useful in practice.</p>
<h3 id="missing-type-checks-with-rtl_query_registry_direct">Missing type checks with RTL_QUERY_REGISTRY_DIRECT</h3>
<p>The more serious bugs in this case arise from the flags set in the <code>RTL_QUERY_REGISTRY_TABLE</code> structs. The <code>RtlQueryRegistryValues</code> API takes in an array of these structs, terminated by an all-zero entry, to describe which registry values should be read from the specified key and how they should be processed and returned. There are two primary modes of operation here: callback or direct. In callback mode, which is the default, the <code>QueryRoutine</code> field of the struct points to a callback function that receives the value read from the registry. In direct mode, the <code>QueryRoutine</code> field is ignored and the value is instead written directly to a buffer whose location is passed in the <code>EntryContext</code> field. Direct mode is selected by including <code>RTL_QUERY_REGISTRY_DIRECT</code> in the <code>Flags</code> field.</p>
<p>In our example, the <code>MajorVersion</code> value is read using the following code:</p>
<figure class="highlight">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">HandlerCallback</span> <span class="n">handlerCallback</span> <span class="o">=</span> <span class="n">NewCallback</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kt">int</span> <span class="n">readValue</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// read the major version from the registry
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="n">RTL_QUERY_REGISTRY_TABLE</span> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"> <span class="nf">RtlZeroMemory</span><span class="p">(</span><span class="n">regQueryTable</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">RTL_QUERY_REGISTRY_TABLE</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Name</span> <span class="o">=</span> <span class="sa">L</span><span class="s">"MajorVersion"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">EntryContext</span> <span class="o">=</span> <span class="o">&</span><span class="n">readValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">RTL_QUERY_REGISTRY_DIRECT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">QueryRoutine</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">status</span> <span class="o">=</span> <span class="nf">RtlQueryRegistryValues</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">RTL_REGISTRY_ABSOLUTE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regPath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span></span></span></code></pre>
</figure>
<p>Here, <code>RTL_QUERY_REGISTRY_DIRECT</code> is used to select direct mode, and the buffer points to <code>readValue</code>, which is an integer variable on the stack. You might notice something important, though: at no point has the code specified what type of value is being read, nor has it specified the size of the buffer. It is clear from the context that this code is expecting to read a <code>REG_DWORD</code>, but what if the <code>MajorVersion</code> value isn’t a <code>REG_DWORD</code>?</p>
<h3 id="a-first-attempt-at-exploitation">A first attempt at exploitation</h3>
<p>Let’s try to exploit this using a <code>REG_QWORD</code>. A <code>REG_DWORD</code> value is a 32-bit unsigned integer, whereas a <code>REG_QWORD</code> is a 64-bit unsigned integer, so if we make <code>MajorVersion</code> a <code>REG_QWORD</code> value instead, then we should be able to overwrite four bytes immediately after <code>readValue</code> on the stack. Since <code>HKEY_CURRENT_USER</code> is writable by low-privilege users, we can create a key somewhere in there, place a <code>REG_QWORD</code> value called <code>MajorVersion</code> in there, and pass the path of that key to the driver. And success, we get a BSOD!</p>
<p>Except… it’s not quite what we wanted. The bugcheck code is <code>KERNEL_SECURITY_CHECK_FAILURE</code>, which isn’t really what we would expect if we successfully overwrote some of the stack. Why is this happening? The answer is in the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlqueryregistryvalues">documentation</a>:</p>
<blockquote>
<p>Starting with Windows 8, if an <code>RtlQueryRegistryValues</code> call accesses an untrusted hive, and the caller sets the <code>RTL_QUERY_REGISTRY_DIRECT</code> flag for this call, the caller must additionally set the <code>RTL_QUERY_REGISTRY_TYPECHECK</code> flag. A violation of this rule by a call from user mode causes an exception. A violation of this rule by a call from kernel mode causes a 0x139 bug check (<code>KERNEL_SECURITY_CHECK_FAILURE</code>).</p>
<p>Only system hives are trusted. An <code>RtlQueryRegistryValues</code> call that accesses a system hive does not cause an exception or a bug check if the <code>RTL_QUERY_REGISTRY_DIRECT</code> flag is set and the <code>RTL_QUERY_REGISTRY_TYPECHECK</code> flag is not set. However, as a best practice, the <code>RTL_QUERY_REGISTRY_TYPECHECK</code> flag should always be set if the <code>RTL_QUERY_REGISTRY_DIRECT</code> flag is set.</p>
<p>Similarly, in versions of Windows before Windows 8, as a best practice, an <code>RtlQueryRegistryValues</code> call that sets the <code>RTL_QUERY_REGISTRY_DIRECT</code> flag should additionally set the <code>RTL_QUERY_REGISTRY_TYPECHECK</code> flag. However, failure to follow this recommendation does not cause an exception or a bug check.
This protective behavior was introduced as a response to <a href="https://learn.microsoft.com/en-us/security-updates/securitybulletins/2011/ms11-011">MS11-011</a>, in which this registry type confusion bug was first reported.</p>
</blockquote>
<p>To summarize, if you try to read from an untrusted registry hive using <code>RtlQueryRegistryValues</code> with <code>RTL_QUERY_REGISTRY_DIRECT</code> set but without also setting <code>RTL_QUERY_REGISTRY_TYPECHECK</code>, then Windows will automatically raise a bugcheck to crash the system and prevent the operation from succeeding.</p>
<p>The <code>RTL_QUERY_REGISTRY_TYPECHECK</code> flag allows the caller to specify an expected type as part of the query table entry, thus mitigating the type confusion bug. Since this flag is not set in our example, a bugcheck will be triggered if we attempt to read from any registry hive other than the following trusted system hives:</p>
<ul>
<li><code>\REGISTRY\MACHINE\HARDWARE</code></li>
<li><code>\REGISTRY\MACHINE\SOFTWARE</code></li>
<li><code>\REGISTRY\MACHINE\SYSTEM</code></li>
<li><code>\REGISTRY\MACHINE\SECURITY</code></li>
<li><code>\REGISTRY\MACHINE\SAM</code></li>
</ul>
<p><code>HKEY_CURRENT_USER</code> is not included within this set, which explains why we saw the <code>KERNEL_SECURITY_CHECK_FAILURE</code> bugcheck when we tried to exploit it that way. This downgrades us from a potential kernel privilege escalation bug to a local denial of service. Still a bug, but not quite as exciting.</p>
<h3 id="finding-writable-keys-in-trusted-hives">Finding writable keys in trusted hives</h3>
<p>However, who says we can’t write values somewhere within these trusted hives? All it takes is a single key within one of those hives with a DACL that allows a lower-privileged user to write to it. Finding these isn’t too hard; the <a href="https://www.powershellgallery.com/packages/NtObjectManager/">NtObjectManager powershell module</a> has a command named <code>Get-AccessibleKey</code> that is perfect for the task:</p>
<p><code>Get-AccessibleKey \Registry\Machine -Recurse -Access SetValue</code></p>
<p>This command searches recursively within the <code>\Registry\Machine</code> object namespace for keys that the current process has permissions to set values within. Running it as a regular desktop user returns thousands of options that can be written without UAC elevation! Nice.</p>
<p>However, for style points, we can go one step further. <a href="https://learn.microsoft.com/en-us/windows/win32/secauthz/mandatory-integrity-control">Mandatory integrity control (MIC)</a>, one of the key access control features in Windows that underpins UAC, allows processes to run with higher or lower privileges than would normally be assigned to the user that ran them. Most desktop processes run at the medium integrity level (IL). Elevating a process via UAC (often referred to as “run as administrator”) typically increases the process’s IL to high. There is also a low IL, which is often used to sandbox certain processes for security reasons, significantly limiting which resources they can access. Any securable object on Windows can have a mandatory label applied to its system access control list (SACL), and that mandatory label specifies the ILs that are allowed to access the object. The SACL is checked before the DACL, meaning that the IL check must pass even if the DACL would normally grant the user permissions to access the object. This means that a process running with a low-integrity security token cannot access a medium-integrity object, and a process running with a medium-integrity security token cannot access a high-integrity object. So, can we find any cases where we could write to one of the trusted system hives from a low-integrity process?</p>
<p>To check for keys that are accessible at a low IL, the first thing we want to do is duplicate our process token and apply a low integrity label to it:</p>
<p><code>$token = Get-NtToken -Primary -Duplicate -IntegrityLevel Low</code></p>
<p>This gives us a copy of our current process’s security token that behaves as if we were running at a low IL. Using this, we then rerun the scan, passing in that modified token:</p>
<p><code>Get-AccessibleKey \Registry\Machine -Recurse -Access SetValue -Token $token</code></p>
<p>This does actually return a few results, on both Windows 10 and 11. Here are two of the most interesting:</p>
<p><code>\REGISTRY\MACHINE\SOFTWARE\Microsoft\DRM</code>
<code>\REGISTRY\MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\PlayReady\Troubleshooter</code></p>
<p>Both of these keys allow a low-integrity token to write to them. The <code>DRM</code> key’s DACL has fairly complex permissions applied but grants the Set Value permission to the Everyone group. The <code>PlayReady\Troubleshooter</code> key’s DACL grants Full Control to Users, ALL APPLICATION PACKAGES, and ALL RESTRICTED APP PACKAGES. Either of these two keys can be abused to plant controlled registry values within a trusted system hive from a low privilege level.</p>
<p>(Note: Whether or not the driver’s request endpoint can be called from a low IL is a different matter, but this is just for fun and style points, so let’s ignore that for now.)</p>
<p>If we set a <code>REG_QWORD</code> value called <code>MajorVersion</code> in the <code>DRM</code> key, then pass that key’s path to the WDF handler, we can now overwrite four bytes of stack past the end of <code>readValue</code> with values that we control. Since <code>handlerCallback</code> was declared adjacent to <code>readValue</code>, there’s a chance that we can overwrite half of that function pointer! If that callback is called later, then we obtain partial control over the instruction pointer, which is a fairly strong primitive for local privilege escalation (LPE). This does depend on stack alignment, however, and it would not be surprising if the 32-bit <code>readValue</code> variable ended up 64-bit aligned, leaving a gap, so this approach may not get us far in practice.</p>
<p>Can we do better?</p>
<h3 id="a-string-is-a-type-of-integer-right">A string is a type of integer, right?</h3>
<p>Ok, so far we’ve only explored what happens when we exploit the type confusion with <code>REG_QWORD</code>, but what happens if we use <code>REG_SZ</code>?</p>
<p>
<img src="https://blog.trailofbits.com/2026/05/05/c/c-checklist-challenges-solved/cc-checklist-challenges-solved-image-1_hu_fe8665a0cbfe6e6b.webp"
alt="“Samuel L. Jackson meme”"
width="972"
height="794"
loading="lazy"
decoding="async" />
</p>
<p>In the case of <code>REG_SZ</code> (i.e., a string value), the documentation says the following about <code>RtlQueryRegistryValues</code>’ behavior in direct mode:</p>
<blockquote>
<p>A null-terminated Unicode string (such as <code>REG_SZ</code>, <code>REG_EXPAND_SZ</code>):
<code>EntryContext</code> must point to an initialized <code>UNICODE_STRING</code> structure. If the <code>Buffer</code> member of <code>UNICODE_STRING</code> is NULL, the routine allocates storage for the string data. Otherwise, it stores the string data in the buffer that <code>Buffer</code> points to.</p>
</blockquote>
<p>Let’s try exploiting this. <code>RtlQueryRegistryValues</code> will interpret the <code>EntryContext</code> field as if it were a <code>UNICODE_STRING</code> struct, but it’s actually pointing at <code>readValue</code>, which is an <code>int</code>. Here’s what a <code>UNICODE_STRING</code> looks like:</p>
<figure class="highlight">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="k">typedef</span> <span class="k">struct</span> <span class="n">_UNICODE_STRING</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">USHORT</span> <span class="n">Length</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">USHORT</span> <span class="n">MaximumLength</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">PWSTR</span> <span class="n">Buffer</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="n">UNICODE_STRING</span><span class="p">,</span> <span class="o">*</span><span class="n">PUNICODE_STRING</span><span class="p">;</span></span></span></code></pre>
</figure>
<p>In the first call that the code makes to <code>RtlQueryRegistryValues</code>, when reading <code>MajorVersion</code>, the value of <code>readValue</code> has been initialized to zero. Since <code>readValue</code> is four bytes and a <code>USHORT</code> is two bytes, interpreting <code>readValue</code> as a <code>UNICODE_STRING</code> at that time will result in both <code>Length</code> and <code>MaximumLength</code> being zero and <code>Buffer</code> containing whatever’s immediately after <code>readValue</code> in the stack. Since the length of the buffer is zero, <code>RtlQueryRegistryValues</code> will just return <code>STATUS_BUFFER_TOO_SMALL</code> and not attempt to write to the <code>Buffer</code> field.</p>
<p>However, let’s take a look at the second call to <code>RtlQueryRegistryValues</code>:</p>
<figure class="highlight">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">version</span><span class="p">.</span><span class="n">Major</span> <span class="o">=</span> <span class="n">readValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">version</span><span class="p">.</span><span class="n">Major</span> <span class="o"><</span> <span class="mi">3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// versions prior to 3.0 need an additional check
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nf">RtlZeroMemory</span><span class="p">(</span><span class="n">regQueryTable</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">RTL_QUERY_REGISTRY_TABLE</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Name</span> <span class="o">=</span> <span class="sa">L</span><span class="s">"MinorVersion"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">EntryContext</span> <span class="o">=</span> <span class="o">&</span><span class="n">readValue</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">Flags</span> <span class="o">=</span> <span class="n">RTL_QUERY_REGISTRY_DIRECT</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">QueryRoutine</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">status</span> <span class="o">=</span> <span class="nf">RtlQueryRegistryValues</span><span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="n">RTL_REGISTRY_ABSOLUTE</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regPath</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">regQueryTable</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nb">NULL</span>
</span></span><span class="line"><span class="cl"> <span class="p">);</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// ...
</span></span></span></code></pre>
</figure>
<p>This part of the code first checks if the <code>MajorVersion</code> value is less than three and, if so, reads the <code>MinorVersion</code> value using the same approach as before. A key observation here is that <code>readValue</code> is not reinitialized between the calls. This gives us some extra control: by leaving <code>MajorVersion</code> as a <code>REG_DWORD</code>, as originally intended by the code, we can have the first <code>RtlQueryRegistryValues</code> call load a value into <code>readValue</code>. Then, when the second call to <code>RtlQueryRegistryValues</code> is made, to read <code>MinorVersion</code>, we control the first four bytes of data pointed to by <code>EntryContext</code>. If <code>MinorVersion</code> is a <code>REG_SZ</code> value, a type confusion occurs where <code>RtlQueryRegistryValues</code> expects <code>EntryContext</code> to point to a <code>UNICODE_STRING</code>, causing the contents of the <code>MajorVersion</code> integer to be reinterpreted as the <code>Length</code> and <code>MaximumLength</code> fields. The only restriction is that we need the major version check to pass (i.e., <code>version.Major</code> must be less than 3) in order for the second registry query to take place. However, this turns out to be easy: if we set the <code>MajorVersion</code> value to <code>0xF000F002</code>, the code will interpret this as <code>-268374014</code> because <code>readValue</code> is a signed 32-bit integer. The <code>Length</code> and <code>MaximumLength</code> fields, however, are unsigned 16-bit integers, causing the <code>0xF000F002</code> value to get interpreted as the following when type confused as a <code>UNICODE_STRING</code>:</p>
<figure class="highlight">
<pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="cl"><span class="n">USHORT</span> <span class="n">Length</span> <span class="o">=</span> <span class="n">F000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">USHORT</span> <span class="n">MaximumLength</span> <span class="o">=</span> <span class="n">F002</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">PWSTR</span> <span class="n">Buffer</span> <span class="o">=</span> <span class="o">????????</span><span class="err">`</span><span class="o">????????</span><span class="p">;</span></span></span></code></pre>
</figure>
<p>The <code>Buffer</code> field ends up pointing at whatever’s next in the stack. If we combine this current approach with the <code>REG_QWORD</code> trick from before, we can also overwrite four bytes of the <code>Buffer</code> pointer during the <code>MajorVersion</code> read. This means we partially control the address being written to, we fully control the length of what is written, and we can write any UTF-16 string there. This gets us a semi-controlled write-what-where primitive in the kernel. Nice!</p>
<p>But can we do <em>even better</em>?</p>
<h3 id="a-fully-controlled-stack-overwrite-with-reg_binary">A fully controlled stack overwrite with REG_BINARY</h3>
<p>Let’s take a look at what happens if we try a <code>REG_BINARY</code> value instead. Here’s what the documentation has to say about such values in direct mode:</p>
<blockquote>
<p>Nonstring data with size, in bytes, greater than <code>sizeof(ULONG)</code>:
The buffer pointed to by <code>EntryContext</code> must begin with a signed <code>LONG</code> value. The magnitude of the value must specify the size, in bytes, of the buffer. If the sign of the value is negative, <code>RtlQueryRegistryValues</code> will only store the data of the key value. Otherwise, it will use the first <code>ULONG</code> in the buffer to record the value length, in bytes, the second <code>ULONG</code> to record the value type, and the rest of the buffer to store the value data.</p>
</blockquote>
<p>This one is a bit more complicated, with two possible cases for the format of the buffer. In both cases, the buffer pointed to by <code>EntryContext</code> is expected to be prefilled with a signed <code>LONG</code> value that tells <code>RtlQueryRegistryValues</code> how large the buffer is. A <code>LONG</code> is just a 32-bit integer, so a signed <code>LONG</code> is functionally equivalent to <code>int</code> for this case. The interesting part is that this length value can either be positive or negative. If the value is negative, the API will copy the <code>REG_BINARY</code> data directly into the buffer pointed to by <code>EntryContext</code>. If the value is positive, it will first write the length of the <code>REG_BINARY</code> data into the first <code>ULONG</code> of the buffer, then it will write the <code>REG_BINARY</code> type value into the second <code>ULONG</code> of the buffer, and finally it will copy the <code>REG_BINARY</code> data into the remainder of the buffer.</p>
<p>You may have figured out the exploit already here. The <code>MinorVersion</code> registry value is only read when the <code>MajorVersion</code> is less than 3. If we set <code>MajorVersion</code> to some negative number, this check will pass. This negative number ends up left in <code>readValue</code> for the second <code>RtlQueryRegistryValues</code> call. If the <code>MinorVersion</code> value is a <code>REG_BINARY</code>, <code>RtlQueryRegistryValues</code> treats the first <code>ULONG</code> in the “buffer” as being the signed length field. Since our “buffer” is just whatever was in <code>readValue</code> from the previous call, this causes <code>RtlQueryRegistryValues</code> to copy the contents of the registry value into the “buffer,” which is really just stack memory starting at <code>readBytes</code>. Since we control the magnitude of the negative number, we therefore control the purported length of the buffer, allowing us to control the length of the overwrite. And, since the contents of the <code>REG_BINARY</code> value can be anything we like, it means we control what is overwritten.</p>
<p>For example, if we create a <code>REG_DWORD</code> value called <code>MajorVersion</code> with a value of <code>0xFFFFFFF4</code>, then create a <code>REG_BINARY</code> value called <code>MinorVersion</code> with a value of <code>00 00 00 00 DE AD BE EF DE AD BE EF</code>, this causes the first <code>RtlQueryRegistryValues</code> call to fill <code>readValue</code> with -12, which the second <code>RtlQueryRegistryValues</code> call interprets as a 12-byte buffer where only the binary should be copied. This results in <code>RtlQueryRegistryValues</code> copying <code>00 00 00 00</code> into <code>readValue</code>, then writing <code>DE AD BE EF DE AD BE EF</code> onto the stack afterwards. Assuming that the <code>handlerCallback</code> function pointer is stored after the <code>readValue</code> variable on the stack, we can now overwrite it with whatever we like. If this callback is invoked anywhere in the future, we gain control over the instruction pointer, leading to a kernel LPE.</p>
<p>But can we do <em>even better still</em>? If you think you can, get in touch! We’d love to hear your tips and tricks.</p>
<h2 id="your-turn">Your turn</h2>
<p>These challenges only scratch the surface of what the <a href="https://appsec.guide/docs/languages/c-cpp/">C/C++ Testing Handbook chapter</a> covers—from seccomp sandbox escapes to Windows path traversal via WorstFit Unicode bugs. Read the chapter and follow the checklist against a codebase you know well. Pair it with a run of the <a href="https://github.com/trailofbits/skills/tree/main/plugins/c-review">c-review skill</a>, if you’re inclined. If you find a pattern we haven’t documented yet, open a PR. We’d especially love to hear from anyone who found a cleaner exploitation path for the driver challenge than the ones we showed here. And, as always, if you need help securing your C/C++ systems, <a href="https://www.trailofbits.com/contact/">contact us</a>.</p>Outrage is letting someone else set the frame - Westenberg69f98b0fccfa0c0001fd4cfc2026-05-05T06:20:33.000Z<img src="https://www.joanwestenberg.com/content/images/2026/05/photo-1750433595079-835ab0171e4a.jpeg" alt="Outrage is letting someone else set the frame"><p>William Randolph Hearst bought the New York Morning Journal in 1895 - and immediately started running stories designed to make his readers furious before they’d finished their breakfast. The pages manufactured a mood, and that mood sold papers.</p><p>Three years later, when his correspondent Frederic Remington cabled from Cuba that there was no war to cover, Hearst replied with the line that became his epitaph: “You furnish the pictures, and I’ll furnish the war.”</p><p>That was the original sin, and the original business model. Find a target, deliver the outrage daily, harvest the engagement and sell the audience to advertisers.</p><p>The economics haven’t changed since 1895.</p><p>Walter Lippmann, writing in <em>Public Opinion</em> in 1922, described how a small class of editors and publicists had taken on the job of “manufacturing consent” by deciding which stories would be put in front of the public and to which framings they would be attached. He thought this was, on balance, fine. Most readers, he said, would never have the time or competence to form views from primary sources. Someone had to do the framing.</p><p><em>Someone.</em></p><p>A century on, the framing is automated // the framers are algorithms tuned to a single metric: <em>how long can we keep you scrolling?</em></p><p>Jonah Berger and Katherine Milkman, studying viral content for the <em>Journal of Marketing Research</em> in 2012, found that the emotion most reliably correlated with sharing was high-arousal anger. Neither sadness, contentment or even joy came close; anger was the engine. Every platform, and ever performer that lives on advertising eventually discovers this and bends its product around the discovery.</p><p>There is nothing neutral about the feed. </p><p>It’s a slot machine built to dispense outrage on whatever schedule keeps you returning. The story in front of you was picked because an algorithm, somewhere, ran the numbers and concluded it would make you feel something corrosive enough to produce a reaction.</p><p>Whose ledger does that reaction land on? Theirs. Your time on the platform is sold to advertisers in increments, your annotated rage in the comments trains the recommendation model, your re-share recruits one more person into the same loop, and your dwell time on each story sharpens the algorithm’s prediction of what will hold you next. The feeling moves through you and leaves a residue on the corporate balance sheet. You absorb the cost, and they book the revenue.</p><p>Chris Voss, the FBI’s lead international kidnapping negotiator, wrote <em>Never Split the Difference</em> in 2016, on the principle that whoever feels more in a negotiation loses. Across the table from a hostage taker, Voss wouldn't match the emotional temperature of the room; he'd lower it, keeping possession of his own pulse while everyone else lost theirs.</p><p>This is a useful posture to steal: you refuse to feel what you’re being told to feel by people whose business model depends on the feeling, and the refusal is procedural. The feeling might turn out to be correct, or it might not; that’s a separate question, evaluated later. Before the feeling arrives in full, you ask: who’s pushing this into my field of view, what do they collect if I take the bait, what’s the response they want from me, and does that response actually serve me?</p><p><em>Call it sequencing.</em></p><ul><li>Feel first and think second, the slot machine wins.</li><li>Think first and feel second (or not at all, depending), and the game stops paying out.</li></ul><p>Rage is metabolically expensive; borrowed rage even more so. You spend cognitive bandwidth on a quarrel imported from a stranger’s algorithm, then arrive at your own work depleted, with less attention left for the people and projects you’d actually choose. The outrage cycle runs on fuel siphoned from your real life. Mary Oliver asked what you plan to do with your one wild and precious life; for a lot of us the answer, when we’re being honest, is: argue with people we’ve never met about events we can’t influence on a platform whose advertisers are paying for our distress.</p><p>But you can, of course, decline.</p><p>Tristan Harris, founding the Center for Humane Technology in 2018, framed it as recognising the asymmetry: a thousand engineers on one side of the screen, optimising for your attention; one tired person on the other side, trying to hold their ground. </p><p>Stop offering the response the system was built to extract, and watch what shows up in the space the rage used to occupy: the work you’ve been avoiding, the conversation you owe someone you actually care about, the book on your nightstand, the neighbour you’ve been meaning to call back. </p><p>The un-cinematic projects that don’t trend and don’t reward you with a hit of validation when you rage-post about them.</p><p>AKA: your actual life, previously crowded and smothered by the outrage.</p><div class="kg-card kg-cta-card kg-cta-bg-grey kg-cta-minimal " data-layout="minimal">
<div class="kg-cta-content">
<div class="kg-cta-content-inner">
<div class="kg-cta-text">
<p dir="ltr"><span style="white-space: pre-wrap;">Westenberg is designed, built and funded by Studio Self. </span><br><span style="white-space: pre-wrap;">Reach out and work with us.</span></p>
</div>
<a href="https://www.thisisstudioself.com/?ref=joanwestenberg.com" class="kg-cta-button " style="background-color: #000000; color: #ffffff;">
Learn more
</a>
</div>
</div>
</div>Night shift - James' Coffee Bloghttps://jamesg.blog/2026/05/05/night-shift2026-05-05T00:00:00.000Z
<p>Writing my post on <a href="https://jamesg.blog/2026/05/04/using-greyscale">how I use my phone in grayscale</a> yesterday got me thinking about other customisations I make to the computing devices I use. The first one that came to mind is that I keep my laptop, and by extension my external display when I am using it, in night shift mode at all times. <a href="https://support.apple.com/en-gb/118583">According to Apple, night shift mode</a> “adjusts the colours of your display to the warmer end of the spectrum – making the display easier on your eyes.”</p><p>I have had night shift mode enabled on my laptop for at least a year or two, and used night shift technology on previous computers in years prior. When night shift is enabled, my display feels less visually intense. I disabled the setting before writing this post and realised, as I feel with greyscale, that I had to toggle the mode back – the cool colours were too jarring now that I am used to night shift.</p><p>Night shift technology means that I see colours on computers differently. I even keep the setting on when I am designing websites and working with colours. My philosophy is that if text is readable in night shift mode, and has been designed with proper colour contrast in mind, it should look good when night shift mode is disabled. In contrast, if text is less readable in night shift mode, I may need to review my design.</p><p>Technically, night shift mode cannot be turned permanently on with an Apple Mac. Instead, I have a custom schedule set that enables night shift from 22:00 to 21:59, which means all day except for the minute between. I have the option selected to “Turn on until tomorrow” and have the colour temperature setting almost at the end of the spectrum to the “More warm” setting. Here is what the options I have set look like on my computer:</p><figure><picture><img alt="The Apple Night Shift window. This image is described in the previous paragraph." loading="lazy" src="https://editor.jamesg.blog/content/images/2026/05/nightshift.png" style=" max-width: 130%;"/></picture><div class="alt"><label><input aria-label="Toggle image alt text on screen" type="checkbox"/>ALT</label><div class="content">The Apple Night Shift window. This image is described in the previous paragraph.</div></div></figure><p>As with grayscale, night shift is part of how I experience my computer. I don’t think about night shift unless I am discussing it with someone.</p><p>In addition to using Apple’s night shift feature, I also have <a href="https://justgetflux.com/" rel="noreferrer">Flux</a> installed, an application that shifts the colour on your computer. I am not exactly sure how Flux interacts with the system-level settings, but I do know that my display gets warmer at night. I like the way I have things set up.</p><p>More broadly, I love that I can customise my computers in this way. Night shift makes it easier for me to work with my computer. This is especially important given the large amount of time I spend using computers. I don’t necessarily recommend having night shift mode on at all times, but I like it.</p>
<a class="tag" href="https://jamesg.blog/2026/05/04/using-greyscale">how I use my phone in grayscale</a>
<a class="tag" href="https://justgetflux.com/">Flux</a>
<a class="tag" href="https://support.apple.com/en-gb/118583">According to Apple, night shift mode</a>
Ribbon - a linkding client - Posts feedhttps://www.coryd.dev/posts/2026/ribbon-a-linkding-client2026-05-04T22:08:00.000Z<p>I built a native iOS client for <a href="https://linkding.link">linkding</a> and launched it, as I usually do, with a sarcastic-ish Mastodon post. I'd been using my instance as a PWA (inasmuch as PWAs are supported on iOS) and found it to be <em>ok</em> but not terribly satisfying. I appreciated the existing clients, but didn't love them. I'd tried them all, but ended up generally using a <a href="https://apps.apple.com/us/app/linkding-web-extension/id6473717543">Safari extension</a> and the web app in Safari.</p>
Bouncy castle, a Lego build and Children's Day - W18 - Joel's Log Fileshttps://joelchrono.xyz/blog/2026-w182026-05-04T15:54:26.000Z<p>Obligatory May The 4th Be With You, by the way. Another week is over, I am still wearing my pajamas and enjoying a day off, which makes this Monday one of the best Mondays of the year thus far. Zero complaints!</p>
<p>Here’s a question, when was the last time you actually just played like a kid? something like tag, or with toys and such? I did some fun stuff this week that made me feel twelve again, it was awesome. It was Children’s Day in Mexico on April 30th, so I guess it makes sense.</p>
<ul>
<li>
<p>🧱 Had some time to assemble a Lego set! The <a href="https://www.lego.com/en-us/product/time-machine-from-back-to-the-future-77256">Time Machine from Back to the Future!</a>, I documented my progress <a href="https://polymaths.social/@joel/statuses/01KQ8Y6CCN668KA8VSBT291ERB">on my polymaths profile</a>, in case you want to see more pics.</p>
</li>
<li>
<p>🥳 A friend of mine had a birthday party, and the highlight of the event was a bouncy castle! Most of the time people our age aren’t allowed to get into those, but this was exclusively for grown-up kids like me. We played for actual hours and all of my joints ended up kinda bruised and burned the next day. But it was absolutely worth it. I am really happy to have had that experience again. We played <em>tag</em> and similar games and ended up very tired, so fun!</p>
</li>
<li>
<p>🐻 A while ago <a href="https://syls.blog/i-made-pixel-bears/">Syl made a pixel bear</a> for me! This was such a fun and unexpected gift to receive, but I hadn’t showed it off anywhere on my site yet. You can now find it on my footer section! Thank you again Syl, it’s a lovely gift. I also took it upon myself to make a few more pixel bears for other friends of mine, this is still work in progress though.</p>
</li>
<li>
<p>🛍️ My parents returned from their short trip and brought back some epic stuff for me, I got two shirts from Avatar (my sister who they visited, picked them for me) and I am enamoured by the Appa one.</p>
</li>
<li>
<p>🏕️ My church rented a <em>quinta</em> (a sort of ranch/country house where parties, weddings, quinceañeras and similar small events are held, often away from the city) for a day, to baptize a few new members of the congregation and celebrate Children’s Day at the same time! After the ceremonies, most families stayed to have lunch and enjoy the day, away from everything and enjoying the pool.</p>
</li>
<li>
<p>🚲 For some reason… I have been interested on maybe acquiring a new bike and actually pedalling to places. I should find a used bycicle somewhere and get to it, I wonder if my shape is good enough for that, to be honest, but I want to try.</p>
</li>
<li>
<p>🃏 I didn’t get in the pool myself, but I brought a bunch of card games, from <em>Exploding Kittens</em>, to <em>Coup</em>, to a pretty try-hard session of <em>Skull</em> and a pretty competitive run of <em>Oh Hell!</em> we also played your typical <em>Spoons</em> and <em>Cheat</em>. Seriously fun stuff when we got 6 or 7 players in many of these.</p>
</li>
<li>
<p>🏀 During a break from card games I went to a small basketball court in the quinta, playing turns throwing the ball into the basket. I somehow did like 6 shots in a row that fell right in the center of the net. I was very surprised with myself to be honest.</p>
</li>
</ul>
<figure class="img">
<picture>
<source srcset="/assets/img/blogs/2026-05-04-weeknotes.webp" type="image/webp"/>
<source srcset="/assets/img/blogs/2026-05-04-weeknotes.png" type="image/png"/>
<img class="mx-auto" src="https://joelchrono.xyz/assets/img/blogs/2026-05-04-weeknotes.png" alt="A collage of the events happening this week. An assembled DeLorean, UFO 50 on my Nintendo Switch, a panel of a romance manga, a photo inside the bouncy castle, a frame from the 100 Meters movie, the Avatar t-shirts designs, my PSP with MGS Peace Walker, and a table with cards on top of it."/>
</picture>
<figcaption class="caption">A collage of the events happening this week. An assembled DeLorean, UFO 50 on my Nintendo Switch, a panel of a romance manga, a photo inside the bouncy castle, a frame from the 100 Meters movie, the Avatar t-shirts designs, my PSP with MGS Peace Walker, and a table with cards on top of it.</figcaption></figure>
<h2 id="watching">Watching</h2>
<p>Just yesterday I watched <strong>100 Meters</strong>, an anime movie, from the same author of <em>Orb: On the Movement of the Earth</em>—my favorite anime series watched this year so far—and it quickly showed me more of that spark that I got from Orb’s writing. This movie portrays the life of a child prodigy who loves to run, and helps another kid how to do it, with a quote that would kickstart a rivalry for years to come: <em>“Running the 100 meters faster than anyone else can solve almost anything.”</em></p>
<p>Is that just a naive child with a lot to learn, or is it worth considering as a core value? There are many points this movie brings up as the film questions, celebrates and critiques the sport, as we wonder how we find the joy in giving all our body has to offer for something as seemingly simple as a 10 seconds sprint.</p>
<p>The animation work here is incredible, switching styles a few times, from traditional animation, to some incredibly detailed rotoscoping, to an absolutely breath-taking continuous take, in ones of a race under heavy rain. This film is great, I implore you to watch it. There are a lot of life lessons to be learned and reflected upon with this movie. It’s raw and visceral, a work of art.</p>
<h2 id="gaming">Gaming</h2>
<p>I am happy to say that I have completed <strong>Metal Gear Solid: Peace Walker</strong>, which you probably already know since I mentioned it on my Monthly Summary a couple days ago. What a fantastic title that was, review coming soon though. Spoiler: go and play it.</p>
<p>I decided to team up with <a href="https://jefklakscodex.com">Wouter</a> and play <strong>UFO 50</strong> at last. It’s a collection of 50 titles and has a sort of meta/alternate history background: these games were developed by a game company from the 80s, for their own console and all. Because of this, we agreed to treat these games as “rented” from Blockbuster or something, which is how I would have experienced these games in real life, playing one for a week straight and then moving to the next, finished or not. I am currently playing <strong>Barbuta</strong>, and having an interesting time. It’s pretty complicated and archaic, but it does have some charm to it, and getting familiar with the map and mechanics has been kinda satisfying. I may write mini-reviews for these.</p>
<p>I am still playing <strong>Tomodachi Life: Living the Dream</strong> as well, and checking up with my Miis every day, there have been some fun shenanigans and things going on here, my Mii got married with Lucca, there are a couple of love triangles going on, and other drama that I find very funny and hilarious. My island is level 34 now. I should spend some time creating an island with an actual design though, I’ve been kinda lazy about it.</p>
<h2 id="reading">Reading</h2>
<ul>
<li><strong>Smoking Behind the Supermarket with You</strong> - Up to chapter 60. I am fully caught up with this romance manga, it is a monthly release so now I have to find something else to read from start to finish I guess. I will patiently wait for how this story progresses. It’s really good stuff.</li>
<li><strong>Heavenly Delusion</strong> - Up to chapter 43. I returned to this sci-fi mystery manga at last, I didn’t really read any new chapters, as I backtracked a few chapters to get familiar with where the story was going. It’s a very unique story with a confusing thread of events that have been slowly threading together. I really, really enjoy it so far.</li>
<li><strong>Spy x Family</strong> - Up to chapter 133. A super fun arc just ended! and Anya got some really fun slice of life moments on this one. Enjoyable comedy as always.</li>
<li><strong>Sakamoto Days</strong> - Up to chapter 229. Gotta admit I kinda lost the thread and need to read some more to get hooked again.</li>
</ul>
<h2 id="around-the-web">Around the Web</h2>
<h3 id="blogposts">Blogposts</h3>
<ul>
<li><a href="https://brandons-journal.com/post/my-budget-emulation-pc">My Budget Emulation PC</a>, I’ve been wanting to do this too, but already got enough gaming devices.</li>
<li><a href="https://brainbaking.com/post/2026/04/a-review-of-physical-nintendo-switch-publishers/">A Short Review Of Physical Nintendo Switch Publishers</a>. This is just a fun time, I am inclined to agree with the ratings given here.</li>
<li><a href="https://anildash.com/2026/04/30/artemis-photos-flickr/">Why are the Artemis II photos on Flickr?</a>. A genuinely interesting look at how Flickr has evolved over the years. I am actually interested on revisiting that website. I actually made an account there when I was like 13 I think and shared some origami pictures on it.</li>
<li><a href="https://hollie.eilloh.net/posts/putting-my-xteink-inside-my-lochby-mini-field-journal-makes-it-easy-to-take-notes-in-my-commonplace-book">Putting my XTEink inside my Lochby Mini Field Journal makes it easy to take notes in my commonplace book</a>. No more text is needed.</li>
<li><a href="https://bojidar-bg.dev/blog/2026-04-29-garfield-attachment/">How I was cured of my Garfield attachment</a>. One of those totally random things I read and can’t stop. It’s a fun anecdote though.</li>
</ul>
<h3 id="youyube">YouYube</h3>
<ul>
<li><a href="https://youtu.be/it_EWg7ReYU">Deconstructing the Deadly Lighting of Resident Evil</a> - The production value of this video is off the charts, and the channel is surprisingly small. If you care about how videogames work I recommend keeping an eye on it.</li>
<li><a href="https://youtu.be/-YQB2tE6-8U">Why Every Generation Uses This One Slang Word | Otherwords</a> - A very cool video. Watch it.</li>
<li><a href="https://youtu.be/BzKXF0T75ps">Why aren’t actors ugly anymore?</a> - I keep noticing how perfect everyone looks on all the posters for Netflix produced movies (and most modern movies in general) and yeah, crazy stuff.</li>
<li><a href="https://youtu.be/_GGO-IOQDoo">How We Keep Making Offices Worse</a> - This was kind of eye-opening. Very sad to see how things are. I am in one of those open office spaces and yeah, it’s meh.</li>
</ul>
<p>This is day 61 of <a href="https://100daystooffload.com">#100DaysToOffload</a></p>
<p>
<a href="mailto:me@joelchrono.xyz?subject=Bouncy castle, a Lego build and Children's Day - W18">Reply to this post via email</a> |
<a href="https://fosstodon.org/@joel/116518368405523798">Reply on Fediverse</a>
</p>[RSS Club] Where are you from? - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=713032026-05-04T11:34:40.000Z<p><mark><em>Psssst!</em> This <strong>top secret</strong> post is only available to RSS subscribers!</mark></p>
<p>A little while ago I added some <a href="https://shkspr.mobi/blog/2025/09/reasonably-accurate-privacy-conscious-cookieless-visitor-tracking-for-wordpress/">locally hosted, privacy first stats</a> to my blog. Using an <a href="https://mailfud.org/geoip-legacy/">offline GeoIP service</a> I can get a <em>very</em> rough idea of where visitors are from.</p>
<p>It doesn't deal with people using a VPN, or their mobile roaming to a different country, or rapid changes in IP allocation - but it's good enough for my purposes.</p>
<p>Here's a quick table showing the vague distribution of RSS Club members.</p>
<table>
<thead>
<tr>
<th align="right"><strong>Country</strong></th>
<th align="left"><strong>Flag</strong></th>
<th align="right"><strong>Total Views</strong></th>
<th align="right"><strong>Percentage</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td align="right">US</td>
<td align="left">🇺🇸</td>
<td align="right">6,242</td>
<td align="right">24.1%</td>
</tr>
<tr>
<td align="right">GB</td>
<td align="left">🇬🇧</td>
<td align="right">5,764</td>
<td align="right">22.2%</td>
</tr>
<tr>
<td align="right">DE</td>
<td align="left">🇩🇪</td>
<td align="right">1,947</td>
<td align="right">7.5%</td>
</tr>
<tr>
<td align="right">NL</td>
<td align="left">🇳🇱</td>
<td align="right">1,669</td>
<td align="right">6.4%</td>
</tr>
<tr>
<td align="right">CN</td>
<td align="left">🇨🇳</td>
<td align="right">1,027</td>
<td align="right">4.0%</td>
</tr>
<tr>
<td align="right">HK</td>
<td align="left">🇭🇰</td>
<td align="right">909</td>
<td align="right">3.5%</td>
</tr>
<tr>
<td align="right">AU</td>
<td align="left">🇦🇺</td>
<td align="right">770</td>
<td align="right">3.0%</td>
</tr>
<tr>
<td align="right">CA</td>
<td align="left">🇨🇦</td>
<td align="right">691</td>
<td align="right">2.7%</td>
</tr>
<tr>
<td align="right">FR</td>
<td align="left">🇫🇷</td>
<td align="right">605</td>
<td align="right">2.3%</td>
</tr>
<tr>
<td align="right">SE</td>
<td align="left">🇸🇪</td>
<td align="right">589</td>
<td align="right">2.3%</td>
</tr>
<tr>
<td align="right">JP</td>
<td align="left">🇯🇵</td>
<td align="right">442</td>
<td align="right">1.7%</td>
</tr>
<tr>
<td align="right">FI</td>
<td align="left">🇫🇮</td>
<td align="right">405</td>
<td align="right">1.6%</td>
</tr>
<tr>
<td align="right">CH</td>
<td align="left">🇨🇭</td>
<td align="right">395</td>
<td align="right">1.5%</td>
</tr>
<tr>
<td align="right">BR</td>
<td align="left">🇧🇷</td>
<td align="right">392</td>
<td align="right">1.5%</td>
</tr>
<tr>
<td align="right">ES</td>
<td align="left">🇪🇸</td>
<td align="right">345</td>
<td align="right">1.3%</td>
</tr>
<tr>
<td align="right">IT</td>
<td align="left">🇮🇹</td>
<td align="right">324</td>
<td align="right">1.3%</td>
</tr>
<tr>
<td align="right">PT</td>
<td align="left">🇵🇹</td>
<td align="right">285</td>
<td align="right">1.1%</td>
</tr>
<tr>
<td align="right">PL</td>
<td align="left">🇵🇱</td>
<td align="right">272</td>
<td align="right">1.0%</td>
</tr>
<tr>
<td align="right">BE</td>
<td align="left">🇧🇪</td>
<td align="right">249</td>
<td align="right">1.0%</td>
</tr>
<tr>
<td align="right">IN</td>
<td align="left">🇮🇳</td>
<td align="right">198</td>
<td align="right">0.8%</td>
</tr>
<tr>
<td align="right">CZ</td>
<td align="left">🇨🇿</td>
<td align="right">153</td>
<td align="right">0.6%</td>
</tr>
<tr>
<td align="right">TR</td>
<td align="left">🇹🇷</td>
<td align="right">134</td>
<td align="right">0.5%</td>
</tr>
<tr>
<td align="right">IE</td>
<td align="left">🇮🇪</td>
<td align="right">126</td>
<td align="right">0.5%</td>
</tr>
<tr>
<td align="right">BG</td>
<td align="left">🇧🇬</td>
<td align="right">121</td>
<td align="right">0.5%</td>
</tr>
<tr>
<td align="right">TW</td>
<td align="left">🇹🇼</td>
<td align="right">114</td>
<td align="right">0.4%</td>
</tr>
<tr>
<td align="right">SG</td>
<td align="left">🇸🇬</td>
<td align="right">110</td>
<td align="right">0.4%</td>
</tr>
<tr>
<td align="right">NZ</td>
<td align="left">🇳🇿</td>
<td align="right">99</td>
<td align="right">0.4%</td>
</tr>
<tr>
<td align="right">ZA</td>
<td align="left">🇿🇦</td>
<td align="right">97</td>
<td align="right">0.4%</td>
</tr>
<tr>
<td align="right">NO</td>
<td align="left">🇳🇴</td>
<td align="right">95</td>
<td align="right">0.4%</td>
</tr>
<tr>
<td align="right">AT</td>
<td align="left">🇦🇹</td>
<td align="right">91</td>
<td align="right">0.4%</td>
</tr>
<tr>
<td align="right">RU</td>
<td align="left">🇷🇺</td>
<td align="right">86</td>
<td align="right">0.3%</td>
</tr>
<tr>
<td align="right">DK</td>
<td align="left">🇩🇰</td>
<td align="right">84</td>
<td align="right">0.3%</td>
</tr>
<tr>
<td align="right">HU</td>
<td align="left">🇭🇺</td>
<td align="right">76</td>
<td align="right">0.3%</td>
</tr>
<tr>
<td align="right">GR</td>
<td align="left">🇬🇷</td>
<td align="right">64</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">UA</td>
<td align="left">🇺🇦</td>
<td align="right">56</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">IS</td>
<td align="left">🇮🇸</td>
<td align="right">49</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">AE</td>
<td align="left">🇦🇪</td>
<td align="right">49</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">GE</td>
<td align="left">🇬🇪</td>
<td align="right">44</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">PR</td>
<td align="left">🇵🇷</td>
<td align="right">44</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">LU</td>
<td align="left">🇱🇺</td>
<td align="right">42</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">IL</td>
<td align="left">🇮🇱</td>
<td align="right">42</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">PH</td>
<td align="left">🇵🇭</td>
<td align="right">41</td>
<td align="right">0.2%</td>
</tr>
<tr>
<td align="right">EE</td>
<td align="left">🇪🇪</td>
<td align="right">36</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">AR</td>
<td align="left">🇦🇷</td>
<td align="right">36</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">ID</td>
<td align="left">🇮🇩</td>
<td align="right">34</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">MY</td>
<td align="left">🇲🇾</td>
<td align="right">31</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">SI</td>
<td align="left">🇸🇮</td>
<td align="right">30</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">BZ</td>
<td align="left">🇧🇿</td>
<td align="right">29</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">TH</td>
<td align="left">🇹🇭</td>
<td align="right">29</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">RO</td>
<td align="left">🇷🇴</td>
<td align="right">25</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">VN</td>
<td align="left">🇻🇳</td>
<td align="right">24</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">LT</td>
<td align="left">🇱🇹</td>
<td align="right">24</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">MQ</td>
<td align="left">🇲🇶</td>
<td align="right">20</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">KR</td>
<td align="left">🇰🇷</td>
<td align="right">20</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">UN</td>
<td align="left">🇺🇳</td>
<td align="right">18</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">RS</td>
<td align="left">🇷🇸</td>
<td align="right">17</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">TN</td>
<td align="left">🇹🇳</td>
<td align="right">15</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">CL</td>
<td align="left">🇨🇱</td>
<td align="right">14</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">SK</td>
<td align="left">🇸🇰</td>
<td align="right">14</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">MX</td>
<td align="left">🇲🇽</td>
<td align="right">14</td>
<td align="right">0.1%</td>
</tr>
<tr>
<td align="right">CO</td>
<td align="left">🇨🇴</td>
<td align="right">11</td>
<td align="right">0.0%</td>
</tr>
<tr>
<td align="right">BD</td>
<td align="left">🇧🇩</td>
<td align="right">10</td>
<td align="right">0.0%</td>
</tr>
<tr>
<td align="right">EC</td>
<td align="left">🇪🇨</td>
<td align="right">10</td>
<td align="right">0.0%</td>
</tr>
<tr>
<td align="right">EG</td>
<td align="left">🇪🇬</td>
<td align="right">10</td>
<td align="right">0.0%</td>
</tr>
</tbody>
</table>
<p>There are a few more rows but, in the spirit of privacy, I've not included some of the more unique countries. Not all of those are unique views - these are aggregate statistics. If your RSS reader is hosted in a different country - or on a large platform - it may only show up inaccurately.</p>
<p>If you don't see your country in this list, please <a href="https://edent.tel/">drop me a comment via your favourite method</a>.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=71303&HTTP_REFERER=Atom" alt="" width="1" height="1" loading="eager"/>Quotes from Tufte’s course - James' Coffee Bloghttps://jamesg.blog/2026/05/04/quotes-from-tuftes-course2026-05-04T00:00:00.000Z
<p>I took Edward Tufte’s <a href="https://www.edwardtufte.com/online-course/">online course on analysing and presenting data</a>. It lasted four hours and was a mixture of reading from his books and listening to lecture material. The course was terrific and I learned so much. As I was taking the course, I took down a few quotes that stood out to me. I feel some of these points have applications that stretch further than data presentation.</p><p>Here is what I noted down. All quotes from Tufte.</p><ul><li>“It is not about how much information there is, but rather how effectively it is arranged.”</li><li>“The information is the interface.”</li><li>“They have come to your presentation for the <em>content.</em>” (Italics represent aural emphasis.)</li><li>“How do I/you/they really know that?”</li><li>“How could I/you/they possibly ever know that?”</li><li>“There are a million possible sequences, so let them [people] decide how to read it on their own.”</li></ul><p>I will be thinking about these for a while.</p><p>The last quote in particular was interesting to me. Early on in the course, Tufte walked through the visual layout of a U.S. National Weather Service website. Seen with his guidance, the information density and layout had new meaning – it was like I saw the page myself once and then saw it again with Tufte’s analysis. He noted how there was a lot of information on the page and people could go to whatever they needed or may find interesting, whether it be the temperature and summary forecast for the day or the more complex, data-rich weather maps.</p><p>The point I took away is that people will navigate how they want: an interface can put the information on the page and help people find for what they are looking but, ultimately, readers will read in their own way. With the web, people can scroll up and down to navigate information; people can become familiar with a website and skip to the part they want. <sup class="footnote-reference" id="f-1"><a href="https://jamesg.blog/longform-feed#1">1</a></sup></p><p>Tufte later went on to talk about how people read documents, which I think is the context in which I noted down the quote.</p><p>People skim, study, scrutinise, read, and re-read documents in their own way. When reading fiction, I sometimes read complex or important sentences twice to make sure I understand them. Whereas in another context, such as reading the news, I might skim more and focus on specific parts of the article – the first few paragraphs, the first sentences in the next few.</p><p>I don’t know exactly what analysis I have on this other than it makes sense, and that I never know what information someone might read or not read on a page. It is always interesting to learn about how people navigate information.</p>
<div class="footnote-definition" id="1"><sup class="footnote-definition-label" id="f-2">1</sup>
<p>One metaphor for skipping to a part of a web page is reading a book. I know to skip the first few pages that have meta information about a book. My cue of where to start reading is usually either the opening acknowledgement or the table of contents. I skim the first few pages so I can get to the main content in a book. So too do I scroll past different elements on web pages to find for what I am looking.</p>
<a href="https://jamesg.blog/longform-feed#f-1">[↩]</a></div>
<a class="tag" href="https://jamesg.blog/longform-feed#1">1</a>
<a class="tag" href="https://jamesg.blog/longform-feed#f-1">[↩]</a>
<a class="tag" href="https://www.edwardtufte.com/online-course/">online course on analysing and presenting data</a>
Vikings and Mead - Cool As Heckhttps://cool-as-heck.blog/vikings-and-mead2026-05-03T19:45:28.000Z<div>I've been thinking a lot about Vikings and mead lately, probably because I've recently started two batches fermenting in the basement: a spiced pomegranate and a pineapple coconut. It's funny how much mythology surrounds this drink versus what actually happened.</div>
<div><br></div>
<div>So here's the thing: Vikings didn't invent mead. Not even close. Archaeologists have found evidence of mead production in China from like 7000 BCE. That's roughly 8,000 years before the first Viking longship set sail. The stuff basically showed up independently all over the place: Africa, Greece, India. Turns out ancient humans everywhere figured out that honey + water + time = alcoholic goodness.</div>
<div><br></div>
<div>The Vikings were more like really enthusiastic fans who committed to the bit.<br>
</div>
<div><br></div>
<div>If you were an average Viking just living your life, you probably weren't sipping mead daily. Honey was scarce in cold Scandinavia. Making mead took time and resources. It was the special occasion drink; feasts, weddings, religious ceremonies, sealing important deals.</div>
<div><br></div>
<div>For everyday drinking? Most Vikings had ale or beer. It was cheaper, easier to make, and safer than water since the fermentation process killed off bacteria. Kids even drank weak versions of it. I've heard that even today in Germany you can get your kid a weak ale with dinner.</div>
<div><br></div>
<div>Kind of makes you appreciate having access to whatever fruit you can find for experiments. Right now I'm curious how the pineapple coconut will turn out. I didn't really plan it out all that much, I'm mostly winging it, but I've learned enough over the last year or so to feel pretty comfortable with that.<br>
</div>
<div><br></div>
<div>Okay, this part is my favorite. The Vikings didn't just drink mead, they literally believed it was magical.</div>
<div><br></div>
<div>There's this story about the Mead of Poetry. After a war between two groups of gods, they made peace by spitting into a bowl (yes, really). From that spit came Kvasir, the wisest being alive. Some dwarves killed him, mixed his blood with honey, and boom, magical mead that gave anyone who drank it the gift of poetry and wisdom.</div>
<div><br></div>
<div>Odin, being Odin, tricked his way into a mountain, seduced a giantess guarding the mead, drank ALL of it, and flew back to the gods as an eagle while spitting some out mid-flight. The stuff he spilled? That's why bad poets exist.</div>
<div><br></div>
<div>Also, in Valhalla there's a magical goat named Heiðrún standing on the roof eating tree leaves, and mead flows from her udders into a massive vat for fallen warriors. Norse mythology is something else.<br>
</div>
<div><br></div>
<div>Here's a fun fact that doesn't make it into the movies: brewing was women's work. The mistress of the household was in charge of making ale and mead. It was a respected skill, often passed down through generations.</div>
<div><br></div>
<div>Some families had "magic sticks". These were wooden stirring sticks that harbored yeast from previous batches. They'd use the same stick to start fermentation, basically an early form of yeast cultivation. I'm over here pitching EC-1118 and D47 from a packet, but they were maintaining yeast colonies on birch sticks for generations. That's dedication.</div>
<div><br></div>
<div>At big feasts, the lady of the house would serve the first drink to her husband, then work her way through guests by rank. She basically controlled the whole vibe of the party.<br>
</div>
<div><br></div>
<div>Did they actually drink from horns? Sometimes. Archaeologists have found plenty of drinking horns in Scandinavia, often decorated with silver or gold. But you can't set a horn down mid-drink without spilling, so they were more for ceremonial toasts.</div>
<div><br></div>
<div>For casual sipping throughout a meal? Wooden cups, bowls, imported glass for the wealthy. The famous Jelling Cup found in Denmark belonged to King Harald Bluetooth himself.</div>
<div><br></div>
<div>Mead's having a moment. Hundreds of new meaderies have opened in the last couple decades, and a lot of them lean hard into the Viking aesthetic. But I think what's cool is that the Vikings treated mead as more than just a beverage. It was about community, connection, marking important moments.</div>
<div><br></div>
<div>Maybe that's why it still resonates with hobbyists these days. In a world of mass-produced beer and cocktail trends, there's something deeply human about simply fermenting honey and water, waiting patiently, and sharing it with people you care about. It's a fun science experiment that also happens to be delicious and you can share it with your friends and family.</div>
<div><br></div>
<div>Anyway, that's where my head's been at since starting the spiced pomegranate and pineapple coconut batches. I even got some yeast similar to what the Vikings used for their ale and mead. I haven't decided what batch to use it with yet, but I'll try to keep posting updates on my batches here. TTFN. 🍺 🍯 </div>
The way to save news is not to create monopolistic monocultures - Werd I/O69f7630d825c3600019fd6a12026-05-03T15:00:29.000Z<p>Link: <a href="https://dicktofel.substack.com/p/some-rationalization-may-finally?ref=werd.io"><em>Some Rationalization May Finally Be Coming for Newsroom Intermediaries, by Richard J. Tofel in Second Rough Draft</em></a></p><p>In his latest post, Dick Tofel talks about a need for consolidation in organizations that support newsrooms (and, in fact, in newsrooms themselves).</p><blockquote>“Devoting limited resources to competing services where one offering is superior not only leads those using the inferior service to poorer results, it also subsidizes entirely unnecessary administrative costs at the inferior service. And in circumstances where competing services are roughly equivalent, mere duplication can also be inefficient—and, as noted above, may place an administrative burden on already over-stressed client newsrooms. Time is one of the scarcest resources of all.”</blockquote><p>I’m worried.</p><p>This isn’t a criticism of Dick Tofel: he calls out the benefits of competition and the difficulty of determining winners in a market. But I do think there are two more things to consider.</p><p>The first is that I don’t believe any intermediary service designed for newsrooms is optimal. That’s not a criticism of <em>them</em>, either: <em>every</em> service has room to grow. Any time you remove competition from a market and hand it to a single privately-owned player (nationalized services are another thing entirely), the offerings stagnate because the driver to improve has gone away. Just ask anyone who remembers the web’s Internet Explorer wilderness years before Firefox disrupted them and forced widespread standardization.</p><p>The second, and probably most important, is that funders are a narrow group of people with a narrow set of perspectives. Unless they’ve done the work to be representative and inclusive in their work and culture, they may miss how one service serves a community better than another and erroneously mark them as duplicative. Or to put it another way, if there is any consolidation in <em>any</em> American market, I don’t trust that organizations run by women and people of color won’t be the ones to lose out.</p><p>This isn’t anyone’s intention, but reducing competition at any level — funders, intermediaries, newsrooms, distributors — has the potential to create monopolies that become gatekeepers for vulnerable communities who need more support, not less. I don’t think that’s what the moment we’re living through needs. We need more ideas, more approaches, more funding, more communities served, and more diversity. The people who want to shut down an effective, independent press want to create a monoculture. The way to combat that is not to create another one.</p>Vertically Aligning Roman Numerals in Code - Terence Eden’s Bloghttps://shkspr.mobi/blog/?p=637752026-05-03T11:34:59.000Z<p>I have a PHP function which uses Roman Numerals. It looks like this:</p>
<pre><code class="language-php">$romanNumerals = [
"Ⅿ" => 1000,
"ⅭⅯ" => 900,
"Ⅾ" => 500,
"ⅭⅮ" => 400,
"Ⅽ" => 100,
"ⅩC" => 90,
"Ⅼ" => 50,
"ⅩⅬ" => 40,
"Ⅹ" => 10,
"Ⅸ" => 9,
"Ⅷ" => 8,
"Ⅶ" => 7,
"Ⅵ" => 6,
"Ⅴ" => 5,
"Ⅳ" => 4,
"Ⅲ" => 3,
"Ⅱ" => 2,
"Ⅰ" => 1
];
</code></pre>
<p>The problem is, the operators don't line up and the whole thing looks messy. Why? Because the <a href="https://shkspr.mobi/blog/2023/03/unicode-roman-numerals-and-screen-readers/">Unicode Roman Numerals</a> are <em>not</em> monospaced! <code>ⅭⅯ</code> is a different width to <code>ⅩC</code> and <code>Ⅷ</code> is only a single character! Copy the above to a text editor and see if you can get neat columns. I bet you can't!</p>
<p>I'm obsessed with <a href="https://shkspr.mobi/blog/2014/11/why-i-vertically-align-my-code-and-you-should-too/">vertically aligning my code</a>. So how to solve this ugly problem?</p>
<p>The <a href="https://phpc.social/@Crell/115329116036130430">answer was simple</a>. Assign keys to the values and then flip the array!</p>
<pre><code class="language-php">$romanNumerals = array_flip([
1000 => "Ⅿ",
900 => "ⅭⅯ",
500 => "Ⅾ",
400 => "ⅭⅮ",
100 => "Ⅽ",
90 => "ⅩC",
50 => "Ⅼ",
40 => "ⅩⅬ",
10 => "Ⅹ",
9 => "Ⅸ",
8 => "Ⅷ",
7 => "Ⅶ",
6 => "Ⅵ",
5 => "Ⅴ",
4 => "Ⅳ",
3 => "Ⅲ",
2 => "Ⅱ",
1 => "Ⅰ"
]);
</code></pre>
<p>There! Doesn't that look much neater!</p>
<p><a href="https://libraries.mit.edu/150books/2011/05/11/1985/">As was written long ago</a>:</p>
<blockquote><p>A computer language is not just a way of getting a computer to perform operations but rather … it is a novel formal medium for expressing ideas about methodology. Thus, programs must be written for people to read, and only incidentally for machines to execute.</p></blockquote>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=63775&HTTP_REFERER=Atom" alt="" width="1" height="1" loading="eager"/>