Shellsharks Blogroll - BlogFlock 2026-05-09T03:02:20.345Z BlogFlock Adepts of 0xCC, destructured, fLaMEd, Trail of Bits Blog, Aaron Parecki, Westenberg, gynvael.coldwind//vx.log (pl), James' Coffee Blog, joelchrono, Evan Boehs, Kev Quirk, cool-as-heck, Posts feed, Sophie Koonin, cmdr-nova@internet:~$, <span>Songs</span> on the Security of Networks, Johnny.Decimal, Werd I/O, Robb Knight, Molly White, Hey, it's Jason!, Terence Eden’s Blog Notable links: May 8, 2026 - Werd I/O 69fdf2dce66c4000011e25da 2026-05-08T14:31:42.000Z <img src="https://images.unsplash.com/photo-1623118176012-9b0c6fa0712d?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDN8fGVjb25vbXl8ZW58MHx8fHwxNzc4MjUwNjc4fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=2000" alt="Notable links: May 8, 2026"><p><em>Most Fridays, I share a handful of pieces that caught my eye at the intersection of technology, media, and society.</em></p><p><em>Did I miss something important? </em><a href="mailto:ben@werd.io" rel="noreferrer"><em>Send me an email</em></a><em> to let me know.</em></p><hr><h3 id="an-invitation-to-build-the-civic-information-economy"><a href="https://www.newsfutures.org/library/civic-information-endowment?ref=werd.io" rel="noreferrer">An Invitation to Build the Civic Information Economy</a></h3><p>This feels like a vital exploration to me:</p><blockquote>&#x201C;What does it take to fund civic information? We often focus on &#x201C;more money,&#x201D; but we limit the field&#x2019;s potential by ignoring better capital design. Today&#x2019;s landscape often sees dollars concentrated in intermediary and national plays&#x2014;often with good reason&#x2014;but without deliberate examination, we risk stifling the imagination of a developing field.&#x201D;</blockquote><p>This is part manifesto, but also part call to action and invitation to participate. Funding for news has often been reactive, filling gaps, but what does it mean to intentionally design a genuine ecosystem with dynamics that support production of, and access to, the civic information we need that is a prerequisite for democracy to function?</p><p>I love how genuinely participative this is: rather than a bunch of people trying to be smart inside institutions, this requires that the people who struggle the most to find information are active co-designers. That feels non-negotiable to me. There&#x2019;s been a pushback against inclusion and equity in news and everywhere recently, but there&#x2019;s no other way to build an ecosystem that genuinely serves everybody. We&#x2019;ve all got to own it. We&#x2019;ve all got to take part. Everyone needs to be represented.</p><p>I have high hopes for this, and I love that the effort exists. It&#x2019;s something we should all support.</p><hr><h3 id="some-rationalization-may-finally-be-coming-for-newsroom-intermediaries"><a href="https://dicktofel.substack.com/p/some-rationalization-may-finally?ref=werd.io" rel="noreferrer">Some Rationalization May Finally Be Coming for Newsroom Intermediaries</a></h3><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>&#x201C;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&#x2014;and, as noted above, may place an administrative burden on already over-stressed client newsrooms. Time is one of the scarcest resources of all.&#x201D;</blockquote><p>I&#x2019;m worried.</p><p>This isn&#x2019;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&#x2019;t believe any intermediary service designed for newsrooms is optimal. That&#x2019;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&#x2019;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&#x2019;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&#x2019;t trust that organizations run by women and people of color won&#x2019;t be the ones to lose out.</p><p>This isn&#x2019;t anyone&#x2019;s intention, but reducing competition at any level &#x2014; funders, intermediaries, newsrooms, distributors &#x2014; has the potential to create monopolies that become gatekeepers for vulnerable communities who need more support, not less. I don&#x2019;t think that&#x2019;s what the moment we&#x2019;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><hr><h3 id="report-on-burnout-in-open-source-software"><a href="https://mirandaheath.website/report-on-burnout-in-open-source-software/?ref=werd.io" rel="noreferrer">Report on Burnout in Open Source Software</a></h3><p>This bleak report on burnout in open source software communities from last year has been doing the rounds. I think it&#x2019;s clearly indicative of where open source is at (and its trajectory), but the solutions aren&#x2019;t immediately clear &#x2014; we know this because similar concerns have been anecdotally highlighted by various people for well over a decade. However, this is the first formal research report I can remember reading.</p><p>It&#x2019;s pretty stark: 60% of open source maintainers work unpaid, 60% have quit or considered quitting, and 44% cite burnout specifically.</p><p>From the author:</p><blockquote>&#x201C;In my report, I draw upon a combination of academic literature and OSS community discussion to identify 6 factors that contribute OSS developer burnout: difficulty getting paid, workload and time commitment, maintenance work as unrewarding, toxic community behaviour, hyper-responsibility and pressure to prove oneself. I then make 4 broad recommendations for how to address it: pay OSS engineers, foster a culture of recognition and respect, grow the community and advocate for maintainers.&#x201C;</blockquote><p>The thing is: <em>who</em> is going to pay OSS engineers? Every attempt to get downstream users to pay out of the goodness of their hearts has failed at scale. There are certainly corporate sponsors of OSS maintainers already, when there&#x2019;s a clear link between an open source project and a company&#x2019;s bottom line. That could certainly be a broader standard, but there are also a ton of open source projects that tie less obviously into bottom lines, or are useful for communities outside large corporations.</p><p>Beyond the lack of direct compensation, it&#x2019;s also a pretty thankless job. Downstream users will often make demands of maintainers that don&#x2019;t take their contexts into account; people who are unpaid and overworked find themselves treated as if their users had paid them large sums of money. Open source users communities can sometimes also be wildly detached from reality; I&#x2019;ve had people tell me that the solution is to move beyond a money-driven society or to have a revolution to overthrow capitalism. I mean, sure, whatever, but is that going to happen tomorrow? In the meantime there&#x2019;s rent to be paid and food to buy.</p><p>The answer may be that, actually, open source doesn&#x2019;t work very well except (1) in mutualistic co-operative communities (2) as a strategic move by corporations who want to change the markets that affect them in some way (3) as a self-promotional way to gain more widespread recognition for your abilities. If that&#x2019;s the case, maintainers should go into it with open eyes: if they&#x2019;re successful, their work <em>will</em> be used by people who make a bunch of money without compensating them in any way.</p><p>If new maintainers understand that they&#x2019;re voluntarily signing up for extraction, I think it&#x2019;s uncomplicated. If they think they&#x2019;re going to &#x201C;win&#x201D; open source and make a living by giving their work away for free, they may be sorely disappointed.</p><hr><h3 id="i-can-never-talk-to-an-ai-anonymously-again"><a href="https://www.theargumentmag.com/p/i-can-never-talk-to-an-ai-anonymously?ref=werd.io" rel="noreferrer">I can never talk to an AI anonymously again</a></h3><p>On first read, I found this a little bit sobering:</p><blockquote>&#x201C;But soon, the entire debate over internet anonymity will be as anachronistic as an iPod Touch. That&#x2019;s because Claude Opus 4.7 is here, and last week, I discovered it could identify me from text I had never published, text from when I was in high school, text from genres I have never publicly written in. And if it can identify me, soon, it will be able to identify many of you.&#x201D;</blockquote><p>Kelsey Piper plugged in 125 words from an unpublished essay and asked Opus 4.7 to identify the author. It listed her as the most likely answer.</p><p>I was skeptical, so I did the same: a few paragraphs from an essay, which I plugged into an account that isn&#x2019;t connected to me. Here&#x2019;s what Claude said:</p><blockquote>&#x201C;Based on the style, themes, and specific concerns in this snippet, my best guess is Ben Werdm&#xFC;ller.<br><br>Several signals point this way: the framing of the open social web as a movement rather than a technical project, the explicit linkage between Mastodon/Bluesky, journalism, and broader liberatory politics, the critique of newsroom demographics (&quot;very white, very coastal, wealthy&quot;), and the historical framing of the early web as an anti-gatekeeping movement. Werdm&#xFC;ller writes frequently about all of these topics together &#x2014; he works at ProPublica on technology, has a long history in the IndieWeb and federated social space (he co-founded Elgg and Known), and his personal blog regularly weaves these exact threads.<br><br>The voice also fits: declarative, slightly polemical, comfortable making sweeping movement-level claims, and willing to name class and racial dynamics directly rather than hedging.&#x201D;</blockquote><p>I&#x2019;ll take that as a compliment! But it&#x2019;s impressive that it was able to figure it out without searching.</p><p>But those are topics I write about regularly. When I gave it a snippet of my short fiction piece <a href="https://outmap.org/sharing-is-caring/?ref=werd.io"><em>Sharing is Caring</em></a>, it wasn&#x2019;t able to find a match:</p><blockquote>&#x201C;The passage has hallmarks of contemporary speculative/dark YA or literary sci-fi short fiction &#x2014; body-swapping or consciousness-transfer premise, a class-inflected ethical horror (poor family selling a daughter&apos;s body to a wealthy disabled girl&apos;s family), tight close-third POV, terse interior monologue in italics-style cut-ins (&quot;Sure. No. Not a problem.&quot;), and the bleak ironic kicker about an eighteenth birthday. That cluster of moves shows up in writers like Ted Chiang, Ken Liu, Carmen Maria Machado, Alice Sola Kim, Karen Russell, or in anthology pieces from venues like Lightspeed, Tor.com, or various YA dystopia collections &#x2014; but none of those is a confident match.&#x201D;</blockquote><p>Again, I&#x2019;ll take the comparisons as a compliment, but there isn&#x2019;t enough of my fiction out there to make a real suggestion. That&#x2019;s going to be true of most people&#x2019;s writing &#x2014; unless they&#x2019;re publishing really regularly on the web, and have been for a while, the models might not know about them. Kelsey had a slightly different result: a school progress report was still identified as hers. But clearly, the reliability here is shakier.</p><p>In other words, we&#x2019;re a little way&#x2019;s off from AI being able to identify most people quite yet. But if you&#x2019;re publishing regularly, out in the open, you should expect your work to be fingerprinted. That <em>does</em> have implications for dissidents and people writing anonymously who have previously done so under their real names.</p> Metal Gear Solid Peace Walker - Joel's Log Files https://joelchrono.xyz/blog/metal-gear-solid-peace-walker 2026-05-07T21:01:23.000Z <p>Metal Gear Solid is a very cool franchise, that is apparently a bit of a mess when it comes to its story and the connection between each game and all of that.</p> <p>I am not as attached on it as I am on Resident Evil. I did not spend countless hours watching video essays about Metal Gear, or Hideo Kojima or the history of it all.</p> <p>I first came in contact with the series when <em>Metal Gear Solid V: The Phantom Pain</em> came out back in 2015. I remember I saw the first part of a let’s play of some Spanish YouTube creator and was very intrigued by the idea of stealth, and the graphics.</p> <p>It didn’t take long for me to find out about <em>Peace Walker</em> for the PSP. I eventually tried it out, but I gave up on it after 4 hours or so, when one of the boss fights proved too much for my teenager brain who refused to look up a guide. It was one of the first games I tried on the system based on internet recommendations, no friend of mine had talked about Metal Gear to me before!</p> <p>Nowadays, the franchise seems to be back on the spotlight with the <em>Metal Gear Solid 3 Delta</em> remake, and with Konami more active in the gaming world overall—they are going to release a new 2D Castlevania soon!—so I thought I may as well check this game. After all, if I beat the archaic Resident Evil games, a PSP title shouldn’t be that hard, right?</p> <p>I played it on original hardware, no save states or fast forwarding was used for this title, yay!</p> <figure class="img"> <picture> <source srcset="/assets/img/blogs/2026-05-07/mgs.webp" type="image/webp" /> <source srcset="/assets/img/blogs/2026-05-07/mgs.png" type="image/png" /> <img class="mx-auto" src="/assets/img/blogs/2026-05-07/mgs.png" alt="Cover of the game" /> </picture> <figcaption class="caption">Cover of the game</figcaption></figure> <h2 id="story">Story</h2> <p>In this game we follow Snake, or Boss, or Big Boss (again, confusing story or something), a highly trained soldier/spy/agent who has abandoned his allegiance to any nation, and decided to form his own military organization: <em>“Militaires Sans Frontiéres”</em> (Army Without Borders, MSF for short) with the goal of providing aid to smaller nations and do good things overall—as much good a private army can do at least?</p> <p>Anyway, the story is set somewhere in the 70s, and begins when a mysterious professor and a teenage girl called Paz (Peace in Spanish) ask him for help to uncover a military operation in Costa Rica. While Paz is oblivious to the situation and is genuine in her pursue for pacifist ideals, Snake and his friend Kaz can tell the “professor” is has some ulterior motives at play here. Alas, they agree to help out given Paz’ enthusiasm, and because there’s something fishy going, and Snake wants to seek answers.</p> <p>As Snake makes his way through enemy terrain, finds new characters, and learns more about the secret military activity in the region, we will realize that there are nukes in Costa Rica, unmanned AI technology, and worst of all, a <em>Metal Gear</em>, a bipedal weapon able to deliver a nuclear bomb from any position in any terrain, called the <em>Peace Walker</em>, powered by an AI built on the image of Snake’s very own mentor. Snake’s mission is to do everything in its power to stop a nuclear launch that would send the world into disarray.</p> <p>Explaining all that in just two paragrphs feels like a diservice to the game, honestly.</p> <p>The story is full of twist and turns, revelations, betrayal and ethical dilemmas. I cannot stress enough how strong the writing and plot development of this game is. There are a couple of things that go on a bit of a roundabout way, but I found the conflict to be extremely interesting, and I loved the characters that we met along the way.</p> <p>There are also lots of cassette tapes with extra dialogue before every mission, which contain story and information about the characters, areas, history and concepts mentioned within the game. These are fully voiced, so it never feels like you are just reading a whole encyclopedia of each character. I listened to all the tapes and it was very entertaining.</p> <p>Snake’s development is great too. He has a cool demeanor, but we’ll soon see that there is a lot of regret and trauma in the things he has done in the past. Either way, he has a lot of great moments and fun interactions with the characters during cutscenes and on the cassette tapes, doing his best to inspire others and command the situation.</p> <p>The rest of the cast is not far behind, Kaz is cool, Paz is inspiring, we’ll meet other characters like Amanda, who leads <em>los Sandinistas</em>, a resistance who want a revolution to free themselves from the americans and corruption, and are happy to join you to achieve it. There’s more characters and they have a lot to say, but no need to mention it all here.</p> <p>Overall, I really loved it all, the dialogue, the discussions on nuclear deterrence, the political intrigue, plenty of conversations about AI and its military use. All of it is handled quite well and it even holds up and feels familiar with some of the things going on today, 16 years after the game’s release. Good stuff.</p> <figure class="img"> <picture> <source srcset="/assets/img/blogs/2026-05-07/mgs1.webp" type="image/webp" /> <source srcset="/assets/img/blogs/2026-05-07/mgs1.png" type="image/png" /> <img class="mx-auto" src="/assets/img/blogs/2026-05-07/mgs1.png" alt="Snake looking very cool with a drawn comic artstyle" /> </picture> <figcaption class="caption">Snake looking very cool with a drawn comic artstyle</figcaption></figure> <h2 id="gameplay">Gameplay</h2> <p>The game features two phases, which reminds me a lot of something like <a href="/blog/fire-emblem/">Fire Emblem</a>, normal missions, and base building management.</p> <p>The missions are done in a map divided by section (similar to old school Monster Hunter), each section with enemies patrolling in a certain pattern, carrying weapons and ready to alert everyone as soon as they spot you.</p> <p>Snake has a variety of equipment: rations, radar, grenades, sleeping guns, assault rifles, and heavier weaponry which can be picked before each mission. One of the main items is the Fulton Recovery System, which let’s you take unconscious enemies or rescue prisoners, to recruit them to your team.</p> <p>The movement set is great, you can walk or or crouch, lay down, hang from ledges. You can hide behind walls, knock to call an enemy’s attention, use close quarters combat (CQC) to incapacitate enemies, among other things.</p> <p>As the adventure progresses, enemies will get tougher too, wearing better armor (which make the sleeping gun less effective) and be more resistant to CQC in general. Even then, most of the missions can be completed without ever having to fight or alert the enemy, this is the case even for some of the boss fights.</p> <p>When a mission is complete, you return Mother Base, where MSF manages its operations. You can assign personnel to different teams, such as Combat, R&amp;D, Medical, Mess Hall and Intel. The members of each team affect different parts of the gameplay. The most noticeable is the R&amp;D team, which will let you unlock new weapons and level them up.</p> <p>As you recruit more people, the base will grow, and members of the MSF can be sent to missions. The game has a CO-OP element similar to Monster Hunter, as many missions can be done in multiplayer as well, there’s also PVP modes. I didn’t engage in any of these though.</p> <p>Boss battles in the game are pretty great. However, the limited controls of the PSP are a big negative. Aiming weapons is not ideal in any of the available control schemes and some of the boss fights will need some precision.</p> <p>There are a few different styles of battle. Some are against military vehicles, which are accompanied by infantry. These can often be completed with pure stealth, using smoke bombs and approaching enemies from multiple angles to get rid of the infantry and make the captain come out of the vehicle, confused as to what’s going on. Of course, you can just dispatch enemies with gunfire if that’s more of your style. All the vehicles can be seized for your use in MSF, so the less you damage them the better.</p> <figure class="img"> <picture> <source srcset="/assets/img/blogs/2026-05-07/mgs2.webp" type="image/webp" /> <source srcset="/assets/img/blogs/2026-05-07/mgs2.png" type="image/png" /> <img class="mx-auto" src="/assets/img/blogs/2026-05-07/mgs2.png" alt="Some soldiers surrounding a tank, a boss fight for Snake to defeat!" /> </picture> <figcaption class="caption">Some soldiers surrounding a tank, a boss fight for Snake to defeat!</figcaption></figure> <p>The boss battles against AI machinery are different. They unmanned weapons are much bigger in size and feature a variety of attack patterns. However, the weakspot on them is pretty clear and you can make use of different equipment to stun them and allow you to deal a lot of damage.</p> <p>The game features plenty of items to aid you on your way, but they can run out as well, so being mindful is important, although requesting aid is also possible with certain equipment you unlock later in the game.</p> <p>All in all, I enjoyed the constant cycle of mission -&gt; manage base -&gt; listen to debrief files -&gt; repeat.</p> <p>There are also lots of side quests to train marksmanship, recover files or <del>kidnap</del> recruit more members for MSF. You can also replay any mission and get more resources like that.</p> <p>As you recruit more personnel, the base will grow and you will be able to send them to their own missions, which will let you level up faster and the like. You can eventually develop your own <em>Metal Gear</em>, but I didn’t delve deeper into that mechanic, since it requires grinding boss fight missions to acquire materials to build it, and I didn’t feel like doing so.</p> <p>There is one big, HUGE bummer featured in some parts of the game though… the absolute worst quick time events ever. Many are pretty easy and normal, but some are truly horrendous stuff. For example, if you are knocked down, you’ll have to move your analog stick like a maniac to stand up again. There are also cutscenes featuring sections where you’ll have to press the same button AS FAST AS YOU CAN, and there will be some that just have a very very short time frame to perform. This is the one really big miss of the game, as there’s no accesibility option to disable these. Some people will never complete this game because of that terrible mechanic. Don’t feel bad about using the Turbo function of your emulator if that’s how you plan to try this game.</p> <figure class="img"> <picture> <source srcset="/assets/img/blogs/2026-05-07/mgs5.webp" type="image/webp" /> <source srcset="/assets/img/blogs/2026-05-07/mgs5.png" type="image/png" /> <img class="mx-auto" src="/assets/img/blogs/2026-05-07/mgs5.png" alt="The Pupa, one of the many unmanned vehicles Snake will need to destroy" /> </picture> <figcaption class="caption">The Pupa, one of the many unmanned vehicles Snake will need to destroy</figcaption></figure> <h2 id="art">Art</h2> <p>The art direction is top notch. The game has incredible 3D graphics that are truly at the top of what the PSP can manage, shoulder to shoulder with the God of War games on the system. All the characters, weaponry and vehicles are modeled with incredible detail. The most impressive feat of course is the size and the scale of some of the boss fights, which rival Monster Hunter’s Ancient Dragon quests, with textures and character models similar to Portable 3rd.</p> <p>Maybe the comparisons make no sense to you, but seriously, it’s mindblowing. The game is as close as it gets to PS2 quality, and with the small screen it’s just perfect. The performance is also fantastic, without any noticeable performance issues other than the loading screens which could be a bit long for a modern day gamer.</p> <p>Every action of the game has animation work to match, when you walk, faster or slowly, the way enemies react and change behaviour, etc.</p> <p>Huge props to the UI design as well. I found managing the base became a breeze, with a very practical UI that hasn’t dated a single day. The menus were cool and there was something to read or learn at any time.</p> <p>Most of the cutscenes however, are not done with 3D animation.</p> <p>Instead, this game features comic-style cutscenes, with dialogue bubbles and a scrappy-looking artstyle that looks absolutely awesome! I must admit that 16-year old me was very, very dissapointed by this for some reason, but reality is that the style holds up excellently to this day, and allows for some absolutely thrilling panelling and action shots. There are also some very emotional scenes, flashbacks and quiet moments that feature strong silouettes or changes in the color palette that are incredible to see, and realistic cutscenes wouldn’t be able to compare.</p> <figure class="img"> <picture> <source srcset="/assets/img/blogs/2026-05-07/mgs3.webp" type="image/webp" /> <source srcset="/assets/img/blogs/2026-05-07/mgs3.png" type="image/png" /> <img class="mx-auto" src="/assets/img/blogs/2026-05-07/mgs3.png" alt="Snake looking very cool, this time in a high quality animated cutscene!" /> </picture> <figcaption class="caption">Snake looking very cool, this time in a high quality animated cutscene!</figcaption></figure> <h2 id="music--sound">Music &amp; Sound</h2> <p>The soundtrack has a very military style to it. It is actually very similar to what <em>Into The Breach</em> does, mostly staying in the background, but pretty present when you are managing Mother Base.</p> <p>During missions, usually set in forests, swamps, jungles and military facilities, a lot of the sound will remain atmospheric, the chant of birds, the steps and hums of the enemy, the crickets singing, and trees swaying will be most of what you hear. Sound is another of the factors to take into account when making your way through each section of the enemy territory.</p> <p>All the sound design is excellent too, and rather satisfying.</p> <p>Music is affected by the actions you take, of course. If an enemy manages to get a glimpse of you, you’ll definitely hear the <em>“What’s that?”</em>, as the music becomes more suspenseful and you feel the enemy getting cautious as it walks in your direction.</p> <p>If you are completely discovered, the surprise sound that made the series iconic will play, and music will start to be more stressful, alarm bells will sound and the enemy radio will request for backup, as they go into an alert status. All of this is perfectly made clear with audio queues and I honestly love it.</p> <p>The sound of the AI enemies is also worth bringing up. Each of the bosses has a certain character to it, and they will also “sing” during cutscenes and during the fight against them, all of this is also a hint to get familiar with it, of course. It’s all rather excellent.</p> <p>There’s nothing but praise for the voice acting as well. David Hayter is Snake, simple as that, and this was his last game before Kojima became kinda obsessed with getting a hollywood star to play the rule. Konami, for all its mistakes, brought him back for the Delta remake, so that’s fun.</p> <p>The rest of the cast is equally amazing. All the extra story and details shared via cassette tapes is so fun to play through. I listened to all of it, it was really enjoyable.</p> <figure class="img"> <picture> <source srcset="/assets/img/blogs/2026-05-07/mgs4.webp" type="image/webp" /> <source srcset="/assets/img/blogs/2026-05-07/mgs4.png" type="image/png" /> <img class="mx-auto" src="/assets/img/blogs/2026-05-07/mgs4.png" alt="Some more of the comic style artwork, the character design and contrast here is awesome" /> </picture> <figcaption class="caption">Some more of the comic style artwork, the character design and contrast here is awesome</figcaption></figure> <h2 id="overall">Overall</h2> <p>What can I say? This is a fantastic title with excellent production and passion behind it, it plays like a miracle on the PSP and is only really limited by a couple of dumb decisions, and a little bit by the hardware itself. I loved going through all of it, but here’s a list with some good stuff, bad stuff and other thoughts about it:</p> <h3 id="the-good">The good</h3> <ul> <li>Story, voice acting, graphics, sound effects, music, UI design, it nails it.</li> <li>The writing, the intrigue, thought provoking moments and suspense.</li> <li>Artwork and cutscenes with a comic book style.</li> <li>Missions are rather short and self-contained, perfect for portable gaming.</li> <li>The cast of characters and the interactions with Snake are interesting.</li> <li>Base management and some customization to upgrade equipment.</li> </ul> <h3 id="the-not-so-good">The not so good</h3> <ul> <li>Quick time events that are super obnoxious.</li> <li>Limited controls due to the PSP’s lack of a second stick or L2 and R2 triggers.</li> <li>Annoying button mashing and stick wiggling mechanics.</li> <li>Loading times can be a bit annoying, nothing unusual for a PSP game.</li> <li>Way too many extra missions can be repetitive.</li> <li>Base management can feel like a grind sometimes.</li> <li>There’s an extra ending that requires dozens of hours of grinding.</li> </ul> <h3 id="some-tips-and-reminders">Some tips and reminders</h3> <ul> <li>You need to keep the arsenal in check for every mission, some things are only useful against bosses and the like.</li> <li>Just use turbo button controls if you emulate, save yourself the button mashing headache.</li> <li>Get familiar with the available control schemes of the game, some are better for boss fights such as “Shooter Mode”.</li> <li>Don’t forget to use the radio for extra tips! You can also get extra info from enemies under your grip.</li> <li>Try to ~kidnap~ recruit everyone and there will be tools later to see which enemies are worth recruiting more later too!</li> </ul> <p>There’s a couple more things I could say but I’ll leave it here for now. This game is truly awesome, I was on the edge of my seat multiple times and I really jaw-dropped at some of the moments and the fact that all of this was running on a PSP in the first place.</p> <p>You can get this game on the PSP of course, but there’s a new release of it coming out in the <em>Metal Gear Solid Master Collection Vol.2</em>, which will release on August 27th 2026, so maybe waiting for it will be best!</p> <p>Give it a try either way, it’s worth a shot.</p> <p>This is day of 63 <a href="https://100daystooffload.com">#100DaysToOffload</a></p> <p> <a href="mailto:me@joelchrono.xyz?subject=Metal Gear Solid Peace Walker">Reply to this post via email</a> | <a href="https://fosstodon.org/@joel/116535322900297208">Reply on Fediverse</a> </p> Plugging the gaps won't save news. It's time to redesign - Werd I/O 69fc98b9e66c4000011e25d3 2026-05-07T13:50:49.000Z <p>Link: <a href="https://www.newsfutures.org/library/civic-information-endowment?ref=werd.io"><em>An Invitation to Build the Civic Information Economy, by Lillian Ruiz, Simon Galperin, and Jennifer Brandel at News Futures</em></a></p><p>This feels like a vital exploration to me:</p><blockquote>&#x201C;What does it take to fund civic information? We often focus on &#x201C;more money,&#x201D; but we limit the field&#x2019;s potential by ignoring better capital design. Today&#x2019;s landscape often sees dollars concentrated in intermediary and national plays&#x2014;often with good reason&#x2014;but without deliberate examination, we risk stifling the imagination of a developing field.&#x201D;</blockquote><p>This is part manifesto, but also part call to action and invitation to participate. Funding for news has often been reactive, filling gaps, but what does it mean to intentionally design a genuine ecosystem with dynamics that support production of, and access to, the civic information we need that is a prerequisite for democracy to function?</p><p>I love how genuinely participative this is: rather than a bunch of people trying to be smart inside institutions, this requires that the people who struggle the most to find information are active co-designers. That feels non-negotiable to me. There&#x2019;s been a pushback against inclusion and equity in news and everywhere recently, but there&#x2019;s no other way to build an ecosystem that genuinely serves everybody. We&#x2019;ve all got to own it. We&#x2019;ve all got to take part. Everyone needs to be represented.</p><p>I have high hopes for this, and I love that the effort exists. It&#x2019;s something we should all support.</p> I've found just the right paper for my Bottom Hole problem - Terence Eden’s Blog https://shkspr.mobi/blog/?p=71393 2026-05-07T11:34:54.000Z <p>A few weeks ago, I went on a mad quest to find <a href="https://shkspr.mobi/blog/2026/03/finding-the-right-bottom-hole-paper/">the newspaper used in 1995's Bottom Hole TV show</a>.</p> <p>During the episode, Eddie starts reading this newspaper:</p> <img src="https://shkspr.mobi/blog/wp-content/uploads/2026/03/No-News-Shocker.webp" alt="Some pissed old fart reading a newspaper called &quot;The Hammersmith Bugle&quot; with the headline &quot;No news shocker...&quot;" width="720" height="544" class="aligncenter size-full wp-image-68986"> <p>Obviously, the "Hammersmith Bugle" is not a real paper and they never ran a headline "No News Shocker". But judging from all the other shots, the prop is based on a <em>real</em> newspaper.</p> <img src="https://shkspr.mobi/blog/wp-content/uploads/2026/03/Seewaw.webp" alt="Two reprobates reading a newspaper." width="720" height="544" class="aligncenter size-full wp-image-68989"> <p>So I decided to <a href="https://www.dirtyfeed.org/tag/newspaper-props/">rip off Dirty Feed's shtick</a> and find out what was used to create the fake newspaper. The quest took me o'er hill and dale. Through the rough hinterlands of Hammersmith and into the nether regions of Wimbledon. By which I mean - I used lots of online archive sources.</p> <p>And it <em>nearly</em> worked! I found all of the <em>internal</em> pages. I also found the back page:</p> <img src="https://shkspr.mobi/blog/wp-content/uploads/2026/03/Cup-Tie-Chaos.webp" alt="Idiot in a pork pie hat reading a paper with the headline &quot;Cup tie chaos&quot;." width="720" height="544" class="aligncenter size-full wp-image-68987"> <img src="https://shkspr.mobi/blog/wp-content/uploads/2026/03/Cup-Tie-Paper.webp" alt="Scan of a newspaper with &quot;Cup tie chaos&quot; as a headline." width="720" class="aligncenter size-full wp-image-68990"> <p>That's from <a href="https://www.britishnewspaperarchive.co.uk/search/results/1994-11-03/1994-11-03?NewspaperTitle=Surrey%2BHerald&amp;IssueId=BL%2F0003604%2F19941103%2F&amp;County=Surrey%2C%20England">The Surrey Herald</a> - but that's a paper with <em>lots</em> of regional editions. None of which had the right headline.</p> <p>So I emailed my (frankly asinine ) request to <a href="https://www.surreycc.gov.uk/culture-and-leisure/history-centre/researchers/guides/newspaper-back-issues">Surrey Museums</a>. They were polite, but unable to help. Their website gave a clue though - the location of the archives of the Surrey Herald:</p> <blockquote><p>Surrey Herald: Chertsey, Addlestone and Byfleet edition (also Walton, Weybridge and Hersham edition Feb 1979 to 1999 at Elmbridge Museum)</p></blockquote> <p>So I contacted the fine people at <a href="https://elmbridgemuseum.org.uk/">Elmbridge Museum</a> who were happy to rummage through their microfiche for me. I expect, much like Indiana Jones, the archivists had to knock down fake walls, find a mystic box containing the treasure, and then dodge various snakes and villains to retrieve the priceless artefact. Or they may have a well designed archival system which is a pleasure to use. I don't know.</p> <p>Anyway! All of which is to say that they very kindly sent me a quick scan of the front page of Surrey Herald's Walton, Weybridge and Hersham edition from November 3rd 1994.</p> <p>Here it is in all its glory!</p> <img src="https://shkspr.mobi/blog/wp-content/uploads/2026/05/Herald-1024.webp" alt="Front page of the newspaper." width="1024" height="1375" class="aligncenter size-full wp-image-71395"> <p>That's a <em>perfect</em> match for what's seen on screen:</p> <img src="https://shkspr.mobi/blog/wp-content/uploads/2026/03/FA-Cup-Mix-up.webp" alt="High resolution clip of a newspaper." width="474" height="588" class="aligncenter size-full wp-image-69069"> <p>Hurrah! Another mystery solved thanks to <a href="https://elmbridgemuseum.org.uk/">publicly funded museums</a>!</p> <h2 id="what-have-we-learned-today"><a href="https://shkspr.mobi/blog/2026/05/ive-found-just-the-right-paper-for-my-bottom-hole-problem/#what-have-we-learned-today">What have we learned today?</a></h2> <ul> <li>Archivists are lovely, generous, and helpful people.</li> <li>Museums are brilliant.</li> <li>Not everything in the world has been digitised.</li> <li>There was <em>quite a lot</em> of news that day no matter what the drunken hacks at the Hammersmith Bugle say.</li> <li>We do not know if centenarian Elsie Bartlett was aware that her photo featured in this seminal part of British comedy.</li> </ul> <img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=71393&HTTP_REFERER=Atom" alt width="1" height="1" loading="eager"> The war between fast and legitimate is here - Westenberg 69fbe42dccfa0c0001fd80f3 2026-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 &quot;high-risk&quot; 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&apos;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&apos;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&apos;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&apos;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&apos;ll grant. </p><p>It&apos;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&apos;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&apos;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&apos;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&apos;m describing are rarely more competent. They&apos;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&apos;s going to be to fix it.</p><p>But the answer to &quot;fast institutions sometimes blow up&quot; can&apos;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&apos;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&apos;ve known marriages in that vein. I&apos;ve known states in that vein. I&apos;ve known companies, etc. </p><p>The new compact will involve some level of negotiated settlement between the two species. And I don&apos;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&apos;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&apos;m wary of declensionist takes that romanticise the slow tier as &quot;the last fortress of human dignity.&quot; 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 &quot;system&quot; its most vulnerable applicants.</p><p>Procedure can and frequently does ossify into the basest of inhumane cruelty. Slow institutions aren&apos;t virtuous because they&apos;re slow; they&apos;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 //&#xA0;will not allow. You can&apos;t do the equivalent of British common law in five years. You can&apos;t do peer review at the speed of preprint. You can&apos;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&apos;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&apos;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&apos;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&apos;re in. If you&apos;re running a startup, you&apos;re in the speed game, and pretending you&apos;re running a regulatory agency is a category error. If you&apos;re running a regulatory agency, you&apos;re in the legitimacy game, and it&apos;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&apos;m not certain anyone &quot;wins&quot; this in any way the word &quot;win&quot; is usually applied. But in a war between institutions, the folks on the losing side are usually the last to figure out they&apos;re at war in the first place.</p> Writing a blog post without a screen - James' Coffee Blog https://jamesg.blog/2026/05/07/writing-a-blog-post-without-a-screen 2026-05-07T00:00:00.000Z <style media="(prefers-color-scheme: dark)">pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #49483e } .highlight { background: #272822; color: #F8F8F2 } .highlight .c { color: #959077 } /* Comment */ .highlight .err { color: #ED007E; background-color: #1E0010 } /* Error */ .highlight .esc { color: #F8F8F2 } /* Escape */ .highlight .g { color: #F8F8F2 } /* Generic */ .highlight .k { color: #66D9EF } /* Keyword */ .highlight .l { color: #AE81FF } /* Literal */ .highlight .n { color: #F8F8F2 } /* Name */ .highlight .o { color: #FF4689 } /* Operator */ .highlight .x { color: #F8F8F2 } /* Other */ .highlight .p { color: #F8F8F2 } /* Punctuation */ .highlight .ch { color: #959077 } /* Comment.Hashbang */ .highlight .cm { color: #959077 } /* Comment.Multiline */ .highlight .cp { color: #959077 } /* Comment.Preproc */ .highlight .cpf { color: #959077 } /* Comment.PreprocFile */ .highlight .c1 { color: #959077 } /* Comment.Single */ .highlight .cs { color: #959077 } /* Comment.Special */ .highlight .gd { color: #FF4689 } /* Generic.Deleted */ .highlight .ge { color: #F8F8F2; font-style: italic } /* Generic.Emph */ .highlight .ges { color: #F8F8F2; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #F8F8F2 } /* Generic.Error */ .highlight .gh { color: #F8F8F2 } /* Generic.Heading */ .highlight .gi { color: #A6E22E } /* Generic.Inserted */ .highlight .go { color: #66D9EF } /* Generic.Output */ .highlight .gp { color: #FF4689; font-weight: bold } /* Generic.Prompt */ .highlight .gs { color: #F8F8F2; font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #959077 } /* Generic.Subheading */ .highlight .gt { color: #F8F8F2 } /* Generic.Traceback */ .highlight .kc { color: #66D9EF } /* Keyword.Constant */ .highlight .kd { color: #66D9EF } /* Keyword.Declaration */ .highlight .kn { color: #FF4689 } /* Keyword.Namespace */ .highlight .kp { color: #66D9EF } /* Keyword.Pseudo */ .highlight .kr { color: #66D9EF } /* Keyword.Reserved */ .highlight .kt { color: #66D9EF } /* Keyword.Type */ .highlight .ld { color: #E6DB74 } /* Literal.Date */ .highlight .m { color: #AE81FF } /* Literal.Number */ .highlight .s { color: #E6DB74 } /* Literal.String */ .highlight .na { color: #A6E22E } /* Name.Attribute */ .highlight .nb { color: #F8F8F2 } /* Name.Builtin */ .highlight .nc { color: #A6E22E } /* Name.Class */ .highlight .no { color: #66D9EF } /* Name.Constant */ .highlight .nd { color: #A6E22E } /* Name.Decorator */ .highlight .ni { color: #F8F8F2 } /* Name.Entity */ .highlight .ne { color: #A6E22E } /* Name.Exception */ .highlight .nf { color: #A6E22E } /* Name.Function */ .highlight .nl { color: #F8F8F2 } /* Name.Label */ .highlight .nn { color: #F8F8F2 } /* Name.Namespace */ .highlight .nx { color: #A6E22E } /* Name.Other */ .highlight .py { color: #F8F8F2 } /* Name.Property */ .highlight .nt { color: #FF4689 } /* Name.Tag */ .highlight .nv { color: #F8F8F2 } /* Name.Variable */ .highlight .ow { color: #FF4689 } /* Operator.Word */ .highlight .pm { color: #F8F8F2 } /* Punctuation.Marker */ .highlight .w { color: #F8F8F2 } /* Text.Whitespace */ .highlight .mb { color: #AE81FF } /* Literal.Number.Bin */ .highlight .mf { color: #AE81FF } /* Literal.Number.Float */ .highlight .mh { color: #AE81FF } /* Literal.Number.Hex */ .highlight .mi { color: #AE81FF } /* Literal.Number.Integer */ .highlight .mo { color: #AE81FF } /* Literal.Number.Oct */ .highlight .sa { color: #E6DB74 } /* Literal.String.Affix */ .highlight .sb { color: #E6DB74 } /* Literal.String.Backtick */ .highlight .sc { color: #E6DB74 } /* Literal.String.Char */ .highlight .dl { color: #E6DB74 } /* Literal.String.Delimiter */ .highlight .sd { color: #E6DB74 } /* Literal.String.Doc */ .highlight .s2 { color: #E6DB74 } /* Literal.String.Double */ .highlight .se { color: #AE81FF } /* Literal.String.Escape */ .highlight .sh { color: #E6DB74 } /* Literal.String.Heredoc */ .highlight .si { color: #E6DB74 } /* Literal.String.Interpol */ .highlight .sx { color: #E6DB74 } /* Literal.String.Other */ .highlight .sr { color: #E6DB74 } /* Literal.String.Regex */ .highlight .s1 { color: #E6DB74 } /* Literal.String.Single */ .highlight .ss { color: #E6DB74 } /* Literal.String.Symbol */ .highlight .bp { color: #F8F8F2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #A6E22E } /* Name.Function.Magic */ .highlight .vc { color: #F8F8F2 } /* Name.Variable.Class */ .highlight .vg { color: #F8F8F2 } /* Name.Variable.Global */ .highlight .vi { color: #F8F8F2 } /* Name.Variable.Instance */ .highlight .vm { color: #F8F8F2 } /* Name.Variable.Magic */ .highlight .il { color: #AE81FF } /* Literal.Number.Integer.Long */ .highlight .nn, .highlight .n{color: light-dark(black, var(--dark-foreground-color)) }</style><style media="(prefers-color-scheme: light)">pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight { background: #f8f8f8; } .highlight .c { color: #3D7B7B; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #F00 } /* Error */ .highlight .k { color: #008000; font-weight: bold } /* Keyword */ .highlight .o { color: #666 } /* Operator */ .highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */ .highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #9C6500 } /* Comment.Preproc */ .highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */ .highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */ .highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #E40000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #008400 } /* Generic.Inserted */ .highlight .go { color: #717171 } /* Generic.Output */ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #04D } /* Generic.Traceback */ .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008000 } /* Keyword.Pseudo */ .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #B00040 } /* Keyword.Type */ .highlight .m { color: #666 } /* Literal.Number */ .highlight .s { color: #BA2121 } /* Literal.String */ .highlight .na { color: #687822 } /* Name.Attribute */ .highlight .nb { color: #008000 } /* Name.Builtin */ .highlight .nc { color: #00F; font-weight: bold } /* Name.Class */ .highlight .no { color: #800 } /* Name.Constant */ .highlight .nd { color: #A2F } /* Name.Decorator */ .highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #00F } /* Name.Function */ .highlight .nl { color: #767600 } /* Name.Label */ .highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #19177C } /* Name.Variable */ .highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */ .highlight .w { color: #BBB } /* Text.Whitespace */ .highlight .mb { color: #666 } /* Literal.Number.Bin */ .highlight .mf { color: #666 } /* Literal.Number.Float */ .highlight .mh { color: #666 } /* Literal.Number.Hex */ .highlight .mi { color: #666 } /* Literal.Number.Integer */ .highlight .mo { color: #666 } /* Literal.Number.Oct */ .highlight .sa { color: #BA2121 } /* Literal.String.Affix */ .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ .highlight .sc { color: #BA2121 } /* Literal.String.Char */ .highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */ .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ .highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ .highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */ .highlight .sx { color: #008000 } /* Literal.String.Other */ .highlight .sr { color: #A45A77 } /* Literal.String.Regex */ .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ .highlight .ss { color: #19177C } /* Literal.String.Symbol */ .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #00F } /* Name.Function.Magic */ .highlight .vc { color: #19177C } /* Name.Variable.Class */ .highlight .vg { color: #19177C } /* Name.Variable.Global */ .highlight .vi { color: #19177C } /* Name.Variable.Instance */ .highlight .vm { color: #19177C } /* Name.Variable.Magic */ .highlight .il { color: #666 } /* Literal.Number.Integer.Long */</style> <style> @font-face { font-family: 'MonaspaceArgon'; src: url('/assets/fonts/MonaspaceArgon-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; } pre, code { font-family: 'MonaspaceArgon', ui-monospace, monospace; } </style> <p><em>This text below post is presented verbatim, as it was typed with only a keyboard: without a display or any assistive technology. Typos are left in. The "Notes after writing" and "My writing setup" sections were typed on my Mac as I prepared to publish the post.</em></p><p>This is my first ever blog post written without a display. I have wanted a tool like this for a while, but my motivation for finally building something was seeing Jo's excellent implementation of the idea. Jo used a Raspberry Pi 400 with a bash script that, on boot, opens a "nano" file into which you can write. Jo's implementation also automatically copied the text file in which you were working over to a flash drive. My script as-is only saves the file to disk, so it can be retrieved later.</p><p>I thought of this idea because I sometimes look away from my computer while I am writing. I do the same on my phone, too: if I am deep in thought, or capturing an idea, I want to focus on the world around me. What if a device could let me note down my ideas without having to worry about managing a display?</p><p>This blog post is written without a display. The experience so far has been fascinating. Since I cannot see what I have typed, I can only focus on the paragraph on which I am on. The only feedback I get from the keyboard is the indicator light that toggles on and off when I enable and disable caps lock, which I do every tmime I type a capital letter (yes, I do this :)).</p><p>One limit of the system is that I am unsure when I have made typos. As I write, I am focusing mainly on the keyboard in front of me, so as to ensure that I don't accidentally drift away in terms of the position of my hands. If my hands were improperly positioned every letter could be one letter off in terms of the spatial positioning of the letters on the keyboard.</p><p>I think this could be mitigated with a clever form of typo correction which lets you know when you have typed the last few words incorrectly. Furthermore, i could be interesting for automatic typo orrection to take place, although this would mean that I would be prone to lose track of exactly what my cursor position is. I can always edit the blog post after I have done, after all.</p><p>Another limitation with the system as it is presently is that there is no way for me to easily know if my blog post is still being captured. If I accidentally trigger a nano keyboard sequence, it would be impossible for me to know that I am in a mode that is not capturing my writing.</p><p>My writing modee rght now is much like when I am using a typewriter: I don't think about rearranging sentences (I can't), or about fixing mistakes that are several words back from my cursor position (side question: what is the typewriter equivalent of the "cursor" position"?).</p><p>I am not sure if I will write another blog post with this medium yet, primarily for the aforementioned reason that I don't haev a good visual ndicator that my typing is being captured. With that said, I am still fascinated by this idea of being able to capture notes without a display. ImagWith this system, I am able to capture notes without the distractions of the display. Although there is still the distraction of whether I made a typo; more research and development is needed to mature this idea further.</p><p>Thank you Jo for brining this idea to life and publishing the foundation of a bash script ath I could use to make my own screenless writing tool.</p><h2 id="notes-after-writing">Notes after writing</h2><p><em>The words below were written using my Mac after having copied the post back to my Mac for publishing.</em></p><ul><li><a href="https://dead.garden/blog/a-computer-thats-just-a-keyboard-no-screen.html" rel="noreferrer">Jo's post about using a computer without a screen</a> to which I made reference in my writing above. I couldn't add a link while I was writing because I didn't have a display. In the future, I could type a reminder like TK (used as a placeholder in writing) so that I could come back and edit the post to add links.</li><li>I deliberately did not edit the post above so you could see the errors and get a better feel for how well this worked in terms of accuracy. When I knew I made a typo, I had to pause to figure out how many keystrokes I should remove. In one case, I wanted to start a sentence from scratch, so I just held down the backspace key for half a second or so. This wasn't the best strategy, which is why "ImagWith this system" appears in the blog post.</li><li>Adding a script to open <code>nano</code> to my <code>~/.profile</code> had a significant limit: this method made it impossible to boot to desktop with <code>startx</code>. Thus, research is needed into how to make the script to open nano run only on boot, and not when <code>startx</code> is executed.</li></ul><h2 id="my-writing-setup">My writing setup</h2><figure><picture><img alt="A Raspberry Pi 400 computer keyboard on a desk next to a notebook, a small Lego typewriter set, my computer keyboard, and my mouse." loading="lazy" src="https://editor.jamesg.blog/content/images/2026/05/IMG_4485-Medium.jpeg" 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">A Raspberry Pi 400 computer keyboard on a desk next to a notebook, a small Lego typewriter set, my computer keyboard, and my mouse.</div></div></figure><p><em>I took this photo after I wrote the blog post, which is why the HDMI cable is plugged in.</em></p> <a class="tag" href="https://dead.garden/blog/a-computer-thats-just-a-keyboard-no-screen.html">Jo's post about using a computer without a screen</a> Affordances of a screenless writing interface - James' Coffee Blog https://jamesg.blog/2026/05/07/affordances-of-a-screenless-writing-interface 2026-05-07T00:00:00.000Z <p><em>This post was written first with my </em><a href="https://jamesg.blog/2026/05/07/writing-a-blog-post-without-a-screen" rel="noreferrer"><em>screenless writing interface</em></a><em>, and then edited in Ghost, which I use to publish blog posts to this website. The extent of my edits were fixing typos and reworking the introduction and conclusion a little bit.</em></p><p>My original concern when brainstorming the idea of a keyboard with which you could type but had no other means of interaction was that the accuracy of writing would be hard to maintain. This concern is not as significant as I thought now I am using the device. Indeed, sometimes you need to try a technology out to really build an impression of how it feels.</p><p>And so I am also writing this post with my screenless writing setup, both to continue experimenting with the medium but also to capture an idea I had that I wanted to explore: the various affordances of different writing interfaces.</p><p>In discussion in a Matrix server I am in about this project, I realised that this device may be more appropriate for creating a first draft of a project. Even if the technology could automatically correct all typos, there are still inherent limitations: not being able to go back through your writing, for example. My first sentence in this blog post was so long that I forgot the first clause so I sort of had to guess how to finish the sentence With that in mind, I knew I could go back later.</p><p>Thus, this device could be used as a means do author notes, drafts for works, or streams of consciousness. I can always edit a post later. Indeed, I found myself more comfortable with the idea of editing a post afterwords precisely because I knew there were limitations that got in the way of making the post as good as it could be the first time around. This feels like a unique property of this particular mode of writing.</p><p>I also started to think about the affordances of typewriters. Typewriters don't allow corrections in the same way as computers; you can type over a word, but you can't completely erase it (unless you used Tippex or the like). It would be cool if this system had a button to read out your current paragraph so you remember your context; indeed, I think there is a lot of work that could be done to explore the idea of screenless writing interfaces of this form factor. <em>(Update after publishing: I totally forgot how this paragraph started which is why the context jumped a bit. I wonder how if this writing form factor makes it harder to compose more complex grammatical structures, such as the sentence with a semi-colon and parentheses that I wrote before forgetting the start of the paragraph. Could a tool like this help improve one's memory?)</em></p><p>Anyway, I think I was talking about the affordances of typewriters. Typewriters give you a physical artefact. This means there is distance between what you have written on a typewriter and publishing it online whereas there is a very narrow distance between a text editing program on a computer and publishing one's work. For this reason, I rarely publish anything I have written on the typewriter: the typewriter is where I like to write personal essays away from my computer, without feeling any obligation to publish what I write.</p><p>I also love using my typewriter to type up letters to friends.</p><p>This all has me wondering: what are the opportunities of a system that lets you write a blog post without having a display set up? I do feel I type with less regard to editing myself as I go, because I can't easily go back and edit something while I am writing. I feel a similar way with typewriters too, where I know that because I can't easily discard a line, if I start a line I will try and finish it. In this way, the typewriter – and the screenless writing interface – pushes me a little bit outside my comfort zone: these mediums make think about how to end a sentence even if I wish I had started it with another word.</p> <a class="tag" href="https://jamesg.blog/2026/05/07/writing-a-blog-post-without-a-screen">screenless writing interface</a> Looking for a bike - Joel's Log Files https://joelchrono.xyz/blog/bike 2026-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 fLaMEd https://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 Feed https://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 &quot;review&quot; 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 fLaMEd https://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 Quirk https://kevquirk.com/my-inital-thoughts-on-thundermail 2026-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. - Westenberg 69fa8961ccfa0c0001fd807f 2026-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&#x2019;ve finished having them, which means they aren&#x2019;t really having them at all. They&#x2019;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&#x2019;s office, it&#x2019;s trivial. Suppression and regulation are different animals. Suppression is shoving the feeling into a closet and pretending it isn&#x2019;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&#x2019;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 &#x201C;harm.&#x201D; 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 &#x201C;brave&#x201D; is a mistake.</p><p>Actual children, watching this, are absorbing the model with terrifying efficiency, and they&#x2019;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&#x2019;ll enact is that feelings are emergencies, and emergencies require an audience. Walk into any third-grade classroom now and you&#x2019;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&#x2019;s Reddit-bound adherents, the Stoics weren&#x2019;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 &#x201C;innovation&#x201D; 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&#x2019;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&#x2019;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&#x2019;ve lost slow time and private time; we&#x2019;ve lost the time when no one asked what you thought before you&#x2019;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&#x2019;re learning to do the same. But you can&#x2019;t regulate what you&#x2019;ve already broadcast - and you can&#x2019;t reclaim a workshop you&#x2019;ve turned into a stage.</p> How I use my phone - James' Coffee Blog https://jamesg.blog/2026/05/06/how-i-use-my-phone 2026-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 &amp; 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 feed https://www.coryd.dev/posts/2026/building-the-future 2026-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 Blog https://shkspr.mobi/blog/?p=70238 2026-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&#39;s blog post</a> where they said that &#34;most of the traffic to my personal website still comes from web feeds&#34; - I wondered if that was true for my site.</p> <p>I&#39;ve been writing this blog for a while. I&#39;ve never much bothered with &#34;aggressive&#34; <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&#39;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&#39;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&#39;m not looking for super-accurate numbers (although I do block as many AI crawlers and bots as possible). I&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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&#39;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&amp;HTTP_REFERER=Atom" alt="" width="1" height="1" loading="eager"/> C/C++ checklist challenges, solved - Trail of Bits Blog https://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&rsquo;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&rsquo;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">&lt;stdio.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;string.h&gt;</span><span class="cp"> </span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;arpa/inet.h&gt;</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 &#34;127.3.3.1&#34; </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">&#34;</span><span class="se">\n</span><span class="s">&#34;</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">&amp;</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">&gt;&gt;</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">&amp;</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">&#34;ping &#39;%s&#39;&#34;</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 &ldquo;127.3.3.1&rdquo;, 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">&amp;</span><span class="n">regPath</span><span class="p">,</span> <span class="o">&amp;</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">&#34;%!FUNC! Failed to retrieve input buffer. Status: %d&#34;</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">&lt;</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">&gt;</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">&#39;\0&#39;</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">&#34;%!FUNC! Buffer length %d was incorrect.&#34;</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">&#34;MajorVersion&#34;</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">&amp;</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">&#34;%!FUNC! Failed to query registry. Status: %d&#34;</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">&#34;%!FUNC! Major version is %d&#34;</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">&lt;</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">&#34;MinorVersion&#34;</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">&amp;</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">&#34;%!FUNC! Failed to query registry. Status: %d&#34;</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">&#34;%!FUNC! Minor version is %d&#34;</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">&#34;MajorVersion&#34;</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">&amp;</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="&ldquo;Samuel L. Jackson meme&rdquo;" 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">&lt;</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">&#34;MinorVersion&#34;</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">&amp;</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&rsquo;t documented yet, open a PR. We&rsquo;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 - Westenberg 69f98b0fccfa0c0001fd4cfc 2026-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&#x2019;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: &#x201C;You furnish the pictures, and I&#x2019;ll furnish the war.&#x201D;</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&#x2019;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 &#x201C;manufacturing consent&#x201D; 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&#x2019;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&#x2019;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&#x2019;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&apos;t match the emotional temperature of the room; he&apos;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&#x2019;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&#x2019;s a separate question, evaluated later. Before the feeling arrives in full, you ask: who&#x2019;s pushing this into my field of view, what do they collect if I take the bait, what&#x2019;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&#x2019;s algorithm, then arrive at your own work depleted, with less attention left for the people and projects you&#x2019;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&#x2019;re being honest, is: argue with people we&#x2019;ve never met about events we can&#x2019;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&#x2019;ve been avoiding, the conversation you owe someone you actually care about, the book on your nightstand, the neighbour you&#x2019;ve been meaning to call back. </p><p>The un-cinematic projects that don&#x2019;t trend and don&#x2019;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 Blog https://jamesg.blog/2026/05/05/night-shift 2026-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 feed https://www.coryd.dev/posts/2026/ribbon-a-linkding-client 2026-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>