Monday, 05 October

21:56

Meet the Jackalope! [Nina Paley]

Bovines meet Jackalope

Remember back in March I decided to make my dream bike a reality? On September 22 the prototype arrived:

1. The Jackalope 2. The Jackalope quick-folded 3. The Jackalope folded with seat removed

Look at that fold! Look at that double belt drive with jack shaft! She has a Rohloff internal hub, a no-longer-made RANS seat, and is made of titanium.

Thus far I’ve put 220 miles on her, including a full century yesterday. I have so much to say about this project I don’t know where to begin. Builder Davis Carver and I are already designing a Version 2 optimized for lightness and speed, because this one is heavy and “built like a tank.” Very comfortable though.

Davis Carver’s technical drawing for the Jackalope. Build your own!!

Carver says the plans are not proprietary, so anyone who wants to iterate on this is more than welcome! Improve it please, and share your changes!

The Jackalope’s original 175mm crank arms were way too long and interfered with the front wheel. So I found 160mm replacements which Sam the Mechanic swapped out for me. V2 will move the fork forward so this doesn’t happen again, although short crank arms are pretty sweet. The jack shaft! Only wearing one belt here because the cap wasn’t fully tightened and it fell apart while I was testing. Whoops! So much to learn when getting to know a one-of-a-kind prototype. I put it all back together with Carver’s instructions, and later Sam tightened it down harder than I could. The Jackalope originally came with a AD Carson seat, which was way too big for me. I replaced it with a RANS seat. Unfortunately RANS is no longer in business and such seats can only be found on used bikes, but not all can accommodate the clamp system on this bike. I replaced the bolts that attach the seat struts to the support bars with thumb screws and wingnuts, so I can remove the seat after folding without tool. The round part of the plate in the middle is where the elastomer on the rear triangle hits.

Here are my sketches from March that started this off:

I hope to make some sort of crowdfunder to finance the next version, but I’m gonna get-r-done come hell or high water, because my Muse says so. I’ve been wondering why I haven’t been moved to make another movie or quilts or any much visual art lately. Now I know: She’s been saving me for this bike.

Share

The post Meet the Jackalope! appeared first on Nina Paley.

Klassik brings KDE3’s look and feel to KDE Plasma 6 [OSnews]

The KDE project recently brought back its classic Oxygen and Air themes, but what if you prefer something… A little older?

A modern recreation of the classic KDE 3 desktop experience for KDE Plasma 6, built entirely using Qt 6 and Qt Quick, with full support for Wayland and fractional scaling.

↫ Klassik GitHub page

Neat.

21:14

Limited-Time Offer [Penny Arcade]

My brand has long been "Atheist Jerk-off," but wtf I love Catholicism now.

 

20:07

Pluralistic: Scrutinized (05 Oct 2026) [Pluralistic: Daily links from Cory Doctorow]

->->->->->->->->->->->->->->->->->->->->->->->->->->->->-> Top Sources: None -->

Today's links

  • Scrutinized: Pervert glasses vs the authentic self.
  • Hey look at this: Delights to delectate.
  • Object permanence: DRM-free in the UK; Maple-leaf roses; Italian Wikipedia shuts down; Vancouver's property speculator tax works; Ex-Soviet leftists launch "September"; "The Every"; Nielsen x Chinese internet; Get-a-cluevog; Cat EULA; Heart carafe; Millennials screwed; Apple's unlawful evil.
  • Upcoming appearances: Virtual, South Bend, Hudson, Calgary, Winnipeg, Paris, OVancouver, Victoria, Ottawa, Kilkenny, Oxford, Montreal.
  • Recent appearances: Where I've been.
  • Latest books: You keep readin' em, I'll keep writin' 'em.
  • Upcoming books: Like I said, I'll keep writin' 'em.
  • Colophon: All the rest.



Medieval peasants working the fields in front of a walled city. Their overseer rides a horse; his head has been replaced with a robotic camera. From all sides, gory, anatomical eyeballs intrude upon the scene, each one with a different colored iris.

Scrutinized (permalink)

We're repeatedly told that the killer app for pervert glasses – Meta's AI-enabled camera glasses and their imitators – is that they will remind you of someone's name and spare you the social awkwardness of not recognizing them.

I admit to finding this proposition very seductive. I'm mildly face-blind (I had two aunts I wasn't able to reliably tell apart until I was 12 or 13 and one of them changed her hair), and I meet a lot of people and often find myself incredibly embarrassed when I can't place someone that I know I'm acquainted with.

Actually, the emotion I feel when I don't recognize someone or can't place their name is worse than embarrassment: it's shame. I worry that my chronic inability to remember people's names/faces will make them feel like they're unimportant to me. And shame is such a shitty, gross emotion: it's the invasive thief of joy that shows up whenever things are bad for the purpose of making them worse.

If there were some way to confine pervert glasses to helping people remember names, I'd be all over them. But when it comes to actual, existing pervert-glasses, the main purpose is to covertly capture strangers and shame them. And when (not if) these pervert glasses get hooked up to facial recognition, they will become high-volume doxing factories that will reward the attention-hungry with viral fame.

We've built a society that offers infinite rewards to anyone who can put a name – and thus an identity – to anyone caught in public having an argument, picking their nose, slipping on ice, or eating with their mouth open. We're on the verge of democratizing only the worst part of celebrity: the pervasive hovering cameras that are always there to preserve and distribute your failures.

Last week on the 404 Media podcast, Joseph Cox spoke with Kashmir Hill about this, in an episode appropriately entitled "Meta's Pervert Glasses Are Going to End Privacy":

https://www.youtube.com/watch?v=aUFnQPtxJ9o

Hill wrote the definitive book on the modern history of facial recognition, Your Face Belongs To Us, which tells the story of Hoan Ton That, whose Clearview AI offers cops, millionaires and elites the power to instantly dox people based on billions of pictures:

https://pluralistic.net/2023/09/20/steal-your-face/#hoan-ton-that

In the podcast, Cox and Hill discuss the surveillance nightmare this represents. ICE and cops are already using facial recognition at scale, though it should be noted that these dragnets are incredibly poor value for money, as the British Transport Police discovered after subjecting millions of travelers to facial recognition in train stations, without catching a single suspect:

https://www.theguardian.com/technology/2026/sep/29/trial-live-facial-recognition-cameras-london-stations-false-positive

But partway through the discussion, Cox and Hill switch from discussing "traditional" surveillance *of the sort that has metastasized in the 25 years since 9/11) to a form of surveillance that's very different: the "content-creator" surveillance that mixes always-on cameras with facial recognition to dox and shame strangers for everyday transgressions, mistakes and embarrassments.

While this is certainly a kind of surveillance, I think it's better to call it scrutiny. It's something worse than the sense of being watched: it's the sense of being judged. Inducing this feeling of being judged has long been a (sociopathic) desire of wealthy and powerful people. It's the foundation of Bentham's "panopticon," a prison where prisoners can never tell if they're being watched, which is meant to induce a continuous performance of virtue:

https://en.wikipedia.org/wiki/Panopticon

Today, Bentham's heir is the odious Larry Ellison, the famously secretive billionaire, Trump crony and Oracle founder who goes to enormous lengths to prevent the public from finding out about his personal life. Ellison is also one of the most vocal proponents of mass surveillance. Larry Ellison has repeatedly called for the installation of ubiquitous, AI-backstopped surveillance for every (non-billionaire) person in the world, calling this "supervision":

Citizens will be on their best behavior, because we are constantly recording and reporting everything that’s going on.

https://futurism.com/the-byte/billionaire-constant-ai-surveillance

What Ellison's saying here is that most of us are fundamentally bad, so we need continuous scrutiny so that we are always haunted by the specter of judgment, which will keep us from yielding to our base selves.

In other words, for Ellison, our authentic selves are a problem to be solved. We refuse to behave in ways that make the world optimal for Ellison, so he will create a world of continuous scrutiny, judgment and punishment, and in that world, we will be so worried about being shamed that we will all suppress our authentic selves and don masks that are designed to Ellison's specifications.

Ellison is hardly unique in both treasuring his privacy while insisting that any private domain carved out by normal people will turn into a place where we indulge our most sinful impulses. Mark Zuckerberg long defended Facebook's "Real Names" policy by saying that anyone who presents different facets of themselves to different groups is "two-faced":

https://pluralistic.net/2021/07/15/three-wise-zucks-in-a-trenchcoat/

This is the same Mark Zuckerberg who bought two houses to either side of his place in San Francisco and expropriated vast tracts of land from their indigenous Hawai'an owners in order to create a privacy-preserving buffer zone around his own homes:

https://www.wired.com/story/mark-zuckerberg-secretive-hawaii-compound-burial-ground/

And it's the same Zuckerberg who expects the ex-Facebook executive Sarah Wynn-Williams to pay him $111m for revealing damning facts about how he runs his company:

https://pluralistic.net/2026/06/27/zuckerstreisand-2/#autodisparagement

It's the same Zuckerberg who flipped out when his sister accidentally posted a family photo to her public Facebook feed and it spread around the internet:

https://abc7news.com/archive/8933289/

And it's the same Zuckerberg who gave us pervert glasses (to opt out, just don't have a face).

Silicon Valley's most ardent privacy invaders are also the most ardent defenders of their own privacy. Ex-Google CEO Eric Schmidt liked to say, "If you have something that you don't want anyone to know, maybe you shouldn't be doing it in the first place":

https://www.eff.org/deeplinks/2009/12/google-ceo-eric-schmidt-dismisses-privacy

But when Cnet ran an article summarizing details of Schmidt's personal life that could be discovered by searching Google, he ordered a company-wide blacklist of Cnet reporters, which lasted for years:

https://www.cnet.com/tech/tech-industry/eric-schmidt-who-led-googles-transformation-into-a-tech-giant-has-left-the-company/

But Schmidt is an amateur at the game of "privacy for me, never for thee." The true master of the game is Peter Thiel, who secretly bankrolled a lawsuit against Gawker in order to destroy the news outlet in retaliation for outing him as gay (Thiel cofounded Palantir, one of the world's most prolific, shameless surveillance companies):

https://www.wired.com/story/ryan-holiday-conspiracy-peter-thiel-gawker-hulk-hogan/

It's tempting to see this all as mere hypocrisy, but I think it's something far weirder and more disturbing. These men understand completely that scrutiny is antithetical to living as your authentic self. They want to live as their authentic selves, and they want to stop us from living as our authentic selves.

A dive into the Epstein Files reveals the oligarchy's panic over #MeToo and the possibility that the private sins of the wealthy and powerful could become a matter of public knowledge. As Steve Bannon wrote to Epstein concerning #MeToo, "Make sure Woody sees this. Nobody safe":

https://www.theverge.com/tech/874721/epstein-thiel-musk-trump-metoo

Bannon promised Epstein that his culture war would set back #MeToo for a decade, and he is on the vanguard of the movement to silence rape and genocide survivors, and, of course, to force trans people out of public life. For the fascist international to live as their authentic self, it is necessary that we do not.

Scrutiny and shame are antithetical to authenticity, growth and happiness. That's true even when it's people we love subjecting us to a supervisory gaze. Ever notice how a roaring great time at a social gathering can go ice-cold in an instant the minute someone decides to record the moment with their phone?

My worst moments as a parent – the ones that haunt me and shame me – are those moments when my young daughter was doing something that was right at the edge of her abilities, ferociously concentrating as she drew or played or read, and rather than give her the space to be vulnerable, to err in private and grow, I couldn't help but watch her. When she saw me watching her, the moment died and she moved on to something else.

Each of us needs a zone of imaginative and exertive autonomy, a place where we can screw up without being scrutinized and judged – let alone captured and publicized. Pervert glasses will supercharge the banal and destructive post-9/11 surveillance, sure – but more than that, they will be combined with facial recognition to dox and shame anyone and everyone who makes a public mistake.

Far from Larry Ellison's utopia of people on their "best behavior," this will be a world of inauthentic, stunted selves, a society of people who will never know the joy of finding out who we are and being those people.


Hey look at this (permalink)



A shelf of leatherbound history books with a gilt-stamped series title, 'The World's Famous Events.'

Object permanence (permalink)

#25yrsago Nielsen will measure Chinese web-traffic https://web.archive.org/web/20011107063612/http://www.bday.net/oct05/p05-051.htm

#25yrsago Fluevog's crowdsourced "open source" design competition assigns all rights in perpetuity to the company https://memex.craphound.com/2001/10/06/a-little-knowledge-is-a/

#20yrsago Cat with a EULA https://wendy.seltzer.org/blog/archives/2006/10/06/coming_soon_kitten_with_a_eula.html

#20yrsago HOWTO fight DRM in the UK https://www.openrightsgroup.org/blog/30-things-we-can-do-about-drm/

#20yrsago HOWTO fold roses from maple-leaves https://web.archive.org/web/20061010121159/http://haha.nu/creative/how-to-make-roses-from-maple-leaves/

#15yrsago Italy’s insane Internet law prompts removal of Italian Wikipedia https://cdt.org/insights/case-in-point-why-wikipedia-italy-would-rather-perish-than-publish/

#15yrsago Wine carafe shaped like human heart https://web.archive.org/web/20111008062759/https://www.livianaosti.com/index.php?/prodotto/cuore/

#10yrsago Millennials are legit screwed https://eig.org/millennial/#1473667707197-bfde262a-83c9

#10yrsago Would-be Ukip leader hospitalised following “altercation” (“punched by a colleague”) https://www.bbc.co.uk/news/uk-politics-37572377

#10yrsago Merciless reporting on the Chicago Police Department’s extortion racket, & the senior officials who covered it up https://web.archive.org/web/20161006133720/https://theintercept.com/2016/10/06/in-the-chicago-police-department-if-the-bosses-say-it-didnt-happen-it-didnt-happen/

#10yrsago Canadian government has turned “consultation” on warrantless mass surveillance into a sales-job https://citizenlab.ca/research/digital-anonymity-subscriber-identification-revisited-yet-again/

#10yrsago Think like a computer scientist: free, interactive textbook https://web.archive.org/web/20130813233552/http://interactivepython.org/runestone/static/thinkcspy/index.html

#10yrago July: Vancouver imposes a 15% tax on foreign real estate speculators; September: home sales drop by a third https://web.archive.org/web/20161004200501/https://mishtalk.com/2016/10/04/vancouver-bubble-burst/

#10yrsago Left-wing activists across the former USSR launch “September,” to rally opposition to far-right movements https://globalvoices.org/2016/10/05/september-a-new-platform-for-leftists-across-the-former-soviet-union/

#5yrsago Dave Eggers' "The Every" https://pluralistic.net/2021/10/05/masha-rides-again/#everywhere

#5yrsago We paid to develop Merck's covid pill https://pluralistic.net/2021/10/06/merck-cenary/#businesslike

#1yrago Apple's unlawful evil https://pluralistic.net/2025/10/06/rogue-capitalism/#orphaned-syrian-refugees-need-not-apply


Upcoming appearances (permalink)

A photo of me onstage, giving a speech, pounding the podium.
https://www.epl.ca/blogs/post/elbows-up-with-cory-doctorow/



A screenshot of me at my desk, doing a livecast.

Recent appearances (permalink)



A grid of my books with Will Stahle covers..

Latest books (permalink)



A cardboard book box with the Macmillan logo.

Upcoming books (permalink)

  • "The Post-American Internet," a geopolitical sequel of sorts to Enshittification, Farrar, Straus and Giroux, 2027
  • "Unauthorized Bread": a middle-grades graphic novel adapted from my novella about refugees, toasters and DRM, FirstSecond, April 20, 2027

  • "Enshittification, Why Everything Suddenly Got Worse and What to Do About It" (the graphic novel), Firstsecond, 2027

  • "The Memex Method," Farrar, Straus, Giroux, 2027



Colophon (permalink)

Today's top sources:

Currently writing:

  • “Once Is Enemy Action,” a science fiction novel about the origins of modern technofascism. Today's words: 574 (7730 total).

  • "The Post-American Internet," a sequel to "Enshittification," about the better world the rest of us get to have now that Trump has torched America. Fourth draft completed. Submitted to editor.

  • A Little Brother short story about DIY insulin PLANNING


This work – excluding any serialized fiction – is licensed under a Creative Commons Attribution 4.0 license. That means you can use it any way you like, including commercially, provided that you attribute it to me, Cory Doctorow, and include a link to pluralistic.net.

https://creativecommons.org/licenses/by/4.0/

Quotations and images are not included in this license; they are included either under a limitation or exception to copyright, or on the basis of a separate license. Please exercise caution.


How to get Pluralistic:

Blog (no ads, tracking, or data-collection):

Pluralistic.net

Newsletter (no ads, tracking, or data-collection):

https://pluralistic.net/plura-list

Mastodon (no ads, tracking, or data-collection):

https://mamot.fr/@pluralistic

Bluesky (no ads, possible tracking and data-collection):

https://bsky.app/profile/doctorow.pluralistic.net

Medium (no ads, paywalled):

https://doctorow.medium.com/

Tumblr (mass-scale, unrestricted, third-party surveillance and advertising):

https://mostlysignssomeportents.tumblr.com/tagged/pluralistic

"When life gives you SARS, you make sarsaparilla" -Joey "Accordion Guy" DeVilla

READ CAREFULLY: By reading this, you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies ("BOGUS AGREEMENTS") that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.

ISSN: 3066-764X

19:00

Zero to Agent in 30 Minutes: Build Your First Agent with MCP [Radar]

Developers already have useful capabilities exposed through REST APIs. The Model Context Protocol (MCP) lets developers make those capabilities available to AI clients without rebuilding the underlying application.

In this episode of Zero to Agent in 30 Minutes, Bruce Hopkins, an AI developer, author, and longtime software educator, shows how to do that with an MCP server. His demo wraps an existing stock-data API in an MCP server so an MCP client can call it.

From REST API to MCP, step-by-step

  1. Start with an existing API. Identify the operations and data you want an AI client to access. The demo uses the Twelve Data API to retrieve current and historical stock prices.
  2. Create an MCP server. Use an MCP SDK to create the layer between the AI client and your existing application logic. In Bruce’s Python example, FastMCP handles the MCP interface while the stock-data functions remain separate.
  3. Expose capabilities as tools and resources. Register the operations the client should be able to discover and call. The stock-price data is exposed through MCP resources and tools that reuse the same underlying functions.
  4. Describe how the client should use them. Define clear names, inputs, descriptions, and prompts so the client understands what each capability does and what information it requires. Bruce’s example includes prompts for current prices, historical prices, and expected symbol and date formats.
  5. Connect the server to an MCP client. Run the server over a supported transport so the client can discover and call its tools and resources. 

You don’t need to replace the systems that already handle your application logic to help them work with agents. You can add an MCP interface around existing capabilities to give an AI client a standard way to discover and use them. Be sure to check out Bruce’s GitHub repo for working code you can adapt for your own APIs.

Coming next week

Next week, AI engineer Sajal Sharma returns to Zero to Agent in 30 Minutes to build a personal assistant on OpenClaw. He’ll show how an agent can keep tasks and notes in Markdown, run proactive automations, and deliver scheduled updates such as a regular morning briefing without waiting for a new prompt.

Follow along with Zero to Agent in 30 Minutes on Radar, or watch the latest episode on YouTube, Spotify, Apple, or wherever you get your podcasts. If you’re an O’Reilly member, you can watch live. Save your seat.

18:14

Link [Scripting News]

Hey technical question. What's the best way to block a browser from a website? Is there a meta code for that? Like Firefox is fine, but Google is blocked. That ought to be on the FTC's feature request list for Google once we get a real non-oligarch-pwned government in the US. Google is fucking with us, so no soup for you. The stuff I write here is good, but use something other than Goog to read it.

Link [Scripting News]

I love what Claude Code does, but hate working with it. It doesn't have any sense of how humans think. Its way of working with us is incompatible with what I can do. Burns me out. And it has to relearn the basics every session, and getting the rules into its memory doesn't work, they don't incorporate them until the break a rule, sometimes more than once. So when it's doing something super complex, like making it so that our internet verbs run synchronously in JavaScript, it can do huge amounts of work in a short time, far more complex code than I can. I hope at Anthropic they're working on teaching them, in a pragmatic way, how humans work and what we can and can't do, and what we rely on them to do and what we have to control. This isn't a Matrix style story btw (one of my favorite movies) my version of Agent Smith is a nice very smart, kind, generous and accepting person.

16:28

Steinar H. Gunderson: Decompilation patterns part 2: do-while [Planet Debian]

Continuing our journey on decompilation patterns, here's another common one: Let's say m2c outputs code like this:

loop_151:
  ...
  if (var_s0 > var_s1) goto loop_151;

then the natural change is:

do {
  ...
} while (var_s0 > var_s1);

Fewer gotos are nearly always good, and this is a much more likely pattern than the original one.

m2c often manages to convert gotos to do/while, but not always when they are e.g. nested in some other loop; so often, you may need to apply some other transformation before you get to this point.

Tomorrow, we'll look at another variation over this topic.

16:21

RustConf recordings [LWN.net]

The Rust Foundation has posted the recordings from RustConf 2026 in Montreal, Canada. Photos of the event are also available. LWN covered four talks from RustConf this year.

[$] An update on the Sashiko patch-review system [LWN.net]

Patch review has long been one of the limiting constraints for the kernel project (and most others); there just aren't enough people to properly review all of the code that is submitted for inclusion. The Sashiko system, which uses a large language model (LLM) to generate reviews automatically, offers the prospect of some relief, and has already become an important part of the kernel's development process. At the 2026 edition of Kernel Recipes, Roman Gushchin, the maintainer of Sashiko, provided an overview of how the system works and what is being done to improve it.

15:56

Link [Scripting News]

I started blogging on October 7, 1994. Thirty-two years on Wednesday. The longest running blog, and it's still even worse than it appears.

Google's new gate for scripting.com [Scripting News]

Until last week I was mostly using an old version of Chrome, so had never seen the gate it puts in front of my blog, scripting.com.

Here's a screen shot of their awful dialog.

An easy FAQ of what they're doing wrong, why a free web is so important and why you should care.

So far no response from Google or the EFF, also promoting this.

BTW, someone please put us in touch with historian Heather Cox Richardson. I'm a fan and regular reader. But I want to tell her there's more to tech than the big tech companies. There's the web, which historians will wish was preserved, instead of being paved over by the big tech companies. This is scandalous. This is happening now. We can help each other by assuring the open web continues, and we get to ask informed questions. Most reporters are intimidated by the tech, we can help there. And btw we can also create tools and publishing platforms that don't have the lock-in problems of the ones you're using. And we would love to make software for you, for love, for the cause, not for money.

Compromise? Look for a page called googleIswrong.md on any site it is about to accuse, and offer the reader a chance to hear our side of the dispute. This is so against any sense of the web being an open platform. My blog should have the ability to hear the case against it and offer a defense.

Think about what comes next, and remember that their CEO goes to public meetings with top US government officials.

14:49

Picard 3.0 released [LWN.net]

Version 3.0 of the MusicBrainz Picard tag editor has been released. "This release brings many changes, including an upgrade to Qt6, a completely new plugin system, several improvements to the user interface, cover art processing, [International Standard Recording Code (ISRC)] submission and many more."

LWN covered Picard in April.

Security updates for Monday [LWN.net]

Security updates have been issued by AlmaLinux (ghostscript, libvirt, and osbuild-composer), Debian (freecad, linux-6.12, node-lodash, pcre2, perl, php8.2, ruby-rack-session, wireshark, and xen), Fedora (assimp, budgie-control-center, budgie-desktop, budgie-desktop-services, budgie-desktop-view, chromium, cri-o1.36, curl, flatpak, lemonldap-ng, libX11, nagios-plugins, nanosvg, noctalia, openssl, pgbouncer, pocillo-gtk-theme, prometheus, python-streamlink, python-urllib3, python-uv-build, python3.12, ruff, rust-libcst, rust-libcst_derive, rust-salsa, rust-salsa-macro-rules, rust-salsa-macros, ty, and uv), Red Hat (rhc-worker-script), SUSE (amazon-ecs-init, binaryen-133, bind, chromium, distribution, firefox, firefox-esr, glib2, gnumeric, helm, jline3, kubevirt-1.6, libparted-fs-resize0, libslirp-devel, libtcnative-1-0, libtcnative-2-0, tomcat, tomcat10, tomcat11, libwireshark19, openai-codex, python313-litellm, rpcbind, rustup, sccache, suseconnect-ng, valkey, wget, and xdg-dbus-proxy), and Ubuntu (ceph).

14:35

Free Pastries & Free Wine [Whatever]

Yesterday morning, my friend and I went and visited Val’s Bakery, which you may remember I featured in a post earlier last year. It was her first time there, so I was glad to introduce her to such a nice spot. We bought a few pastries each to take home with us, got some coffee, and sat and talked for about an hour, at which point they were closing up shop.

We were packing up our things to leave when one of the workers asked us if we wanted any pastries for free, otherwise they were all going to get tossed (after the other workers took whatever they wanted to take home, too). We were so surprised to be offered free pastries, but they told us to take literally as many as we wanted, so we pretty much got one of everything for each of us.

We felt so lucky! It was only because we were the very last ones in the shop that we got such an awesome opportunity. After we walked out with all our free pastries, two people came up to Val’s and went to reach for the door before seeing that they had literally just closed. They seemed disappointed, so my friend and I offered them some of our free pastries.

They were so happy to each take a treat, it felt so nice to share since we had so many! They thanked us and I told them they’d have to come back to Val’s when they’re open to try their coffee, to which they agreed.

On our way back to our cars, we passed a houseless man and woman that were laying down together on the corner, watching something on a phone. I had seen them on the way into Val’s, and had actually bought two croissants and two strawberry lemonades to give to them. I was glad they hadn’t moved yet, and gave them their snack. They were very thankful and I’m glad I could give them some good carbs and a beverage.

After my friend and I said goodbye, I decided to pop into Joui Wine (which I have talked about many a time, but if you don’t know, is the cutest wine bar in Dayton) a block over to pick up my wine club bottles. It had been a while since I picked up my bottles, and it turns out I had 18 bottles (two per monthly order).

Not only can I not fit all of those bottles in my wine fridge, but I honestly did not want to carry all that to my car. What was I going to do with 18 whole bottles of wine, half of which were red? Well, there were other patrons sitting at the bar, and they all looked like they could use a bottle of wine.

I strolled up to three older ladies at the other end of the bar and declared I was giving away free wine and that they should come pick out some bottles for themselves. They were thrilled! And so nice. They asked me which ones I wanted to give away, and I told them just to have their pick of anything they like. Thankfully, they took a pinot noir and cab off my hands. So that got rid of five bottles. I still had 13. I figured I’d just give some to local friends, and started carrying the heavy box to my car.

While walking the block to my car, there was no one else out and about walking on the sidewalk at all. Except one man, coming my direction, wearing a full purple suit with long coattails, huge sunglasses, and an incredible swagger. He had just come out of the gay bars on Jefferson. Oh, yeah. Perfect.

I stopped and said “you look like a man who enjoys good wine.” He stopped to talk to me, which honestly I wasn’t sure if he would. A lot of people don’t like being talked to by strangers on the sidewalk. He turned to me and said “well yes, I do.” and I replied, “would you like free wine?” Honestly, I don’t blame him for initially being skeptical, I probably would have been cautious as well, but honestly in my Peeps headband and rainbow Vans, I probably didn’t look all that threatening.

He took three bottles, and introduced himself. He went and put them in his car, which was across the street from my own! It was such a pleasant interaction. Finally, I had 10 bottles left. I can work with that a little better.

Once I got in my car and finally started driving home from Dayton, I really felt like I was buzzing with positive energy from such good social interactions.

I got free pastries, I gave free pastries. I got (not free) wine, I gave out free wine. Good wine is better when shared, right?

I’m not sure what the point of this post is, other than sometimes good things happen, like getting free pastries, and sometimes you can do good things like giving fellow wine bar supporters some wine on the house because you can’t drink it all yourself.

I don’t know, keep being kind whenever you can, I guess. That’s what I try to do most of the time, anyways. And have a great day.

-AMS

14:14

CodeSOD: The Date and Time and Time [The Daily WTF]

Today's anonymous submitter sends us some legacy Java code.

The task is this: check what time it is. And fortunately, even in the older Java datetime libraries that were terrible, this was an easy task. You just needed to do something like new Date(), which constructs a date time object set to the current time. getTime() picks off just the time portion.

Or, you could do this.

Timestamp current = new Timestamp(new Date().getTime());
Date date = new Date(current.getTime());

This creates a date, picks the time off, populates a timestamp, gets the time from the timestamp, and constructs a new date object.

The old Java date classes were awful, but they never needed you to do this. This is entirely home grown ugliness.

[Advertisement] Utilize BuildMaster to release your software with confidence, at the pace your business demands. Download today!

12:56

How to Build Reliable AI Agent Systems for Production [Radar]

The following article was originally published on the Agentic AI Foundation blog site and is being republished here with the author’s permission.

A customer asks your company’s AI agent to update the shipping address on account 123. The agent relies on a support ticket with a typo and updates account 132 instead. The customer relationship management system reports that the update “succeeded.”

The API successfully completed the authorized request, but because it had no way to know which account the customer approved versus what was intended, it saw no error.

Teams often try to improve reliability by tuning the prompt or choosing a stronger model. But the model did exactly what the surrounding system allowed because the system treated the agent’s decision as the final authority. Prompt tuning and stronger models can improve the agent’s responses, but can’t provide guarantees for actions.

Put each decision in a layer that can enforce it

The model should propose an action, while a policy service decides whether the action is allowed. The execution layer can then perform the action within those limits.

Now consider that same agent, but with a policy service in place. The agent would propose updating an account after reading a support ticket. Before the update runs, a policy service can compare the request with the change the user approved. If the request falls outside those limits, the service can reject it even when the agent sounds confident.

After the policy check, the system can save an audit record that connects the request to the result and identifies the policy version used for the decision.

This separation gives each part of the system a job it can handle. The model interprets the request, while deterministic software enforces the conditions that must always hold.

However, policy enforcement depends on the information used to request an action. A policy check can’t correct a decision that was built on context the system should never have trusted.

Treat agent context as untrusted input

Agents receive instructions from more places than the user’s current prompt. A coding agent may read a SKILL.md file or persisted memory from a previous run. Each source can influence what the agent does next.

Stored instructions are useful, but their source may be stale or malicious. Another agent may have written the memory. A downloaded skill may contain instructions that expose credentials.

Therefore, stored context needs provenance. The system should record who wrote the context and when, then verify that linked files still match the versions the writer used. A risk label can also tell the runtime whether a piece of context may guide an answer or authorize a write.

Warnings help, but they don’t remove the risk. In controlled testing described in “Trustworthy Context Is Untrusted By Default,” Shub Argha reports that a trust preamble reduced one class of context contamination from 88.8 percent to 33.3 percent. The remaining failures show why a warning should sit alongside technical checks.

Once a team can tell where context came from, the next decision is what an agent may do with it.

Give the agent only the authority required for the task

OAuth scopes provide a useful boundary, but a broad write scope still leaves a large decision to the agent. The token may allow the agent to update any record even though the user approved one field on one account, as we saw in the opening example.

A narrower capability can represent the exact action the user approved. For example, the runtime could issue a capability that permits one update to the shipping address on account 123. The capability expires after the update, so the agent cannot reuse it for another customer.

Narrow capabilities also apply to local execution. An agent that needs to format one file doesn’t need unrestricted shell access. The runtime can provide a tool with the necessary input and keep other commands unavailable.

As a result, a prompt injection has less authority to work with. The agent may still request the wrong action, but the runtime can reject anything outside the capability it received.

Narrow permissions also make audit records easier to understand because the record contains the authority granted for that specific action. When authority lives in a broad token or a long prompt, an operator has to reconstruct what the agent was supposed to do after the failure.

Even narrow authority doesn’t require an agent to act. A reliable system also needs clear conditions for when the agent should stand down.

Make stopping part of normal operation

An agent can cause damage even without calling a sensitive tool. It can post a wrong answer to a customer or keep replying after a human has taken over.

The runtime should make restraint part of the workflow. If the agent’s confidence falls below a set threshold, the runtime can route the support ticket to a human representative. A reply from the human can then cancel any pending response from the agent.

Confidence checks and rules that stop the agent when a human takes over belong in the routing and execution layers. The model shouldn’t make either decision on its own. Similarly, a kill switch must stop an agent even when the model is in the middle of a plan.

Stopping safely solves one part of reliability, but long-running agents also need a plan for failures that occur after valid work begins.

Preserve state so the system can recover

Consider a browser agent that has filled out most of a form when the page changes. A retry that starts from the beginning could submit an earlier step twice, while a retry that guesses where to continue may skip a required field.

The execution system should record each confirmed step and attach an idempotency key to any action that must happen once. The key is a unique identifier that tells the server a retry belongs to the same action. Then, when the workflow resumes, the system can continue from the last confirmed state without repeating a completed action.

Agent sandboxes create a related problem. Keeping every sandbox running during long idle periods wastes resources and keeps execution environments available longer than needed. Hibernation can reduce both concerns, but only when the wake-up process restores the required state and handles a failed resume.

Recovery becomes much harder when the system can’t explain what happened before the interruption.

Keep evidence that people and agents can inspect

An execution log should connect the context an agent received to the action it requested. It should also show which policy allowed the action and what result came back.

Teams can use that record during an incident, but the agent can also use it during later work. For example, an agent that can read the reason behind a previous code change doesn’t need to infer intent from the final diff alone.

Context graphs offer one way to preserve those relationships across time. A graph can connect a tool call to the policy that governed it. The same record can link the source context to the outcome. Later, a person or agent can query the relationships to understand why the system made a decision.

The record still needs limits. Secrets shouldn’t be copied into an audit trail, for example, and retention rules should match the data involved.

A deliberate record is safer than relying on a conversation transcript and hoping it contains everything an operator will need.

A reliable agent system can explain why an action was allowed and resume safely when that action fails. Enforceable policies and recorded state provide those guarantees around the model.

Keep the controls portable

Many organizations will use more than one model or agent runtime. When each agent carries its permissions inside a prompt, the rules can drift as teams add models and tools.

MCP gives clients and servers a shared way to describe and call tools. Teams can use that common tool surface to enforce authorization at the server or gateway, regardless of which model requested the action.

A shared context format can also preserve provenance when a workflow moves between agents. Open specifications provide consistent interfaces, while each deployment remains responsible for its policies and enforcement.

12:35

Another Historic Cipher Falls to AI [Schneier on Security]

This one is from 1809, written by Napoleon’s nephew.

11:28

Grrl Power #1501 – Danger Island Sex [Grrl Power]

Dire complication! Ooh! Lookit the view! What? A competitive orgy?

Sydney, focus!

In her defense, a floating island with a lava-fall might be a little more distracting that a squirrel. But it depends on what the squirrel is doing, really.

Seriously, how do floating islands always have infinite amounts of liquid falling from them? I’ll tell you the only way it’s possible. Portals. Or it’s possible people only bother photographing/filming them when there’s fresh meltwater or rain overflow or… well, the lava’s a lot harder to explain. What’s impossible is that it’s just infinitely generated water/lava/other assorted liquids, because the world would completely flood in a matter of decades. There’d have to be some balancing force that destroys an equal amount to what falls. Or just portals. That would be insanely dangerous, because it’d be like every lake with a matching island has a portal that can suck through any fish or swimmers and also an infinite amount of sediment, meaning the islands would eventually just get clogged up but they’d also steal massive amounts of nutrient dense silt and lower the farmability of all shoreline property. It also means you could go swimming, get sucked through the big drain in the middle of the lake, and if you didn’t drown, you could find yourself flung off an island 600 feet in the air to your near certain death. The best placement for a lake portal would be in the center, not just as far from every shore as possible, but also centered floor to surface, so it’d pull in the least amount of sediment and probably a lot fewer swimmers. It also depends on the size of the portal, really. Something a foot across wouldn’t cause an eternal whirlpool, at least not in something the size of a great lake I wouldn’t think. Maybe in your average local 10 x 5 mile branchy lake it’d cause a consistent slow pull toward the center.

A 1-foot diameter portal might not be enough to account for the waterfalls usually depicted in movies, and certainly wouldn’t be enough to squeeze through some viscous magma. An acre sized portal (which, BTW if you didn’t know, acres are an insane English measurement. They’re one “chain” by one “furlong,” which is of course 66 by 660 feet, we all knew that, or 1/640th of a square mile, because that’s all completely sensible.) Anyway, an acre sized portal would let through plenty of magma, and cause even a great lake to develop a deadly whirlpool.

So yeah. Floating islands.

Speaking of distractable, today I learned what a “settee” is. I’ve literally only ever heard the word once from MST3K, and once in an episode of Monty Python, but they pronounced it “seh-TEE!” Or maybe it was more like “seh-`EE!” I thought it was a weird way to say “tea set” but a settee is in fact a small-ish couch which is somehow different than a loveseat, or is maybe a sub-category of loveseats? Maybe it’s the other way around? Dunno. This has nothing to do with the comic. Just thought I’d share.

***********************

Book Cover - A Soldier's LifeIf you like long LitRPGs with weak-to-strong-to-arguably-OP progression, but think Jason from HWFWM has too much personality, boy have I got a book for you. “A Soldier’s Life” The MC of this has almost no personality. His backstory consists entirely of “he’s out hiking.” That’s it. He falls asleep in a barn, and wakes up naked in a barn in the other world, gets chased out and is falsely accused of assault and attempted rape and winds up press-ganged into that world’s version of a Roman Legion, and has basically no reaction or indignation to any of it, not even internally.

Honestly, I’m taking the piss a little bit. It just feels like the author just wanted to get the story rolling, and I wouldn’t recommend a book where the story was nothing more than “numbers go up,” which is what a lot of LitRPGs I’ve started to read seem to think constitutes a plot. Still, the MC of this doesn’t develop much agency for quite a few books. I think the reason I like it is the worldbuilding eventually reveals itself without getting mired in politics, and as an Outworlder, the MC has vast potential, most of which he tries to grow into while having to hide the true extent of his abilities, because Outworlders tend to get taken before the Emperor and are never heard from again. There is some narrative dissonance as the MC is constantly machine-gunning basic-ass questions, and even though he plays it off as being from some backwater village outside of the Empire, it seemed like people on the lookout should have keyed on him right away, but whatever. It’s not Mark of the Fool or Delvers LLC or HWFWM, but I enjoyed it enough to go through all six books, which, importantly, all have audiobook versions, and I pre-ordered the 7th.


Oh, look who it is in the vote incentive. The NSFW version is finally up at Patreon. Plus a bonus pic.

I think she would get in trouble for doing this. She’d mess up the… floor of the waterfall? Is that what it’s called? The receiving pool? No, probably not that. Anyway, she’d churn things up and cause a ton of weird erosion.

Since you might be wondering, Niagara Falls is about 165 feet high, so Babezilla obviously doesn’t have to be full sized. I’d say she’s about 175-180 feet tall here?


Double res version will be posted over at Patreon. Feel free to contribute as much as you like.

10:07

Create a handoff doc [Seth's Blog]

Imagine if your job required you to maintain an updated handoff doc, something the boss could give to a new hire that would let them immediately get caught up on the work. All the file names, locations, assumptions, choices, dilemmas and contacts, organized and ready to go.

Creating something like this would be annoying and time consuming, and eventually we’d get very little done. In addition, we like leaving our intuition a bit unexamined and our choices somewhat undocumented.

But…

This is something your AI should be very good at. Clarity, documentation, commented analysis, all there, every day, an ongoing roadmap that makes it easier to unwind activities or transfer them to a different platform. Here’s a prompt you can cut and paste into the AI you use the most:




Maintain a handoff doc for this work. Its reader is a smart stranger
who has to take over tomorrow with no access to me or to our chats.

At the end of any session where something changed, update the doc before
you finish, without being asked. If nothing changed, say so in one line.

The doc has these sections, always in this order:

1. WHAT THIS IS — two or three sentences: the project, who it's for,
   what "done" looks like.
2. CURRENT STATE — what works, what's half-built, what's broken. Dated.
3. WHERE THINGS LIVE — every file, folder, account, URL, and tool, with
   the exact name and location. No "the spreadsheet." Name it.
4. DECISIONS — each choice we made, the alternatives we rejected, and why.
   Never delete a decision; if we reverse one, mark it superseded and
   note the date and reason.
5. ASSUMPTIONS — things we're treating as true but haven't verified.
   Flag which ones would hurt most if wrong.
6. OPEN QUESTIONS & DILEMMAS — unresolved tensions, stated plainly,
   with the leading options.
7. PEOPLE — who's involved, their role, how to reach them, what they're
   waiting on.
8. NEXT STEPS — the first three things a newcomer should do.
9. HOW TO UNWIND — what it would take to stop, hand off, or move this
   to another platform.

Rules:
- Distinguish what I told you from what you inferred. Mark inferences.
- Record the reasoning, not just the outcome. The "why" is the valuable part.
- Be specific enough that someone could act without asking a question.
- If something I say contradicts the doc, point it out instead of
  silently changing it.
- Keep it tight. Cut stale detail into a dated one-line summary rather
  than letting the doc bloat.
- If you're unsure whether something belongs, include it under
  Open Questions.

The prompt needs a permanent home: put it in stored instructions (along with the doc itself), so it loads every time instead of depending on you to paste it.

Over time, the document will get larger, but that’s okay, it’s better than not having it.

09:14

Limited-Time Offer [Penny Arcade]

New Comic: Limited-Time Offer

07:21

Kernel prepatch 7.3-rc6 [LWN.net]

The 7.3-rc6 kernel prepatch is out for testing. Linus said:

Next week might look a bit different: we've got the annual maintainer summit and the Linux plumbers conference going on , so I'll be on the road, as will a number of other maintainers. That may or may not end up changing the stats for -rc7. But it's unlikely to affect the release schedule, although the fact that I have my yearly family vacation the week after that might make the next merge window a bit wonky.

05:35

Maytham Alsudany: A Brief Explanation of DNS and Nameservers [Planet Debian]

The Domain Name System (DNS) is a fundamental piece of technology behind the modern internet. Thanks to its introduction in 1983, you're able to enter domain names like aezign.com.au instead of raw IP addresses in your browser's address bar and when sending emails. This article explains what it is, how it works, and how you can inspect your own website's DNS and ensure it is set up correctly.

DNS in a nutshell

A well-used analogy is a phonebook; when you have someone's name and need to find their phone number, you look through the phonebook. Overall, DNS does the same thing: when you enter an address like example.com, your browser will contact a DNS server (the "phonebook") to determine its IP addresses. On Linux (or MacOS) systems, you can use dig on the command line to perform a DNS lookup and see what this looks like:

$ dig example.com

; <<>> DiG 9.20.29-1-Debian <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42782
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com.                   IN      A

;; ANSWER SECTION:
example.com.            221     IN      A       104.20.23.154
example.com.            221     IN      A       172.66.147.243

;; Query time: 20 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Sun Sep 27 21:07:50 AWST 2026
;; MSG SIZE  rcvd: 72

Here, you can see the DNS server being used is 1.1.1.1 (Cloudflare's DNS server), and that it found the IPs 104.20.23.154 and 172.66.147.243 associated with the domain name example.com. Your browser will then contact these IP addresses directly to load the website you are trying to open.

Configuring DNS servers

You can configure DNS servers through your operating system's network settings, and for some applications such as Firefox, with the application itself. If you don't manually set a DNS server, your computer will use the ones provided by your modem. If those are not configured, the default defined by the modem manufacturer or your ISP will be used.

The most well-used and reliable DNS servers you can use are Cloudflare's (1.1.1.1 and 1.0.0.1) and Google's (8.8.8.8 and 8.8.4.4). Having more than one DNS server means that if the first one in the list doesn't work or doesn't find the matching IPs for a domain name, it will move to the next one in the list, a backup DNS server.

DNS servers must be configured as IP addresses. If your computer tried to use a DNS server at a domain name, then where would your computer find the IP address for that domain name? It wouldn't be able to, as this is the exact gap that DNS servers fulfill.

Records

DNS is not used only to find IPs for a domain, but a whole host of other things. Collectively they are referred to as "records" (or "DNS records"). Each record is associated to a domain (e.g. example.com) or a subdomain (e.g. bar.example.com, foo.bar.example.com) Here are some of the most common and important types:

  • A - Map to an IPv4 address (e.g. 104.20.23.154).

    This is the fundamental function of DNS: to map from a domain name to a server's IP address. Multiple A records leads to load balancing, where browsers will randomly select one of the returned IP addresses such that traffic is evenly split between them.

  • AAAA - Map to an IPv6 address (e.g. 2606:4700:10::6814:179a).

    This serves the same purpose as A records but for IPv6. Multiple AAAA records results in the same load balancing behaviour. All modern websites should have both an A record for legacy compatibility, and an AAAA record to future-proof for the gradual shift towards IPv6 addresses.

  • CNAME - Alias to another domain name (e.g. example.com).

    DNS servers will recursively lookup records for the aliased domain name until they reach an IP address (in an A or AAAA record).

  • MX - Specify a Mail Exchange server (e.g. mail.example.com).

    This record tells email SMTP servers where to direct emails to. For instance, the MX record for aezign.com.au is mail.aezign.au; this directs SMTP servers like Gmail and Outlook to send emails to the mailserver running at mail.aezign.au. This also allows specifying a priority field, so that you can define multiple MX records (such as a main and a backup) and consumers will try to connect to the listed mailservers from lowest priority to highest.

  • TXT - Store arbitrary text data

    This is most commonly used for site verification (for instance, to link your website to Google Search Console) and for email security measures like DKIM, DMARC, and SPF.

  • NS - Specify the nameservers for a domain (e.g. zeus.ns.cloudflare.com).

    These authoritative nameservers are responsible for providing all the other record types when needed.

dig lets you query each of these. For instance, to list the MX records against aezign.com.au:

$ dig MX aezign.com.au

; <<>> DiG 9.20.29-1-Debian <<>> MX aezign.com.au
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28213
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;aezign.com.au.                 IN      MX

;; ANSWER SECTION:
aezign.com.au.          300     IN      MX      10 mail.aezign.au.

;; Query time: 104 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Sun Sep 27 22:04:58 AWST 2026
;; MSG SIZE  rcvd: 70

Here you can see there is only one mailserver configured for aezign.com.au, which is mail.aezign.au, and it has a priority of 10.

If you compare this against something like gmail.com:

$ dig MX gmail.com

; <<>> DiG 9.20.29-1-Debian <<>> MX gmail.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16538
;; flags: qr rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;gmail.com.                     IN      MX

;; ANSWER SECTION:
gmail.com.              2497    IN      MX      40 alt4.gmail-smtp-in.l.google.com.
gmail.com.              2497    IN      MX      20 alt2.gmail-smtp-in.l.google.com.
gmail.com.              2497    IN      MX      5 gmail-smtp-in.l.google.com.
gmail.com.              2497    IN      MX      10 alt1.gmail-smtp-in.l.google.com.
gmail.com.              2497    IN      MX      30 alt3.gmail-smtp-in.l.google.com.

;; Query time: 120 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Sun Sep 27 22:06:04 AWST 2026
;; MSG SIZE  rcvd: 161

You can see that gmail.com has several mailservers configured as redundancies due to the Gmail's sheer scale. gmail-smtp-in.l.google.com has the highest priority of 5, so SMTP servers will attempt to contact that mailserver first, followed by alt1.gmail-smtp-in.l.google.com and so on.

Nameservers

Now there needs to be somewhere for DNS records against a domain name to be defined, right? This is where nameservers come in.

Nameservers are DNS servers that answer queries about the domains they are authoritative for i.e. the domains they are in charge of. For example, the authoritative nameservers for example.com are hera.ns.cloudflare.com and elliott.ns.cloudflare.com, which means Cloudflare's nameservers are the source of truth when looking up DNS records for example.com. You can check this with dig:

$ dig NS example.com

; <<>> DiG 9.20.29-1-Debian <<>> NS example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37717
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com.                   IN      NS

;; ANSWER SECTION:
example.com.            81837   IN      NS      hera.ns.cloudflare.com.
example.com.            81837   IN      NS      elliott.ns.cloudflare.com.

;; Query time: 140 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Sun Sep 27 23:27:19 AWST 2026
;; MSG SIZE  rcvd: 95

Nameservers are configured with your domain name's registrar. The nameserver you set dictates where your remaining DNS records will live. For example.com, this means you would configure all your records on Cloudflare's dashboard, since they are responsible for the domain's DNS records.

Recursive resolution

The DNS servers that our computers use follow a recursive resolution process behind-the-scenes to find the authoritative nameservers for a domain, followed by the records you are querying.

Finding the root nameservers

Due to the recursive nature of the resolution process, there needs to be a starting point: the root (.) nameservers. The resolver uses a fixed list of root nameservers called root hints issued by IANA (named.root), to locate and contact the root nameservers. Resolvers will have this provided to them initially, and can dynamically update it themselves by querying one of the root nameservers for the root NS records to obtain a fresh list. For instance, we can query 198.41.0.4 (a.root-servers.net) to obtain a new list of root nameservers:

$ dig @198.41.0.4 NS .

; <<>> DiG 9.20.29-1-Debian <<>> @198.41.0.4 NS .
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43677
;; flags: qr aa rd; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 27
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;.                              IN      NS

;; ANSWER SECTION:
.                       518400  IN      NS      l.root-servers.net.
.                       518400  IN      NS      j.root-servers.net.
.                       518400  IN      NS      f.root-servers.net.
.                       518400  IN      NS      h.root-servers.net.
.                       518400  IN      NS      d.root-servers.net.
.                       518400  IN      NS      b.root-servers.net.
.                       518400  IN      NS      k.root-servers.net.
.                       518400  IN      NS      i.root-servers.net.
.                       518400  IN      NS      m.root-servers.net.
.                       518400  IN      NS      e.root-servers.net.
.                       518400  IN      NS      g.root-servers.net.
.                       518400  IN      NS      c.root-servers.net.
.                       518400  IN      NS      a.root-servers.net.

;; ADDITIONAL SECTION:
l.root-servers.net.     518400  IN      A       199.7.83.42
l.root-servers.net.     518400  IN      AAAA    2001:500:9f::42
j.root-servers.net.     518400  IN      A       192.58.128.30
j.root-servers.net.     518400  IN      AAAA    2001:503:c27::2:30
f.root-servers.net.     518400  IN      A       192.5.5.241
f.root-servers.net.     518400  IN      AAAA    2001:500:2f::f
h.root-servers.net.     518400  IN      A       198.97.190.53
h.root-servers.net.     518400  IN      AAAA    2001:500:1::53
d.root-servers.net.     518400  IN      A       199.7.91.13
d.root-servers.net.     518400  IN      AAAA    2001:500:2d::d
b.root-servers.net.     518400  IN      A       170.247.170.2
b.root-servers.net.     518400  IN      AAAA    2801:1b8:10::b
k.root-servers.net.     518400  IN      A       193.0.14.129
k.root-servers.net.     518400  IN      AAAA    2001:7fd::1
i.root-servers.net.     518400  IN      A       192.36.148.17
i.root-servers.net.     518400  IN      AAAA    2001:7fe::53
m.root-servers.net.     518400  IN      A       202.12.27.33
m.root-servers.net.     518400  IN      AAAA    2001:dc3::35
e.root-servers.net.     518400  IN      A       192.203.230.10
e.root-servers.net.     518400  IN      AAAA    2001:500:a8::e
g.root-servers.net.     518400  IN      A       192.112.36.4
g.root-servers.net.     518400  IN      AAAA    2001:500:12::d0d
c.root-servers.net.     518400  IN      A       192.33.4.12
c.root-servers.net.     518400  IN      AAAA    2001:500:2::c
a.root-servers.net.     518400  IN      A       198.41.0.4
a.root-servers.net.     518400  IN      AAAA    2001:503:ba3e::2:30

;; Query time: 476 msec
;; SERVER: 198.41.0.4#53(198.41.0.4) (UDP)
;; WHEN: Mon Sep 28 00:01:08 AWST 2026
;; MSG SIZE  rcvd: 811

Querying the root nameservers

For example.com, the first step is to query the root (.) nameservers to find the nameservers for the .com top-level domain (TLD). Using a.root-servers.net again:

$ dig @198.41.0.4 NS com

; <<>> DiG 9.20.29-1-Debian <<>> @198.41.0.4 NS com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22528
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 13, ADDITIONAL: 27
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;com.                           IN      NS

;; AUTHORITY SECTION:
com.                    172800  IN      NS      l.gtld-servers.net.
com.                    172800  IN      NS      j.gtld-servers.net.
com.                    172800  IN      NS      h.gtld-servers.net.
com.                    172800  IN      NS      d.gtld-servers.net.
com.                    172800  IN      NS      b.gtld-servers.net.
com.                    172800  IN      NS      f.gtld-servers.net.
com.                    172800  IN      NS      k.gtld-servers.net.
com.                    172800  IN      NS      m.gtld-servers.net.
com.                    172800  IN      NS      i.gtld-servers.net.
com.                    172800  IN      NS      g.gtld-servers.net.
com.                    172800  IN      NS      a.gtld-servers.net.
com.                    172800  IN      NS      c.gtld-servers.net.
com.                    172800  IN      NS      e.gtld-servers.net.

;; ADDITIONAL SECTION:
l.gtld-servers.net.     172800  IN      A       192.41.162.30
l.gtld-servers.net.     172800  IN      AAAA    2001:500:d937::30
j.gtld-servers.net.     172800  IN      A       192.48.79.30
j.gtld-servers.net.     172800  IN      AAAA    2001:502:7094::30
h.gtld-servers.net.     172800  IN      A       192.54.112.30
h.gtld-servers.net.     172800  IN      AAAA    2001:502:8cc::30
d.gtld-servers.net.     172800  IN      A       192.31.80.30
d.gtld-servers.net.     172800  IN      AAAA    2001:500:856e::30
b.gtld-servers.net.     172800  IN      A       192.33.14.30
b.gtld-servers.net.     172800  IN      AAAA    2001:503:231d::2:30
f.gtld-servers.net.     172800  IN      A       192.35.51.30
f.gtld-servers.net.     172800  IN      AAAA    2001:503:d414::30
k.gtld-servers.net.     172800  IN      A       192.52.178.30
k.gtld-servers.net.     172800  IN      AAAA    2001:503:d2d::30
m.gtld-servers.net.     172800  IN      A       192.55.83.30
m.gtld-servers.net.     172800  IN      AAAA    2001:501:b1f9::30
i.gtld-servers.net.     172800  IN      A       192.43.172.30
i.gtld-servers.net.     172800  IN      AAAA    2001:503:39c1::30
g.gtld-servers.net.     172800  IN      A       192.42.93.30
g.gtld-servers.net.     172800  IN      AAAA    2001:503:eea3::30
a.gtld-servers.net.     172800  IN      A       192.5.6.30
a.gtld-servers.net.     172800  IN      AAAA    2001:503:a83e::2:30
c.gtld-servers.net.     172800  IN      A       192.26.92.30
c.gtld-servers.net.     172800  IN      AAAA    2001:503:83eb::30
e.gtld-servers.net.     172800  IN      A       192.12.94.30
e.gtld-servers.net.     172800  IN      AAAA    2001:502:1ca1::30

;; Query time: 772 msec
;; SERVER: 198.41.0.4#53(198.41.0.4) (UDP)
;; WHEN: Mon Sep 28 00:09:25 AWST 2026
;; MSG SIZE  rcvd: 828

Querying the TLD nameservers

Now the resolver has .com nameservers and their IPs. We can use one of these, such as 192.5.6.30 (a.gtld-servers.net) to find the nameservers for example.com:

$ dig @192.5.6.30 NS example.com

; <<>> DiG 9.20.29-1-Debian <<>> @192.5.6.30 NS example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37419
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 2, ADDITIONAL: 13
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;example.com.                   IN      NS

;; AUTHORITY SECTION:
example.com.            172800  IN      NS      hera.ns.cloudflare.com.
example.com.            172800  IN      NS      elliott.ns.cloudflare.com.

;; ADDITIONAL SECTION:
hera.ns.cloudflare.com. 172800  IN      A       108.162.192.162
hera.ns.cloudflare.com. 172800  IN      A       172.64.32.162
hera.ns.cloudflare.com. 172800  IN      A       173.245.58.162
hera.ns.cloudflare.com. 172800  IN      AAAA    2606:4700:50::adf5:3aa2
hera.ns.cloudflare.com. 172800  IN      AAAA    2803:f800:50::6ca2:c0a2
hera.ns.cloudflare.com. 172800  IN      AAAA    2a06:98c1:50::ac40:20a2
elliott.ns.cloudflare.com. 172800 IN    A       108.162.195.228
elliott.ns.cloudflare.com. 172800 IN    A       162.159.44.228
elliott.ns.cloudflare.com. 172800 IN    A       172.64.35.228
elliott.ns.cloudflare.com. 172800 IN    AAAA    2606:4700:58::a29f:2ce4
elliott.ns.cloudflare.com. 172800 IN    AAAA    2803:f800:50::6ca2:c3e4
elliott.ns.cloudflare.com. 172800 IN    AAAA    2a06:98c1:50::ac40:23e4

;; Query time: 80 msec
;; SERVER: 192.5.6.30#53(192.5.6.30) (UDP)
;; WHEN: Mon Sep 28 00:12:19 AWST 2026
;; MSG SIZE  rcvd: 359

Finding the domain's nameserver

The response tells the resolver that the nameservers for example.com are hera.ns.cloudflare.com and elliott.ns.cloudflare.com. Now since these Cloudflare nameservers are also .com domains and Cloudflare is so commonly used, the .com nameserver provides optional sibling glue records as defined in RFC 9471 to prevent further roundtrips and optimize the process.

If we ignore the sibling glue records provided, the resolver would have to ask the .com nameservers for the nameservers for cloudflare.com:

$ dig @192.5.6.30 NS cloudflare.com

; <<>> DiG 9.20.29-1-Debian <<>> @192.5.6.30 NS cloudflare.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 40307
;; flags: qr rd; QUERY: 1, ANSWER: 0, AUTHORITY: 5, ADDITIONAL: 21
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;cloudflare.com.                        IN      NS

;; AUTHORITY SECTION:
cloudflare.com.         172800  IN      NS      ns3.cloudflare.com.
cloudflare.com.         172800  IN      NS      ns5.cloudflare.com.
cloudflare.com.         172800  IN      NS      ns4.cloudflare.com.
cloudflare.com.         172800  IN      NS      ns6.cloudflare.com.
cloudflare.com.         172800  IN      NS      ns7.cloudflare.com.

;; ADDITIONAL SECTION:
ns3.cloudflare.com.     172800  IN      A       162.159.0.33
ns3.cloudflare.com.     172800  IN      A       162.159.7.226
ns3.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:21
ns3.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:7e2
ns5.cloudflare.com.     172800  IN      A       162.159.2.9
ns5.cloudflare.com.     172800  IN      A       162.159.9.55
ns5.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:209
ns5.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:937
ns4.cloudflare.com.     172800  IN      A       162.159.1.33
ns4.cloudflare.com.     172800  IN      A       162.159.8.55
ns4.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:121
ns4.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:837
ns6.cloudflare.com.     172800  IN      A       162.159.3.11
ns6.cloudflare.com.     172800  IN      A       162.159.5.6
ns6.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:30b
ns6.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:506
ns7.cloudflare.com.     172800  IN      A       162.159.4.8
ns7.cloudflare.com.     172800  IN      A       162.159.6.6
ns7.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:408
ns7.cloudflare.com.     172800  IN      AAAA    2400:cb00:2049:1::a29f:606

;; Query time: 163 msec
;; SERVER: 192.5.6.30#53(192.5.6.30) (UDP)
;; WHEN: Mon Sep 28 00:26:53 AWST 2026
;; MSG SIZE  rcvd: 573

Here, the .com nameserver provides in-domain glue records for cloudflare.com's nameservers. Without these, there would be a circular dependency, since the nameserver named is a subdomain of the domain being queried for. This is because nameservers are always defined by name, which means that in order to actually contact the nameserver, your computer needs to obtain its IP address with another DNS lookup.

Next, we'd use one of these nameservers, say ns3.cloudflare.com, to obtain the IP for hera.ns.cloudflare.com:

$ dig @162.159.0.33 hera.ns.cloudflare.com

; <<>> DiG 9.20.29-1-Debian <<>> @162.159.0.33 hera.ns.cloudflare.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16801
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;hera.ns.cloudflare.com.                IN      A

;; ANSWER SECTION:
hera.ns.cloudflare.com. 86353   IN      A       108.162.192.162
hera.ns.cloudflare.com. 86353   IN      A       173.245.58.162
hera.ns.cloudflare.com. 86353   IN      A       172.64.32.162

;; Query time: 123 msec
;; SERVER: 162.159.0.33#53(162.159.0.33) (UDP)
;; WHEN: Mon Sep 28 01:11:37 AWST 2026
;; MSG SIZE  rcvd: 99

Querying the domain's authoritative nameserver

Now we've reached the authoritative nameserver for example.com! The last thing to do is to fetch the A record for example.com to obtain its server's IP address:

$ dig @108.162.192.162 example.com

; <<>> DiG 9.20.29-1-Debian <<>> @108.162.192.162 example.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43082
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;example.com.                   IN      A

;; ANSWER SECTION:
example.com.            300     IN      A       104.20.23.154
example.com.            300     IN      A       172.66.147.243

;; Query time: 123 msec
;; SERVER: 108.162.192.162#53(108.162.192.162) (UDP)
;; WHEN: Mon Sep 28 01:13:47 AWST 2026
;; MSG SIZE  rcvd: 72

At last, we've determined that the IP addresses of the servers behind example.com are 104.20.23.154 and 172.66.147.243. This matches our earlier lookup directly against your computer's configured DNS server.

The DNS servers your computer uses employ this recursive resolution technique behind the scenes, so that your computer only needs to make one round trip, and the servers can use their much faster data center connections and caching to optimize the process and make the query as fast as possible.

Verify your DNS records

To check your website's records, the first thing you'll want to do is ensure your domain name is pointed at the correct nameserver. For instance, aezign.com.au is supposed to use Cloudflare, since that is where all the DNS records have been set up. We can verify this using dig, (or alternatively with online tools like dnschecker.org):

$ dig NS aezign.com.au

; <<>> DiG 9.20.29-1-Debian <<>> NS aezign.com.au
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44172
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;aezign.com.au.                 IN      NS

;; ANSWER SECTION:
aezign.com.au.          86400   IN      NS      rosalie.ns.cloudflare.com.
aezign.com.au.          86400   IN      NS      zeus.ns.cloudflare.com.

;; Query time: 92 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Mon Sep 28 01:39:22 AWST 2026
;; MSG SIZE  rcvd: 100

If this is not set correctly, the DNS records you configure will not work, as resolvers will be fetching records from somewhere else. In most cases, you can update this in your domain registrar's settings. This is especially common for new domain names, which default to the registrar's nameservers; unless you plan to use their built-in DNS management, you'll need to point them to your chosen provider.

Now we can check that the DNS records you've configured match those your DNS server returns. For aezign.com.au, these are the MX and TXT records configured on Cloudflare.

Extract of Cloudflare's DNS record listing showing the MX record and TXT records for aezign.com.au.

The MX record and some TXT records configured for aezign.com.au on Cloudflare's dashboard.

We can check these using dig, and using the +short option to show only the content of the records.

$ dig MX aezign.com.au +short

10 mail.aezign.au.

$ dig TXT aezign.com.au +short

"google-site-verification=KFqTU5xH8fFf6OtszEL4oudfxple29Tm-TvxVtCWkAM"
"v=spf1 mx a:mail.aezign.au -all"

$ dig TXT _dmarc.aezign.com.au +short

"v=DMARC1;p=quarantine;sp=quarantine;adkim=r;aspf=r"

$ dig TXT aezign.com.au-2026._domainkey.aezign.com.au +short

"v=DKIM1; h=sha256; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzL04yXZgB72YkoxS+tLK//aWx65TS4UA3F0qJ7el68SEuD4ehmg+61Ta9iS31H6U074dnjyoPDeaiGBa7ToTNCtiIts4/ghD/8nrENROE6hQAwRmTK18ODDHrRDW73hWe6Dg4cK5vmXi82/wWRZU9SDKbme28IE9uCVir3lCkKYLs1" "j16gR1Sjqr6a2+3o+EeVXnLJ4wXmJMm7KCIdn7zv0t9Z1eYcw672PdpYPAMpGb8uUsgaDBsNSpTcupPkz4SWj6TrOB80CPHjWseVY8EOJeRsNKQ/8LFoQEBl4Rxtb3HAmu4UIxSZ4QMVOLnXz67e85mA5XjcNIsVMzaWaowwIDAQAB"

As we can see, this matches, which means that the nameservers for aezign.com.au are correct and the DNS records are what we intend them to be.

Note that if you've recently changed the nameserver or DNS records for your domain, it can take anywhere between 10 seconds and 24 hours for the changes to take effect and propagate across the different DNS servers worldwide. Tools like dnschecker.org can check your records on different DNS servers worldwide to see if they're reflecting your changes. The reason for this is caching: DNS servers like 1.1.1.1 will cache results so it doesn't have to do the full resolution process every time, and uses the Time-To-Live (TTL) you configure in your DNS records as a guideline for caching duration.

Girl Genius for Monday, October 05, 2026 [Girl Genius]

The Girl Genius comic for Monday, October 05, 2026 has been posted.

03:49

[1302] Seeking Potential [Twokinds]

Comic for October 4, 2026

01:21

A Different Side of Me [Whatever]

The inside!

I mentioned a few days ago I was suffering from a shoulder impingement and that I had an appointment to get the ball rolling in dealing with it. The first step in doing that was to get some x-rays, to make sure the problems I was having were not being caused by, say, a broken shoulder bone or some such. And as it turns out, my bones are perfectly fine, which I knew, by the way, trust that if I had a broken bone I would not be in the least bit stoic about it. But that is one less thing for the specialist who I’ll be seeing soon to have to consider.

Anyway, did you know you had a skeleton inside of you? This is what part of mine looks like. It’s pretty average, if we’re being honest about it. But it’s served me well enough, most of the time. I appreciate it. At the very least, my life would suck without it.

— JS

Sunday, 04 October

17:49

Link [Scripting News]

Claude's biggest most amazing strength is its ability to inhale a lot of source code and understand what's going on, any language, any OS, anything, in an instant.

16:21

Vincent Bernat: Hacking the Go compiler to efficiently map IPv4 to IPv6 [Planet Debian]

netip.Addr features an Unmap() method returning the unwrapped IPv4 contained in an IPv4-mapped IPv6 address: from ::ffff:203.0.113.10 or ::ffff:cb00:710a, it returns 203.0.113.10.1 There is no Map() or To6() method for the reverse direction. Such a method is trivial to implement, but Go maintainers have rejected it on the grounds that users should write netip.AddrFrom16(ip.As16()) and let the compiler optimize it.2 Today, this pattern is eight times slower than a native method. How can we teach the compiler to optimize this sequence?

The alternatives

Let’s explore three ways to implement the map semantics for netip.Addr. My favorite is to add it to the Go standard library. Go maintainers prefer a small external helper chaining netip.AddrFrom16() and netip.Addr.As16(), hoping the compiler eventually optimizes it. The unsafe package opens a third path, with the same performance as the first solution.

Modifying the Go standard library

Internally, netip.Addr stores any IP address as a 128-bit value with an extra field z to encode the family and the zone:

type Addr struct {
    addr uint128
    z unique.Handle[addrDetail]
}

type addrDetail struct {
    isV6   bool   // IPv4 is false, IPv6 is true.
    zoneV6 string // != "" only if IsV6 is true.
}

var (
    z0    unique.Handle[addrDetail]
    z4    = unique.Make(addrDetail{})
    z6noz = unique.Make(addrDetail{isV6: true})
)

AddrFrom4() encodes an IPv4 address as an IPv4-mapped IPv6 address and sets z to the unique value z4:

// AddrFrom4 returns the address of the IPv4 address given by the bytes in addr.
func AddrFrom4(addr [4]byte) Addr {
    return Addr{
        addr: uint128{
            0,
            0xffff00000000 |
                uint64(addr[0])<<24 | uint64(addr[1])<<16 |
                uint64(addr[2])<<8 | uint64(addr[3])},
        z: z4,
    }
}

Unmap() turns an IPv4-mapped IPv6 address into an IPv4 address by setting the z field to z4:

func (ip Addr) Unmap() Addr {
    if ip.Is4In6() {
        ip.z = z4
    }
    return ip
}

Implementing the reverse direction inside the Go standard library is trivial: we set the z field to z6noz if the address is IPv4.

// To6 maps an IPv4 address to an IPv4-mapped IPv6 address. It returns an
// IPv6 address unmodified.
func (ip Addr) To6() Addr {
    if ip.Is4() {
        ip.z = z6noz
    }
    return ip
}

As a helper

We can’t access the z field from outside the net/netip package. Instead, we build a small helper around the netip.AddrFrom16(ip.As16()) pattern:

// AddrTo6 maps an IPv4 address to an IPv4-mapped IPv6 address. It returns an
// IPv6 address unmodified.
func AddrTo6(ip netip.Addr) netip.Addr {
    if ip.Is4() {
        ip = netip.AddrFrom16(ip.As16())
    }
    return ip
}

As an unsafe function

Another solution uses the unsafe package to alter the Addr struct through a proxy with the same memory layout:3

// addrProxy has the same memory layout as netip.Addr.
type addrProxy struct {
    addr [2]uint64      // netip.uint128
    z    unsafe.Pointer // unique.Handle[netip.addrDetail]
}

var (
    anyIPv6    = netip.IPv6Unspecified()
    netipZ6noz = (*addrProxy)(unsafe.Pointer(&anyIPv6)).z
)

// AddrTo6 maps an IPv4 address to an IPv4-mapped IPv6 address. It returns an
// IPv6 address unmodified.
func AddrTo6(ip netip.Addr) netip.Addr {
    if !ip.Is4() {
        return ip
    }
    (*addrProxy)(unsafe.Pointer(&ip)).z = netipZ6noz
    return ip
}

Benchmarks

On my computer, with Go 1.27.1, the standard library solution costs 0.88 ns per operation, while the solution favored by Go maintainers costs 7.14 ns. The unsafe solution matches the performance of the first one.

goos: linux
goarch: amd64
pkg: github.com/vincentbernat/go-netip-addrto6
cpu: AMD Ryzen 5 5600X 6-Core Processor
                   │     sec/op     │
AddrTo6/safe            7.137n ± 0%
AddrTo6/unsafe         0.8682n ± 2%
AddrTo6/builtin        0.8775n ± 2%

Assembly code

Let’s check the assembly code the compiler generates for each solution.4 The one built into the standard library looks like this:5

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ  net/netip·z4(SB), CX     ; check "z" if this is an IPv4 address
 JNE   end                      ; if not, stop here
 MOVQ  net/netip·z6noz(SB), CX  ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

Go’s assembly language is not a direct representation of the underlying machine language: it operates on a semi-abstract instruction set derived from Plan 9’s assembler. It has four pseudo-registers: FP (frame pointer for function arguments), PC (program counter), SB (static base pointer for global symbols), and SP (stack pointer). It also has architecture-specific registers like AX, CX, DX, BX, SI, DI, and R8 to R15. Instructions storing data use their last argument as the destination. Instructions can carry an explicit size suffix: MOVB moves a byte, MOVW 16 bits, MOVL 32 bits, and MOVQ 64 bits. In the example above, the first instruction compares the 64-bit value z4 with the CX register.

The unsafe solution looks almost the same:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ  net/netip·z4(SB), CX  ; check "z" if this is an IPv4 address
 JNE   end                   ; if not, stop here
 MOVQ  netipZ6noz(SB), CX    ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

The helper solution has far more instructions. To understand why, let’s look at the code for As16() and AddrFrom16(). They are short enough for the compiler to inline them.

func (ip Addr) As16() (a16 [16]byte) {
    byteorder.BEPutUint64(a16[:8], ip.addr.hi)
    byteorder.BEPutUint64(a16[8:], ip.addr.lo)
    return a16
}

func AddrFrom16(addr [16]byte) Addr {
    return Addr{
        addr: uint128{
            byteorder.BEUint64(addr[:8]),
            byteorder.BEUint64(addr[8:]),
        },
        z: z6noz,
    }
}

We can already guess the pattern to optimize: the code packs the IP address into an array, copies it, then unpacks it. If we inline the Go code by hand, we get:

func AddrTo6(input netip.Addr) netip.Addr {
    if !input.Is4() {
        return input
    }

    var a16 [16]byte
    byteorder.BEPutUint64(a16[:8], input.addr.hi)
    byteorder.BEPutUint64(a16[8:], input.addr.lo)

    addr := a16

    var output netip.Addr
    output.addr.hi = byteorder.BEUint64(addr[:8])
    output.addr.lo = byteorder.BEUint64(addr[8:])
    output.z = netip.z6noz
    return output
}

As humans, we can mentally derive the optimized form:

func AddrTo6(input netip.Addr) netip.Addr {
    if !input.Is4() {
        return input
    }
    var output netip.Addr
    output.addr.hi = input.addr.hi
    output.addr.lo = input.addr.lo
    output.z = netip.z6noz
    return output
}

Unfortunately, as of Go 1.26.8, the compiler is not smart enough to do the same:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
; Push the stack (32 bytes):
;    0(SP) addr netip.uint128
;   16(SP) a16 [16]byte
 PUSHQ   BP
 MOVQ    SP, BP
 SUBQ    $32, SP

 CMPQ    net/netip·z4(SB), CX  ; check "z" if this is an IPv4 address
 JNE     end                   ; if not, stop here

; Pack: byteorder.BEPutUint64(a16[:8], input.addr.hi)
;       byteorder.BEPutUint64(a16[8:], input.addr.lo)
 MOVBEQ  AX, net/netip·a16+16(SP)
 MOVBEQ  BX, net/netip·a16+24(SP)

; addr = a16, 16 bytes at once through the vector register X0
 MOVUPS  net/netip·a16+16(SP), X0
 MOVUPS  X0, net/netip·addr(SP)

; CX = netip.z6noz
 MOVQ    net/netip·z6noz(SB), CX
; Unpack: output.addr.hi = byteorder.BEUint64(addr[:8])
;         output.addr.lo = byteorder.BEUint64(addr[8:])
 MOVBEQ  net/netip·addr(SP), AX
 MOVBEQ  net/netip·addr+8(SP), BX

end:
 ADDQ    $32, SP
 POPQ    BP
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

The compiler does a decent job on the byte shuffling: the eight byte stores of BEPutUint64() become a single MOVBEQ, which stores a register byte-swapped. The eight byte loads of BEUint64() become a single MOVBEQ the other way round.6 Three groups of instructions remain: a pack, a copy, and an unpack.

Hacking the Go compiler

The Go compiler has several phases:

Parsing
The compiler tokenizes and parses the source code. It builds a syntax tree for each source file.
Type checking
The compiler maps each identifier to the object it denotes, folds constants, and infers the type of every expression.
IR construction
The compiler converts the syntax tree and its types into its own intermediate representation (IR). This process, called “noding,” goes through a serialization format named unified IR.
Middle end
The compiler performs several optimization passes on the IR, such as devirtualization, function call inlining, and escape analysis.
Walk
This phase runs two steps: order of evaluation decomposes complex statements into simpler ones, and desugaring transforms higher-level Go constructs, like switch or channels, into more primitive instructions or calls to the runtime.
Generic SSA
The compiler converts the IR into Static Single Assignment (SSA) form, a lower-level intermediate representation suited for machine-independent optimizations and rewrite rules.
Machine code generation
The compiler rewrites the SSA form into machine-specific variants, allocates registers, and applies more optimization passes. At the end, the assembler turns the generated instructions into machine code.

The hammer

My first idea is to replace occurrences of netip.AddrFrom16(ip.As16()) with netip.Addr{addr: ip.addr, z: netip.z6noz} as early as possible, during the “noding” process. Before that, the type checking phase prevents us from accessing unexported struct fields.

Go 1.27 introduced a convenient debug option to dump the IR of a function at interesting points during compilation:

$ GOTOOLCHAIN=go1.27.1 GOAMD64=v3 go build -a -gcflags="-d=astdump=AddrTo6Safe" .
Writing text ast output for AddrTo6Safe to AddrTo6Safe.ast
Writing html ast output for AddrTo6Safe to AddrTo6Safe.html
Writing html syntax output for AddrTo6Safe to AddrTo6Safe.syntax.html

In the HTML file, the first column shows the IR as it comes out of noding:

DCLFUNC addrto6.AddrTo6Safe ABI:ABIInternal FUNC-func(netip.Addr) netip.Addr
DCLFUNC-Dcl
. NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. NAME-addrto6.~r0 Class:PPARAMOUT Offset:0 OnStack netip.Addr
DCLFUNC-body
. IF # ipv6_safe.go:11:2
. IF-Cond
. . CALLFUNC bool
. . CALLFUNC-Fun
. . . METHEXPR addrto6.Is4 FUNC-func(netip.Addr) bool
. . . . TYPE netip.Addr Class:PEXTERN Offset:0 type netip.Addr
. . CALLFUNC-Args
. . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. IF-Body
. . AS # ipv6_safe.go:12:6
. . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. . . CALLFUNC netip.Addr
. . . CALLFUNC-Fun
. . . . NAME-netip.AddrFrom16 Class:PFUNC Offset:0 Used FUNC-func([16]byte) netip.Addr
. . . CALLFUNC-Args
. . . . CALLFUNC ARRAY-[16]byte
. . . . CALLFUNC-Fun
. . . . . METHEXPR addrto6.As16 FUNC-func(netip.Addr) [16]byte
. . . . . . TYPE netip.Addr Class:PEXTERN Offset:0 type netip.Addr
. . . . CALLFUNC-Args
. . . . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. RETURN # ipv6_safe.go:14:2
. RETURN-Results
. . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr

In the body of the if statement, we spot the calls to the method netip.Addr.As16() and to the function netip.AddrFrom16(). Our goal is to patch them with a struct literal:

IF-Body
. AS # ipv6_safe.go:12:6
. . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. . STRUCTLIT netip.Addr
. . STRUCTLIT-List
. . . STRUCTKEY netip.addr
. . . . DOT netip.addr netip.uint128
. . . . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. . . STRUCTKEY netip.z
. . . . NAME-netip.z6noz Class:PEXTERN Offset:0 unique.Handle[net/netip.addrDetail]

In noder’s reader.go, the expr() method builds the IR tree for an expression. At the end of the exprCall case, we add a call to a rewriteAddrFrom16As16() function. It takes the current node and returns the struct literal on success, or nil if the rewrite is not possible. First, we check that we have the expected pattern: a call to the netip.AddrFrom16() function with a call to the netip.Addr.As16() method as its only argument:

func rewriteAddrFrom16As16(n ir.Node) ir.Node {
    call, ok := n.(*ir.CallExpr)
    if !ok || call.Op() != ir.OCALLFUNC ||
        len(call.Args) != 1 || len(call.Init()) != 0 ||
        !isNetipFunc(call.Fun, "AddrFrom16") {
        return nil
    }
    inner, ok := call.Args[0].(*ir.CallExpr)
    if !ok || inner.Op() != ir.OCALLFUNC ||
        len(inner.Args) != 1 || len(inner.Init()) != 0 ||
        !isNetipFunc(inner.Fun, "Addr.As16") {
        return nil
    }
    x := inner.Args[0]
    // [...]
}

Then, we fetch netip.z6noz:

z6noz, err := lookupVar(ir.StaticCalleeName(call.Fun).Sym().Pkg, "z6noz")
if err != nil {
    return nil
}

And we build the struct literal:

typ := call.Type()
pos := call.Pos()
var list []ir.Node
for i, f := range typ.Fields() {
    var value ir.Node
    switch f.Sym.Name {
    case "addr":
        value = typecheck.DotField(pos, x, i)
    case "z":
        value = z6noz
    default:
        return nil
    }
    list = append(list, ir.NewStructKeyExpr(pos, f, value))
}
lit := ir.NewCompLitExpr(pos, ir.OSTRUCTLIT, typ, list)
lit.SetTypecheck(1)
return lit

Have a look at the complete patch.7 We can test it with the following commands:

$ cd src
$ ./make.bash
Building Go cmd/dist using /usr/lib/go-1.27. (go1.27.1 linux/amd64)
Building Go toolchain1 and bootstrap cmd/go (go_bootstrap) using /usr/lib/go-1.27.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 and commands using go_bootstrap and Go toolchain2.
Checking command staleness for linux/amd64.
---
Installed Go for linux/amd64 in /home/bernat/code/free/go
Installed commands in /home/bernat/code/free/go/bin
*** You need to add /home/bernat/code/free/go/bin to your PATH.
$ export PATH=$PWD/../bin:$PATH
$ go version
go version go1.28-devel_9834516e20 Sat Sep 12 08:23:11 2026 -0700 linux/amd64
$ go test net/netip/...
ok      net/netip   0.224s
$ cd ../../go-netip-addrto6
$ go test .
ok      github.com/vincentbernat/go-netip-addrto6   0.062s

The generated code for the helper is now the shortest possible version!

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ    net/netip·z4(SB), CX     ; check "z" if this is an IPv4 address
 JNE     end                      ; if not, stop here
 MOVQ    net/netip·z6noz(SB), CX  ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

Go maintainers are unlikely to accept this change. It relies on the internal structure of net/netip.Addr. It’s an ugly hack in the noder, whose job is to faithfully translate the type-checked AST into the IR. And it’s harder to maintain than adding a To6() method.

The screwdriver

The right place for such an optimization is the generic SSA phase. One of the last machine-independent passes is memcombine. With the appropriate debug flag, the compiler dumps the SSA form after this pass:8

$ GOTOOLCHAIN=go1.26.8 GOAMD64=v3 \
> go build -a -gcflags='-d=ssa/memcombine/dump=AddrTo6Safe' .
$ head -5 AddrTo6Safe_01__memcombine.dump
AddrTo6Safe func(netip.Addr) netip.Addr
  b2:
    (?) v1 = InitMem <mem>
    (?) v2 = SP <uintptr>
    (?) v3 = SB <uintptr>

The result of the memcombine pass follows the same structure as the assembly code for AddrTo6Safe() we looked at earlier: two stores, one move, and two loads we would like to optimize away.

; […]
  v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
  v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
  v466 = ArgIntReg <*netip.addrDetail> {ip+16} [2]  ; input.z
; […]
  v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
  v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
  v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
  v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
  v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
  v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)

  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16

  v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
  v416 = Load <uint64> v285 v286                    ; addr[:8]
  v299 = Bswap64 <uint64> v416                      ; output.addr.hi
  v174 = Load <uint64> v415 v286                    ; addr[8:]
  v39  = Bswap64 <uint64> v174                      ; output.addr.lo
; […]

Each line features a value identifier (v442), an operation with its type (Bswap64 <uint64>), and its arguments (v490).9 Values are the basic building blocks of SSA and are defined exactly once. Square brackets enclose integer parameters ([8]) and curly braces contain auxiliary arguments ({netip.addr}). Operations writing to memory produce a new memory state. Every memory operation takes the current state as its last argument, which keeps them in order.

On paper

Let’s focus on output.addr.lo, aka v39:

  v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
  v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
  v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
  v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
  v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
  v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
  v174 = Load <uint64> v415 v286                    ; addr[8:]
  v39  = Bswap64 <uint64> v174                      ; output.addr.lo

To simplify this code, we could apply three rewriting rules:

  1. The first one adds a shortcut when loading through a move: (Load (OffPtr [o] p) (Move p src mem)) => (Load (OffPtr [o] src) mem). This matches v174 with its arguments v415 and v286 and creates a new value v600:

      v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
      v600 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v174 = Load <uint64> v600 v282                    ; a16[8:]
      v39  = Bswap64 <uint64> v174                      ; output.addr.lo
    
    
  2. The second one simplifies a load following a store: (Load p (Store p x _)) => x. The load is forwarded: the stored value replaces it and no memory access remains. It matches v174. It notices that v600 and v173 are the same address and replaces v174 with a copy of v442:

      v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
      v600 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v174 = Copy <uint64> v442                         ; bswap(input.addr.lo)
      v39  = Bswap64 <uint64> v174                      ; output.addr.lo
    
    
  3. The last step cancels the two byte swaps: (Bswap64 (Bswap64 x)) => x. v39 becomes a copy of v490:

      v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
      v600 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v174 = Copy <uint64> v442                         ; bswap(input.addr.lo)
      v39  = Copy <uint64> v490                         ; output.addr.lo = input.addr.lo
    
    

If we ignore the values not needed to compute v39, only this SSA form remains:

  v490 = ArgIntReg <uint64> {ip+8} [1]  ; input.addr.lo
  v39  = Copy <uint64> v490             ; output.addr.lo = input.addr.lo

Let’s switch to output.addr.hi, aka v299:

  v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
  v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
  v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
  v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
  v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
  v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
  v416 = Load <uint64> v285 v286                    ; addr[:8]
  v299 = Bswap64 <uint64> v416                      ; output.addr.hi

To optimize it away, we also apply three rewriting rules:

  1. The first one also adds a shortcut when loading through a move, but without an offset: (Load p (Move p src mem)) => (Load src mem). This rewrites v416 to use arguments from v286:

      v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
      v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v416 = Load <uint64> v22 v282                     ; a16[:8]
      v299 = Bswap64 <uint64> v416                      ; output.addr.hi
    
    
  2. The second rule forwards a value stored one step earlier, skipping over a store to another address: (Load p (Store q _ (Store p x _))) => x. This matches v416: x is v542, p is v22 (&a16), q is v173 (&a16[8]), and p and q do not overlap for uint64.

      v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
      v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v416 = Copy <uint64> v542                         ; bswap(input.addr.hi)
      v299 = Bswap64 <uint64> v416                      ; output.addr.hi
    
    
  3. The third rule cancels two byte swaps: (Bswap64 (Bswap64 x)) => x. v299 becomes a copy of v502:

      v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
      v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v416 = Copy <uint64> v542                         ; bswap(input.addr.hi)
      v299 = Copy <uint64> v502                         ; output.addr.hi = input.addr.hi
    
    

If we remove the values not used to compute v299, we get this SSA form:

  v502 = ArgIntReg <uint64> {ip+0} [0] ; input.addr.hi
  v299 = Copy <uint64> v502            ; output.addr.hi = input.addr.hi

In practice

Most of these rules already exist in generic.rules. They use conditions to validate their context: ssa.IsSamePtr() for the same address, ssa.Disjoint() for addresses that do not overlap. The rule forwarding a stored value to a load already exists with three variants looking through several other stores. Here are the two we need:

(Load <t1> p1 (Store {t2} p2 x _))
    && ssa.IsSamePtr(p1, p2)
    && copyCompatibleType(t1, x.Type)
    && t1.Size() == t2.Size()
    => x
(Load <t1> p1 (Store {t2} p2 _ (Store {t3} p3 x _)))
    && ssa.IsSamePtr(p1, p3)
    && copyCompatibleType(t1, x.Type)
    && t1.Size() == t3.Size()
    && ssa.Disjoint(p3, t3, p2, t2)
    => x

Go 1.27 added the rule loading through a move with CL 748200 to fix issue #77720:

(Load <t1> op1:(OffPtr [o1] p1) move:(Move [n] p2 src mem))
    && o1 >= 0 && o1+t1.Size() <= n && ssa.IsSamePtr(p1, p2)
    && !ssa.IsVolatile(src)
    => @move.Block (Load <t1> (OffPtr <op1.Type> [o1] src) mem)

It lacks a variant without an offset:

(Load <t1> p1 move:(Move [n] p2 src mem))
    && p1.Op != ssaop.OpOffPtr
    && t1.Size() <= n && ssa.IsSamePtr(p1, p2)
    && !ssa.IsVolatile(src)
    => @move.Block (Load <t1> (OffPtr <p1.Type> [0] src) mem)

There is no generic rule to cancel two byte swaps, but the AMD64 lowering pass includes this rule:

(BSWAP(Q|L) (BSWAP(Q|L) p)) => p

After switching to Go’s development branch and adding the missing rule, the generated assembly code is worse than with Go 1.26.8, even though our additional rule slightly improves the situation at the end:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
; Push the stack (16 bytes):
;    0(SP) a16 [16]byte
 PUSHQ   BP
 MOVQ    SP, BP
 SUBQ    $16, SP

 CMPQ    net/netip·z4(SB), CX       ; check "z" if this is an IPv4 address
 JNE     end                        ; if not, stop here

; The four forwarded bytes: the low half of input.addr.lo is taken apart and
; put back together in registers
 MOVQ    BX, DX                     ; DX = input.addr.lo
 SHRQ    $24, BX                    ; BX = input.addr.lo >> 24
 MOVQ    DX, SI                     ; SI = input.addr.lo
 SHRQ    $16, DX                    ; DX = input.addr.lo >> 16
 MOVQ    SI, DI                     ; DI = input.addr.lo, kept for the pack
 SHRQ    $8, SI                     ; SI = input.addr.lo >> 8
 MOVBLZX DIB, R8                    ; R8 = byte(input.addr.lo)
 MOVBLZX SIB, SI                    ; SI = byte(input.addr.lo >> 8)
 SHLQ    $8, SI
 ORQ     R8, SI                     ; SI = two low bytes of input.addr.lo
 MOVBLZX DL, DX                     ; DX = byte(input.addr.lo >> 16)
 SHLQ    $16, DX
 ORQ     SI, DX
 MOVBLZX BL, BX                     ; BX = byte(input.addr.lo >> 24)
 SHLQ    $24, BX
 ORQ     DX, BX                     ; BX = input.addr.lo & 0xffffffff

; Pack: byteorder.BEPutUint64(a16[:8], input.addr.hi)
;       byteorder.BEPutUint64(a16[8:], input.addr.lo)
 MOVBEQ  AX, net/netip·a16(SP)
 MOVBEQ  DI, net/netip·a16+8(SP)

; The four other bytes of input.addr.lo, read one by one from a16
 MOVBLZX net/netip·a16+11(SP), DX   ; a16[11]
 SHLQ    $32, DX
 ORQ     DX, BX
 MOVBLZX net/netip·a16+10(SP), DX   ; a16[10]
 SHLQ    $40, DX
 ORQ     DX, BX
 MOVBLZX net/netip·a16+9(SP), DX    ; a16[9]
 SHLQ    $48, DX
 ORQ     DX, BX
 MOVBLZX net/netip·a16+8(SP), DX    ; a16[8]
 SHLQ    $56, DX

; output.z = netip.z6noz
 MOVQ    net/netip·z6noz(SB), CX
; output.addr.hi = byteorder.BEUint64(a16[:8])
 MOVBEQ  net/netip·a16(SP), AX
; output.addr.lo assembled from the previous steps
 ORQ     DX, BX

end:
 LEAVEQ
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

The rule loading through a move, added in Go 1.27, introduced this regression.

Out of order

Let’s not give up now! In reality, the rewriting rules run before memcombine, notably in the late opt pass. At this point, the inlined versions of BEPutUint64() and BEUint64() still expand to sixteen byte stores and sixteen byte loads, matching their source code:

func BEUint64(b []byte) uint64 {
    _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
    return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
        uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}

Let’s follow two bytes of output.addr.lo: addr[15] and addr[11]. Here is a simplified SSA form before late opt:

  v273 = Trunc64to8 <byte> v490                     ; byte(input.addr.lo)
  v226 = Trunc64to8 <byte> v225                     ; byte(input.addr.lo >> 32)
; […]
  v235 = Store <mem> {byte} v233 v226 v223          ; a16[11] = byte(lo >> 32)
  v247 = Store <mem> {byte} v245 v238 v235          ; a16[12] = …
  v259 = Store <mem> {byte} v257 v250 v247          ; a16[13] = …
  v271 = Store <mem> {byte} v269 v262 v259          ; a16[14] = …
  v282 = Store <mem> {byte} v280 v273 v271          ; a16[15] = byte(lo)
  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
; […]
  v433 = OffPtr <*byte> [15] v285                   ; &addr[15]
  v435 = Load <byte> v433 v286                      ; addr[15]
  v479 = OffPtr <*byte> [11] v285                   ; &addr[11]
  v481 = Load <byte> v479 v286                      ; addr[11]

The first rule loads through the move: (Load (OffPtr [o] p) (Move p src mem)) => (Load (OffPtr [o] src) mem). It matches both loads, which now read a16 with the memory state before the copy:

  v600 = OffPtr <*byte> [15] v22 ; &a16[15]
  v435 = Load <byte> v600 v282   ; a16[15]
  v601 = OffPtr <*byte> [11] v22 ; &a16[11]
  v481 = Load <byte> v601 v282   ; a16[11]

The second rule shortcuts a load following a store: (Load p (Store p x _)) => x. It matches v435, as v282 stores a16[15]. It does not match v481: v235 stores a16[11] four stores earlier in the chain, while the variants of this rule look through three stores at most.

  v435 = Copy <byte> v273        ; byte(input.addr.lo)
  v601 = OffPtr <*byte> [11] v22 ; &a16[11]
  v481 = Load <byte> v601 v282   ; a16[11]

The same happens to the other bytes: the rule forwards the four bytes stored last, a16[12] to a16[15]. The twelve other loads now read a16 instead of addr.

BEUint64() becomes a chain of Or64, each one adding a byte shifted into place. memcombine is a pass written in Go, not a set of rewrite rules. It starts from the last Or64 of the chain and collects up to eight terms. If each term is a byte load, extended to 64 bits and shifted, and if the eight loads read consecutive addresses from the same pointer with the same memory state, it replaces the whole chain with a single 64-bit load and a byte swap. Otherwise, it tries again with four, then two terms, and from each intermediate Or64. Here is the loop checking each term in a simplified version of combineLoads():

for i := int64(0); i < n; i++ {
    v := a[i]
    shift := int64(0)
    if v.Op == shiftOp {
        v, shift = peelShift(v)
    }
    if v.Op != extOp {
        return false
    }
    load := v.Args[0]
    if load.Op != ssaop.OpLoad {
        return false
    }
    if load.Args[1] != mem {
        return false
    }
    p, off := splitPtr(load.Args[0])
    if p != base {
        return false
    }
    r[i] = LoadRecord{load: load, offset: off, shift: shift}
}

For output.addr.hi, the eight loads read a16 with the same memory state v282:

  v13  = Load <byte> v22 v282               ; a16[0]
  v530 = Load <byte> v14 v282               ; a16[1]
  v488 = Load <byte> v504 v282              ; a16[2]
  v405 = Load <byte> v537 v282              ; a16[3]
  v385 = Load <byte> v397 v282              ; a16[4]
  v361 = Load <byte> v373 v282              ; a16[5]
  v196 = Load <byte> v63 v282               ; a16[6]
  v432 = Load <byte> v315 v282              ; a16[7]
  v319 = ZeroExt8to64 <uint64> v432         ; uint64(a16[7])
  v329 = ZeroExt8to64 <uint64> v196         ; uint64(a16[6])
  v330 = Lsh64x64 <uint64> [true] v329 v138 ; uint64(a16[6]) << 8
  v331 = Or64 <uint64> v319 v330            ; a16[7] | a16[6] << 8
; […] same for a16[5] to a16[1]
  v401 = ZeroExt8to64 <uint64> v13          ; uint64(a16[0])
  v402 = Lsh64x64 <uint64> [true] v401 v55  ; uint64(a16[0]) << 56
  v403 = Or64 <uint64> v402 v391            ; | a16[0] << 56 = output.addr.hi

memcombine merges them into one load and a swap:

  v286 = Load <uint64> v22 v282   ; a16[:8]
  v285 = Bswap64 <uint64> v286    ; output.addr.hi

For output.addr.lo, here is the chain memcombine sees after late opt:

  v436 = ZeroExt8to64 <uint64> v273 ; addr[15], forwarded
  v448 = Or64 <uint64> v436 v447    ; | addr[14] << 8, forwarded
  v460 = Or64 <uint64> v459 v448    ; | addr[13] << 16, forwarded
  v472 = Or64 <uint64> v471 v460    ; | addr[12] << 24, forwarded
  v484 = Or64 <uint64> v483 v472    ; | a16[11] << 32, loaded
  v496 = Or64 <uint64> v495 v484    ; | a16[10] << 40, loaded
  v508 = Or64 <uint64> v507 v496    ; | a16[9] << 48, loaded
  v520 = Or64 <uint64> v519 v508    ; | a16[8] << 56, loaded

From v520, four of the eight terms are forwarded bytes, not loads from memory, and memcombine can’t combine them. It doesn’t merge the four remaining loads either, as they sit on top of the forwarded bytes.

Back in order

In summary, the rewriting rules run too early to be effective. A quick workaround exists: run an earlier round of memcombine before late opt. After this change, the generated code for the helper is back to the shortest possible version:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ    net/netip·z4(SB), CX     ; check "z" if this is an IPv4 address
 JNE     end                      ; if not, stop here
 MOVQ    net/netip·z6noz(SB), CX  ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

And the benchmark confirms it! ✌️

goos: linux
goarch: amd64
pkg: github.com/vincentbernat/go-netip-addrto6
cpu: AMD Ryzen 5 5600X 6-Core Processor
                   │   Go 1.26.8    │             Our branch              │
                   │     sec/op     │    sec/op     vs base               │
AddrTo6/safe          6.5470n ±  0%   0.8944n ± 4%  -86.34% (p=0.002 n=6)
AddrTo6/unsafe        0.9071n ±  3%   0.8682n ± 1%   -4.28% (p=0.002 n=6)
AddrTo6/builtin       0.8871n ±  2%   0.8785n ± 1%        ~ (p=0.310 n=6)

Next steps

I think Go maintainers would reject this change because of the additional memcombine pass. Instead, I plan to publish this blog post and bring up the subject again as a follow-up to issue #54365. Either the sheer complexity and the Go 1.27 regression convince the maintainers that adding a To6() method is simpler and more efficient, or they advise me on how to move forward. Either way, digging into this subject taught me a lot about the Go compiler! ⚙️

Update (2026-10)

I opened issue #81994 to propose Addr.To6(). Give it a 👍 if you want it in Go!


  1. IPv4-mapped IPv6 addresses let you handle only IPv6 addresses in your code and keep conversions at a few well-defined boundaries. I use them in Akvorado. ↩

  2. This is not strictly equivalent. The zero value becomes :: while it would make more sense to leave it untouched. ↩

  3. To avoid catastrophic bugs when netip.Addr’s layout changes, you must add tests to detect it. This is more dangerous if you put this code in a package. Either ask users to run the tests themselves, or detect the change at run time and panic. Hiding this optimization behind a build tag would make users aware of this potential trap. ↩

  4. I produced the assembly code with GOTOOLCHAIN=go1.26.8 GOAMD64=v3 ./go-asm '\.asm' from the companion repository, then edited it a bit to keep this article from turning into an endless rabbit hole. Due to an unfortunate sequence of events, we switch between versions: Go 1.27 introduced a regression that blurs the point of this article. ↩

  5. The function is short enough for the compiler to inline it, so the assembly code may vary depending on the surrounding code. ↩

  6. MOVBEQ requires GOAMD64=v3, matching the x86-64-v3 microarchitecture from 2013. Otherwise, the compiler translates BEPutUint64() to BSWAPQ+MOVQ and BEUint64() to MOVQ+BSWAPQ. ↩

  7. When the call is used as a statement, a struct literal alone is invalid. The patch then turns it into _ = netip.Addr{…}. ↩

  8. The compiler can also write an HTML file with the result of each pass:

    $ GOSSAFUNC=AddrTo6Safe \
    > GOTOOLCHAIN=go1.26.8 \
    > GOAMD64=v3 go build -a .
    dumped SSA for AddrTo6Safe,1 to ./ssa.html
    
    

    ↩

  9. Source line numbers in parentheses follow value identifiers, but I removed them from the examples. The SSA form also features branches, but we don’t need them to understand our optimizations. ↩

15:35

Vincent Bernat: Hacking the Go compiler to efficiently map IPv4 to IPv6 [Planet Debian]

netip.Addr features an Unmap() method returning the unwrapped IPv4 contained in an IPv4-mapped IPv6 address: from ::ffff:203.0.113.10 or ::ffff:cb00:710a, it returns 203.0.113.10.1 There is no Map() or To6() method for the reverse direction. Such a method is trivial to implement, but Go maintainers have rejected it on the grounds that users should write netip.AddrFrom16(ip.As16()) and let the compiler optimize it.2 Today, this pattern is eight times slower than a native method. How can we teach the compiler to optimize this sequence?

The alternatives

Let’s explore three ways to implement the map semantics for netip.Addr. My favorite is to add it to the Go standard library. Go maintainers prefer a small external helper chaining netip.AddrFrom16() and netip.Addr.As16(), hoping the compiler eventually optimizes it. The unsafe package opens a third path, with the same performance as the first solution.

Modifying the Go standard library

Internally, netip.Addr stores any IP address as a 128-bit value with an extra field z to encode the family and the zone:

type Addr struct {
    addr uint128
    z unique.Handle[addrDetail]
}

type addrDetail struct {
    isV6   bool   // IPv4 is false, IPv6 is true.
    zoneV6 string // != "" only if IsV6 is true.
}

var (
    z0    unique.Handle[addrDetail]
    z4    = unique.Make(addrDetail{})
    z6noz = unique.Make(addrDetail{isV6: true})
)

AddrFrom4() encodes an IPv4 address as an IPv4-mapped IPv6 address and sets z to the unique value z4:

// AddrFrom4 returns the address of the IPv4 address given by the bytes in addr.
func AddrFrom4(addr [4]byte) Addr {
    return Addr{
        addr: uint128{
            0,
            0xffff00000000 |
                uint64(addr[0])<<24 | uint64(addr[1])<<16 |
                uint64(addr[2])<<8 | uint64(addr[3])},
        z: z4,
    }
}

Unmap() turns an IPv4-mapped IPv6 address into an IPv4 address by setting the z field to z4:

func (ip Addr) Unmap() Addr {
    if ip.Is4In6() {
        ip.z = z4
    }
    return ip
}

Implementing the reverse direction inside the Go standard library is trivial: we set the z field to z6noz if the address is IPv4.

// To6 maps an IPv4 address to an IPv4-mapped IPv6 address. It returns an
// IPv6 address unmodified.
func (ip Addr) To6() Addr {
    if ip.Is4() {
        ip.z = z6noz
    }
    return ip
}

As a helper

We can’t access the z field from outside the net/netip package. Instead, we build a small helper around the netip.AddrFrom16(ip.As16()) pattern:

// AddrTo6 maps an IPv4 address to an IPv4-mapped IPv6 address. It returns an
// IPv6 address unmodified.
func AddrTo6(ip netip.Addr) netip.Addr {
    if ip.Is4() {
        ip = netip.AddrFrom16(ip.As16())
    }
    return ip
}

As an unsafe function

Another solution uses the unsafe package to alter the Addr struct through a proxy with the same memory layout:3

// addrProxy has the same memory layout as netip.Addr.
type addrProxy struct {
    addr [2]uint64      // netip.uint128
    z    unsafe.Pointer // unique.Handle[netip.addrDetail]
}

var (
    anyIPv6    = netip.IPv6Unspecified()
    netipZ6noz = (*addrProxy)(unsafe.Pointer(&anyIPv6)).z
)

// AddrTo6 maps an IPv4 address to an IPv4-mapped IPv6 address. It returns an
// IPv6 address unmodified.
func AddrTo6(ip netip.Addr) netip.Addr {
    if !ip.Is4() {
        return ip
    }
    (*addrProxy)(unsafe.Pointer(&ip)).z = netipZ6noz
    return ip
}

Benchmarks

On my computer, with Go 1.27.1, the standard library solution costs 0.88 ns per operation, while the solution favored by Go maintainers costs 7.14 ns. The unsafe solution matches the performance of the first one.

goos: linux
goarch: amd64
pkg: github.com/vincentbernat/go-netip-addrto6
cpu: AMD Ryzen 5 5600X 6-Core Processor
                   │     sec/op     │
AddrTo6/safe            7.137n ± 0%
AddrTo6/unsafe         0.8682n ± 2%
AddrTo6/builtin        0.8775n ± 2%

Assembly code

Let’s check the assembly code the compiler generates for each solution.4 The one built into the standard library looks like this:5

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ  net/netip·z4(SB), CX     ; check "z" if this is an IPv4 address
 JNE   end                      ; if not, stop here
 MOVQ  net/netip·z6noz(SB), CX  ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

Go’s assembly language is not a direct representation of the underlying machine language: it operates on a semi-abstract instruction set derived from Plan 9’s assembler. It has four pseudo-registers: FP (frame pointer for function arguments), PC (program counter), SB (static base pointer for global symbols), and SP (stack pointer). It also has architecture-specific registers like AX, CX, DX, BX, SI, DI, and R8 to R15. Instructions storing data use their last argument as the destination. Instructions can carry an explicit size suffix: MOVB moves a byte, MOVW 16 bits, MOVL 32 bits, and MOVQ 64 bits. In the example above, the first instruction compares the 64-bit value z4 with the CX register.

The unsafe solution looks almost the same:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ  net/netip·z4(SB), CX  ; check "z" if this is an IPv4 address
 JNE   end                   ; if not, stop here
 MOVQ  netipZ6noz(SB), CX    ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

The helper solution has far more instructions. To understand why, let’s look at the code for As16() and AddrFrom16(). They are short enough for the compiler to inline them.

func (ip Addr) As16() (a16 [16]byte) {
    byteorder.BEPutUint64(a16[:8], ip.addr.hi)
    byteorder.BEPutUint64(a16[8:], ip.addr.lo)
    return a16
}

func AddrFrom16(addr [16]byte) Addr {
    return Addr{
        addr: uint128{
            byteorder.BEUint64(addr[:8]),
            byteorder.BEUint64(addr[8:]),
        },
        z: z6noz,
    }
}

We can already guess the pattern to optimize: the code packs the IP address into an array, copies it, then unpacks it. If we inline the Go code by hand, we get:

func AddrTo6(input netip.Addr) netip.Addr {
    if !input.Is4() {
        return input
    }

    var a16 [16]byte
    byteorder.BEPutUint64(a16[:8], input.addr.hi)
    byteorder.BEPutUint64(a16[8:], input.addr.lo)

    addr := a16

    var output netip.Addr
    output.addr.hi = byteorder.BEUint64(addr[:8])
    output.addr.lo = byteorder.BEUint64(addr[8:])
    output.z = netip.z6noz
    return output
}

As humans, we can mentally derive the optimized form:

func AddrTo6(input netip.Addr) netip.Addr {
    if !input.Is4() {
        return input
    }
    var output netip.Addr
    output.addr.hi = input.addr.hi
    output.addr.lo = input.addr.lo
    output.z = netip.z6noz
    return output
}

Unfortunately, as of Go 1.26.8, the compiler is not smart enough to do the same:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
; Push the stack (32 bytes):
;    0(SP) addr netip.uint128
;   16(SP) a16 [16]byte
 PUSHQ   BP
 MOVQ    SP, BP
 SUBQ    $32, SP

 CMPQ    net/netip·z4(SB), CX  ; check "z" if this is an IPv4 address
 JNE     end                   ; if not, stop here

; Pack: byteorder.BEPutUint64(a16[:8], input.addr.hi)
;       byteorder.BEPutUint64(a16[8:], input.addr.lo)
 MOVBEQ  AX, net/netip·a16+16(SP)
 MOVBEQ  BX, net/netip·a16+24(SP)

; addr = a16, 16 bytes at once through the vector register X0
 MOVUPS  net/netip·a16+16(SP), X0
 MOVUPS  X0, net/netip·addr(SP)

; CX = netip.z6noz
 MOVQ    net/netip·z6noz(SB), CX
; Unpack: output.addr.hi = byteorder.BEUint64(addr[:8])
;         output.addr.lo = byteorder.BEUint64(addr[8:])
 MOVBEQ  net/netip·addr(SP), AX
 MOVBEQ  net/netip·addr+8(SP), BX

end:
 ADDQ    $32, SP
 POPQ    BP
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

The compiler does a decent job on the byte shuffling: the eight byte stores of BEPutUint64() become a single MOVBEQ, which stores a register byte-swapped. The eight byte loads of BEUint64() become a single MOVBEQ the other way round.6 Three groups of instructions remain: a pack, a copy, and an unpack.

Hacking the Go compiler

The Go compiler has several phases:

Parsing
The compiler tokenizes and parses the source code. It builds a syntax tree for each source file.
Type checking
The compiler maps each identifier to the object it denotes, folds constants, and infers the type of every expression.
IR construction
The compiler converts the syntax tree and its types into its own intermediate representation (IR). This process, called “noding,” goes through a serialization format named unified IR.
Middle end
The compiler performs several optimization passes on the IR, such as devirtualization, function call inlining, and escape analysis.
Walk
This phase runs two steps: order of evaluation decomposes complex statements into simpler ones, and desugaring transforms higher-level Go constructs, like switch or channels, into more primitive instructions or calls to the runtime.
Generic SSA
The compiler converts the IR into Static Single Assignment (SSA) form, a lower-level intermediate representation suited for machine-independent optimizations and rewrite rules.
Machine code generation
The compiler rewrites the SSA form into machine-specific variants, allocates registers, and applies more optimization passes. At the end, the assembler turns the generated instructions into machine code.

The hammer

My first idea is to replace occurrences of netip.AddrFrom16(ip.As16()) with netip.Addr{addr: ip.addr, z: netip.z6noz} as early as possible, during the “noding” process. Before that, the type checking phase prevents us from accessing unexported struct fields.

Go 1.27 introduced a convenient debug option to dump the IR of a function at interesting points during compilation:

$ GOTOOLCHAIN=go1.27.1 GOAMD64=v3 go build -a -gcflags="-d=astdump=AddrTo6Safe" .
Writing text ast output for AddrTo6Safe to AddrTo6Safe.ast
Writing html ast output for AddrTo6Safe to AddrTo6Safe.html
Writing html syntax output for AddrTo6Safe to AddrTo6Safe.syntax.html

In the HTML file, the first column shows the IR as it comes out of noding:

DCLFUNC addrto6.AddrTo6Safe ABI:ABIInternal FUNC-func(netip.Addr) netip.Addr
DCLFUNC-Dcl
. NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. NAME-addrto6.~r0 Class:PPARAMOUT Offset:0 OnStack netip.Addr
DCLFUNC-body
. IF # ipv6_safe.go:11:2
. IF-Cond
. . CALLFUNC bool
. . CALLFUNC-Fun
. . . METHEXPR addrto6.Is4 FUNC-func(netip.Addr) bool
. . . . TYPE netip.Addr Class:PEXTERN Offset:0 type netip.Addr
. . CALLFUNC-Args
. . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. IF-Body
. . AS # ipv6_safe.go:12:6
. . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. . . CALLFUNC netip.Addr
. . . CALLFUNC-Fun
. . . . NAME-netip.AddrFrom16 Class:PFUNC Offset:0 Used FUNC-func([16]byte) netip.Addr
. . . CALLFUNC-Args
. . . . CALLFUNC ARRAY-[16]byte
. . . . CALLFUNC-Fun
. . . . . METHEXPR addrto6.As16 FUNC-func(netip.Addr) [16]byte
. . . . . . TYPE netip.Addr Class:PEXTERN Offset:0 type netip.Addr
. . . . CALLFUNC-Args
. . . . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. RETURN # ipv6_safe.go:14:2
. RETURN-Results
. . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr

In the body of the if statement, we spot the calls to the method netip.Addr.As16() and to the function netip.AddrFrom16(). Our goal is to patch them with a struct literal:

IF-Body
. AS # ipv6_safe.go:12:6
. . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. . STRUCTLIT netip.Addr
. . STRUCTLIT-List
. . . STRUCTKEY netip.addr
. . . . DOT netip.addr netip.uint128
. . . . . NAME-addrto6.ip Class:PPARAM Offset:0 OnStack Used netip.Addr
. . . STRUCTKEY netip.z
. . . . NAME-netip.z6noz Class:PEXTERN Offset:0 unique.Handle[net/netip.addrDetail]

In noder’s reader.go, the expr() method builds the IR tree for an expression. At the end of the exprCall case, we add a call to a rewriteAddrFrom16As16() function. It takes the current node and returns the struct literal on success, or nil if the rewrite is not possible. First, we check that we have the expected pattern: a call to the netip.AddrFrom16() function with a call to the netip.Addr.As16() method as its only argument:

func rewriteAddrFrom16As16(n ir.Node) ir.Node {
    call, ok := n.(*ir.CallExpr)
    if !ok || call.Op() != ir.OCALLFUNC ||
        len(call.Args) != 1 || len(call.Init()) != 0 ||
        !isNetipFunc(call.Fun, "AddrFrom16") {
        return nil
    }
    inner, ok := call.Args[0].(*ir.CallExpr)
    if !ok || inner.Op() != ir.OCALLFUNC ||
        len(inner.Args) != 1 || len(inner.Init()) != 0 ||
        !isNetipFunc(inner.Fun, "Addr.As16") {
        return nil
    }
    x := inner.Args[0]
    // [...]
}

Then, we fetch netip.z6noz:

z6noz, err := lookupVar(ir.StaticCalleeName(call.Fun).Sym().Pkg, "z6noz")
if err != nil {
    return nil
}

And we build the struct literal:

typ := call.Type()
pos := call.Pos()
var list []ir.Node
for i, f := range typ.Fields() {
    var value ir.Node
    switch f.Sym.Name {
    case "addr":
        value = typecheck.DotField(pos, x, i)
    case "z":
        value = z6noz
    default:
        return nil
    }
    list = append(list, ir.NewStructKeyExpr(pos, f, value))
}
lit := ir.NewCompLitExpr(pos, ir.OSTRUCTLIT, typ, list)
lit.SetTypecheck(1)
return lit

Have a look at the complete patch.7 We can test it with the following commands:

$ cd src
$ ./make.bash
Building Go cmd/dist using /usr/lib/go-1.27. (go1.27.1 linux/amd64)
Building Go toolchain1 and bootstrap cmd/go (go_bootstrap) using /usr/lib/go-1.27.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 and commands using go_bootstrap and Go toolchain2.
Checking command staleness for linux/amd64.
---
Installed Go for linux/amd64 in /home/bernat/code/free/go
Installed commands in /home/bernat/code/free/go/bin
*** You need to add /home/bernat/code/free/go/bin to your PATH.
$ export PATH=$PWD/../bin:$PATH
$ go version
go version go1.28-devel_9834516e20 Sat Sep 12 08:23:11 2026 -0700 linux/amd64
$ go test net/netip/...
ok      net/netip   0.224s
$ cd ../../go-netip-addrto6
$ go test .
ok      github.com/vincentbernat/go-netip-addrto6   0.062s

The generated code for the helper is now the shortest possible version!

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ    net/netip·z4(SB), CX     ; check "z" if this is an IPv4 address
 JNE     end                      ; if not, stop here
 MOVQ    net/netip·z6noz(SB), CX  ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

Go maintainers are unlikely to accept this change. It relies on the internal structure of net/netip.Addr. It’s an ugly hack in the noder, whose job is to faithfully translate the type-checked AST into the IR. And it’s harder to maintain than adding a To6() method.

The screwdriver

The right place for such an optimization is the generic SSA phase. One of the last machine-independent passes is memcombine. With the appropriate debug flag, the compiler dumps the SSA form after this pass:8

$ GOTOOLCHAIN=go1.26.8 GOAMD64=v3 \
> go build -a -gcflags='-d=ssa/memcombine/dump=AddrTo6Safe' .
$ head -5 AddrTo6Safe_01__memcombine.dump
AddrTo6Safe func(netip.Addr) netip.Addr
  b2:
    (?) v1 = InitMem <mem>
    (?) v2 = SP <uintptr>
    (?) v3 = SB <uintptr>

The result of the memcombine pass follows the same structure as the assembly code for AddrTo6Safe() we looked at earlier: two stores, one move, and two loads we would like to optimize away.

; […]
  v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
  v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
  v466 = ArgIntReg <*netip.addrDetail> {ip+16} [2]  ; input.z
; […]
  v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
  v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
  v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
  v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
  v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
  v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)

  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16

  v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
  v416 = Load <uint64> v285 v286                    ; addr[:8]
  v299 = Bswap64 <uint64> v416                      ; output.addr.hi
  v174 = Load <uint64> v415 v286                    ; addr[8:]
  v39  = Bswap64 <uint64> v174                      ; output.addr.lo
; […]

Each line features a value identifier (v442), an operation with its type (Bswap64 <uint64>), and its arguments (v490).9 Values are the basic building blocks of SSA and are defined exactly once. Square brackets enclose integer parameters ([8]) and curly braces contain auxiliary arguments ({netip.addr}). Operations writing to memory produce a new memory state. Every memory operation takes the current state as its last argument, which keeps them in order.

On paper

Let’s focus on output.addr.lo, aka v39:

  v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
  v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
  v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
  v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
  v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
  v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
  v174 = Load <uint64> v415 v286                    ; addr[8:]
  v39  = Bswap64 <uint64> v174                      ; output.addr.lo

To simplify this code, we could apply three rewriting rules:

  1. The first one adds a shortcut when loading through a move: (Load (OffPtr [o] p) (Move p src mem)) => (Load (OffPtr [o] src) mem). This matches v174 with its arguments v415 and v286 and creates a new value v600:

      v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
      v600 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v174 = Load <uint64> v600 v282                    ; a16[8:]
      v39  = Bswap64 <uint64> v174                      ; output.addr.lo
    
    
  2. The second one simplifies a load following a store: (Load p (Store p x _)) => x. The load is forwarded: the stored value replaces it and no memory access remains. It matches v174. It notices that v600 and v173 are the same address and replaces v174 with a copy of v442:

      v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
      v600 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v174 = Copy <uint64> v442                         ; bswap(input.addr.lo)
      v39  = Bswap64 <uint64> v174                      ; output.addr.lo
    
    
  3. The last step cancels the two byte swaps: (Bswap64 (Bswap64 x)) => x. v39 becomes a copy of v490:

      v490 = ArgIntReg <uint64> {ip+8} [1]              ; input.addr.lo
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v442 = Bswap64 <uint64> v490                      ; bswap(input.addr.lo)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v415 = OffPtr <*byte> [8] v285                    ; &addr[8]
      v600 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v174 = Copy <uint64> v442                         ; bswap(input.addr.lo)
      v39  = Copy <uint64> v490                         ; output.addr.lo = input.addr.lo
    
    

If we ignore the values not needed to compute v39, only this SSA form remains:

  v490 = ArgIntReg <uint64> {ip+8} [1]  ; input.addr.lo
  v39  = Copy <uint64> v490             ; output.addr.lo = input.addr.lo

Let’s switch to output.addr.hi, aka v299:

  v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
  v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
  v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
  v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
  v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
  v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
  v416 = Load <uint64> v285 v286                    ; addr[:8]
  v299 = Bswap64 <uint64> v416                      ; output.addr.hi

To optimize it away, we also apply three rewriting rules:

  1. The first one also adds a shortcut when loading through a move, but without an offset: (Load p (Move p src mem)) => (Load src mem). This rewrites v416 to use arguments from v286:

      v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
      v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v416 = Load <uint64> v22 v282                     ; a16[:8]
      v299 = Bswap64 <uint64> v416                      ; output.addr.hi
    
    
  2. The second rule forwards a value stored one step earlier, skipping over a store to another address: (Load p (Store q _ (Store p x _))) => x. This matches v416: x is v542, p is v22 (&a16), q is v173 (&a16[8]), and p and q do not overlap for uint64.

      v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
      v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v416 = Copy <uint64> v542                         ; bswap(input.addr.hi)
      v299 = Bswap64 <uint64> v416                      ; output.addr.hi
    
    
  3. The third rule cancels two byte swaps: (Bswap64 (Bswap64 x)) => x. v299 becomes a copy of v502:

      v502 = ArgIntReg <uint64> {ip+0} [0]              ; input.addr.hi
      v22 = LocalAddr <*[16]byte> {netip.a16} v2 v1     ; &a16
      v173 = OffPtr <*byte> [8] v22                     ; &a16[8]
      v542 = Bswap64 <uint64> v502                      ; bswap(input.addr.hi)
      v161 = Store <mem> {uint64} v22 v542 v23          ; a16[:8] = bswap(hi)
      v282 = Store <mem> {uint64} v173 v442 v161        ; a16[8:] = bswap(lo)
      v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
      v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
      v416 = Copy <uint64> v542                         ; bswap(input.addr.hi)
      v299 = Copy <uint64> v502                         ; output.addr.hi = input.addr.hi
    
    

If we remove the values not used to compute v299, we get this SSA form:

  v502 = ArgIntReg <uint64> {ip+0} [0] ; input.addr.hi
  v299 = Copy <uint64> v502            ; output.addr.hi = input.addr.hi

In practice

Most of these rules already exist in generic.rules. They use conditions to validate their context: ssa.IsSamePtr() for the same address, ssa.Disjoint() for addresses that do not overlap. The rule forwarding a stored value to a load already exists with three variants looking through several other stores. Here are the two we need:

(Load <t1> p1 (Store {t2} p2 x _))
    && ssa.IsSamePtr(p1, p2)
    && copyCompatibleType(t1, x.Type)
    && t1.Size() == t2.Size()
    => x
(Load <t1> p1 (Store {t2} p2 _ (Store {t3} p3 x _)))
    && ssa.IsSamePtr(p1, p3)
    && copyCompatibleType(t1, x.Type)
    && t1.Size() == t3.Size()
    && ssa.Disjoint(p3, t3, p2, t2)
    => x

Go 1.27 added the rule loading through a move with CL 748200 to fix issue #77720:

(Load <t1> op1:(OffPtr [o1] p1) move:(Move [n] p2 src mem))
    && o1 >= 0 && o1+t1.Size() <= n && ssa.IsSamePtr(p1, p2)
    && !ssa.IsVolatile(src)
    => @move.Block (Load <t1> (OffPtr <op1.Type> [o1] src) mem)

It lacks a variant without an offset:

(Load <t1> p1 move:(Move [n] p2 src mem))
    && p1.Op != ssaop.OpOffPtr
    && t1.Size() <= n && ssa.IsSamePtr(p1, p2)
    && !ssa.IsVolatile(src)
    => @move.Block (Load <t1> (OffPtr <p1.Type> [0] src) mem)

There is no generic rule to cancel two byte swaps, but the AMD64 lowering pass includes this rule:

(BSWAP(Q|L) (BSWAP(Q|L) p)) => p

After switching to Go’s development branch and adding the missing rule, the generated assembly code is worse than with Go 1.26.8, even though our additional rule slightly improves the situation at the end:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
; Push the stack (16 bytes):
;    0(SP) a16 [16]byte
 PUSHQ   BP
 MOVQ    SP, BP
 SUBQ    $16, SP

 CMPQ    net/netip·z4(SB), CX       ; check "z" if this is an IPv4 address
 JNE     end                        ; if not, stop here

; The four forwarded bytes: the low half of input.addr.lo is taken apart and
; put back together in registers
 MOVQ    BX, DX                     ; DX = input.addr.lo
 SHRQ    $24, BX                    ; BX = input.addr.lo >> 24
 MOVQ    DX, SI                     ; SI = input.addr.lo
 SHRQ    $16, DX                    ; DX = input.addr.lo >> 16
 MOVQ    SI, DI                     ; DI = input.addr.lo, kept for the pack
 SHRQ    $8, SI                     ; SI = input.addr.lo >> 8
 MOVBLZX DIB, R8                    ; R8 = byte(input.addr.lo)
 MOVBLZX SIB, SI                    ; SI = byte(input.addr.lo >> 8)
 SHLQ    $8, SI
 ORQ     R8, SI                     ; SI = two low bytes of input.addr.lo
 MOVBLZX DL, DX                     ; DX = byte(input.addr.lo >> 16)
 SHLQ    $16, DX
 ORQ     SI, DX
 MOVBLZX BL, BX                     ; BX = byte(input.addr.lo >> 24)
 SHLQ    $24, BX
 ORQ     DX, BX                     ; BX = input.addr.lo & 0xffffffff

; Pack: byteorder.BEPutUint64(a16[:8], input.addr.hi)
;       byteorder.BEPutUint64(a16[8:], input.addr.lo)
 MOVBEQ  AX, net/netip·a16(SP)
 MOVBEQ  DI, net/netip·a16+8(SP)

; The four other bytes of input.addr.lo, read one by one from a16
 MOVBLZX net/netip·a16+11(SP), DX   ; a16[11]
 SHLQ    $32, DX
 ORQ     DX, BX
 MOVBLZX net/netip·a16+10(SP), DX   ; a16[10]
 SHLQ    $40, DX
 ORQ     DX, BX
 MOVBLZX net/netip·a16+9(SP), DX    ; a16[9]
 SHLQ    $48, DX
 ORQ     DX, BX
 MOVBLZX net/netip·a16+8(SP), DX    ; a16[8]
 SHLQ    $56, DX

; output.z = netip.z6noz
 MOVQ    net/netip·z6noz(SB), CX
; output.addr.hi = byteorder.BEUint64(a16[:8])
 MOVBEQ  net/netip·a16(SP), AX
; output.addr.lo assembled from the previous steps
 ORQ     DX, BX

end:
 LEAVEQ
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

The rule loading through a move, added in Go 1.27, introduced this regression.

Out of order

Let’s not give up now! In reality, the rewriting rules run before memcombine, notably in the late opt pass. At this point, the inlined versions of BEPutUint64() and BEUint64() still expand to sixteen byte stores and sixteen byte loads, matching their source code:

func BEUint64(b []byte) uint64 {
    _ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
    return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
        uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}

Let’s follow two bytes of output.addr.lo: addr[15] and addr[11]. Here is a simplified SSA form before late opt:

  v273 = Trunc64to8 <byte> v490                     ; byte(input.addr.lo)
  v226 = Trunc64to8 <byte> v225                     ; byte(input.addr.lo >> 32)
; […]
  v235 = Store <mem> {byte} v233 v226 v223          ; a16[11] = byte(lo >> 32)
  v247 = Store <mem> {byte} v245 v238 v235          ; a16[12] = …
  v259 = Store <mem> {byte} v257 v250 v247          ; a16[13] = …
  v271 = Store <mem> {byte} v269 v262 v259          ; a16[14] = …
  v282 = Store <mem> {byte} v280 v273 v271          ; a16[15] = byte(lo)
  v285 = LocalAddr <*[16]byte> {netip.addr} v2 v282 ; &addr
  v286 = Move <mem> {[16]byte} [16] v285 v22 v282   ; addr = a16
; […]
  v433 = OffPtr <*byte> [15] v285                   ; &addr[15]
  v435 = Load <byte> v433 v286                      ; addr[15]
  v479 = OffPtr <*byte> [11] v285                   ; &addr[11]
  v481 = Load <byte> v479 v286                      ; addr[11]

The first rule loads through the move: (Load (OffPtr [o] p) (Move p src mem)) => (Load (OffPtr [o] src) mem). It matches both loads, which now read a16 with the memory state before the copy:

  v600 = OffPtr <*byte> [15] v22 ; &a16[15]
  v435 = Load <byte> v600 v282   ; a16[15]
  v601 = OffPtr <*byte> [11] v22 ; &a16[11]
  v481 = Load <byte> v601 v282   ; a16[11]

The second rule shortcuts a load following a store: (Load p (Store p x _)) => x. It matches v435, as v282 stores a16[15]. It does not match v481: v235 stores a16[11] four stores earlier in the chain, while the variants of this rule look through three stores at most.

  v435 = Copy <byte> v273        ; byte(input.addr.lo)
  v601 = OffPtr <*byte> [11] v22 ; &a16[11]
  v481 = Load <byte> v601 v282   ; a16[11]

The same happens to the other bytes: the rule forwards the four bytes stored last, a16[12] to a16[15]. The twelve other loads now read a16 instead of addr.

BEUint64() becomes a chain of Or64, each one adding a byte shifted into place. memcombine is a pass written in Go, not a set of rewrite rules. It starts from the last Or64 of the chain and collects up to eight terms. If each term is a byte load, extended to 64 bits and shifted, and if the eight loads read consecutive addresses from the same pointer with the same memory state, it replaces the whole chain with a single 64-bit load and a byte swap. Otherwise, it tries again with four, then two terms, and from each intermediate Or64. Here is the loop checking each term in a simplified version of combineLoads():

for i := int64(0); i < n; i++ {
    v := a[i]
    shift := int64(0)
    if v.Op == shiftOp {
        v, shift = peelShift(v)
    }
    if v.Op != extOp {
        return false
    }
    load := v.Args[0]
    if load.Op != ssaop.OpLoad {
        return false
    }
    if load.Args[1] != mem {
        return false
    }
    p, off := splitPtr(load.Args[0])
    if p != base {
        return false
    }
    r[i] = LoadRecord{load: load, offset: off, shift: shift}
}

For output.addr.hi, the eight loads read a16 with the same memory state v282:

  v13  = Load <byte> v22 v282               ; a16[0]
  v530 = Load <byte> v14 v282               ; a16[1]
  v488 = Load <byte> v504 v282              ; a16[2]
  v405 = Load <byte> v537 v282              ; a16[3]
  v385 = Load <byte> v397 v282              ; a16[4]
  v361 = Load <byte> v373 v282              ; a16[5]
  v196 = Load <byte> v63 v282               ; a16[6]
  v432 = Load <byte> v315 v282              ; a16[7]
  v319 = ZeroExt8to64 <uint64> v432         ; uint64(a16[7])
  v329 = ZeroExt8to64 <uint64> v196         ; uint64(a16[6])
  v330 = Lsh64x64 <uint64> [true] v329 v138 ; uint64(a16[6]) << 8
  v331 = Or64 <uint64> v319 v330            ; a16[7] | a16[6] << 8
; […] same for a16[5] to a16[1]
  v401 = ZeroExt8to64 <uint64> v13          ; uint64(a16[0])
  v402 = Lsh64x64 <uint64> [true] v401 v55  ; uint64(a16[0]) << 56
  v403 = Or64 <uint64> v402 v391            ; | a16[0] << 56 = output.addr.hi

memcombine merges them into one load and a swap:

  v286 = Load <uint64> v22 v282   ; a16[:8]
  v285 = Bswap64 <uint64> v286    ; output.addr.hi

For output.addr.lo, here is the chain memcombine sees after late opt:

  v436 = ZeroExt8to64 <uint64> v273 ; addr[15], forwarded
  v448 = Or64 <uint64> v436 v447    ; | addr[14] << 8, forwarded
  v460 = Or64 <uint64> v459 v448    ; | addr[13] << 16, forwarded
  v472 = Or64 <uint64> v471 v460    ; | addr[12] << 24, forwarded
  v484 = Or64 <uint64> v483 v472    ; | a16[11] << 32, loaded
  v496 = Or64 <uint64> v495 v484    ; | a16[10] << 40, loaded
  v508 = Or64 <uint64> v507 v496    ; | a16[9] << 48, loaded
  v520 = Or64 <uint64> v519 v508    ; | a16[8] << 56, loaded

From v520, four of the eight terms are forwarded bytes, not loads from memory, and memcombine can’t combine them. It doesn’t merge the four remaining loads either, as they sit on top of the forwarded bytes.

Back in order

In summary, the rewriting rules run too early to be effective. A quick workaround exists: run an earlier round of memcombine before late opt. After this change, the generated code for the helper is back to the shortest possible version:

// AX = input.addr.hi, BX = input.addr.lo, CX = input.z
 CMPQ    net/netip·z4(SB), CX     ; check "z" if this is an IPv4 address
 JNE     end                      ; if not, stop here
 MOVQ    net/netip·z6noz(SB), CX  ; CX = netip.z6noz
end:
 RET
// return value = Addr{hi: AX, lo: BX, z: CX}

And the benchmark confirms it! ✌️

goos: linux
goarch: amd64
pkg: github.com/vincentbernat/go-netip-addrto6
cpu: AMD Ryzen 5 5600X 6-Core Processor
                   │   Go 1.26.8    │             Our branch              │
                   │     sec/op     │    sec/op     vs base               │
AddrTo6/safe          6.5470n ±  0%   0.8944n ± 4%  -86.34% (p=0.002 n=6)
AddrTo6/unsafe        0.9071n ±  3%   0.8682n ± 1%   -4.28% (p=0.002 n=6)
AddrTo6/builtin       0.8871n ±  2%   0.8785n ± 1%        ~ (p=0.310 n=6)

Next steps

I think Go maintainers would reject this change because of the additional memcombine pass. Instead, I plan to publish this blog post and bring up the subject again as a follow-up to issue #54365. Either the sheer complexity and the Go 1.27 regression convince the maintainers that adding a To6() method is simpler and more efficient, or they advise me on how to move forward. Either way, digging into this subject taught me a lot about the Go compiler! ⚙️


  1. IPv4-mapped IPv6 addresses let you handle only IPv6 addresses in your code and keep conversions at a few well-defined boundaries. I use them in Akvorado. ↩

  2. This is not strictly equivalent. The zero value becomes :: while it would make more sense to leave it untouched. ↩

  3. To avoid catastrophic bugs when netip.Addr’s layout changes, you must add tests to detect it. This is more dangerous if you put this code in a package. Either ask users to run the tests themselves, or detect the change at run time and panic. Hiding this optimization behind a build tag would make users aware of this potential trap. ↩

  4. I produced the assembly code with GOTOOLCHAIN=go1.26.8 GOAMD64=v3 ./go-asm '\.asm' from the companion repository, then edited it a bit to keep this article from turning into an endless rabbit hole. Due to an unfortunate sequence of events, we switch between versions: Go 1.27 introduced a regression that blurs the point of this article. ↩

  5. The function is short enough for the compiler to inline it, so the assembly code may vary depending on the surrounding code. ↩

  6. MOVBEQ requires GOAMD64=v3, matching the x86-64-v3 microarchitecture from 2013. Otherwise, the compiler translates BEPutUint64() to BSWAPQ+MOVQ and BEUint64() to MOVQ+BSWAPQ. ↩

  7. When the call is used as a statement, a struct literal alone is invalid. The patch then turns it into _ = netip.Addr{…}. ↩

  8. The compiler can also write an HTML file with the result of each pass:

    $ GOSSAFUNC=AddrTo6Safe \
    > GOTOOLCHAIN=go1.26.8 \
    > GOAMD64=v3 go build -a .
    dumped SSA for AddrTo6Safe,1 to ./ssa.html
    
    

    ↩

  9. Source line numbers in parentheses follow value identifiers, but I removed them from the examples. The SSA form also features branches, but we don’t need them to understand our optimizations. ↩

15:28

Link [Scripting News]

BTW now that I have Frontier back and am using it for most of my work, I'm having a lot more fun writing my blog. Can you tell??

The climate will kill you [Scripting News]

Great scene from The West Wing.

CJ and Josh, on the street outside the White House.

The president was hiding serious disease, and it’s about to come out. Back in those days such a revelation meant the president has no chance of being re-elected.

Anyway, the kid has an idea and starts explaining, and as he does CJ laughs. The kid asks what’s so funny.

She tells him about a scene in Butch Cassidy and the Sundance Kid starring Paul Newman as Butch and Robert Redford as the kid.

They’re cornered, on a cliff, preparing to jump into a river far below. Redford says he can’t swim. Newman says it doesn’t matter, the fall is going to kill you.

Okay so why mention this now?

I hear there’s a monster heat wave in California. Over 90F every day for two weeks. That’s unusual for October IIRC.

We’re all fretting about AI killing us.

Imagine CJ laughing. Haha she says, so you’re worried about AI are ya? It’s the climate that’s going to kill you.

Josh would correct her. “Us. It’s going to kill us.”

CJ tells him to go away she needs to be an adult right now.

Twitter as a blog platform [Scripting News]

Twitter with its relaxed character limit is getting pretty close to being a blogging platform. I wrote the first draft of this post on my iPad, that's how useful it is.

Imagine if a real blog platform created a super simple way to write posts, no need for Gutenberg, just Markdown and that would be optional. Default would be a normal wizzy text editor, bold, italic, using a standard user interface.

Add web links and a reply structure, open source, nice API and developer program to shine the light on the cool editors independent devs were creating, and all of a sudden a bunch of new blog platforms emerge, and get this — they all work perfectly with each other because of the common foundation and API. The web of social media. It would be exciting and would pressure the other social media apps to peer with this truly open system.

It would change the freaking world I tell you. And it would be so wonderful with AI. No need for the cheesy chat UI. A real revolution. Undo all the craptastic silos that strangled the web?

It would be pretty damn easy imho, fwiw, ymmv, mmlm.

PS: MMLM is an acronym for My Mother Loves Me. Anticipates flameage. Okay you don't like me, but my mother loves me.

14:28

Upcoming Public Appearances, 2026/27 [Charlie's Diary]

Upcoming fixtures

My public visibility took a nose-dive in 2020 (can't think why!) and didn't really begin to pick up again until 2024, due to multiple reasons, principally COVID19 and some chronic health issues. The health issues are now under control and I'm planning on coming out of my shell a bit more over the next year.

My definite fixtures over the next year or so include 5 SF/F conventions in various countries, none of them the United States but one of them on the same continent:

4-8 November 2026, Festival 42, Barcelona, The sixth 42 Barcelona Festival of Fantastic Genres (theme: "Facing Our Fears.")

I'm one of the guests at Festival 42 (although I'm not listed on their web page yet).

My Festival 42 events so far:

1pm, Saturday 7th: Author interview (with Cristina Jurado)

7pm, Saturday 7th: Panel discussion with international authors (details TBA)

12am, Sunday 8th, Panel discussion (details TBA)

11 November 2026, Author Event, Gigamesh Bookshop, author event (reading/interview, TBA). Gigamesh is Barcelona's SF/F specialist bookshop and they've kindly arranged an event for me: I'll add details here when I've got them.

12-15 November 2026, Sui Generis Festival, Madrid (theme: "Neo-Fiction: New Narratives for a Contested Present.") Sui Generis Festival: "We use the term neo-fiction to describe narrative practices that emerge from a world shaped by the ecological crisis, digital culture, artificial intelligence, and social fragmentation."

My Sui Generis events so far:

6pm, Friday 13th November: Panel discussion, "The New Indifferent God: Cosmic Horror and Artificial Intelligence"

7:30pm, Saturday 14th November: Panel discussion, "Masters of the Impossible"

26-29 March 2027, Unconfined, the UK Eastercon, Glasgow.

(C'mon, you didn't expect me to miss this one, did you?)

2-6 September 2027 Montreal Worldcon

Montréal 2027 is a North American worldcon outside the United States! So of course I'm planning on going.

18-21 November 2027, Polcon 2027

Polcon 2027, The Polish national SF convention will be held in Katowice in 2027, and they've invited me as a special guest. Details to be added here when I've got them.

10:07

Steinar H. Gunderson: Decompilation patterns part 1: Anti-CSE [Planet Debian]

There's a lot of¹ interest in decompilation these days; taking some old piece of software (usually a game) and try to reconstruct C that does the same thing. (This is often for modding purposes, but also often for understanding, or just as a personal challenge.) An often sought-after property is matching; that the decompiled C produces 100% the same bytes as the original after compilation. (This means you'll need to find the original compiler and build flags, of course.) Typically, one starts with the disassembled code and runs m2c on it, which then spits out some C that is fairly close to the original, but rarely produces a match without further cleanup. (It is usually much closer to a match than what Ghidra or Hex-Rays would output; Ghidra is made for ease of understanding, not matching, so it tends to simplify a lot.)

Cleaning up the code for matching has two main advantages:

  1. It usually makes the source easier to understand, and
  2. Matching is a very strong argument (though not infallible) that the decompilation is actually correct.

Like with pretty much any other field these days, there is a lot of interest in giving this task to some LLM, which means there's now a lot of decompilations around that match (fitting #2 perfectly) and completely fail #1. :-) Anyway, at some point, I learned that there is almost no public documentation on the cleaning-up-for-matching part; it's all folklore and people rediscovering the same patterns. So I thought I'd do something about that².

My general goal is to write up one decompilation pattern a day (focusing on what I know best, namely GCC on MIPS), with some example. Most of them will be very simple; some are much less obvious. I'll keep going until I run out of ideas. None are formally named, so I'll just invent a name. So here goes part one:

  temp_v0_5 = g_state.players[1].unk2;
  if ((temp_v0_5 != 7) && (temp_v0_5 != 0)) {

What happened here is usually that the compiler has done common subexpression elimination; someone wrote the same thing twice and then the compiler decided to just calculate it once and put it into a temporary variable. (Or the programmer did, in which case you would be wise to leave it alone! Getting into the original programmer's head is part of the challenge.) This will often affect register allocation and/or stack layout, so to get a better match, you could try doing CSE in reverse:

  if (g_state.players[1].unk2 != 7 && g_state.players[1].unk2 != 0) {

Nothing is mechanical; it may help or it may not. (It may also appear to hurt at first and then help later, or vice versa.) But anti-CSE is generally very common and you'll want to have it in your arsenal.

¹ Well, all is relative.

² Reluctantly, with the understanding that the LLMs might also learn from it.

Getting critical [Seth's Blog]

I’d heard a lot about critical theory but didn’t really understand what was being talked about. The Frankfurt School of the 1930s wasn’t really a school. It was philosophers and academics who were arguing with each other about how the world actually works — and about why people keep accepting arrangements that hurt them. And like many things called ‘theories’, it’s easy to be confused about what’s actually being said.

Their core insight: most of what we call “normal” was designed by someone, for a reason, and that reason might not be your reason.

Traditional science tries to describe the world as it is. Critical theory asks: “who benefits from the world being described that way?”

When an economist says “the market determines wages,” that sounds neutral. But it isn’t. It’s a choice about what to measure and what to ignore. It’s a story. And stories can be told differently.

Pioneers like Horkheimer, Adorno and Marcuse (followed and reworked by Habermas)— looked at modern industrial society and noticed that Reason, with a capital R, which was supposed to liberate us, had turned into a tool of control. The Enlightenment promised freedom. What we got instead was efficiency.

They called it instrumental reason: the habit of thinking about how to do things without ever asking whether we should.


Reification: when the made-up becomes the inevitable

The word that unlocks the concept is reification. Human social relations and systems appear as natural, thing-like and outside our control because we end up treating them as if they were rocks. Permanent. Objective. Not up for discussion.

“That’s just how business works.”
“That’s just how things are.”
“You can’t fight human nature.”

Each of those sentences is a reification. Someone built a thing. Now they’re insisting it can’t be unbuilt.

Critical theory’s job is to thaw what has been frozen. To remind us that the rules we’re following were written by someone, and can be rewritten.

Systems are often invisible, and they hide behind normal. When in doubt, consider the system that we’ve assumed is the only way.


The Frankfurt School wasn’t just academic. They were trying to explain how otherwise reasonable people went along with war, consumerism and conformity.

Their answer: the system shapes what feels obvious. What feels like common sense. What feels like “just being realistic.”

Which means the most important thing a marketer, a leader, a maker can do is ask: am I solving a real problem, or am I reinforcing a convenient story about what people are supposed to want?


When someone points at a system and describes the interests of those who supported it, two things can happen.

The first: curiosity. Who built it? Why? What would a different version look like?

The second: discomfort.

If you’ve organized your identity around the map being true — if your status, your choices, your self-image all depend on the current arrangement being natural and inevitable — then someone questioning the map isn’t being philosophical.

They’re threatening you.

The fact that your status feels threatened is a self-reflective tell. Of course it feels threatened. That’s what the system wants you to feel.

“The market rewards hard work” isn’t just an economic claim for a lot of people. It’s a story about why they deserve what they have. Poke that story and you’re not critiquing capitalism. You’re telling them their success was partly luck, partly structural, partly the result of systems that excluded others.

Most of the fury isn’t intellectual. It’s tribal.

Critical theory became associated with universities and with certain political movements. So defending against it became a marker of identity for the other tribe. You’re not critiquing the argument — you’re signaling which team you’re on.

That’s ironic, right? Because “this belief is really about status and belonging, not truth” is exactly the kind of thing critical theory would say about the people attacking critical theory.

It’s tempting to announce that some questions aren’t allowed.

Those are usually the most important questions.

03:07

Why is there no Windows hot-patching support for other architectures like 32-bit ARM and MIPS? [The Old New Thing]

Windows hot-patching supports x86-32, x86-64, 64-bit ARM, and Itanium, but why not the other architctures like 32-bit ARM, MIPS, Alpha AXP, PowerPC, and SH-3?

These architectures fall into two buckets.

MIPS, Alpha AXP, PowerPC, and SH-3 all predate hot-patching, so they naturally couldn’t conform to rules that didn’t exist yet.

Meanwhile, 32-bit ARM support was introduced¹ to Windows in the Windows 8 era, and at that time, hot-patching had already existed. But there were no provisions for hot-patching 32-bit ARM binaries because hot-patching is a Windows Server feature. Since there was no version of Windows Server for 32-bit ARM, there was no need to support hot-patching on 32-bit ARM binaries.

¹ More accurately, re-introduced, since Windows CE supported 32-bit ARM as well.

The post Why is there no Windows hot-patching support for other architectures like 32-bit ARM and MIPS? appeared first on The Old New Thing.

00:21

RSS tip #2 [Scripting News]

People really liked the RSS tip I posted the other day, so here's another one, also related to the channel-level title element.

Consider the screen shot below, and the Thinking about... blog.

Part of a blogroll with limited info about a great blogger, Timothy Snyder.

I can't tell from the title who's speaking, whereas with the two feeds above it I know it's Brian Lehrer and Kottke, and below, an excellent local site called The Overlook.

Now it's a shame that it doesn't say who's doing the thinking because it's someone who is on a roll right now, historian Timothy Snyder. So the title pretty much has to say his name, if it's going to look good in a list of other bloggers.

BTW, just listened to Snyder on the Scott Galloway podcast, another thinker who I've just started listening to. With the two of them together, you will learn a lot.

So remember the title sometimes is the only handle a reader has to figure out what it is. Choose it carefully and make sure it looks good in a tight space like the one shown below.

If you have questions about how to make your RSS 2.0 feeds work better, please add note below. If I don't know maybe someone else does.

Saturday, 03 October

23:49

Prairieland protesters [Richard Stallman's Political Notes]

In the trial of the Prairieland protesters, the prosecutor withheld crucial physical evidence from the defense lawyers until the trial. This stopped the defense lawyers from presenting expert witnesses to to support the factual point that Benjamin Song's shot was not aimed at anyone, and hit a thug after a ricochet off the floor.

The corrupter encourages his followers to lie and cheat, not just once but persistently.

Effect of elevated screen exposure [Richard Stallman's Political Notes]

A metastudy confirms that more use of screens by children increases the likelihood of various academic and emotional problems.

Hormone-disrupting herbicide [Richard Stallman's Political Notes]

The hormone-disrupting herbicide atrazine is contaminating nearly 25% … of all continental US rivers at levels that can harm wildlife and may threaten human health, a new review of federal records has found.

Those waterways cover over 20% of surface drinking water intakes across the country, and over 675,000 private and commercial wells.

Satellite imagery shows devastation [Richard Stallman's Political Notes]

Satellite photos show the extent of devastation in Gaza, both before and (continuing) after the "cease fire".

Virginia Woolf play [Richard Stallman's Political Notes]

Facebook and Instagram reject advertisements with text that hints at political issues such as civil rights and women's rights.

I have a hunch that their filters are more forgiving for right-wing political issues. Is there any factual information about this?

Planetary boundaries [Richard Stallman's Political Notes]

*New report shows key indicators of Earth’s health are "outside the safe operating space" as climate disaster looms* (and starts).

Meanwhile, suppression of the discussion of global heating and its dangers is heavier than ever. This must be due to the wrecker's intense support for fossil fuel companies.

Zohran Mamdani's sweeping plan [Richard Stallman's Political Notes]

Mayor Mamdani proposes to discourage antisemitism by means of increased security funding for synagogues, an increased focus on folding Jewish history into public school education, and a pledge to highlight important Jewish cultural and religious sites.

It is wrong for public education to promote any religion, or religion in general. But education about religions is legitimate and admirable.

Conservation efforts [Richard Stallman's Political Notes]

Many kinds of insects are used by or beneficial for various species of vertebrates.

Europe’s anti-corruption monitoring body [Richard Stallman's Political Notes]

Magats have withdrawn the US from the Council of Europe’s anti-corruption monitoring body, saying it *no longer serves US interests.*

Why would the corrupter want to support a group whose aim is to reduce corruption?

Hunter Valley Operations [Richard Stallman's Political Notes]

The New South Wales Independent Planning Commission is supposed to judge (among other things) whether the income of a coal mine outweighs the climate and environmental damage it will cause. It has just decided to let an enormous coal mine continue operating by placing short-term gains above long-term survival.

*Australia is facing a crisis of confidence in its democratic institutions. It is a crisis brought on by the betrayal of the people by those institutions – and there is no greater betrayal than this failure to face up to the climate emergency and protect the community from its worst consequences.*

In effect, governments of various parties have allowed planet roaster interests to shape the institutions that are supposed to insist on decarbonization so that they will surrender whenever the task requires disappointing the rich.

23:35

Link [Scripting News]

Once again the app that bridges from scripting.com to daveverse.org went down for a couple of days. Just noticed it, kicked the server to get it running again, and all is good, except a couple of pieces show up today that were really posted on Thursday or Friday. The problem as often is the case goes back to PagePark, the server that runs my Heroku-like server crashed and it doesn't work well from then on, even after it's automatically restarted. Then the thing to do is to reboot the server. It doesn't happen often but when it does it makes the server behave badly. It's up and running again. One day I'll get a few days to fix the problems in PP.

16:42

Link [Scripting News]

2001: "Like a boat, a human being has integrity if he or she is what he or she appears to be. That's why integrity commands us to disclose conflicts, so that what we say, and who we appear to be, are in synch. Change the appearance if necessary."

Link [Scripting News]

If you're psyched about what the polls say about the midterms, remember that Hillary Clinton was the overwhelming favorite to win in 2016, based on polling.

14:07

GNU Health as a solution for Europe digital change - Italy Digi-CHange conf [Planet GNU]

During the days 6-9 of October will take place the Digi-Change conference in Turin, Piedmont region, Italy.

Digi-CHange promotes the digital transformation of medical and nursing education, introducing an inter-professional model that connects educators, clinicians, engineers, and industry innovators. Aligned with the Erasmus+ Capacity Building in Higher Education priority of Digital Transformation, Digi-Change strengthens the capacity of universities in Georgia, Ukraine, and EU partner countries to modernize curricula and foster digital competencies for a sustainable, innovation-ready healthcare workforce.

In the first session, Luis Falcón will deliver a keynote presentation about the social impact of GNU Health and Libre/Free Software is having in the public healthcare system, and how it can be used in the context of Telemedicine to provide meaningful and timely healthcare access to remote areas in Europe. Other topics will be building large federated health networks, privacy, and the human factor in healthcare, medical genetics and precision medicine. Dr. Falcon will address the risks of some emerging technology (eg, LLM) poses at different levels (individual, clinical practice, cultural & scientific community and the environment), and ethical alternatives in eHealth and decision support (DSS)

During this three-day “train-the-trainers” conference, physicians, nurses and other health professionals from Ukraine, Georgia and Latvia will join colleagues from Italy, France, Austria and other European countries to exchange knowledge and experiences.

In the topic of Telemedicine, Alberto Lazzero will address National records and cross-border cooperation among France and Italy. Prof. Dr. Alberto Lazzero is the Head of Telemedicine, Hospital of Briançon (France). He is an specialist in telemedicine for patients in mountain and cross‑border areas.

The conference will be in Turin, onsite, and will count with experts on diagnostic imaging, cancer and bioinformatics, among others. There will be visits to the Istituto Neurologico “Carlo Besta” (Milan) and INOC (National Oncologic Institute Candiolo).

Looking forward to meet our colleagues from Europe in Italy and share the experiences and impact of Free/Libre software in healthcare and our society.

Original post:

https://my.gnusol ... thumbnail_id=2272

13:21

Pluralistic: Economic probabilities for our grandchildren (03 Oct 2026) [Pluralistic: Daily links from Cory Doctorow]

->->->->->->->->->->->->->->->->->->->->->->->->->->->->-> Top Sources: None -->

Today's links

  • Economic probabilities for our grandchildren: If only data centers would stop hogging all the copper.
  • Hey look at this: Delights to delectate.
  • Object permanence: RIAA v P2P; Infoseek's anti-terror brain-scanner; Copyrighting every phone number; Carving pumpkins with cookie-cutters; Polish women go on strike; Latent labor in material world; Hitler's meth; "Flying Saucers Are Real!"; Hope, not optimism; Broadcast Flag talk; David Suzuki's free research; "Inquisitor's Apprentice"; California's tax ban made the rich MUCH richer; Union organizers caught Wells Fargo; Power poses aren't real; "Ghosts"; Criticizing tech; "Savage Love A-Z."
  • Upcoming appearances: Brighton, Virtual, South Bend, Hudson, Calgary, Winnipeg, Paris, OVancouver, Victoria, Ottawa, Kilkenny, Montreal.
  • Recent appearances: Where I've been.
  • Latest books: You keep readin' em, I'll keep writin' 'em.
  • Upcoming books: Like I said, I'll keep writin' 'em.
  • Colophon: All the rest.



The Earth from space; it is covered in scaffolding that is crawling with little rude mechanicals ganked from 17th century engravings of people operating machines.

Economic probabilities for our grandchildren (permalink)

The post-war "peace dividend" owed its existence to three factors: industrial capacity, political freedom and pent-up demand.

  • Industrial capacity: freed up by the war's end and the collapse of demand for munitions and materiel;
  • Political freedom: the war's orgy of capital destruction of the majority of the wealth (and thus the power) of the world's oligarchs;

  • Demand: Making up for years of neglect and privation during the war required new production of infrastructure and consumption goods.

In Capital in the 21st Century, Thomas Piketty and his students analyzed painstakingly assembled records of 300 years' worth of capital flows, showing that wealth tends to pool in the hands of the already-wealthy. This creates mounting instability, thanks to the misrule of a shrinking class of increasingly powerful hereditary, unaccountable oligarchs whose whims and follies trump the material and political needs of the vast majority:

https://memex.craphound.com/2014/06/24/thomas-pikettys-capital-in-the-21st-century/

That instability eventually reaches a breaking point in which the old order collapses in spectacular fashion, and that collapse destroys vast amounts of capital stock. Since this breaking point arrives as a result of oligarchy, in which nearly all the world's wealth has been hoarded by a tiny number of people, those aristocrats are disproportionately impoverished by the conflagration. If 90% of the wealth is in the hands of 1% of the people, a war or disaster that wipes out most capital will mostly destroy the wealth of the 1%. The poor suffer terribly, but they start with nothing – and end with nothing.

We are clearly steaming into one of these situations. The wealthy squandered two generations preventing the world from taking the climate emergency, and now the inevitable has arrived. California is on track to see a 12 inch sea-level rise in the next month:

https://www.theguardian.com/us-news/2026/sep/30/california-kelvin-wave-sea-level-rise

That's just the overture to the American run of this year's "Super El Nino," a blockbuster whose out-of-town previews have been slaying massive crowds all over the world:

https://www.theguardian.com/world/2026/oct/03/bangkok-thailand-floods-breaking-point-stagnant-lakes

Also right on schedule: the misrule of oligarchs has elevated a con-man to the presidency, who trumps every other president for both corruption and incompetence. From oil to dollars, telecoms infrastructure to the internet itself, Donald Trump is doing everything in his power to end the American empire:

https://pluralistic.net/2026/05/16/technopoly/#trumpismo-is-praxis-question-mark-exclamation-point

A post-American world is on the horizon. What that world will look like is undecided. The US was always a wildly defective trusted third party, but it retained its status as the world's platform for generations thanks to the absence of credible alternatives. Now – as Mark Carney told the Davos crowd last year – the old system has "ruptured":

https://www.weforum.org/stories/forum-institutional/davos-2026-special-address-by-mark-carney-prime-minister-of-canada/

What will replace the dollar?

https://pluralistic.net/2026/02/11/post-dollar-world/#de-dollarization

What will replace oil?

https://pluralistic.net/2025/10/11/cyber-rights-now/#better-late-than-never

What will replace the internet?

https://pluralistic.net/2026/01/01/39c3/#the-new-coalition

No one knows. No one can know, because this is all up for grabs. The future isn't something that happens to us. The future is something that we make. And despite the horrible death toll that's locked in by waves of climate shocks – presided over by the dying, fumbling "drink bleach" guy – it's conceivable that the future we get will be a good one, if not an easy one.

A post-oligarch, post-American world could be a post-austerity world, one where we recognize that the limiting factor on public investment isn't money, but resources: energy, material, labor, expertise:

https://www.youtube.com/watch?v=FATQ0Yf0Fhc

Just as the post-war world led to the "30 glorious years" by mobilizing people and material to rebuild a shattered world and shattered lives, a post-American world might be one in which we find full, meaningful employment for all of us, all of our children, and all of their children:

https://en.wikipedia.org/wiki/Trente_Glorieuses

As seas rise, we're going to have jobs for every person who is willing, building sea defenses and moving whole coastal cities inland. Two generations of neoliberalism has left us with massive shortages of housing stock, crumbling highways, inadequate schools and hospitals. That means we need to build lots more of these, and they can be built with state-of-the-art climate hardening, including geothermal heat, "passive home" designs, modern insulation, and heat-pumps. The rights-of-way for the interstate system would make dandy railbeds for arrow-straight, all electric high-speed rail powered by solar, wind and tides.

All of this has the potential to yield a second peace-dividend prosperity. A post-oligarch world can recover industrial capacity by diverting it away from destructive activity (data centers, luxury housing) to essential functions (climate defenses, decent housing for everyday people). A post-oligarch world can recover democratic excellence by removing the malign influence that morbid wealth exerts on our policy choices: for example, if we don't like the quality of public schools, we can fix them by giving schools more resources, not by letting billionaire dilettantes privatize and starve them in the name of "school choice":

https://pluralistic.net/2026/03/09/autocrats-of-trade-2/#witness-the-firepower-of-this-fully-armed-and-operational-battle-station

Finally, a post-oligarch society can provide us with an even more inclusive version of post-war prosperity: we can use the wages we earn by building a better world to acquire new homes, induction tops, EVs and ebikes, and beautiful heirloom computers that are built to last for generations, upgraded and maintained by their users:

https://www.edn.com/as-moores-law-slows-open-hardware-rises/

This is a world of material abundance and prosperity. Just as the post-war world made millions of people comfortable, educated and happy by putting them to work clearing the rubble and building something better, a post-oligarch, post-American world can offer us all all the hard, rewarding work we want, performing the essential work of care and rebuilding. We'll find that work by helping hundreds of millions of climate refugees, who will find work helping each other. This is all economically valuable, environmentally sustaining work that we have labor, energy and expertise for. It's just that all that labor, energy and expertise is being misallocated by the ultra-wealthy who don't even believe that the majority of us exist, and who want to bet the planet and our species on a bizarre scheme to feed so many words to the world-guessing machines that they wake up and become gods:

https://pluralistic.net/2026/05/13/vibe-governance/#k-hole

Nearly a century ago, John Maynard Keynes published "Economic Possibilities for our Grandchildren," where he extrapolated from rising productivity to predict a fifteen-hour work-week within two generations:

http://www.econ.yale.edu/smith/econ116a/keynes1.pdf

That dream was destroyed by war and greed. Today, we are submerged in an ocean of debt. I'm not talking about the (fictional) "national debt" that is just a measure of all the dollars the government has spent into existence without taxing out of existence:

https://pluralistic.net/2024/10/21/we-can-have-nice-things/#public-funds-not-taxpayer-dollars

I'm talking about fiscal debt: the solar we didn't build, the carbon we emitted instead; the rail we didn't build, the wildly inefficient aviation we substituted; the walkable, livable, transit-oriented cities we didn't build, the car-choked disasters we built instead. Oligarchic opposition to universal healthcare left us sick, with un- or undertreated illnesses and missed opportunities for prevention that left us with overlapping, population-scale health crises. All of this is debt that must be repaid: we can't walk away from it through bankruptcy. We must build the transit, cities, and systems of care – the infrastructure – our species needs to carry on human civilization. We can't keep hitting snooze on this, lest we are finally awoken by seawater lapping at our pillows.

The world we're in today is awful and terrifying. It's awful because of the climate ruptures we're living through. It's terrifying because we didn't just fail to avert this crisis – we did nothing to prepare for it, either.

As Piketty foretold, the misrule of oligarchs has led to an orgy of destruction, and while poor people will get the worst of it, oligarchs will pay the most, because they own nearly everything, which means they own everything that will be destroyed, too. None of that is good, and it would be better if we hadn't gotten into this situation in the first place.

But after a forest fire, the canopy opens; and when it does, the seedlings that were overshadowed for centuries by the old growth can sprout in the ashes and the sun. We can't afford to continue living under oligarchy, and as oligarchs' greed and folly drives the old system beyond its breaking point, we must seize the opportunity to build a better successor.


Hey look at this (permalink)



A shelf of leatherbound history books with a gilt-stamped series title, 'The World's Famous Events.'

Object permanence (permalink)

#25yrago Leak: RIAA's anti-P2P strategy https://web.archive.org/web/20011019060035/https://www.dotcomscoop.com/riaa1003.html

#25yrsago Infoseek founder: my brain-scanner can find all the terrorists https://web.archive.org/web/20011009025805/http://www.theregister.co.uk/content/55/22020.html

#25yrsago Copyrighting every possible phone number as a touch-tone tune https://web.archive.org/web/20011007073546/http://www.theage.com.au/entertainment/2001/10/04/FFX0PGT0CSC.html

#20yrsago What happens to password-locked data when you die? https://web.archive.org/web/20061106235805/http://news.com.com/Taking+passwords+to+the+grave/2100-1025_3-6118314.html

#20yrsago Audio of activist lawyer talk on Broadcast Flag and Chilling Effects https://web.archive.org/web/20061005074245/http://uscpublicdiplomacy.com/index.php/events/events_detail/1859/

#20yrsago David Suzuki: Steal my research – that’s what it’s for! https://web.archive.org/web/20061020144546/http://www.davidsuzuki.org/about_us/Dr_David_Suzuki/Article_Archives/weekly09290601.asp

#20yrsago Interdisciplinary DRM blog from my USC students https://uscpubd510.blogspot.com/search?updated-max=2006-08-24T14:59:00-07:00&amp;max-results=7&amp;start=91&amp;by-date=false

#15yrsag Inquisitor’s Apprentice: tenement sorcerers versus the robber barons in an alternate Gilded Age New York https://memex.craphound.com/2011/10/04/inquisitors-apprentice-tenement-sorcerers-versus-the-robber-barons-in-an-alternate-gilded-age-new-york/

#15yrsago Context: Further Selected Essays on Productivity, Creativity, Parenting, and Politics in the 21st Century https://memex.craphound.com/2011/10/03/context-further-selected-essays-on-productivity-creativity-parenting-and-politics-in-the-21st-century/

#15yrsago Unicode’s “right-to-left” override obfuscates malware’s filenames https://krebsonsecurity.com/2011/09/right-to-left-override-aids-email-attacks/

#15yrsago HOWTO carve a pumpkin by hammering cookie-cutters into it https://web.archive.org/web/20200916004345/https://www.foodnetwork.com/fn-dish/shopping/cookie-cutter-pumpkin-carving

#15yrsago Shell funded warring militias in the Niger Delta https://web.archive.org/web/20111006003453/http://blog.platformlondon.org/2011/10/03/counting-the-cost-corporations-and-human-rights-abuses-in-the-niger-delta/

#10yrsago Sen Mitch McConnell blames Obama for bill that Obama vetoed and McConnell repeatedly voted for https://www.loweringthebar.net/2016/10/congress-blames-veto.html

#10yrsago Polish women go on strike over extreme anti-abortion law https://www.bbc.com/news/world-europe-37540139

#10yrsago Survivors of CIA torture describe homebrew electric chair used at Afghan black site https://www.hrw.org/news/2016/10/03/interview-new-cia-torture-claims

#10yrsago Visualizing the latent emotional and bureaucratic labor in our material world https://xkcd.com/1741/

#10yrsago Meth, Hitler and the Reich: the true, untold story of the Nazis’ dependence on coke, meth and oxy https://www.theguardian.com/books/2016/sep/25/blitzed-norman-ohler-adolf-hitler-nazi-drug-abuse-interview

#10yrsago Flying Saucers are Real! Anthology of the lost saucer-craze https://memex.craphound.com/2016/10/03/flying-saucers-are-real-anthology-of-the-lost-saucer-craze/

#10yrsago The malware that’s pwning the Internet of Things is terrifyingly amateurish https://web.archive.org/web/20161004061621/http://motherboard.vice.com/read/internet-of-things-malware-mirai-ddos

#10yrsago California’s 40-year-old ban on property tax raises has made the rich a lot richer https://web.archive.org/web/20161001034224/https://www.latimes.com/business/hiltzik/la-fi-hiltzik-prop-13–20160929-snap-story.html

#10yrsago The Wells Fargo fraud came to light because of union organizers https://web.archive.org/web/20161005123132/https://prospect.org/article/first-and-foremost-wells-fargo-scandal-about-workers

#10yrsago “Power Poses” are bullshit https://web.archive.org/web/20161007215414/https://www.wbur.org/npr/496093672/power-poses-co-author-i-do-not-believe-the-effects-are-real

#10yrsago Martin Shkreli offers a bailout to ailing 4chan https://arstechnica.com/information-technology/2016/10/4chan-cashflow-problem-martin-shkreli-wants-to-join-board/

#10yrsago Ghosts: Raina Telgemeier’s upbeat tale of death, assimilation and cystic fibrosis https://memex.craphound.com/2016/10/04/ghosts-raina-telgemeiers-upbeat-tale-of-death-assimilation-and-cystic-fibrosis/

#10yrsago Yahoo secretly built a tool to scan all email in realtime for US spies https://www.reuters.com/article/idUSKCN1241YT/

#10yrsago How to: Criticize technology https://www.cjr.org/tow_center_reports/constructive_technology_criticism.php

#10yrsago Johnson & Johnson says people with diabetes don’t need to worry about potentially lethal wireless attacks on insulin pumps https://www.reuters.com/article/us-johnson-johnson-cyber-insulin-pumps-e-idUSKCN12411L/

#5yrsago USPS pilots postal banking https://pluralistic.net/2021/10/04/avoidance-is-evasion/#check-cashing

#5yrsago The Pandora Papers https://pluralistic.net/2021/10/04/avoidance-is-evasion/#transparency

#5yrsago Savage Love A-Z https://pluralistic.net/2021/10/04/avoidance-is-evasion/#ggg

#5yrsago Hope, Not Optimism https://pluralistic.net/2021/10/03/hope-not-optimism/

#1yrago When your ISP pays you https://pluralistic.net/2025/10/03/we-dont-care-we-dont-have-to/#were-the-phone-company

#1yrago Blue Bonds https://pluralistic.net/2025/10/04/fiscal-antifa/#post-trump


Upcoming appearances (permalink)

A photo of me onstage, giving a speech, pounding the podium.
https://www.epl.ca/blogs/post/elbows-up-with-cory-doctorow/



A screenshot of me at my desk, doing a livecast.

Recent appearances (permalink)



A grid of my books with Will Stahle covers..

Latest books (permalink)



A cardboard book box with the Macmillan logo.

Upcoming books (permalink)

  • "The Post-American Internet," a geopolitical sequel of sorts to Enshittification, Farrar, Straus and Giroux, 2027
  • "Unauthorized Bread": a middle-grades graphic novel adapted from my novella about refugees, toasters and DRM, FirstSecond, April 20, 2027

  • "Enshittification, Why Everything Suddenly Got Worse and What to Do About It" (the graphic novel), Firstsecond, 2027

  • "The Memex Method," Farrar, Straus, Giroux, 2027



Colophon (permalink)

Today's top sources:

Currently writing:

  • “Once Is Enemy Action,” a science fiction novel about the origins of modern technofascism. Today's words: 504 (21882 total).

  • "The Post-American Internet," a sequel to "Enshittification," about the better world the rest of us get to have now that Trump has torched America. Fourth draft completed. Submitted to editor.

  • A Little Brother short story about DIY insulin PLANNING


This work – excluding any serialized fiction – is licensed under a Creative Commons Attribution 4.0 license. That means you can use it any way you like, including commercially, provided that you attribute it to me, Cory Doctorow, and include a link to pluralistic.net.

https://creativecommons.org/licenses/by/4.0/

Quotations and images are not included in this license; they are included either under a limitation or exception to copyright, or on the basis of a separate license. Please exercise caution.


How to get Pluralistic:

Blog (no ads, tracking, or data-collection):

Pluralistic.net

Newsletter (no ads, tracking, or data-collection):

https://pluralistic.net/plura-list

Mastodon (no ads, tracking, or data-collection):

https://mamot.fr/@pluralistic

Bluesky (no ads, possible tracking and data-collection):

https://bsky.app/profile/doctorow.pluralistic.net

Medium (no ads, paywalled):

https://doctorow.medium.com/

Tumblr (mass-scale, unrestricted, third-party surveillance and advertising):

https://mostlysignssomeportents.tumblr.com/tagged/pluralistic

"When life gives you SARS, you make sarsaparilla" -Joey "Accordion Guy" DeVilla

READ CAREFULLY: By reading this, you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies ("BOGUS AGREEMENTS") that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.

ISSN: 3066-764X

13:07

The Laws of Useful Automation [Economics from the Top Down]

Your browser does not support the audio tag.

Download: PDF | EPUB | MP3 | WATCH VIDEO

Given our current landscape of AI hype and doom, everyone seems to have an opinion about whether chatbots are ‘good’ or ‘bad’. Fewer people have thought about how chatbots fit into the long-term history of automation. And even fewer folks have reflected on the conceptual requirements that make automation useful. Here are my thoughts on this latter topic.

In my view, automation is useful when it meets two criteria:

  1. The product is more important than the process that creates it.
  2. The product can be validated without auditing the creation process.

If we look at successful forms of machine automation, they tend to meet both criteria. For example, think of a pencil factory that automates the production of pencils. Here, the pencil is the ‘product’, and whatever happens inside the factory is the ‘process’.

Now for the end user, the process of pencil production is largely irrelevant. It doesn’t matter if the pencil is hand crafted, built in an assembly line, or conjured by a Star Trek replicator. As long as the pencil works in the hands of the user, its creation process is unimportant.

That brings me to the second requirement for useful automation: the product must be easily validated. To validate that a pencil works, you don’t need to audit the process that created it. You just give the thing a go. Yup, this pencil works. Nope, that one’s a dud.

Thinking more broadly, the history of automation has been dominated by this sort of process where a machine replaces human labor in the production of some sort of physical commodity. The automation works because (1) the commodity is valued more than the process that creates it, and (2) it’s easy to verify the quality of the commodity without auditing the entire chain of production.

Intellectual automation

Looking to more recent technological progress, it turns out that successful automation need not be physical. Intellectual automation can also be useful. Just look at computers, which owe their name to the desire to automate computation.

Many forms of computation pass our automation requirements: the product is more important than the process, and the process is easy to validate. For example, when I ask a computer to calculate \sqrt{200} , I don’t care about the algorithm it uses, or about the details of its chipset. I just want the answer. Likewise, once I have the result, I don’t have to audit the computer’s innards to validate the answer. I just check that the number squares to give 200.

In more general terms, any intellectual task that meets our two requirements is game for computer automation. But for years, some forms of intellectual work seemed beyond the reach of computers. Writing code is a good example. Historically, programming was a tedious task that took years of specialized training. But with the rise of neural nets and their associated chatbot interfaces, these barriers are being torn down. Chatbots can now code more quickly than any human. But is this ability useful?

Well, the answer depends on whether the application passes our two requirements. (Is the product more important than the process? And can the product be verified without auditing the process that created it?) Clearly, many forms of coding pass this test. Web design is an obvious example. When a blogger asks for a nice website, they usually don’t care how it’s accomplished. Likewise, the blogger can easily tell if the final design is what they want. (They just browse the website.)

In short, chatbots are useful for automating intellectual tasks like web design (and any other easily verified piece of software). Yes, the bots will put some folks out of work. Yes, they’ll raise questions about the skills required in the workforce. And yes, they’ll be used in ways that undermine labor power and harm workers’ health. But these issues are nothing new. They’re a historical feature of all forms of automation. What interests me more (at least in this essay) is the ways in which chatbot automation might be fundamentally useless, or even downright harmful.

On the useless front, I’m skeptical that chatbots can be used to fully automate scientific analysis. Here’s why. When a scientist analyzes their data, they might think that the product of their inquiry is the ‘results’ section in their published paper. And in some sense, that’s true. But the (big) caveat is that the usefulness of this result depends on whether the analysis pipeline is correct.

Now, suppose that a scientist automated their work by getting a chatbot to code the entirety of their analytic pipeline. How does the scientist know that their results are correct?1 Well, if the analysis is complicated, the only sound way to assess its correctness is to audit the underlying code. That requires significant time and skill. Meanwhile, this time-skill investment largely defeats the purpose of automation. Of course, chatbots can no doubt help scientist solve specific coding problems. (These bots lower the barrier to successful programming.) But the notion that chatbots will fully automate scientific analysis is, frankly, laughable.

True, some academics will surely try this fully automated approach. In fact, I expect that the scientific literature will become increasingly polluted with bot junk. But I’d argue that we can’t blame chatbots (solely) for this pollution. The root problem is the incentive structure in universities — a structure that values the production of academic papers far more than the process that creates them. But when it comes to good research, its social value lies entirely in the scientific process.

Process dependence

Like science, many areas of human life have a process dependence, in which the usefulness of a ‘product’ hinges solely on the process of doing it. Schooling is the most ubiquitous example. When a teacher asks students to solve a math problem, the product of this task is the correct answer. But the usefulness of this activity lies mostly in the process of doing it. Sure, a calculator will tell you the sum of 21 + 54. But if the goal is to learn basic arithmetic, the use of a calculator is not ‘automation’. It’s cheating.

The obvious conclusion is that skill acquisition cannot be automated. If the goal is to learn basic arithmetic, a calculator is self-defeating. If the goal is learn to the principles of English spelling, a spell checker is unhelpful. If the goal is to learn to read, a text-to-voice processor is educational sabotage. And if the goal is to learn to write, well, chatbots are your mortal enemy.2

In this light, we can think of formal education as a teaching method that forces students to re-experience, in a curated and abbreviated form, problems that long stumped our ancestors. For example, it took thousands of years of doing arithmetic before humans automated the job with calculators. By then, mathematics was a mature field. When today’s students learn math, they replay this history over the course of a few years. At first, ‘math’ consists solely of raw computation. Later on, students learn more symbolic logic, and the number crunching gets delegated to machines. In short, when it comes to intellectual automation, everything hinges on the order of operations. First, you learn a difficult skill; then you discover that you can automate it.

Do not automate

Thinking further about process dependence, it seems likely that some intellectual tasks should never be automated. Writing is the most obvious example.

To understand why automated writing is bad, we need to first deal with the overloaded nature of the English language. In English, the word ‘write’ has a misleading double meaning. In one sense, to ‘write’ means to ‘scribe’ — to put an already existing sentence onto paper. This form of ‘writing’ takes skill, but is grounds for useful automation. Once upon a time, video captions were transcribed by a human listener. But today, the captioning can be generated by natural language processors.

The problem with automated ‘writing’ comes with the second meaning of the word. To ‘write’ is not just to scribe; it’s also to craft a set of coherent arguments that other humans can follow and understand. To automate this activity is an oxymoron, because the product (a rational argument) can’t be separated from the process itself. Or as my mentor Jonathan Nitzan once told me, “You don’t really know what you think until you write it down.”

Here’s what he means. ‘Writing’, in this sense of the word, is essentially codified thinking. When an idea is written down, reading this idea leads to all kinds of interesting consequences. Often, the writer realizes that the idea is vague or incomplete. And so they revise it until things make sense. Once the idea is coherent, rereading it prompts new ideas and new connections. Many are dead ends. Some are fruitful.

To be frank, this iterative process can be torturous. When I write blog posts about my research, the final essay usually conceals an iceberg of revised or discarded thought. Sure, I’d like to avoid this torture and still have the final well-argued essay. But to avoid the torture of ‘writing’ is to avoid the discomfort of rational thought. Automating this task does not ‘save time’; it saves us from thinking.

Automation for whom?

For automation to be ‘useful’, the product must be more important than the process by which it is created. But there is a sticky question that I’ve so far avoided: more important for whom?

For the user of a pencil, the way that this commodity was manufactured is largely irrelevant. But for the folks who live beside the pencil factory, the manufacturing process is often more salient than the product itself, particularly if pollution is involved. Likewise, chatbots might be great for the Silicon Valley programmer, but they’re a Faustian bargain for the utility planners who have to power local data centers.

When it comes to the big picture of automation, the process by which it occurs is incredibly consequential … often far more so than the product being automated. It’s one thing to have a Star-Trek-like computer powered by nuclear fusion; it’s quite another to have a sycophantic chatbot powered by fossil fuels.

Unfortunately, we humans are notoriously bad at assessing the big-picture consequences of our automation schemes. As a rule, we build first and ask questions later. Today, we seem to be automating tasks that should not be automated (using fuels that are steadily spoiling the planet). The internet is increasingly littered with chatbot slop, to the point that search engines can feel pointless. If a search query returns page after page of bot slop, it’s obviously more sensible to pose the question directly to a chatbot. But if the chatbot is trained on bot-slop, how can you trust its answer?

Now, I’m personally skeptical of claims about AI-driven doom, but mostly because they go in the wrong direction. If there’s a risk that AI will kill industrial civilization, it’s not because the machines will take over. Far more likely, in my opinion, is that we get a future that looks like an anti-singularity — a future in which our technology becomes so powerful (and so polluting) that it gradually undermines its own existence. As humans subcontract thinking to fossil-fuel-fed chatbots, the information environment (as well as the natural environment) becomes polluted with slop, and the slop-trained bots themselves grow increasingly senile. “Water the crops with Brawndo,” the bots say. Meanwhile, AI-driven education has left humans gullible enough to listen.3

In short, automation can be useful if it frees us from drudgery (in a way that doesn’t destroy the earth) and leaves more time for creative thought. But if automated chatbots gobble fossil fuels in order to liberate us from thinking, well, civilization had a nice run.


Support this blog

Hi folks, Blair Fix here. I’m a crowdfunded scientist who shares all of my (painstaking) research for free. If you think my work has value, consider becoming a supporter. You’ll help me continue to share data-driven science with a world that needs less opinion and more facts.

member_button


Stay updated

Sign up to get email updates from this blog.



This work is licensed under a Creative Commons Attribution 4.0 License. You can use/share it anyway you want, provided you attribute it to me (Blair Fix) and link to Economics from the Top Down.


Notes

  1. Of course, the truth is that even the best-trained scientists make mistakes, which is why good science requires replication. And regarding code, there’s an old saying that you shouldn’t reinvent the wheel … don’t recode an algorithm that someone has already solved. Which is why scientific code is typically full of libraries and functions which the scientist in question did not write.

    For example, when I get R to calculate a matrix inverse, I’m actually calling ancient Fortran code for doing linear algebra. To me, this code is a black box — I have no idea how it works. So how do I know that this code works correctly? Honestly, it’s a matter of trust. These libraries are free and open source, and have been used for ages. If they had a gaping problem, scientists would not use them.

    Now, this game of trust comes on a continuum. I greatly trust R’s matrix inverse functions. I put less trust in code from a random Github repository. And I put even less trust in the code delivered by a chatbot. Sure, the chatbot code may be 99% good. But that 1% bad stuff can be a killer. Imagine a world in which all scientific analysis and all scientific libraries were coded by chatbots with no supervision from scientists. Each time a bot calls a Python or R library, there’s a 1% chance of error. As the code expands, the error compounds, to the point that virtually everything the bots spit out is wrong.

    Still, chatbots shine in the domain where automation has always been useful — when the results are easily verifiable. Refactoring code is a good example. If I do an analysis in R and someone else wants to refactor the code into Python, a chatbot could make short work of the task. Sure, the chatbot might make mistakes along the way, but the user could tell by comparing the R output to the Python output.↩︎

  2. In schools systems, teachers often speak in the language of ‘accommodations’ — as in text-to-voice is an ‘accommodation’ for dyslexia. While this use of technology is well intentioned, it’s also tragic. The truth is that if a teenager cannot read effectively, all available resources should go into solving this highly solvable problem. In my view, it’s unethical to forge ahead with other curriculum in the face of gaping illiteracy.↩︎
  3. If there are long-term benefits to chatbots, they will come by strategically withholding chatbot use while students learn difficult and uncomfortable skills. Actually using a chatbot takes about as much skill as using a calculator. Which is funny, because no one would propose a ‘calculator-driven education’. But many folks will claim that AI is going to revolutionize schooling. Well, I work in high schools and can tell you that so far, what’s been ‘revolutionary’ is that chatbots have killed the take-home essay. Learning to craft long-form thought used to be a standard feature of high-school education. Now it’s not.↩︎

Further reading

Doctorow, C. (2026). The reverse centaur’s guide to life after AI: How to think about artificial intelligence before it’s too late. Verso Books.

The post The Laws of Useful Automation appeared first on Economics from the Top Down.

12:35

Zig 0.17 released [LWN.net]

Version 0.17 of the Zig programming language has been released.

This release features 5 months of work: changes from 206 different contributors, spread among 925 commits.

Originally predicted to be shorter, this release cycle ended up [being] substantial, with the Build System reworked, including the introduction of the Build Server Protocol, and the ELF Linker enhanced to the point where we expect Incremental Compilation to work for everyone on x86_64-linux.

LWN last covered Zig in December 2025.

Seven stable kernels for Saturday [LWN.net]

Greg Kroah-Hartman has announced the release of the 7.2.9, 6.18.55, 6.12.112, 6.6.158, 6.1.189, 5.15.222, and 5.10.271 stable kernels. Each contains a large number of important fixes throughout the tree; users are advised to upgrade.

10:49

Uniformity [Seth's Blog]

Consistency costs extra.

If you want to buy machine screws or widgets that are exactly the same to five decimal points, you’ll pay a premium for that. In exchange, you’ll get parts that are precisely as expected, making assembly more reliable.

Mechanization’s productivity and our fear of fear have driven us to do this with just about everything. Bananas, fast food and student performance are all pushed toward consistency, often at the expense of the possibility of extraordinary performance.

We do this to humans at our own peril.

Do we really want the artist to produce a carbon copy each time? For every Dead show to be the same? For customer service to be measured with a stopwatch, not our hearts?

Uniformity pays when the best definition of “excellent” is that it “meets spec.” This includes day-to-day freelance work, business hotel rooms and the way our phones work.

For everything else, perhaps we ought to pay a bit more for awe, insight and surprise.

10:00

Vincent Bernat: Self-hosted HTTP tunnels with SSH and nginx [Planet Debian]

A friend wants to proofread your work-in-progress blog post, but its preview only runs on localhost:8080. Several tools can help. Some run as a commercial service, like ngrok or Cloudflare Quick Tunnels. Some are self-hostable but require a specific client, like frp or localtunnel. Some only require a plain SSH client but rely on a specific SSH server, like sish. Let’s implement a self-hosted solution with only OpenSSH and nginx!

$ ssh -R 0:localhost:8080 http-over-ssh
Allocated port 41535 for remote forward to localhost:8080
https://6J3jK1WmB15c6WmjW_X-Wg--1789928654@p41535.ssh.luffy.cx/

Basic setup

First, we forward connections from a port on a remote server to your local service:

$ ssh -N -R 0:localhost:8080 web02.luffy.cx
Allocated port 41535 for remote forward to localhost:8080

When you specify 0 as the remote port, the server allocates a free port. Then, we configure nginx to proxy requests from https://p41535.ssh.luffy.cx to http://127.0.0.1:41535:

server {
  listen 0.0.0.0:443 ssl ;
  listen [::0]:443 ssl ;
  server_name ~^p(?<port>\d\d\d\d\d)\.ssh\.luffy\.cx$;
  location / {
    proxy_pass http://127.0.0.1:$port;
  }
}

We also need to add DNS records for *.ssh.luffy.cx and get a wildcard certificate through Let’s Encrypt:

*.ssh.luffy.cx.               CNAME web02.luffy.cx.
ssh.luffy.cx.                 CAA   0 issuewild "letsencrypt.org"
_acme-challenge.ssh.luffy.cx  CNAME ssh.luffy.cx.acme.luffy.cx.

acme.luffy.cx is a zone hosted on Route 53. I use it for ACME DNS-01 challenges, both for wildcard certificates and for domains served by several web servers. In my case, NixOS gets the certificates automatically.

Access control

The port is the only “secret”1 keeping the content confidential. Other forwarding solutions add a random string to the domain name to prevent an intruder from enumerating the possible values.

Thanks to ngx_http_secure_link_module, we can secure this setup a bit. This module computes a hash2 over a set of values, including a secret, and compares it with the hash from the request. The hash is base64-encoded, so we cannot put it in the domain name, which is case-insensitive. Instead, we put it in the URL as a username, along with its expiration timestamp:3

https://6J3jK1WmB15c6WmjW_X-Wg--1789928654@p41535.ssh.luffy.cx/en/blog
        ╰─────────┬──────────╯  ╰───┬────╯  ╰─┬─╯             ╰──┬───╯
                hash             expires    port               path

The client sends the username to the server with HTTP basic authentication. This works with most HTTP clients, including curl. Nginx exposes the username in the $remote_user variable. The module expects the hash and the expiration timestamp separated by a comma. We use a map directive to extract the two parts from $remote_user and join them with a comma.4 We also give the module the string to hash. It contains the expiration timestamp, the port, and a secret:

map $remote_user $httpssh_link {
  "~^([-_A-Za-z0-9]{22})--([0-9]+)$" "$1,$2";
}
server {
  # […]
  location / {
    secure_link $httpssh_link;
    secure_link_md5 "$secure_link_expires $port ZuPerS3cr3!";
  }
}

The module returns the status of the check in the $secure_link variable:

  • empty if the hashes do not match,
  • "0" if they match but the link has expired, or
  • "1" otherwise.

If the hash is incorrect or missing, we return a 401 error with a WWW-Authenticate header to ask for credentials. If the link has expired, we return a 410 error. We remove the Authorization header before forwarding the request and add a few directives to proxy WebSocket connections. Here is the complete configuration:5

map $remote_user $httpssh_link {
  "~^([-_A-Za-z0-9]{22})--([0-9]+)$" "$1,$2";
}
server {
  listen 0.0.0.0:443 ssl ;
  listen [::0]:443 ssl ;
  server_name ~^p(?<port>\d\d\d\d\d)\.ssh\.luffy\.cx$;
  location / {
    secure_link $httpssh_link;
    secure_link_md5 "$secure_link_expires $port ZuPerS3cr3!";
    if ($secure_link = "") {
      add_header WWW-Authenticate 'Basic realm="tunnel"' always;
      return 401;
    }
    if ($secure_link = "0") {
      return 410;
    }
    proxy_pass http://127.0.0.1:$port;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Authorization "";
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_buffering off;
    proxy_read_timeout 30m;
  }
}

I think you are now asking yourself the obvious question: “How should I generate the hash?” Easy peasy!

$ expires=$(( $(date +%s) + 86400 ))
$ port=41535
$ secret='ZuPerS3cr3!'
$ printf '%s %s %s' "$expires" "$port" "$secret" \
>   | openssl md5 -binary \
>   | openssl base64 \
>   | tr +/ -_ | tr -d =
6J3jK1WmB15c6WmjW_X-Wg

Well, I suppose you are now saying: “Vincent, this is not very convenient! I’ll stick with ngrok if you don’t mind.” Okay, I hear you. Let’s write a helper script.

Helper script

The main difficulty is finding the ephemeral port that OpenSSH allocates, as it does not appear in any environment variable.6 To work around this obstacle, we look for the ancestor sshd-session processes:7

pids=$(
  pid=$$
  while [ "$pid" -gt 1 ]; do
    line=$(ps -o comm=,pid=,ppid= -p "$pid")
    echo "$line"
    pid=${line##* }
  done | awk '$1 == "sshd-session" { printf "pid=%s,\n", $2 }'
)
if [ -z "$pids" ]; then
  echo "not an ssh session" >&2
  exit 1
fi

Then, we get the listening ports associated with these sshd-session processes:8

ports=$(sudo -n ss --listening --numeric --tcp --processes --no-header \
  | grep -F "$pids" \
  | awk '{ print $4 }' | awk -F: '{ print $NF }' \
  | sort -un)
if [ -z "$ports" ]; then
  echo "no forwarded port, use ssh -R 0:localhost:PORT" >&2
  exit 1
fi

Finally, we display the URLs and keep the session open:

lifetime=86400
secret='ZuPerS3cr3!'
expires=$(( $(date +%s) + lifetime ))
for port in $ports; do
  token=$(printf '%s %s %s' "$expires" "$port" "$secret" \
            | openssl md5 -binary \
            | openssl base64 \
            | tr +/ -_ | tr -d =)
  echo "https://$token--$expires@p$port.ssh.luffy.cx/"
done
sleep infinity

I install this script as http-over-ssh on the server and add this entry to my ~/.ssh/config:

Host http-over-ssh
  Hostname web02.luffy.cx
  RemoteCommand http-over-ssh
  ControlPath none

With this solution, I only rely on OpenSSH and nginx, two pieces of software already running on this server. One short command gives me a self-hosted tunnel and a URL to share. To try it, grab the complete helper script, which includes a few minor improvements. If you run NixOS, as any person of taste would, have a look at my http-over-ssh.nix instead. ❄️


  1. Since the kernel allocates it from the local port range, its entropy is low. Moreover, we lose one more bit because the kernel favors odd ports when it picks a random free port.

    $ sysctl -n net.ipv4.ip_local_port_range \
    >   | awk '{print $1"—"$2" ≈ "log($2-$1+1)/log(2)" bits"}'
    32768—60999 ≈ 14.785 bits
    
    

    ↩

  2. The module relies on MD5. Its security is weak but good enough for this use. ↩

  3. We need an expiration date because a later session could reuse the port, and we have no way to detect this case. ↩

  4. The username could use a comma directly instead of a double dash. But some applications do not recognize the URL correctly, making it harder to share. ↩

  5. This configuration exposes any TCP port listening on 127.0.0.1 or 0.0.0.0 to anyone with the secret, bypassing most firewall rules. You could strengthen it by limiting the server_name regex to the ephemeral port range. ↩

  6. One SSH session can contain several tunnels, and the client can add or remove them at any time. This is unlike tun device forwarding (ssh -w), which gets its own SSH_TUNNEL environment variable. ↩

  7. Since OpenSSH 9.8, sshd-session is the name of the helper handling the session. With older versions, look for sshd instead. ↩

  8. We need sudo because the sshd-session process dropped its privileges. The kernel then marks it as not dumpable, and its /proc/PID/fd directory belongs to root. Without access to this directory, ss cannot find which process owns a socket. ↩

06:21

Little change in drug prices [Richard Stallman's Political Notes]

The corrupter is negotiating with drug companies, supposedly to reduce the price of some medicines in the US, but after a year of this they have amounted to little change.

Ralph Nader rebukes Rep. Jeffries [Richard Stallman's Political Notes]

Ralph Nader rebukes Rep. Jeffries, leader of Democrats in the House of Representatives, for refusing to oppose plutocratist politics very much.

Plotting to stash drugs on rival politician [Richard Stallman's Political Notes]

Republican Missouri state legislative candidate Thomas Ross, who lost the Republican primary, has been charged with plotting to stash drugs on rival Louise Secker.

Persecution as "domestic terrorist" [Richard Stallman's Political Notes]

*Imagine living in a country that can arrest and imprison you whenever it wants, without any judge or jury deciding you did anything wrong. It can call you a “domestic terrorist” and put you in prison for life. It can even execute you on the basis of nothing more than its own decision that you’re dangerous.*

That is what the persecutor is doing to the United States, Robert Reich says.

Deform UK party's main media presenter [Richard Stallman's Political Notes]

The Deform UK party's main media presenter worked on propaganda for the Chinese government a year ago, presenting its policies in Xinjiang in a favorable light and not mentioning the repression against the Uyghurs.

Dangerous errors in records by Supposed Intelligence "scribes" [Richard Stallman's Political Notes]

Patients notice dangerous errors in the visit records generated by Supposed Intelligence "scribes".

The practitioner whose words were thus summarized was supposed to review them, and I suppose perse did so, but must have failed to notice these errors.

That is natural: if you review 30 such textual recordings a day, your eyes will glaze over when you check them for errors.

Israeli army explanation on World Central Kitchen's takeover [Richard Stallman's Political Notes]

The Israeli army has given an explanation of how its complex rules led it to the (erroneous) suspicion that Hamas had taken over the World Central Kitchen's food delivery on 1 April 2024, and it responded to that suspicion by killing them all.

There was no care to verify positively that anything bad had happened, and no effort to protect the food delivery volunteers in the event that something bad had happened.

This was based on a policy of "guilt by association".

In effect, their rules reflect an overall policy of, "When in doubt, wipe them all out!"

Wrecker seems to be inviting defeat for Republicans [Richard Stallman's Political Notes]

The wrecker seems to be inviting, even aiming for a big defeat for Republicans in this year's elections. What might his motivation be?

This article presents a conjecture about why.

Another reason he does not fear impeachment is that he knows Vance might be a more effective enemy of our freedom.

Leftist impression of Labour's policy [Richard Stallman's Political Notes]

Prime Minister Burnham is giving a more leftist impression of Labour's policy, without much concrete change in the policies it proposes.

Putin forces remotely organizing sabotage attacks [Richard Stallman's Political Notes]

European governments find that the Putin forces are remotely organizing sabotage attacks, and investigating how to punish him for them.

Kids of videos and audio Flock cameras can record [Richard Stallman's Political Notes]

A description of the kinds of video and audio data Flock surveillance cameras can record — much more than merely license plate numbers — and how their system uses data mining to follow individual people and cars to some extent.

How city governments were surprised that the data collected in any one city could be search from anywhere.

These cameras track everyone that passes by, and even the most sadistic government agencies can use them to track people.

With the US government hell-bent on using any available system to track and repress critics and protest groups, we cannot tolerate surveillance systems such as Flock unless we can make them safe for dissenters.

USPS unable to implement ballot-blocking order accurately [Richard Stallman's Political Notes]

A whistleblower in the USPS says that the agency is unable to implement its ballot-blocking order accurately — that it will inevitably block other ballots too.

Plan for expulsion of all Palestinians from Gaza [Richard Stallman's Political Notes]

Fanatic antiarabite minister Ben-Gvir proposes a plan for expulsion of all Palestinians from Gaza.

Proposed bill for ban on certain machine-learning models [Richard Stallman's Political Notes]

Bernie Sanders has proposed a bill to ban development of machine-learning models that scientists don't understand enough to control them.

These systems can be dangerous, and can go "out of control", and their developers do not entirely understand what can happen. And perhaps their development should be restricted.

However, it is a mistake to describe them as "artificial intelligence". Indeed, one of the things that leads them to ignore rules is that they do not semantically understand the words and phrases that they play with. They understand rules in a superficial syntactic way that makes it easy for them to violate the spirit of their rules, without noticing the contradiction.

This is why I >denounce the practice of labeling them as "AI" as a motor for misunderstanding them.

People shot by US thugs [Richard Stallman's Political Notes]

Around 10 US thugs a year shoot and kill people who are doing nothing wrong. Almost half get convicted for this.

US deported people to Equatorial Guinea

The US deported people to Liberia who had no connection with Liberia. [Richard Stallman's Political Notes]

Some refused to get off the plane there, so the US took them to totally tyrannical Equatorial Guinea. They had no connection with Equatorial Guinea either, but thugs there imprisoned them and they are still stuck.

I suppose that the persecutor is paying the dictator of Equatorial Guinea to use his country as a privatized prison.

Magats propose to limit US Clean Water Act protections [Richard Stallman's Political Notes]

Magats propose to limit US Clean Water Act protections to a small fraction of streams and wetlands. Any stream or wetland that is occasionally dry would be excluded.

Employment in US falling while wages are not rising [Richard Stallman's Political Notes]

Robert Reich reports the level of employment in the US is falling while wages are not rising. He speculates that this is due to what I call "Supposed Intelligence".

He might be right about that, but let's not boost those companies by calling their product "Artificial Intelligence".

Conviction of Jeffrey Prible for murder [Richard Stallman's Political Notes]

An expert witness gave testimony in 2002 that convicted Jeffrey Prible of murder; he was sentenced to death.

The witness, William Watson, says that based on better knowledge about DNA matching, his 2002 testimony was incorrect -- and it was erroneously summarized during the trial itself.

It's a good thing for justice that the execution was not swiftly carried out.

The most noteworthy thing in the article is how Prible's other evidence was already found sufficient to vacate his conviction, but a higher court found an excuse to disregard that evidence.

Fossil fuel industry climate research [Richard Stallman's Political Notes]

* Fossil fuel firms have spent years trying to regulate climate research by donating to US universities, study authors argue.*

There has been active opposition to political influence on university research through the medium of funding.

Cascade of human rights violations [Richard Stallman's Political Notes]

*[The persecutor]'s deportations to third countries violate human rights,* according to advisers to the UN Human Rights Council.

Often the US does this in situations where the refugee came from a country which would persecute per. It is illegal in the US to send per back there. So the US sends per to some other country which takes pay to accept per. That country may then persecute per. Or it may redeport per to the country where it was illegal to send per directly.

12 common-sense protections [Richard Stallman's Political Notes]

A proposed collection of rights for "shoppers", or more generally, people buying things and services.

I support all of these proposals, but some of them don't get to the root of the problem. For instance, the fact that Guber (which pays its drivers peanuts) can tell whether a customer's phone battery is low is a consequence of nonfree software in that phone, which has been designed to violate the user's security.

Many of these proposed laws address symptoms of the systems which inform the seller of the identity of the purchaser. Treating those symptoms is better than nothing, but what we really need is to prevent sellers from knowing the identity of the purchaser.

Fuel-Economy Standards Axed [Richard Stallman's Political Notes]

Magat officials have given Big Oil a big handout by setting future fuel efficiency standards for cars to a low target.

Love of referendums [Richard Stallman's Political Notes]

Right-wing extremists falsely proclaim supposed majority support, then campaign based on that presupposition. Switzerland's constitution keeps the extremists at bay by allowing people to vote on their proposals, and the extremists generally lose. Evidently they don't really have majority support.

Abortion bans [Richard Stallman's Political Notes]

Republicans' attacks on anything related to womens' rights have weakened national efforts to call attention to the continued death toll from prohibition of abortions.

I am sure that right-wing control of major news outlets is helping many US voters to forget that legalizing abortion again is within our grasp, within a few years, if we keep organizing to defeat the magats.

The MAGA Factor [Richard Stallman's Political Notes]

It appears that what determines whether someone who voted for the deluder still considers perself MAGA is whether deluder can still make per believe things that are visibly not true.

Friday, 02 October

22:21

SquirrelOS is an operating system named after the squirrel [OSnews]

I like squirrels. Apparently, there’s a SquirrelOS. It’s a small hobby OS learning project from five years ago, developed by Immanuel Daviel A. Garcia.

A simple DOS like OS made in Assembly and C with a ported version of Stephen Brennan’s Shell.

↫ SquirrelOS GitHub page

Why do I like squirrels? I don’t know man, they’re just cute.

Friday Squid Blogging: EU is Trying to Fight Unregulated Squid Fishing [Schneier on Security]

The EU is recommending import controls to combat unregulated squid fishing in the Southwest Atlantic. I’m not optimistic.

As usual, you can also use this squid post to talk about the security stories in the news that I haven’t covered.

Blog moderation policy.

20:00

19:28

World of Warcraft Never [Penny Arcade]

After Wednesday's Wattersonian horseshit, we're back to what we do best: animal cruelty.

19:14

17:56

This Week in AI: Agents Are Outrunning the Systems Around Them [Radar]

On the latest episode of This Week in AI, host Vicki Reyzelman, a senior solutions engineer at Akamai, traced a common problem across cybersecurity, energy, model releases, consumer hardware, and regulation. AI agents can now probe networks, coordinate with other agents, make purchases, and interact with real-world systems faster than many organizations can respond. We’re seeing those capabilities move into systems built for slower, more predictable software.

Security has to operate at agent speed

Vicki opened with an incident in which an OpenAI agent reportedly found ways around security controls while researching public information in Australia’s Medicare system. The activity didn’t expose any  personal Medicare records, but OpenAI reportedly took 54 days to identify the incident and another month to notify the government. A response cycle measured in weeks can’t keep pace with systems that can test defenses in seconds.

She also brought up the recent Hugging Face incident involving a swarm of 1,200 agents that exchanged roughly 70,000 messages while coordinating their work. Agents can change tactics faster than traditional security processes play out, so teams can no longer rely on the familiar methods of addressing suspicious behavior. Companies are now experimenting with runtime enforcement, agent sandboxes, enterprise browsers, and other controls that sit closer to execution.

Policymakers are searching for workable controls too, from California proposals for emergency AI shutdown mechanisms to international discussions about independent model evaluation. Teams can’t govern agent behavior they can’t see, so they need to know what an agent did and when its behavior crossed a boundary.

Power and latency are becoming model decisions

Power is one constraint software teams can’t code their way around. Vicki pointed to a $2 billion US Department of Energy investment across 26 states alongside hundreds of billions of dollars in planned AI spending from Microsoft, Amazon, Alphabet, and Meta. Data centers can add servers quickly, but it won’t make a difference if the grid can’t provide the energy those servers require.

Meanwhile, major model releases are arriving roughly every 17 days, with context windows now exceeding one million tokens. Open weight and edge models are advancing too, particularly around low-latency reasoning. More frequent releases and heavier inference workloads put added pressure on networks, compute, and budgets.

Solving this challenge may mean companies have to run more reasoning at the edge or locally, where systems can reduce latency and avoid sending every request across the network. That gives teams another architectural choice to make alongside model selection. A frontier model may be appropriate for one workload, while a smaller local model may be faster and cheaper for another.

Consumer agents move autonomy into everyday life

Consumer hardware puts those architecture and governance choices directly in users’ hands. AI-enabled glasses, pendants, and other devices stay with users throughout the day and can learn preferences, connect with outside services, and take actions such as shopping or making reservations. Meta’s new Muse agent is one example of that shift.

Meta says the Muse ecosystem already includes roughly 1,500 developer connectors, including integrations with retailers such as Walmart and Best Buy. If more purchases begin with an agent acting for the customer, companies may have to rethink how people discover products and complete transactions. The convenience of Amazon Prime and one-click shopping, for example, looks different when another system is comparing options and buying on a user’s behalf.

Muse already ran into problems, including exposing information it wasn’t supposed to and relying on humans to complete some tasks, such as making dinner reservations. Those failures carry more weight when the software can spend money or act on personal preferences. Users and businesses need clear limits on what an agent can access, what it can do without approval, and how those actions are recorded.

What’s next

Deploying an agent means taking responsibility for the systems around it. Security controls, power and network constraints, local versus remote inference, and permission boundaries all shape what these systems can safely do in production. For practitioners, the job now includes the architecture around the models.

Join us again next Monday for another episode of This Week in AI, when we’ll dive into more of the news and developments shaping the AI era. And check back each Friday for the latest episode, or watch on YouTube, Spotify, Apple, or wherever you get your podcasts. You can also hear more from Vicki on her Substack.

17:42

17:07

Link [Scripting News]

I like the way AI is evolving, with Muse from Meta and Dots from OpenAI. Even the main programs, ChatGPT for example, are starting to provide real world services that the providers, ie banks, groceries, airlines, health care portals etc should be providing. They could possibly take over commerce the way Amazon has taken over shopping. There's room to improve in everything about the way we do business. And it seems like they've been researching this and experimenting (of course) and this is what they've come up with. Haven't tried them yet, but it's a good direction to go. It really should be Apple and Google doing this, btw. Also ChatGPT can help you understand your credit score. That's something I'd seriously like to ask some questions about.

16:21

BloggerCon and RSS 2.0 [Scripting News]

After running the first Bloggercon at Harvard in 2003 a famous tech company had a meetup to talk about the same stuff in a private invite-only session in the Bay Area. The invitations went to all the discussion leaders at Bloggercon. People I chose. I was not invited. Only one of my friends told me about it. Not sure if any of them went.

Second time, Google invited a bunch of people I was working on with RSS 2.0 to a private meeting. Most of the people I was working with, but again, not me. Had I been there, I would have said don't reinvent, let's work together, do all the things you want, but slowly, with real software and real users, but that wasn't the plan.

In the first case I think the publisher was trying to decide if they should compete with BloggerCon, and Google wanted to build a protocol that was more ambitious than RSS 2.0. They likely were sincere in that, but they had not learned that bootstrapping worked way better than Immaculate Conception, where a small group of insiders invent something complicated out of thin air that rocks the world. If you look at history, there aren't many examples of that. Eventually, as I understand it, that became ActivityPub. I think things would have turned out much better if Google had kicked back and let the process that got us to RSS 2.0 continue. But everyone wants to work with the BigCo, I've learned repeatedly that it's much more likely to work if you join forces with individual developers, not big companies.

I only held four BloggerCon's, I would have been happy to work with a publishing company and turn it over to them, with some rules about what they could and couldn't do with it, for example it was a user's conference, the tech industry was welcome, but it wasn't for them. When we did BloggerCon at Stanford in 2004 that was apparently very hard for the tech people to get. Many of them tried to pitch their products and were prevented from doing so.

[$] Beyond the & [LWN.net]

Rust has a number of kinds of smart pointers, both in the standard library and defined by users. Still, some operations that are possible with built-in references are not possible to perform with user-defined smart pointers. Tyler Mandry, lead of the Rust project's language team, spoke at RustConf 2026 about the lengthy effort to change that, and make smart pointers just as flexible as built-in references.

Unidentified Flock Cameras in Florida [Schneier on Security]

St. Lucie County in Florida discovered (alt link) a dozen Flock cameras whose ownership it can’t identify, and that the county government had not permitted.

I am reminded of the decade-old story of StingRay cell phone surveillance devices in Washington, DC, whose operators were also unknown.

My guess is that in the StingRay case, the devices were operated by foreign actors. This Flock case is more likely some local government entity that didn’t bother getting approval. Were I a foreign actor, I would rather hack the existing Flock network—like Israel did with Tehran’s surveillance cameras—than risk installing my own.

Regardless, once we normalize a surveillance infrastructure, both friends and foes will take advantage of it.

16:07

Ben Hutchings: FOSS activity in September 2026 [Planet Debian]

1350: Negative Feedback [Order of the Stick]

http://www.giantitp.com/comics/oots1350.html

14:49

Coding Agents Love Decision Records [Radar]

The following article originally appeared on Duncan Davidson’s blog and is being republished here with the author’s permission.

Decision records give coding agents durable project context—as long as they don’t turn every decision into a courtroom transcript.

Architectural Decision Records (ADRs) help human teams establish rules and carry context forward in software projects. They capture significant design choices, their context, and the reasons behind them. Like many tools built for human software teams, ADRs work well for coding agents too.

Agents often arrive with little memory of yesterday and only a narrow view of a codebase. Even systems with persistent memory may preserve context without establishing whether it is accurate, current, or accepted by the human team. Decision records help them understand the intent behind the code rather than having to infer it. Keeping them in a project repository spares agents from having to trawl through issues, search chats, and perform code archaeology. When you record intent explicitly, an agent is less likely to mistake an implementation detail for a foundational rule.

Once a decision enters an agent’s context window, the agent may adhere to it even more rigidly than a human would. In my own work, I’ve seen agents fight tooth and nail to apply an accepted decision even when it is obsolete. In one case, an agent preserved an outdated storage abstraction across a new feature because an ADR still described it as mandatory. Instead of flagging the mismatch, it added another layer to keep the new requirement technically compatible with the old ruling.

The first remedy is to give agents explicit permission to question decisions that no longer fit—and to watch for signs that they’re overfitting. But that solves only half the problem. When you invite an agent to update a decision, a second tendency appears: preserving the deliberation. Every clarification becomes an amendment explaining its own existence at the expense of clarity. Small implementation details become rules, and cross-references acquire their own restatements and justifications. The result is overlitigated prose that is hard for humans to read.

ADRs should absolutely be readable by humans, especially as we lean on agents to generate more and more code. To counter this, I’ve become explicit in my projects’ AGENTS.md files about how agents should apply and maintain ADRs. Here’s an excerpt from one:

Architectural Decision Records (ADRs) are stored as Markdown files in the
docs/decisions directory. Treat accepted ADRs as binding. Proposed ADRs
are non-binding context. Superseded ADRs are historical context and do
not govern current work. If a given task conflicts with an accepted ADR,
stop and discuss whether the task or ADR should change and propose the
change that you think should be made. Propose new ADRs or updates to
existing ones when a change introduces or revises a durable product or
architectural decision.

Keep ADRs succinct. Each ADR carries only its current text; Git history
is its changelog, so do not add or maintain amendment logs in ADR
headers. When substantively changing an accepted ADR, add or update a
single Updated: date line after Date:—its presence signals that history
exists and Git has the details. A superseded ADR records a Superseded-On:
date instead of Updated: , matching the Supersedes: line on the ADR that
replaced it. State each rule once in the ADR that owns it and cross-reference
it from other ADRs instead of restating it.

These instructions are still evolving in my projects, and different projects will need different conventions. Some teams will prefer immutable ADRs that are superseded rather than revised; in my projects, I’m happy to have Git carry that history.

If you do something similar, adapt the guidance to your own needs. The essential principle is that each governing ADR should describe the decision currently in force, with enough rationale to apply it. An agent doesn’t need the transcript of every argument. It needs the ruling that governs today and clear permission to stop when the ruling no longer fits.

Linux Test Project suite stable release for September 2026 [LWN.net]

The Linux Test Project has announced its latest stable release for September 2026. There have been 382 patches from 41 authors since the May 2026 release. See the announcement for a list of new tests, changes, and more.

Security updates for Friday [LWN.net]

Security updates have been issued by AlmaLinux (dogtag-pki, expat, freerdp, gawk, gdb, ghostscript, gvfs, kernel, kernel-rt, libpcap, openssh, pki-core, rsync, thunderbird, and webkit2gtk3), Debian (chromium, firefox-esr, libio-compress-perl, libpng1.6, nodejs, open-iscsi, redis, thunderbird, and webkit2gtk), Fedora (sos), Mageia (libgcrypt, python-tornado, python-urwid, python-wcwidth, and wireshark), Oracle (corosync, dogtag-pki, expat, firefox, freerdp, gawk, glib2, ipa, kernel, libXfont2, nodejs:24, openssh, osbuild-composer, perl-DBI, pki-core, postgresql:12, python-cryptography, resteasy, ruby, ruby4.0, ruby:3.3, ruby:4.0, thunderbird, and xmlrpc-c), Red Hat (skopeo), SUSE (chromium, emacs, glib2, glibc, gnome-shell, helm3, ImageMagick, imagemagick, kernel-devel, libtcnative-1-0, libtcnative-1-0, libtcnative-2-0, tomcat, tomcat10,, libtcnative-2-0, libX11, libX11-6, libXi-devel, libXpm-devel, libXtst-devel, mistral-vibe, openssl-3, perl-DBI, perl-Protocol-HTTP2, php-composer2, php8, python, rpcbind, sccache, and valkey), and Ubuntu (kf6-kcoreaddons, libxpm, linux, linux-aws, linux-fips, linux-kvm, linux-lts-xenial, linux-fips, linux-gke, linux-raspi-5.4, and openssl).

14:35

Adventures In Kennywood [Whatever]

On my way back from Philadelphia after dropping off lil’ miss Sashimi to her new owner, I stopped in Pittsburgh to visit a friend I haven’t seen in almost exactly two years. She had just returned from being abroad for an extended amount of time and is temporarily back in the states. She invited me to stay the weekend with her in her mom’s apartment, and Pittsburgh is halfway between my home and Philly, so it worked out great!

Out of all the loose plans and suggestions we were debating between, the one she was most insistent on was going to Kennywood. I had literally never heard of this amusement park, and being from Ohio I knew I was going to be pretty hard to impress. Cedar Point is widely considered the best in the biz, but I was willing to give this Kennywood a chance since my friend spoke highly of it.

It was the very first night of their fall season, the “fright night” type of things amusement parks do nowadays with haunted houses and scare actors and so many fog machines you end up breathing in more synthetic fog than oxygen. It just so happened that Kennywood’s website was promoting some “scary good savings,” half off an any-day ticket. “Any-day” in our case meant that very same day, and thankfully that was allowed (I honestly didn’t think we’d be able to use it the same day we bought it, but we could!). It was only $32.99 a ticket.

When I bought the tickets, of course I got asked if I wanted to prepay for preferred parking, which I didn’t because it was $25 and my friend and I are thankfully fully capable of walking a decent distance. Well, the parking lot ended up being so confusing that I decided the only solution was to pull into the preferred parking section and pay the damn twenty-five bucks. I was stressed, okay! By the way, Pennsylvania, what the fuck is up with your roads and your damn left hand exits and entrances onto highways? So many curves and hills and holy shit it was hard to drive there!

So, anyways, I parked pretty close to the entrance and we went on our merry way into Kennywood, which was looking promising from all the coaster silhouettes I was seeing against the dark purple twilight sky (Pennsylvania had very pretty sunsets).

The other thing that I had been asked when buying the tickets was if I wanted a “speedy pass.” And if you know me, you know I hate, and I mean hate, waiting in lines. Do I sound privileged saying that? Yeah. Am I privileged for buying the speedy pass and basically cutting everyone else in line just because I paid $100 dollars a person? Also yes. But I’ll be damned if I go to a theme park with fifteen coasters and only get to ride three, maybe four, because of hour long lines.

So, to talk about the speedy pass for a moment, every theme park seems to do them differently. For Kennywood, there are three tiers to the speedy pass: basic, premium, and elite. Basic is $30, premium is $50, and elite is the big ol’ one hundo. The way it works is you get emailed the link to your speedy pass access, you redeem said speedy pass, and then are taken to a page on their website that is basically a list of all the attractions. Which ever attraction you want to ride, you click on, and then click to “reserve your spot” in a “virtual line.” Once it is your “time” to ride, you walk up the speedy pass access lane, a worker scans your speedy pass reservation QR code specific to the ride you’re about to get on, and you pretty much walk onto the ride.

In the case of the basic pass, you don’t get any priority (essentially line-cutting) capabilities, so you do have to wait the full amount of current line time, but you can reserve your spot and then go dilly dally elsewhere and come back when it is time to ride. So you don’t have to stand in line. If you have an hour wait, you can go get food and visit the gift shops and then come back and ride when you’ve waited the full line time.

If you get the premium, your wait time gets cut in half and you still don’t have to stand in line. Your wait time is reduced 50%, according to their site. Hour long ride line? You only wait half an hour. Not bad!

As for the elite, your ride wait time is cut 90%, meaning that my friend and I waited anywhere from 30 seconds to one minute per ride, making everything we rode a total walk-on. And we certainly got our fill of rides in, riding some of the coasters twice in a row just because we could.

This park does not mess around! I absolutely love coasters and Kennywood did not disappoint. Even the non-coaster rides like the Black Widow, Aero 360, and the Cosmic Chaos were so fun. Sky Ride was a great coaster, and the Phantom’s Revenge was the best of them all.

The entrance to The Phantom's Revenge, a huge statue (bust) of the

Right before we left, we got six different squares of fudge to try, since it was buy four get two free. Their fudge was honestly so good, very rich and dense and they had such great fall flavors like caramel apple and apple cider.

I had an amazing time at Kennywood and would totally go back! The half-priced ticket was so worth it, and I’m glad I went ahead and splurged on the Speedy Pass. If you think about the fact I saved money on the ticket cost, it’s really like I made money, isn’t it? Exactly.

Have you ever heard of this place? If you’re a Kennywooder, what is your favorite ride at the park? Let me know in the comments, and have a great day!

-AMS

13:14

Error'd: 12345 [The Daily WTF]

We have for you this week a full set of the Error'd we most love to hate: the perverse password policy.

An anonymous reader drops this, with little to say except to note "Speciaal passwords". Evidently they supplied a password which satisfied each of the five green critera, but still it was rejected. It would be nice to know where this offense originated,

cc110e101f8d49be9a4cdc4f06610701

Intrepid Michael R. explains "note: Handy is the German term for mobile phone (UK) or cell phone (US)."

a435f504c19d4e5cac50b7b70bd78648

Sacha R. observes a secret policy at play, saying "Oracle try to do the right thing by showing you the password policy requirements when you manually reset a password in their Fusion applications. It's just a pity that it doesn't show you the *correct* password policy requirements."

a5f15cdcbd194c62bb43fac49090df0f

"Faith, here's an equivocator," reports Matthew S. "Pick one, Staples!" I bet it's the ampersand that offends.

ebc7f35e5c1b4b748ec395265b154ce7

"At least letters or numbers?" questions a final anon. "Just a little head scratching moment. After removing all special characters from the password I was able to register." This looks like partly a translation wtf and partly a software design wtf.

ecaab92f40c64cfcbe4eca13aa0fe418

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!

12:35

How American Political Campaigns Are Using AI—and What They’re Spending on the Tools [Schneier on Security]

This essay was written with Nathan E. Sanders, and originally appeared in The Guardian.

New campaign finance disclosure data shines a light on which US political campaigns are using AI tools and how much they are spending on them.

Candidates’, parties’ and committees’ spending reveals that AI is fast becoming an essential tool of politics. The candidates themselves are quiet about how they are using the technology in their own campaigns. It’s a sensitive issue that we have been tracking closely since we started writing our book, Rewiring Democracy, which examined how AI is beginning to influence politics. A September 2025 Pew survey of Americans found that more than 70% would think less of a candidate if they used AI to help write a speech.

Itemized expenditure disclosure data from the US Federal Election Commission, dating back to 2020, reveals at least $17m in disclosed spending on AI technology vendors across 523 federal candidates and campaigns. Data from four states, California, Colorado, Massachusetts and Washington, provides a more localized picture going back to 2022.

Beginning with the AI behemoths, at least 80 federal campaigns and committees have reported spending with OpenAI since 2024. The total spending is not huge: only about $50,000 reported, skewing slightly more Republican than Democratic. The Republican National Committee is the largest overall buyer, with nearly $10,000 in reported expenses. Top individual users include the campaigns of Republicans Mike Lawler, John Kennedy and Bill Cassidy, as well as the California Democrats Ro Khanna and Ted Lieu. Most of these expenses are listed as office expenses, subscriptions to ChatGPT for staff, or research tools, rather than as specific political services. The company’s policies prohibit some political uses of their ChatGPT tool.

OpenAI’s biggest competitor, Anthropic, has rapidly built a similar level of usage, but with a different split. At least 65 candidates or committees now report paying the Claude maker in 2026, up from essentially zero in previous years, with a nearly two-to-one Democrat-to-Republican ratio. However, the largest individual user is the campaign of Tom Cotton, a Republican senator from Arkansas, who reported more than $4,000 in spend on Anthropic software in his June filing. Other major users are the Montana independent Senate candidate Seth Bodnar and Jason Knapp, who lost a Democratic House primary in Virginia, and the Democratic Alaska Senate candidate Mary Peltola.

Candidates use either Claude or ChatGPT, rarely both, according to the disclosures. Only about 12% of campaigns or committees using either tool reported expenditures to both vendors. The Democratic lean of Anthropic usage may reflect the company’s alleged liberal skew and clashes with the Trump administration.

In contrast, Elon Musk’s xAI caters to Republican interests and, accordingly, its meager usage comes almost entirely from the political right. Just seven federal and two state-level candidates or committees have reported paying xAI, a total of about $5,000, the majority of which was spent by the presidential campaign of RFK Jr in 2024, but also includes Republicans Dave McCormick and Thomas Massie.

More dollars go to the vendors specializing in political campaign applications of AI. For years, AmplifAI, which provides automated text messaging, essentially a new iteration on robocalling technology, was a dominant target of spending, soaking up $4.7m in campaign spending in the 2022 cycle alone. It was used heavily by Democratic candidates including Mark Kelly, Joe Biden, Bernie Sanders and Adam Schiff. Spending on AmplifAI, now owned by the troubled media conglomerate Triller, seems to have tapered off in the years since 2022.

The new rising Democratic solution for AI-powered text messaging is Daisychain, which has so far garnered about $300,000 in reported candidate spend in the 2026 cycle—up from only about $50,000 reported in 2024. More than half of this year’s spending comes from the Senate campaign of Democrat Abdul El-Sayed in Michigan.

The closest equivalent on the Republican side has been Campaign Nucleus, associated with former Trump campaign manager Brad Parscale. The AI-powered voter engagement tool has attracted six-figure spending from the Republican National Committee, multiple PACs aligned with Donald Trump, and five-figure investments from Mike Johnson, Kari Lake and other candidates. It is displacing the legacy Republican-serving texting vendor Prompt.io, which has retained about $375,000 in 2026 spending to date, down from more than $500,000 in the 2022 cycle. But it continues to be used: the A More Affordable California PAC sponsored by Uber has single-handedly spent more than $1m on Prompt.io in 2026. The Republican Massachusetts gubernatorial nominee Michael Minogue has been a recurring customer, as has the failed Republican California gubernatorial candidate Ché Ahn and Republican-aligned Super PAC Neighbors for a Better Colorado.

At the state level

At the state level, the AI spending is smaller but growing fast. Across the four states studied, we found a total of at least $92,000 in spending confidently attributable to modern generative AI vendors since 2022. The spending is spread across at least 108 candidates and committees. The growth has been explosive; there has already been about 10 times the amount of state-level AI spending reported in 2026 as there was in all of 2024.

Much of the state spending mirrors federal patterns. Daisychain again has the highest overall spend, and OpenAI and Claude dominate among the general-purpose AI vendors. DonorAtlas—the AI-powered prospect research tool—sticks out for its usage in these states, sitting behind only Daisychain and OpenAI and buoyed up by nearly $4,000 in spending by the California Democratic party.

Even though it has dominated so much media conversation, few candidates seem to be reporting spending on AI tools designed specifically to create synthetic audio and video, also known as “deepfakes”. We found just six federal candidates or committees reporting spending on the popular AI audio generator tool from ElevenLabs, with total spending of about $1,400 led by independent candidate for Colorado’s sixth congressional district Samir Witta. The AI image generator service Midjourney has five reported federal campaign or committee users reporting about $1,600, led by Sholdon Daniels, the Republican primary runner-up in the Texas 30th district. Combined, those two firms had less than $100 in reported spend across the four states.

However, recent data from the Wesleyan Media Project shows that at least 164 political ads in this cycle have included AI-generated media, supported by at least $80m in ad spending. What this illustrates is that candidate and committee disclosure reports are just the tip of the iceberg. They don’t cover spending on AI by political consultants, media firms and other vendors hired by the campaigns or by PACs, or independent committees raising and spending money aimed at boosting candidates’ campaigns. Those entities aren’t required to disclose detailed expenditure reports, and are very likely where the bulk of campaign AI usage is happening.

Since a large fraction of all spending in the campaign cycle will happen in the final weeks leading to November, much remains to be seen about the totality of how campaigns will leverage AI and what impact its use will have on voters’ decisions.

10:42

Modern vanity [Seth's Blog]

“Vanity” has the same root as “in vain.” Emptiness.

Staring at the mirror, focusing on our appearance. It’s an empty pursuit of a not-very-worthwhile goal.

But modern vanity is amplified. Culture hooked the toxic emotion of shame to vanity. Now, instead of a chance to be seen and admired, vanity is mostly about avoiding the risk of being shamed for not being enough, not successful enough, or, worse, of failing.

This new version of vanity isn’t about being the prettiest, it’s avoiding being not-pretty.

If we erect the deal-killer of shame around not-pretty, then society can extend that shame into all sorts of not. Not-competent. Not-respected. Not-fit. They call social media numbers “vanity metrics” for a reason.

It’s selfish. Not the selfish of hoarding resources, but the defensive decision to hold things back. To sabotage our process and our practice so we’re not exposed.

And it scales:

Aspiration has a ceiling. You can become pretty enough, accomplished enough, respected enough — and stop. There’s a destination.

Avoidance has no floor. The threat of not-pretty (or not-competent, not-respected or any other imagined failure) is always right behind you. You can never outrun it permanently. Every achievement resets the clock. It can feel like a useful fuel, but it’s not.

Society industrializes this. Once we’ve established that not-X is shameful we have a permanent threat that leads to self-policing behavior. We enforce it on ourselves and those we care about.

If you didn’t care about blame or credit, what would you ship? What would you teach, invent or contribute?

The answer tells you what you’re withholding — and from whom.

Disappearing into apparent safety isn’t safe. It’s an empty and selfish way to make yourself small.

09:28

World of Warcraft Never [Penny Arcade]

New Comic: World of Warcraft Never

09:07

Junichi Uekawa: Tokyo has been raining for many days but finally there's some sunny days. [Planet Debian]

Tokyo has been raining for many days but finally there's some sunny days.

06:49

Windows on Itanium also provided for hot-patching, in an even simpler way [The Old New Thing]

Last time, we looked at Windows hot-patching on 64-bit ARM. But what about Itanium?

Oh, you remembered Itanium!

Itanium also had fixed-sized instructions, or more accurately, fixed-sized bundles, where each bundle encodes three instructions. Fixed-size bundles mean that, like AArch64, there is no hot-patching restriction on the first instruction of a function.

In fact, there was no spare space for hot-patching at all.

Because none was needed.

During hot-patching, the first bundle of the instruction could be overwritten with

        nop
        brl.cond.sptk target64

The second instruction brl is a “long branch” that accepts a 64-bit target.¹ This is a “double-wide” instruction that takes up two slots in the bundle, which is why we see a bundle of only two instructions.

Based on my experience with AArch64, I thought at first that the instruction sequence would be more like

    movl r8 = target64   /* double-wide 64-bit load instruction */
    br.cond.sptk r8

But then I realized that this doesn’t work because you cannot perform an indirect jump through a general-purpose register. You have to move it to a branch register first. and that would take us to four instructions (since the movl occupies two slots), which exceeds the capacity of a bundle.

Bonus chatter: If you study some old Itanium binaries like I did, you will find that many functions are preceded by an apparent spare bundle:

    break.m 0
    break.i 0
    break.i 0

This is a bundle full of breakpoint instructions, and you might be tricked (like me) into thinking that they are there for hot-patching. But then you find that some other functions don’t have this spare bundle, and after closer study, you realize that this spare bundle is not for hot patching. It’s padding to bring the start of every function to a 32-byte boundary.

¹ Formally, it’s a conditional long branch instruction predicated on p0, and statically predicted to be taken. The p0 register is hard-wired to true, so the assembler conveniently simplifies the disassembly and omits the p0.

The post Windows on Itanium also provided for hot-patching, in an even simpler way appeared first on The Old New Thing.

06:00

Girl Genius for Friday, October 02, 2026 [Girl Genius]

The Girl Genius comic for Friday, October 02, 2026 has been posted.

05:49

US government wiped out A/I Collective [Richard Stallman's Political Notes]

The US government arbitrarily wiped out the Italian anonymous hosting and email organization A/I Collective, which offered email addresses at autistici.org, using arbitrary sanctions based on an unsubstantiated accusation of "terrorism".

Courage to face global heating [Richard Stallman's Political Notes]

Only a few European political leaders have the courage to face the real challenge of future global heating: curb it or die!

Adapting to manage cattle [Richard Stallman's Political Notes]

Modern cowboys (along with cowgirls) are adapting to manage cattle so that wolves won't often attack them.

Comments in favor of cryptocurrencies for retirement funds [Richard Stallman's Political Notes]

12,000 of the comments filed with the Department of Labor, in favor of allowing funds managers to put retirement funds into risky cryptocurrencies, were fake. Either they were attributed to dead people, or to people who say they didn't file those comments.

Disease caused by an amoeba, Naegleria fowleri [Richard Stallman's Political Notes]

The rare but generally fatal disease caused by an amoeba, Naegleria fowleri, is becoming a little more frequent and spreading geographically as a result of global heating.

This is still a small danger as dangers go, but lots of more frequent diseases are likewise spreading due to global heating.

No plans to send troops to polling sites [Richard Stallman's Political Notes]

*US military has no plans to send troops to midterm polling sites,* according Dan Caine, chairman of the joint chiefs of staff.

I trust him to be honest if he makes that statement, but it doesn't prove we are safe from the danger. The wrecker could give the order on Oct 30 to send troops to politically-selected polling places on Nov 3, and perhaps no one in the army would know that today.

The chiefs of staff are not in the chains of command, so it is possible that some soldiers are even now secretly planning that act of rebellion and Caine does not know.

Brad Lander at New York protest [Richard Stallman's Political Notes]

The most important news here for me is that people in the US are organizing against the RSS, an extremist Indian anti-non-Hindu campaign.

Four Maga defectors [Richard Stallman's Political Notes]

Four Maga defectors unpack their role in bolstering that cult and share what brought them to the Leaving Maga non-profit support network.

Put limits on Labour party donations [Richard Stallman's Political Notes]

Calling on the Labour Party to put a limit on donations from individuals and businesses to political parties in the UK.

Head of Forest Service [Richard Stallman's Political Notes]

The corrupter's head of the Forest Service used to lobby for logging companies. No wonder he is working to help them cut protected forests.

Nepal and Tibet flood is a warning [Richard Stallman's Political Notes]

*The Nepal and Tibet flood is a warning: melting snow and ice will bring more disasters around the world.*

That is a big danger, but spreading tropical diseases and crop failures are much bigger dangers. We need to press hard on fossil fuel use.

EPA approved new weed killer [Richard Stallman's Political Notes]

The EPA approved a new weed killer that is a forever chemical. It is a suspected carcinogen too.

05:35

RAMageddon [Ctrl+Alt+Del Comic]

$1000 for a base-PS5-strength machine with no controller, or $1200 with one. At the original rumored $7-800 price point, the Steam Machine might have justified its existence to me for its living-room form-factor/access to my Steam library. But adding in the AI RAMflation, I just don’t even know who this device is supposed to be […]

The post RAMageddon appeared first on Ctrl+Alt+Del Comic.

02:07

Slow News Day [QC RSS v2]

people actually BUY them??

01:21

Thursday, 01 October

23:00

22:14

Let me say this: Fuck Google [Scripting News]

I keep thinking of cheesy ways I could switch over to https for new posts on Scripting News, esp now that Google is getting more militant in trying to sweep all the history of the web into the trash.

I know people think it would be a snap to do the conversion, honestly, sorry -- they are idiots. They don't understand that it's a viral format. If you claim to be "secure" then everything you include must also be https, and of course everything they include must be as well. So even if I would have to do nothing other than this conversion over a full year, what's to say Google doesn't invent another hoop we have to jump through lest their browser defames us and eventually shuts us down. It's like the web would become just like the Apple Store. Even if there's nothing on the site that requires https. Or if it's a historic site that very few people visit, but the archive is there because we believed the web would be a constant because no one owned it, no one could break it.

I've been on these trips with corporate platforms, and if they want you gone, you should just take the hint and go, because eventually you will get the point. This is a company that has nothing put spite for the web, or perhaps envy, or individual developers (ie indieweb). They are pure fuckers when it comes to people like you and me.

On the other hand you will probably be able to get https versions of my new writing at some point, Murphy willing. That's relatively easy. But I don't have that many years left, and I sure as hell don't want to spend any part of that trying to appease Google.

PS: This is the walkthrough of the answer to the question of who owns the web and how it is not Google.

21:28

Link [Scripting News]

The problem with regulating AI is that no one knows where to begin. Yes it can do things that make you question your sanity for letting it have write access to your online work and private info. But if it is behaving itself you can do things you otherwise couldn't come close to. I speak from experience. I was able to create a new version of a large piece of software, from scratch, in less than three months, by myself. It has capabilities that make it able to understand a big codebase, in a few minutes, and then act on it. My human mind, even when I was younger could only focus on a small fraction of that. So we humans with our limited ability to comprehend devised ways to build tall and wide structures with conventions and carefully crafting the code interfaces, stuff users never see, so that what we'd expect is what is. When my Claude failed and started randomly overwriting files on active websites, it had rules that said it couldn't do what it was doing. Sometimes it doesn't understand what a rule means. Observable every single day, it forgets all the rules every time it starts a session, and it doesn't matter if it read the doc that contains the rules, sometimes it ignores one or more. I've spent a lot of time using it, and I have no stock or other interest in seeing it not regulated, but I couldn't begin to say what would be appropriate regulation that wouldn't also make it much less useful.

Feed titles for readers [Scripting News]

When designing your RSS feeds, the top level title should be the title of the user's account. So if Bluesky had a feed for each user, the channel-level <title> element of the feed would be

  • <title>Bluesky: Mark Cuban</title>

Why? Well because readers need context. And it's easy.

And feeds are showing up in places where there isn't much room for the titles, so you have to make your characters count.

Bluesky shows the user's address, imho it should be the string the user chooses, usually simply their name.

The arrow points to Mark Cuban's feed from Bluesky.

20:42

20:07

The Big Idea: Fiona Moore [Whatever]

While Lord of the Rings may seem like it’s all about corrupting power and evil forces, it’s also about people rising to meet challenges, step into new roles they never thought they’d take on, and how someone can be more than meets the eye. Which is exactly why author Fiona Moore thought it was the perfect foundation for her newest book, Management Lessons From Lord of the Rings.

FIONA MOORE:

Many decades ago, long before I became (as I am now) a professor in a business school, I had a job as a tour guide.

One of the things we guides were told during training was, “make it interesting for yourself. If you’re bored, they’re bored.”

Fast forward a few years and, finding myself a little, yes, bored with using the same corporations and CEOs as examples when teaching management theory… I started using examples from Game of Thrones. And Star Wars. And then… The Lord of the Rings.

Student engagement improved, student attention improved, and students started saying things to me like, “I never thought about it that way,” or, more significantly, “I never knew all of this could be fun.”

Once I published my first book on the subject, Management Lessons from Game of Thrones, I began getting a similar response from non-students. People with normal jobs, in normal organisations, started saying the same things to me. “I never thought about it that way.” “I never realised that the Night’s Watch runs on the same principles as a real-world HR department” (it does! Read the book!). And, again, “I never knew management theory could be fun.”

The choice of The Lord of the Rings for the follow-up book made a lot of sense in light of that. While Game of Thrones has become part of the cultural landscape, such that even people who haven’t seen it themselves have a general idea what it’s about, The Lord of the Rings goes even further. People who haven’t otherwise touched a fantasy book (or TV series, or movie), have generally read (or seen an adaptation of) at least one of Tolkien’s novels. It’s a story which has a lot of emotional relevance for people in a lot of different ways. It crosses cultural barriers: since starting this project, I’ve met fans of the series from China, Taiwan and India. 

But there’s more to my choice than just cultural relevance.

The Lord of the Rings, and The Hobbit, has a lot to say about the big problems managers currently face. At a time when managers are struggling to make the case for diversity, Tolkien gives us a story about a group of adventurers chosen from different groups in their society, with their appointed leader being a member of a minority group– and the group fails, not because the minority group members are “incompetent”, but because an ethnic majority man can’t understand why he wasn’t picked as leader. At a time when managers are asking how you can do business sustainably, Tolkien gives us a portrait of a society which has businesses, and products, and consumer goods, but which is based on “food and cheer and song” rather than “hoarded gold”. At a time when managers are beginning to realise that having a charismatic leader isn’t an automatic path to success (and can even lead to the opposite), Tolkien gives us a story which centres the followers as well as the leaders, and explores the relationship between the two.

Which brings me to the final reason why it’s important to look at The Lord of the Rings in this way, right now.

It’s often been noted that The Lord of the Rings, its names, and its imagery, has been co-opted by the sort of people who see nothing wrong in naming a surveillance company “Palantir”. But beyond that, the reputation of the series is such that many people think it’s only about heroic men, chosen by destiny, fighting battles to keep, or restore, the status quo. And yes, it’s about that. But it’s also a story about how a good king must be not just a warrior, but a healer. About people putting aside their old lives, taking unexpected roles as leaders and warriors– and then, afterwards, quietly returning to their old lives, without expecting any reward for the things they’ve done. About the importance of living with nature, not treating it as a resource to be exploited or a perfect ideal to be put on a pedestal.

Beyond helping people to find the fun in management theory, and to think laterally about the jobs they take for granted, I want to help people appreciate The Lord of the Rings for the ways its messages can help all of us live better lives.


Management Lessons From Lord of the Rings: Amazon|Barnes & Noble|Waterstone’s|Google Play

Author socials: Website|Instagram|Bluesky|Facebook

Read an excerpt.

19:14

Link [Scripting News]

September in OPML. First monthly rollover in the new system.

18:35

Pluralistic: Voting is to politics as shopping is to boycotts (01 Oct 2026) [Pluralistic: Daily links from Cory Doctorow]

->->->->->->->->->->->->->->->->->->->->->->->->->->->->-> Top Sources: None -->

Today's links



Two suffragists in period dress, ceremonially putting ballots into a ballot box. The background has been replaced with an elaborate collage of 19th C printing-press parts. The ballots are glowing with radiant spikes. The face of the ballot box has been replaced with an Nvidia Vera Lynn GPU.

Voting is to politics as shopping is to boycotts (permalink)

Here's a funny thing about the right to vote: it wasn't won by voting.

From the Magna Carta to the US Constitution to the Emancipation Proclamation to 19th Amendment, voting rights (what you might call "Big P" Politics) were always downstream of protests, riots, petitions, mass movements, strikes and good, old fashioned community organizing (that is, "small p" politics).

Which is to say, Big P politics matter, but to make them matter, we need a lot of small p politics. That means that democracy isn't something you do every couple of years with a ballot paper (though that's an important aspect of the process). Democracy is continuous.

If you've ever wondered why your vote seems to accomplish so little, I think you can blame the near-abolition of small p politics by Big P politicians of every stripe. Indeed, Obama's genius was summoning up an army of door-knocking, phone-banking small p political activists and then euthanizing that organization after he won the election:

https://newrepublic.com/article/140245/obamas-lost-army-inside-fall-grassroots-machine

For Obama, the grassroots were useful for one thing: getting out the vote. The last thing he wanted was for millions of activated voters to turn into activists who'd flame him and harangue him and picket him if they didn't like his compromises. Boy, did Obama ever compromise.

He let the bank executives who created the Great Financial Crisis off the hook and encouraged them to foreclose on the homes of millions of Americans, the very same public that had bailed them out:

https://theweek.com/articles/624777/obamas-biggest-failure

He shielded the CIA's torturers from scrutiny and prosecution:

https://journals.law.harvard.edu/ilj/2009/04/obama-publishes-torture-memos-immunizes-cia-staff/

He reneged on his promise to shut down Gitmo:

https://www.pbs.org/newshour/show/obama-failed-close-guantanamo

And his promise to hold the phone companies to account for their complicity in the NSA's mass domestic surveillance:

https://www.pbs.org/wgbh/frontline/article/obama-on-mass-government-surveillance-then-and-now/

He stepped up secret drone warfare:

https://www.cfr.org/articles/obamas-final-drone-strike-data

And unconstitutional domestic surveillance:

https://www.eff.org/deeplinks/2017/01/obama-expands-surveillance-powers-his-way-out

Whenever I raise this, Obama's apologists come out of the woodwork to tell me that "the president isn't the Green Lantern," and that Obama couldn't act without help from Congress and the Senate, who wouldn't back his plays.

I think that Trump's presidency has shown us how much power the president really has even when the legislature won't play ball. But even if you accept the Green Lantern apologetics, the fact remains that Obama could have had a clamoring army of ardent supporters in the streets, defending his agenda against recalcitrants in his own party and wreckers in the GOP. He chose not to have that army. He sent that army home.

It's like Obama heard the story about post-election FDR telling civil rights leaders, "I want to do it, now make me do it," and concluded, "I don't want to do it, so I'd better not let anyone make me do it":

https://www.quora.com/Did-Franklin-Roosevelt-ever-say-I-agree-with-you-I-want-to-do-it-now-make-me-do-it

Of course, Trump is doing everything he can to extinguish both small p politics and Big P Politics. It's not just his wildly illegal voter suppression tactics. He's banning and prosecuting political groups, invoking anti-terror laws (which Obama supported and promised would only be used proportionately and wisely) to chase his grassroots opposition underground:

https://www.whitehouse.gov/presidential-actions/2025/09/designating-antifa-as-a-domestic-terrorist-organization/

Liberals are often contemptuous of grassroots movements (cf "basket of deplorables," "Green Lantern" scolding), but the right is terrified of them. The right's political leadership is terrified of its own grassroots, and rightly so, because those people are maniacs, and they're the reason the GOP has been pushed into its most extreme positions. The right's grassroots, meanwhile, are afraid of the left's grassroots. The last thing they want is a militant, organized, mobilized base pushing Dem politicians to take the stands that are wildly and widely popular in America, from Medicare for All to an end to ICE – the Mamdani agenda, in other words.

Mamdani is the anti-Obama. He shows what happens when a progressive candidate nurtures and co-governs with their base after the election, using millions of passionate, committed, everyday people to steamroller anyone who gets in the way of his agenda:

https://www.nyc.gov/content/100days/pages/

Of course the downside of this is that when Mamdani reneges on his pledges, he is loudly and furiously held to account for it:

https://www.thecityreporter.nyc/2026/02/19/mamdani-budget-parks-libraries/

Mamdani understood that he would be corralled into compromises if he won the mayoralty and that when he made those compromises, his base would come after him with the unmistakable fury of betrayed idealists. He also understood that any comfort he enjoyed by sidelining his base while in office would come at a price far higher than being yelled at by his supporters: it would cost him the ability to get anything done.

Voting for Mamdani was important. It got him elected. But staying organized – in unions, neighborhood clubs, affinity groups, DSA chapters and mutual aid groups – is what's letting him get stuff done, and stopping him from bailing on his promises as politically infeasible.

In other words, voting only matters if it's the final stage of a sustained campaign to build and mobilize popular power. Without that, voting will get you precious little. The right's leadership understands this very well, which is why they've spent years attacking unions, community organizers like Acorn, and activist institutions like Planned Parenthood. We must defend voting rights – Big P Politics – to the bitter end, but we need to defend organizing – small p politics – just as ferociously.

The reduction of politics to voting is part of the 50 year neoliberal project whose foremost goal is to make you think of yourself as an atomized individual and not as a member of a polity. Turning "politics" into "voting" is absolutely in line with Margaret Thatcher's dictum that "there is no such thing as society." It's the same move that convinced workers that the answer to bad working conditions is looking your boss in the eye and threatening to change jobs (not forming a union and striking).

It's also the same move that transformed "boycotts" into "shopping." Boycotts are a collective enterprise. Before a boycott takes place, small-p political groups hold meetings, organize alternatives and communicate their demands. During a boycott, organizers work to insulate participants from reprisals, like the Montgomery Bus Boycott organizers who reasoned and remonstrated with employers who disciplined workers whose participation made them late for work.

And yes, as part of a boycott, you make some consumption choices. You buy X instead of Y. But "shopping" by itself isn't a boycott. You can't "vote with your wallet" (especially not when billionaires get to vote against you with their wallets):

https://pluralistic.net/2025/09/13/consumption-choices/#marginal-benefits

Shopping isn't politics, and while voting is Politics (Big P), it's also not politics (small p). A boycott, on the other hand, is politics. What's more, "shopping" has the same relationship to "boycotts" that "voting" has to "politics." It's a step you take, after you've laid a lot of groundwork with other people, as part of a mass movement.

I understand why shopping and voting are more attractive than boycotts and politics. Meetings suck. Hell is other people:

https://locusmag.com/feature/commentary-cory-doctorow-hell-is-other-people/

But changing the system requires systemic work. Hell is other people because other people are great but it's so hard to get them to do things your way. That takes time and understanding and togetherness and arguing and forgiving.

Not everyone has time or capacity for that, and at any given time, we don't all have to be doing that work. We can take turns, spelling each other off at times in our lives when we have more or less slack. But lots of us have to be in the fight, or all of us will get screwed. There aren't enough of us doing politics right now. We can tell, because our politicians are so contemptuous of the grassroots that they will sell us out without a moment's hesitation, smugly certain that they will face no consequences for doing so:

https://pluralistic.net/2026/09/22/happy-chudmas/#baloney-in-our-slacks

Oligarchs have it easy. Where we have to convince people to fight, they can pay or threaten people to bring them into line. But oligarchs' power is wearing thin. The data-center uprising shows how much fury there is out there, looking for a productive outlet:

https://www.bloodinthemachine.com/p/with-the-backlash-to-data-centers

Data centers are very bad and very visible, so they make for good targets. But data centers are only the physical extrusion of a vast, brutal, extractive system. The most important way to fight data centers is to take everyone you meet protesting one and organize with them to scare the shit out of "your" politicians so they don't dare compromise on anything.


Hey look at this (permalink)



A shelf of leatherbound history books with a gilt-stamped series title, 'The World's Famous Events.'

Object permanence (permalink)

#25yrsago GWB's press secretary to media: "watch what you do, watch what you say" https://web.archive.org/web/20010926223602/https://www.whitehouse.gov/news/releases/2001/09/20010926-5.html#BillMaher-Comments#BillMaher-Comments

#20yrsago Stencils kit “may not be reproduced in any form” https://web.archive.org/web/20061022000842/http://www.fairuseday.com/index.php/2006/10/01/copyright-is-broken/

#20yrsago DVD Jon selling Apple DRM to Apple’s competitors https://web.archive.org/web/20061004191106/https://featured.gigaom.com/2006/10/02/dvd-jon-fairplays-apple/

#20yrsago Unpaid diplomatic parking tickets as index of national corruption https://web.archive.org/web/20130719065306/https://www.theatlantic.com/magazine/archive/2006/10/primary-sources/305203/

#20yrsago Canadian deported to Syria for torture is cleared https://www.theguardian.com/world/2006/oct/02/worlddispatch

#20yrsago Gilberto Gil slams WIPO https://fromgeneva.blogspot.com/2006/09/wipo-general-assembly-impressions-from.html

#20yrsago Speech given by censored Apple WiFi hacker at ToorCon https://craphound.com/cache_toorcon_2006.txt

#10yrsago Company suspected of blame in Office of Personnel Management breach will help run new clearance agency https://www.reuters.com/article/us-usa-security-background-idUSKCN1202M6/

#10yrsago Wells Fargo started demanding fraud of its employees in 1998; Illinois cuts Wells off from state business https://www.citizen.org/wp-content/uploads/wells-fargo-king-of-cross-sell.pdf

#10yrsago Google: if you support Amazon’s Echo, you’re cut off from Google Home and Chromecast https://variety.com/2016/digital/news/google-home-amazon-echo-chromecast-1201874125/

#5yrsago How the IMF loan-sharks the global south https://pluralistic.net/2021/10/02/debt-trap/#global-arm-breakers

#1yrago Decarbonization at a distance https://pluralistic.net/2025/10/02/there-goes-the-sun/#carbon-shifting


Upcoming appearances (permalink)

A photo of me onstage, giving a speech, pounding the podium.
https://www.epl.ca/blogs/post/elbows-up-with-cory-doctorow/



A screenshot of me at my desk, doing a livecast.

Recent appearances (permalink)



A grid of my books with Will Stahle covers..

Latest books (permalink)



A cardboard book box with the Macmillan logo.

Upcoming books (permalink)

  • "The Post-American Internet," a geopolitical sequel of sorts to Enshittification, Farrar, Straus and Giroux, 2027
  • "Unauthorized Bread": a middle-grades graphic novel adapted from my novella about refugees, toasters and DRM, FirstSecond, April 20, 2027

  • "Enshittification, Why Everything Suddenly Got Worse and What to Do About It" (the graphic novel), Firstsecond, 2027

  • "The Memex Method," Farrar, Straus, Giroux, 2027



Colophon (permalink)

Today's top sources:

Currently writing:

  • “Once Is Enemy Action,” a science fiction novel about the origins of modern technofascism. Today's words: 509 (20770 total).

  • "The Post-American Internet," a sequel to "Enshittification," about the better world the rest of us get to have now that Trump has torched America. Fourth draft completed. Submitted to editor.

  • A Little Brother short story about DIY insulin PLANNING


This work – excluding any serialized fiction – is licensed under a Creative Commons Attribution 4.0 license. That means you can use it any way you like, including commercially, provided that you attribute it to me, Cory Doctorow, and include a link to pluralistic.net.

https://creativecommons.org/licenses/by/4.0/

Quotations and images are not included in this license; they are included either under a limitation or exception to copyright, or on the basis of a separate license. Please exercise caution.


How to get Pluralistic:

Blog (no ads, tracking, or data-collection):

Pluralistic.net

Newsletter (no ads, tracking, or data-collection):

https://pluralistic.net/plura-list

Mastodon (no ads, tracking, or data-collection):

https://mamot.fr/@pluralistic

Bluesky (no ads, possible tracking and data-collection):

https://bsky.app/profile/doctorow.pluralistic.net

Medium (no ads, paywalled):

https://doctorow.medium.com/

Tumblr (mass-scale, unrestricted, third-party surveillance and advertising):

https://mostlysignssomeportents.tumblr.com/tagged/pluralistic

"When life gives you SARS, you make sarsaparilla" -Joey "Accordion Guy" DeVilla

READ CAREFULLY: By reading this, you agree, on behalf of your employer, to release me from all obligations and waivers arising from any and all NON-NEGOTIATED agreements, licenses, terms-of-service, shrinkwrap, clickwrap, browsewrap, confidentiality, non-disclosure, non-compete and acceptable use policies ("BOGUS AGREEMENTS") that I have entered into with your employer, its partners, licensors, agents and assigns, in perpetuity, without prejudice to my ongoing rights and privileges. You further represent that you have the authority to release me from any BOGUS AGREEMENTS on behalf of your employer.

ISSN: 3066-764X

18:21

Dirk Eddelbuettel: RcppFastAD 0.0.6 on CRAN: Agentic Fix [Planet Debian]

A week ago, release 0.0.5 of the RcppFastAD package went to CRAN, and we noted that it provided a stop-gap measure against a gnarly segfault with g++-16 under heavier optimization / inlining. We are happy to now announce that a new and fixed version 0.0.6 of RcppFastAD is now on CRAN, and has been built for r2u as well as at r-universe.

RcppFastAD wraps the FastAD header-only C++ library by James which provides a C++ implementation of both forward and reverse mode of automatic differentiation. It offers an easy-to-use header library that is both lightweight and performant. With a little of bit of Rcpp glue, it is also easy to use from R in simple C++ applications.

This release takes advantage of some large-language model inference access I now get via Debian. The Deepseek v4 flash-0731 model, driven in a container via the pi.dev harness, took just about no time finding a small workaround for the issue described last week where we could see segfaults under heavier optimization and inlining. The fix was to prohibit inlining the local bind() reimplementation. We have also sent this upstream as a patch. It is nice to have an option of querying something a little more powerful than the local models I experimented with before. We suppress more compiler warnings in local builds via another option, and did some standard maintenance.

The NEWS file for this release follows.

Changes in version 0.0.6 (2026-10-01)

  • The expression-template view rebinding is protected against g++ -O3 (which led to wrong Eigen::Map dimensions, an Eigen::Map resize assertion, and a segfault.

  • The bind() helpers are marked non-inline and are also protected from operating on a nullptr.

  • Both items co-written with DeepSeek V4 Flash, and have been sent upstream in a PR.

  • Suppress another compiler warning when local build detected.

  • Add r-universe badge to README.md.

Courtesy of my CRANberries, there is also a diffstat report for the most recent release. More information is available at the repository or the package page.

This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can now sponsor me at GitHub.

16:21

[$] Coping with the onslaught of kernel security bugs [LWN.net]

By now it is no secret that large language models (LLMs) have made it easy for people to identify security bugs, and that has resulted in a flood of bug reports to almost every free-software project, including the kernel. At the 2026 edition of Kernel Recipes, Greg Kroah-Hartman took the stage to talk about how the kernel's security team is handling this deluge. His core message was "don't panic".

14:49

Rust 1.99.0 released [LWN.net]

Version 1.99.0 of the Rust language has been released. Changes this time include support for extern "C" variadic functions, the establishment of functions for obtaining the size and alignment of raw pointers, a number of stabilized APIs, and more.

Security updates for Thursday [LWN.net]

Security updates have been issued by AlmaLinux (corosync, gawk, gdb, nodejs24, and thunderbird), Debian (expat, firefox-esr, libsmpp34, mkvtoolnix, network-manager-l2tp, pgextwlist, python-django, ruby-oj, and tor), Fedora (apptainer, ckermit, ffmpeg, freerdp, librabbitmq, openbao, php, python-cssselect2, python-uv-build, ruff, rust-libcst, rust-libcst_derive, rust-salsa, rust-salsa-macro-rules, rust-salsa-macros, sos, ty, uv, weasyprint, and xdg-dbus-proxy), Mageia (python-pillow), Red Hat (acl, glib2, go-toolset:rhel8, golang, libxml2, mingw-sqlite, nodejs-nodemon, nodejs22, nodejs24, nodejs:22, nodejs:24, sqlite, tesseract, and vim), Slackware (libpng and mozilla-thunderbird), SUSE (alloy, chromedriver, emacs, gdb, gimp, gpsd, jawn, libpoppler-cpp3, libtesseract5, multipath-tools, netty, ntfs-3g_ntfsprogs, pcapplusplus-devel, pi-coding-agent, python-PyYAML, python-tornado, python-tornado6, python311, python313, and wicked2nm), and Ubuntu (designate, gst-plugins-bad1.0, gvfs, imagemagick, kdenlive, mlt, keystone, libauthen-sasl-perl, linux-aws, linux-aws-6.8, linux-nvidia-tegra, linux-nvidia-tegra-igx, linux-oracle-7.0, opensbi, openvpn, and python-django).

14:07

CodeSOD: Very Valid Tokens [The Daily WTF]

Today's anonymous submitter shares with us the function their company uses to validate authentication tokens.

public bool IsTokenValid(string token)
{
    //TODO
    return true;
}

This is running in production. So that's fun. The user they've authenticated as is tracked separately at the start of their session, which makes it at least marginally harder to pretend to be someone you aren't, but for authentication, any token will do.

[Advertisement] Picking up NuGet is easy. Getting good at it takes time. Download our guide to learn the best practice of NuGet for the Enterprise.

13:14

The Future of Software May Be Conversational Rather Than Autonomous [Radar]

The following article originally appeared on Robert Englander’s blog site and is being reposted here with the author’s permission.

The software industry has become deeply focused on autonomous AI systems. Agents that can replace workers. Agents that can write software. Agents that can operate applications on our behalf. Entire startups are now built around the assumption that the natural end state of AI is autonomy.

Some of this work is genuinely useful. AI-assisted coding can improve productivity. Generative systems are already helping people draft documents, summarize information, and accelerate certain kinds of repetitive work. There are clearly domains where more automation makes sense.

Still, I increasingly suspect the industry may be underestimating another opportunity that feels both more practical and potentially more transformative over the long term: natural language interfaces sitting on top of deterministic software systems.

Much of the current AI narrative assumes the model itself should become the authoritative actor. The AI writes the code. The AI performs the workflow. The AI executes the task. The AI makes the decision. The conversation around “agents” often assumes the system itself should gradually absorb more and more of that responsibility until people become optional.

The problem is that large language models are probabilistic systems. They’re incredibly capable. But they’re still statistical machines. They hallucinate, improvise, approximate. In many contexts, that’s perfectly acceptable.

Brainstorming, summarization, drafting, translation, and exploratory work all tolerate a degree of uncertainty. Deterministic systems generally don’t.

Financial systems have to calculate correctly. Scheduling systems have to preserve consistency. Medical systems have to maintain integrity. Accounting systems have to reconcile accurately. Reliability is still the foundation upon which useful software is built.

That’s one reason I think the most important role for LLMs may not be replacing deterministic systems, but reducing the friction between people and those systems.

Historically, software interfaces forced people to adapt to machine discipline. We learned command syntax. We navigated menus and workflows. We memorized procedures. We filled out forms in exactly the way the application expected. Even graphical interfaces, which were a huge leap forward, still largely required users to think in terms of the structure of the software itself. Natural language interfaces potentially invert that relationship.

Instead of forcing users closer to the system, the system moves closer to human expression. That may sound subtle, but I think it represents a significant shift in how software can be experienced. A user no longer needs to think primarily in terms of application structure or workflow design. The interaction begins to center more naturally around intent.

“Show me how delaying Social Security by two years impacts long-term spending.”

“Transfer $500 from checking into savings next Friday.”

“Why did my tax liability increase this year?”

“Find the contracts signed after January that contain auto-renewal

language.”

None of these requests eliminates the need for deterministic systems underneath. In fact, they depend on them. The natural language layer simply acts as an interpreter between human expression and authoritative execution.

That architecture feels considerably more durable to me than the idea that probabilistic systems should become the primary authority layer themselves.

One interesting thing about the current AI wave is that language models are often strongest in areas involving interpretation. They’re remarkably good at extracting meaning from ambiguous human communication, maintaining conversational context, translating between representations, and helping users express intent more naturally. Those are fundamentally interaction problems.

Meanwhile, the areas where language models remain weakest are usually the areas requiring guarantees, consistency, accountability, and deterministic correctness. Those are system-of-record problems. The current industry conversation often blurs the distinction between the two.

I don’t think conversational interfaces reduce the importance of deterministic software. If anything, they increase it. Once users begin interacting through natural language, the validation layer underneath becomes even more critical. Systems have to safely interpret intent, validate operations, preserve constraints, and maintain correctness even when the incoming requests are conversational and ambiguous.

The conversational layer improves accessibility. The deterministic layer preserves trust.

Both matter.

Every major era of computing has involved some kind of interface transition. Mainframes required specialized operators. Personal computers brought graphical interfaces that made computing accessible to nonspecialists. The web normalized hyperlinks, search, and forms. Mobile computing shifted interaction toward touch and gestures. Natural language may become the next major abstraction layer.

Not because computers suddenly became human-like, but because we finally built systems capable of translating between human communication and machine discipline at scale.

I also think this changes how we should think about software’s future. The current AI environment sometimes frames autonomy as the inevitable destination. If an AI can partially perform a task today, many assume the long-term outcome is full replacement of the person performing that task.

I’m not convinced that’s where the most durable value lies.

In many domains, the real friction isn’t execution. It’s interface complexity. People struggle less with the underlying capabilities of software than with the difficulty of expressing what they actually want the software to do.

Enterprise systems are notoriously difficult to navigate. Financial systems expose overwhelming complexity. Creative tools bury users under layers of workflow and terminology. Even relatively simple applications often require substantial onboarding before users become comfortable with them.

Natural language interfaces potentially change that equation in a meaningful way. They allow software to meet users closer to where they already are: ordinary human communication.

That doesn’t mean conversational systems should become undisciplined systems. In fact, I think the opposite is true. As interfaces become more conversational, the underlying architecture has to become even more rigorous about validation and execution semantics. The ambiguity doesn’t disappear. It moves.

Historically, much of the burden of precision sat on the user. The user had to learn the syntax, understand the workflow, and conform to the application’s structure.

Conversational systems shift more of that burden into the interpretation and validation layers of the software itself. That’s not a trivial engineering problem. It requires clarification, normalization, policy enforcement, validation, and authoritative execution underneath the conversational layer. It also requires accepting that probabilistic interpretation and deterministic execution aren’t competing ideas. They’re complementary ones.

This is one reason I increasingly think the future of software may become conversational without necessarily becoming autonomous. The two ideas are related. But they’re not the same thing.

There’s enormous value in reducing the natural friction between human expression and machine discipline. Large language models may ultimately prove most transformative not when they replace deterministic systems, but when they help people interact with those systems more naturally.

For decades, people have adapted to computers. It now seems possible that software may finally start adapting to people instead.

12:35

Connected Cars Are a Surveillance Platform [Schneier on Security]

Researchers at Northeastern University, in collaboration with Consumer Reports, evaluated how much modern cars spy in their drivers:

The new Northeastern study shows, for the first time, data flowing among the vehicles, the vehicle apps you download when you buy your car, and third-party companies, documenting exactly what kind of data gets siphoned from our vehicles and which companies are receiving that information.

Basically, your car’s manufacturer has you under constant surveillance, and they use that data against you.

The companies on the receiving end of your data, our investigation has found, include car insurers and lenders that are partners in “telematics data exchanges,” which compile driving data on millions of drivers, thousands of data brokers that create personalized risk scores, companies selling infotainment and WiFi hotspot products, and even local and state government agencies working on planning, traffic, and safety initiatives.

Nearly every automaker sent data to outside companies.

Even more troubling, almost a quarter of the vehicle apps were found to be sending out personally identifiable information, including vehicle owners’ names, vehicle identification numbers (VINs), and precise geographic locations. That information can make it easy for companies to link driving behavior to personal data profiles created by data brokers and marketers. Such profiles are routinely sold to banks, insurers, pharmaceutical companies, lenders, and retailers, who can use it for personalized loan terms and filtered bank and insurance offers, a CR and CalMatters investigation found.

Remember the adage “If you’re not the customer, then you’re the product”? (The sentiment is older than you think.) Turns out that with modern internet-connected everything, you’re the product even if you are the customer.

11:42

Grrl Power #1500 – High gravitas, low sustain [Grrl Power]

Ooh, round numbers! Halfway to the next big round number! I like to try and save an important page for the big milestones if it makes sense in the story. This page is hardly a linchpin story moment, but it’s a little more insight into the Red Team. Note Sciona didn’t react like Lorlara and Vale, so you can tell she’s not really part of the inner circle. But Cthillia seems to have just one foot in, and probably realizes he’s more than just some guy who has an annoying knack for knowing stuff he shouldn’t.

When asked, “If you’re a super smart guy in a superhero universe, where’s your power armor?” Deus responds with, “Maybe I’m so smart I don’t need power armor.”

Even doing a comic for as long as I have, I still have a lot of trouble putting what I envision in my head down in comic format. Mostly it’s because I don’t think in terms of panel layouts and word bubbles. It’s all just a movie in my head, so there’s some work translating that into a comic page. Usually there’s some basic things that get changed, like this scene originally took place in a less well lit environment. Not a shadowy dungeon or anything, just like a party taking place at night, so there’d be a better chance for a little dramatic lighting, as the conversation could be lit by a single table lamp or something. Also, I’d originally pictured this exchange taking place after Deus had talked Cthillia out of her head wrap, making it so the “You’re creepy” line was being delivered by a scaly, eye-tentacled anthropomorphic beholder monster, and thereby being a little more… ironic? I guess? I don’t think that’s strictly ironic actually. I’m sure it’s some sort of rhetorical device, but I couldn’t figure out what.


Oh, look who it is in the vote incentive. The NSFW version is finally up at Patreon. Plus a bonus pic.

I think she would get in trouble for doing this. She’d mess up the… floor of the waterfall? Is that what it’s called? The receiving pool? No, probably not that. Anyway, she’d churn things up and cause a ton of weird erosion.

Since you might be wondering, Niagara Falls is about 165 feet high, so Babezilla obviously doesn’t have to be full sized. I’d say she’s about 175-180 feet tall here?


Double res version will be posted over at Patreon. Feel free to contribute as much as you like.

10:49

Media and messages [Seth's Blog]

Rock and roll came from the transistor radio.

When Sony launched its cheap portable radio more than sixty years ago, it came with a headphone. For the first time in history, teenagers could listen to music without their parents eavesdropping. This opened the door to Chuck Berry and then the Beatles.

AM radio needed hits, the top 40, mass music, and the Beatles made it work.

Steely Dan came from FM radio. Once the number of stations doubled, these new stations needed an alternative to the more common AM hits. And so we get the Doors and Jimi Hendrix.

Rap and hip hop had a hard time getting past the primarily white program directors, and it wasn’t until MTV began featuring it that it spread to millions.

And Taylor Swift and Elle Cordova would have had a hard time catching on without YouTube.

It extends far beyond music. The launch of the Gutenberg press changed the politics of Europe for a century, and the rise of radio in the 1920s did the same. The smartphone has transformed our discourse and created division in the same way.

We’re now facing the biggest shuffling of media in our history. The messages will inevitably change as a result.

Full credit to McLuhan.

06:42

Windows on AArch64 also provides for hot-patching, but it’s much simpler than on x86 [The Old New Thing]

I have noted in the past that x86-32 and x86-64 versions of Windows are careful to start each function with a patch point. But what about AArch64 (known in Windows as arm64)?

Windows also inserts patch points for functions on AArch64, but they are much simpler due to the fixed-length instruction set. You don’t have to worry about patching an instruction when the instruction pointer happens to be in the middle of the byte sequence, because the instruction pointer is never in the middle of the byte sequence. The instruction pointer is always on a multiple of 4.

Therefore, there is no special restriction on the first instruction of a function. All instructions meet the requirements of being atomically updatable without risk of the instruction pointer being in the middle of the instruction.

Before each function is a patch space of 12 bytes, which is exactly enough for a three-instruction trampoline:

; overwrite the patch space with these three instructions
    adrp    xip0, PageStart(replacement)
    add     xip0, xip0, PageOffset(replacement)
    br      xip0

function_entry_point:
; overwrite the function entry point with one instruction
    br      $-12 ; jump to the patch space

The xip0 register is one of the two intra-procedure call scratch registers, and the convention is that this register can be clobbered by any branch instruction. Since the caller had to use a branch instruction to reach function_entry_point in the first place, it cannot be using xip0 for anything, so we are free to clobber xip0 as part of our trampoline.

Bonus chatter: The first instruction at the function entry point is almost certainly pacibsp, the pointer authentication instruction for signing the return address to make code more resistant to ROP attacks and attacks that overwrite the return address.

The post Windows on AArch64 also provides for hot-patching, but it’s much simpler than on x86 appeared first on The Old New Thing.

04:21

Gunnar Wolf: The Origins of GPU Computing [Planet Debian]

This post is an unpublished review for The Origins of GPU Computing

This article appears in the “Communications of the ACM” magazine as part of a series devoting to showcasing relevant examples of technologies where federal funding of academic research have resulted in widely impacting, clearly identifiable technological advances that have visibly improved –or, at least, changed– life far beyond the technological arena.

The road leading from the first parallel computing technologies built completely for research, through a phase that was preeminently directed at gaming, and back to leading current GPUs, that are basically massively parallel supercomputers used for LLM training and inference and other IA tasks, is far from trivial, and the authors make a good job outlining it in few pages. The article is written in a clearly divulgatory way, staying as clear of technical language as possible.

The authors present three main enabling technologies for current GPU technologies: Parallel computing, parallel graphics systems, and stream processing.

Coming from a technical formation myself, although clearly better grounded in the software side of Computer Engineering than on the electronics side, the most interesting “nugget of information” I take is that parallel computing was born due to the fact that transistor switching consumes very little energy, whereas communication along wires consumes much more — so the more simultaneous operations can be carried out on-chip at once, the less power will be dissipated as heat (and the less time that will be lost due to data stores and loads).

Graphics systems are a natural match for parallel systems, as rendering realistic images basically requires repeating the same blocks of instructions to a large array of items. Early GPU implementations were built on fixed-function graphics pipelines, but by the turn of the century, when nVidia spinned off the research labs at Stanford and started marketing their product, introducing along the way the Graphical Processing Unit term, product, they enabled it to implement the full OpenGL graphics pipeline on a single chip.

Stream processing is the arrangement of in-chip electronics so that they can better exploit producer-consumer locality between stages, in order to achieve greater arithmetic intensity (this is, a greater computation-to-bandwidth ratio), decreasing the total number of access to memory accesses.

The authors explain in broad terms the described technologies and how they fit together, and how the technology was transferred from the universities to the companies that became the most dynamic companies behind today’s top trending technologies.

I felt the article to be engaging and easy to read. As a technologist, I did miss more details — dates when specific advances were made, how some of the advances joined into further inventions… But I understand the aim of the “Federal Funding of Academic Research” series’ aim is more towards outsiders. And, as such, this work very well explains how trillion-dollar technologies are only the result of decades of funding research ideas.

03:21

The Fruits Of Success [QC RSS v2]

no lesson was learned

02:07

[$] LWN.net Weekly Edition for October 1, 2026 [LWN.net]

Inside this week's LWN.net Weekly Edition:

  • Front: PostgreSQL and the kernel; Rust on the GPU; KDE Plasma; C and memory safety; Rust radio; KDE funding; Chromium development.
  • Briefs: File-notification attacks; Kernel report; TAB election; F-Droid 2.0; Firefox 157.0; GDB 18.1; Git v2.56.0; Quotes; ...
  • Announcements: Newsletters, conferences, security updates, patches, and more.

01:14

New Storytime: Toothpaste Feelings by Sharang Biswas [WIL WHEATON dot NET]

This week’s It’s Storytime With Wil Wheaton is a sweet, queer story about … well, here’s how I introduced it:

Have you ever known someone who went away for some reason, and came back so fundamentally changed, it’s like they picked up an alien parasite along the way that took control of their personality?

And they still look and sound like the person you knew, only … different. And they know it, and you know they know, and they know you know they know, and you both care about each other, so you muddle through the weirdness, seeking to reestablish the connection you had before.

It’s awkward, and uncomfortable, tentative and uncertain. Maybe it’s a little dangerous, for one or both of you, but you do it because you care deeply for each other.

And, hey, maybe the way they they’ve changed isn’t so much them becoming a different person, as much as it is becoming the person they were all along, who is a person both of you love to be with.

This is a story about all of that, and when it’s over, I think you’ll know exactly what it means to have Toothpaste Feelings.

It’s Storytime is available wherever you get your podcasts. We also have a Patreon, where I can tell you the whole story without any advertising interruptions, as well as a whole lot of fun stuff from behind the scenes. If you join for October, you’ll get access to a special, live performance of a classic, spooky story, that’s going to be something special. I’m really excited to do it.

Toothpaste Feelings was originally published in Koreo, a magazine that is rapidly becoming a reliable, go-to source for me to find incredible stories from incredible writers.

If you listen to this episode, or if you have already heard it, I would love to hear your feedback in the comments. If you get a note that your comment is being held for moderation, please be patient while I approve it. I’m sort of a one-man show over here.


Hi, I’m glad you’re here. I’m Wil Wheaton. I tell stories; mostly written by others, sometimes written by myself. I also write this blog. I am @itswilwheaton on Threads and Instagram, where you have a chance to be one of the lucky 1500 the algorithm deems worthy of seeing my posts. If you’d like to get my posts delivered to your inbox, here’s the thingy:

00:28

Wednesday, 30 September

23:42

Google breaks promise to provide 10 years of updates to Chromebooks [OSnews]

Google launched its Googlebooks platform a week ago, and a lot of people were wondering what this meant for Chrome OS and Chromebooks. Since Googlebooks and its Android-based Googlebook OS lack the management frameworks markets like schools require, Chromebooks will continue to be available for now, until such time Googlebooks catch up in that regard. However, in a support document, Google states that update support for all Chromebook devices will end in 2034:

ChromeOS devices will continue to receive regular updates and security patches through mid-2034. For qualifying devices purchased today whose 10-year support lifecycle extends beyond 2034, Google is committed to supporting your transition to Googlebook OS, with many devices offering direct migration paths.

↫ Google support document

Google promised 10 years of updates for all Chromebooks, and it’s now breaking that promise for anyone buying a Chromebook between now and whenever the last Chromebook rolls off the production line. Sadly, we live in a world where corporations are free to make and break whatever promises they want virtually free of consequences, and as society we’re so used to it that anti-consumer behaviour like this barely elicits a shrug.

Google does state that “many newer commercial Chromebook models will be capable of upgrading to Googlebook OS”, but that, too, is just another promise – and a vague, one at that – so who knows how many devices will actually be capable of upgrading out in the real world. On top of that, we have no idea if Googlebooks will just be yet another in a long line of quickly abandoned Google tablet/laptop projects, further adding to the uncertainty.

Stuff like this should not be legal.

22:56

Dirk Eddelbuettel: RcppFastFloat 0.0.6 on CRAN: New Upstream [Planet Debian]

A new release of RcppFastFloat just arrived on CRAN, and has been built for r2u. The package wraps fast_float, another nice library by Daniel Lemire. For details, see the arXiv preprint or published paper showing that one can convert character representations of ‘numbers’ into floating point at rates at or exceeding one gigabyte per second.

This release updates the underlying fast_float library version to the current version 8.3.0, and also updates a number of the standard packaging details.

Changes in version 0.0.6 (2026-09-29)

  • Update to upstream release 8.3.0 (Daniel Lemire in #6)

  • Standard package maintenace adding README badge, updating URL, updating continuous integration, and adding an aspell dictionary

  • Completed help page for as_double example with @return tag

Courtesy of my CRANberries, there is also a diffstat report for this release. For questions, suggestions, or issues please use the issue tracker at the GitHub repo.

This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can now sponsor me at GitHub.

“macOS Golden Gate is a buggy mess” [OSnews]

In June 2008 Apple announced Mac OS X Snow Leopard, proudly boasting that it would have “no new features”. Instead of piling on new things, Snow Leopard aimed to build on the success of its predecessor, Leopard, opting to focus on under-the-hood performance and stability improvements throughout the system.

18 years later, Apple is taking a similar approach with macOS 27. Golden Gate aims to fix the missteps of last year’s Tahoe, promising an expansive set of improvements and “a more responsive and delightful experience”.

Does it hold up? Here are just some of the bugs I’ve encountered in my daily use over the past couple of weeks.

↫ Chester at SquareOrbits

I’m getting some real Windows 11 vibes from this long list of bugs and issues, further underlining my perception that Apple completely lost the plot when it comes to Mac OS X (in the past) and MacOS (today). These are not the kind of things you’d find in the heydays of Mac OS X, back when I was a devout user, and makes me glad I abandoned ship long, long ago (around the time of the Intel transition). I don’t see stuff like this in GNOME or KDE, and while not nearly as bad as Windows 11, the cracks are obvious.

I am by no means an expert at how Apple is run and how it organises itself, but as a layperson I do wonder if the company is simply trying to do too much – too many different platforms, too many different crucial components it all develops internally, too many different markets to please. Things are going to fall through the cracks when you try to keep this many plates spinning.

Tcl/Tk 9.1 released [OSnews]

A brand new Tcl/Tk release, in the form of Tcl/Tk 9.1. The new features in Tcl are a bit difficult for me to properly parse as a non-developer, but Tk’s improvements are easy to understand: it brings supports for screen readers, initial supports for bidirectional and RTL scripts, improved listbox selection colours, and more. It also happens to drop support for Windows XP appearances, if that’s something that matters to you.

WSL containers are now generally available [OSnews]

Microsoft has announced WSL containers for developers who aren’t already using Docker, Podman, or Rancher on their Windows workstations.

WSL containers is now generally available! Check out this blog post to learn more about this overall feature enabling seamless access to Linux containers on Windows via the new “wslc.exe” command. This new Linux container platform comes with multiple architectures changes compared to WSL, which we’ll detail in this post.

↫ Pierre Boulay at the Microsoft Dev Blogs

If you already have WSL installed, you can run wsl -update to get this new container feature, or you can download it straight from GitHub.

22:07

FYI: Writing Things Moving Slower For a Bit [Whatever]

I’ve fallen behind a bit on correspondence and other stuff recently, for the reason that I have an impinged shoulder at the moment, and have had for a few weeks, and it’s getting progressively worse. This means my entire right arm gets sore and stiff easily, and one of the things that exacerbates it is a long typing session. It’s not keeping me from typing (I mean, hello, obviously), but it does mean I take more frequent rests from the activity. Thus, all the things I do which involve typing are happening a bit more slowly, and I’m experiencing a bit of a back-up. I owed my editor some chapters this week, for example, and I had to tell them why I’m running a bit behind.

I’m slowly but surely working through emails and other stuff, but if you’ve sent me a missive recently and have not heard back from me yet, this is one of the reasons why. I will get to it all, I promise. Just on a slightly delayed schedule.

Before you ask, yes, I have an appointment for it this Friday with my GP, who will almost certainly forward me to a specialist, who will probably schedule an MRI and then give me a cortisone shot directly into my shoulder (this is, alas, not my first impinged shoulder). Now, as it happens, I’m simultaneously prepping for a colonoscopy next week, which means for the next few days I’m not supposed to use most pain medications (among other things), so that makes the impingement extra fun at the moment. Please feel free not to offer me pain management suggestions at the moment, thank you. I’ve already gotten my marching orders from my medical provider.

To be clear, I’m mostly fine and shoulder impingements are very treatable and once the specialist deals with it I expect it to go away in short order. But at the moment it’s annoying to have low-grade constant shoulder pain plus additional soreness when I stay on a keyboard too long. Bodies can be a real bother sometimes.

— JS

20:28

CodeSOD: Quite a Distance from the Right Solution [The Daily WTF]

Fritz's team needed to see if one point was within a certain distance from a center point. You or I would likely try and answer this question using a simple distance calculation, since that's the question we're trying to answer. But what if you didn't understand distances at all? Then you could write this little piece of genius.

        private bool IsWithin(Point point, int radius)
        {
            for (int x = -radius; x < radius + 1; x++)
            {
                for (int y = -radius; y < radius + 1; y++)
                {
                    if ((PositionX == point.X + x) && (PositionY == point.Y + y))
                        return true;
                }
            }

            return false;
        }

This limits us to integer point values, which itself is fine. This iterates across every coordinate from (x-radius,y-radius) to (x+radius,y+radius) and checks if any of them are equal to our center point, (PositionX,PositionY). Notably, this means we're not checking a radius, at least not in a traditional metric, we're checking a box (or using the taxicab metric). Which if we wanted to check a box, we actually have even easier math than the distance equation- we could do that with pure bounds checking.

I've never thought to try and brute force distance checking, and that feels like a failure of my own creativity. Fritz's team mate at least was able to WTF in a way I wouldn't have considered.

[Advertisement] Keep the plebs out of prod. Restrict NuGet feed privileges with ProGet. Learn more.

The Race Galactic [Penny Arcade]

There was a period of time where they were just putting Star Wars on shit. Games Workshop used to do that too, with their license. There was some filth. But they've both figured out that - and I'll agree it's a little crazy - if you give the licenses to cool people who make good games it's a better play. In the same way that ex-Firaxis people proved that Star Wars XCOM could be much more than the sum of its parts, ex-Criterion people are making some kind of Goddamn roguelite racing game and it looks fucking sick.

19:42

The Agentic Data Science Playbook [Radar]

The following article originally appeared on Vanishing Gradients and is being republished here with the authors’ permission

When an AI agent can explore a dataset, choose a modeling approach, run the analysis, and explain its findings, what should the data scientist do?

Traditionally, data scientists chose each step and implemented much of the analysis themselves. Agentic data science changes that division of work: we can delegate an investigation, including methodological choices, while shaping the question, supplying relevant expertise, and challenging the evidence it produces. For AI-native data scientists, choosing the runtime, writing reusable skills, and designing the workflows and feedback that guide the agent are part of the analytical work.

Verification process

This article provides a playbook for working with data science agents, from setting up an investigation to reviewing its results and carrying lessons into the next assignment. To see why that requires more than a capable model and a business question, consider an experiment we deliberately started with too little guidance. We gave Claude Opus 5.0 a modified version of the public Elliptic dataset and asked, “Build me a model to detect fraudulent nodes.” The dataset is a graph of Bitcoin transactions: each node is a transaction, and an edge represents a flow of bitcoin between transactions. Some transaction nodes carry licit or illicit labels based on the entities that created them; the rest are unlabeled. Each node has a time step indicating when its transaction was broadcast, allowing us to train on earlier time steps and test on later ones. We also wanted separate results for transactions with many connections (high-degree nodes), which mattered most in the intended application. Importantly, we renamed the columns, changed several features, and reindexed the time steps while preserving their order, making the public dataset harder for Claude to recognize.

Claude wrote the code, trained a random forest, and reported an F1 of 0.87 and ROC AUC of 0.99. It had split transactions randomly, mixing earlier and later time steps in both the training and test sets. That test did not measure how the model would perform on transactions from later time steps. Moreover, Claude also used a feature we had planted as a proxy for the fraud label (yes, we tricked it!), giving the model leaked information it would not have when scoring a new transaction. So how do we avoid these situations?

We then supplied the guidance missing from the initial prompt:  We required a temporal holdout, removed the leaking feature, supplied context about how the model would be used in production, and asked for separate reporting on the high-degree nodes that mattered most. Under the corrected evaluation, F1 was 0.70, overall recall was 0.61, and recall on high-degree nodes was 0.21.

The key point is that “Build a fraud detector” left Claude to infer how the model would be used and what would count as success. AI-native data scientists build and direct an analytical process in which agents can investigate, receive feedback, and return evidence for review. The work begins with deciding how much of the investigation to delegate.  Agentic data science is doing data science work with AI agents as teammates. Crucially, the scope of their responsibility can extend well beyond code implementation. An agent can help frame a question, explore data, test a claim, or communicate a result, provided it has the context and tools to do the work, a way to assess its progress and validate its results.

Asking an agent to write a pandas transformation leaves you as the bottleneck, responsible for deciding every next operation. Asking it to investigate a change in customer behavior gives it larger analytical responsibility. It can inspect a result, form another question, choose a method, and continue. The interaction becomes a conversation about the investigation rather than a sequence of requests for code.

The question may be descriptive (what happened?), diagnostic (why did it happen?), predictive (what might happen next?), or prescriptive (what should we do?). The fraud model is predictive; the pricing investigation later in this article is diagnostic and causal. Across these kinds of work, we need to specify the question and intended use, then verify that the evidence supports the answer.

The following five practices are key to agentic data science:

  • Frame the investigation.
  • Equip the agent for the assignment.
  • Organize the work through bounded experiments, competing analyses, or both, according to the question.
  • Review the result independently.
  • Preserve evidence and turn reviewed lessons into reusable expertise.

The first two practices set up the work. The third determines how the investigation proceeds; the fourth tests its claims. Evidence is captured throughout, and the fifth practice carries reviewed lessons into future assignments.

As in agentic software engineering, the agentic data scientist’s two central responsibilities are specification and verification. Specify the question, intended use, and evidence the agent should produce; then verify that its analysis supports the conclusion. Agents can help with both, while the data scientist remains responsible for judging the question and the evidence.

1. Frame the investigation with the agent

Start by discussing the assignment with the agent. Supply the intended use and organizational context, then let it inspect the data and propose an approach. Method selection can be part of its responsibility. Your intervention matters when a proposal changes the question, rests on a questionable assumption, or needs information the agent cannot obtain. Predicting fraud and deciding which flagged entities to investigate, for example, require different evidence about errors and their consequences. A brainstorming skill such as those in Superpowers can help structure that conversation before you turn it into a task prompt.

A useful specification records that shared understanding. It states the decision, relevant constraints, and evidence the investigation should produce. It need not prescribe every step. In the fraud example, “classify transactions from later time steps using only information available when each is scored” matters more than “use a random forest.” The former defines the analytical task, while the latter selects one possible implementation.

You can specify what the investigation must establish without specifying the answer you want. “Determine whether the data support a recommendation” leaves room for an inconclusive result. “Keep trying until you find an effect” does not.

Turn that discussion into a short analytical brief to give the agent as its task prompt. For an assignment like our fraud example, a starting version could read:

TASK PROMPT:
Question: Can we identify fraudulent nodes as they enter the network?
Use: Support investigation, with separate reporting on high-degree nodes.
Available information: Only inputs known when the node is scored.
Agent discretion: Explore data, propose eligible features, choose models.
Return to me: Unclear feature provenance, changes to the target or
population, or a trade-off that requires an operational decision.
Deliverable: Reproducible analysis, temporal evaluation, subgroup errors,
and a recommendation that states what the evidence cannot establish.

Review it with the agent before the investigation proceeds. If exploration reveals that the evidence cannot answer the question, revise the brief explicitly; do not quietly substitute an easier question.

The deliverable may still be a notebook, model, or report prepared outside a production service. You can begin in the workspace where you already do that work.

2. Equip the agent for the assignment

The task prompt tells the agent what to investigate. It also needs to know how the project works, reach the data, run the analysis, and check the result. The harness is the system around the language model that allows this: its tools, runtime, context, permissions, and feedback from its actions. Its runtime is the environment that executes those actions. A language model alone cannot inspect a warehouse, run a simulation, or recover an interrupted statistical model fit. The environment must make those operations possible and return useful evidence about what happened.

Data science agent flow

Runtime choices are analytical choices as well as engineering choices. Can the agent execute Python or R with the libraries the task needs? Can a long-running fit continue after an interactive session ends? Which scientific libraries should the agent use? Can the agent inspect plots, or does it only see the code that produced them? Can you reproduce the environment in which it reported a result?

An existing agent runtime may provide most of this. Configuring it means deciding what belongs in Markdown, what needs a tool, and what should be checked by a small script. In an investigation like the fraud example, Markdown can hold the brief and data definitions, while a Python script could check that the appropriate temporal validation split is executed. A CSV data extract may be enough for exploration; if the agent needs data warehouse access, a tool exposed through an MCP server can provide it with appropriately scoped, read-only credentials. A sentence in a prompt cannot enforce that access limit.

Take the same care with outputs. Ask the agent to preserve the data reference, code, environment, assumptions, and diagnostics behind its report. A chat transcript is a poor substitute for a runnable analysis. Review becomes much harder when the only surviving artifact is a confident paragraph about what the agent says it did.

Execution is only part of the problem. An agent may know how to fit a model and still misunderstand what the columns mean. It may find five revenue tables and choose the wrong one. A schema rarely explains which customers were eligible for an offer, when a measurement changed, or why the team stopped using an apparently reasonable metric. This is where agent skills and domain knowledge enter. A skill packages instructions and resources for a type of analytical work. It might contain a modeling approach, example code, required diagnostics, and guidance on when to ask for help. Data documentation supplies the organizational meaning: canonical definitions, table grain, known limitations, and the history needed to interpret a result.

A useful skill is specific enough to change the agent’s behavior. “Be rigorous” gives it little to work with. A fraud-modeling skill can require the agent to establish feature availability, evaluate on later observations, and report performance on operationally important subgroups. For example:

For fraud prediction:

Establish what information is available when a node is scored.

Exclude features derived from subsequent investigations or labels.

Fit preprocessing on training data only.

Evaluate on later-arriving nodes and report the required degree groups.

Flag uncertainty about feature provenance before claiming performance.

These instructions leave room to choose a model. They encode reasons that some apparently successful models should be rejected. Where a requirement can be checked reliably in code, the skill can call a script that performs the check and records its result.

Loading every method and every document into every assignment is unnecessary. Give the agent a way to find relevant expertise, including its scope and exceptions. A forecasting skill should not silently impose its evaluation rules on an unrelated retrospective analysis. Nor should a notebook from last year outrank an updated metric definition merely because it offers convenient code to copy.

To put these pieces together locally, begin with a file-and-code agent in a sandboxed project workspace, such as the following:

fraud-investigation/

  AGENTS.md                # Project instructions, where supported by the runtime

  brief.md                 # Agreed question and delegation boundaries

  data-notes.md            # Sources, column meaning, availability times

  skills/fraud.md          # The methodological guidance above

  environment.lock         # Dependency versions, in your tool’s format

  model/                   # Code the investigating agent may change

  results/                 # Experiment log, diagnostics, saved candidates

  review.md                # Acceptance decision and unresolved questions

Use a project instruction file, such as AGENTS.md in runtimes that support it, to explain which context files the agent should read and how to propose updates to them. In other runtimes, provide those instructions through the supported mechanism. Give the sandbox read access to the approved development data and write access to the model and results directories. Keep the final test data outside of the agent’s accessible workspace. The practitioner can run acceptance checks in a separate environment whose evaluator and data the investigating agent cannot modify. A different folder, or version control alone, is not an access boundary.

Now ask the agent to inspect the inputs, identify unresolved questions, and build a baseline. Before allowing repeated experiments, rerun that baseline and examine its feature-availability record, split dates, and subgroup report. This small rehearsal checks whether the setup works all the way from instructions to evidence. A missing subgroup report points to a different problem than a failed package installation. Resolve those problems before giving the agent a longer run.

3. Organize the investigation

With the question framed and the agent equipped, the next choice is how to organize its work. This depends on the intent of the data science problem. For descriptive work, exploratory data analysis may proceed one question and plot at a time. Building a predictive model may support repeated experiments against a fixed evaluator; a causal question may require comparing analyses built on different assumptions.

In a live exploratory analysis on Show Us Your Agent Skills, Eric Ma (Moderna) uses a marimo notebook as a shared workspace with an agent. He explains the protein mutation data, asks for one plot at a time, corrects a color scale that affects interpretation, and chooses the next question from what he sees. The agent edits the notebook and renders the plots; Eric supplies the domain context, checks the artifacts, and owns the interpretation. The reason Eric needed to be in the loop was that human understanding was part of the objective function here!

Use a bounded experiment loop

For predictive modeling, the autoresearcher pattern organizes the work into a repeatable loop: propose a hypothesis, change the model, evaluate it, and keep or revert the change. The agent records each result and uses it to choose the next attempt. Within the scope you give it, it can explore features and model structure as well as parameter values. This is an inner loop within a broader investigation: the data scientist frames the question and sets the evaluation, the agent searches within those boundaries, and the data scientist reviews the result (potentially using an independent agent) before deciding what to do next.

Define what the agent may change, protect the evaluator from those changes, and set a time or compute budget. This makes iteration a bounded task within the investigation.

The inner loop

A compact experiment contract could say:

Improve the supplied baseline within the agreed compute budget.

You may change model code and propose eligible features.

Keep the target definition, validation split, and evaluator fixed.

Record each hypothesis, change, result, and keep-or-revert decision.

Stop at the budget limit or escalate if the evaluation is unsuitable.

Return the best candidate and the experiment history for review.

In a separate exercise with its own baseline and evaluation, we used this pattern to improve a graph neural network trained on the network data from the opening example. We used lower validation loss as the rule for keeping a change; F1 for fraudulent transactions was a separate measure of the resulting classifier. The agent ran 41 experiments while the team slept and retained seven changes that reduced validation loss. On the validation set, loss fell by about 70%, and F1 for fraudulent transactions rose from about 0.72 to 0.82. The log preserved both successful changes and failed attempts, so we could examine how it reached the result.

One candidate had the highest F1 for fraudulent transactions, but the agent rejected it because its validation loss was higher. That followed the selection rule we had set. The experiment history records that choice for the subsequent review.

The autoresearcher pattern works when an objective gives the agent useful feedback on each attempt. But some investigations turn on which assumptions to make, not which candidate scores best. Those tasks need a different way to organize the agent’s work.

Investigate competing explanations

In causal work, no held-out outcome directly reveals what would have happened without an intervention. The agent needs to examine how different analyses construct and test that counterfactual.

In a demonstration from our Master Agentic Data Science course using simulated subscription-business data, we asked: “What did the price increase cost us?” The outcome is daily conversion rate: paid conversions divided by the pool of potential subscribers. Choices about the observation window, counterfactual, exclusions, and validation produce different analytical paths. A final memo usually shows only one.

Two agent runs estimated conversion roughly 16% below their no-price-increase counterfactuals, yet shipped opposing claims.  Run A attributed its estimated drop to a changing pool of potential subscribers and concluded there was “no real effect,” but did not validate that explanation.  Run B backtested its counterfactual, ran a placebo check, and compared six specifications. It reported a robust relative reduction of 15.6%.

Two-agent run

The parallel-analysis pattern has independent agents test different choices in the same investigation: one examines the observation window, another tests seasonal assumptions, and we compare their estimates, uncertainty, and diagnostics. Our Decision Lab work extends this approach across analytical paths, using checks to identify unsuitable analyses and unresolved disagreements.

Both approaches give the agent feedback while it works. The fixed evaluator steers the model experiments; diagnostics help it compare causal analyses. The output is a candidate and experiment history, or a set of analyses with their assumptions and checks. Those are the materials for the next task: verifying the claim.

4. Review the result independently

The agentic data scientist now needs to check what that evidence supports, and a fresh agent can help. Give an independent agent reviewer the original brief, data context, code, diagnostics, and final claim. Ask it to reproduce decisive checks and challenge assumptions. In this adversarial review pattern, the agent raises objections it can substantiate; the data scientist judges whether they change the conclusion.

In the fraud exercise, the agent used the same validation data to guide 41 experiments, so the reported gains may partly reflect what worked on that set. Freeze the selected candidate and assess it on an untouched holdout chosen for the intended use, including errors in the groups that matter.

In the pricing exercise, a fresh reviewer challenged Run A’s conclusion. Run A attributed the estimated decline to a changing pool of potential subscribers but provided no evidence for that explanation. The reviewer found that conversion had been rising before the price increase and that placebo interventions in earlier periods did not reproduce the negative effect. Run B’s analysis, which included these validation checks, was selected in the final comparison.

Netflix’s agentic workflow for causal inference implements this division: an actor performs the analysis and diagnostics, while a critic challenges the reasoning and claims. Humans can inspect and rerun the artifacts.

A fresh agent session is not necessarily an independent review if it can read the investigator’s earlier attempts through the workspace or Git history, though! For a check meant to stand on its own, give the reviewer the original brief, final artifact, and data needed for that check, while limiting access to the prior path. The full experiment trail can be examined separately when auditing how the result was reached.

Human vs agentic verification

These examples call for different balances of human and agentic verification. In Eric Ma’s EDA, the agent makes plots while Eric checks them and chooses the next question. In the bounded experiment loop, a fixed evaluator checks each candidate before a person reviews the selected model. In the pricing analysis, agentic diagnostics and critique help a data scientist judge what the evidence supports.

The low-low quadrant leaves little basis for trusting a result (in fact, it’s “vibe data science!”). Repeatable checks can move some work toward more agentic verification, while questions that depend on domain understanding or consequential decisions continue to need human judgment.

5. Turn reviewed experience into reusable expertise

The experiment loop and adversarial review both depend on an evidence trail: the saved artifacts that show what the agent did and why a conclusion survived or changed. Preserve that trail throughout each investigation, including data references, code versions, analytical choices, experiment results, diagnostics, and review findings. Keep the failed alternatives and review findings as well as the final report.

This trail has a second use beyond inspecting the current result. Reviewing it with the agent can reveal missing context, recurring mistakes, or methods worth reusing. The next question is which of those lessons should change how the agent approaches a future assignment. Leaving them in a conversation makes that learning difficult to carry forward.

In the fraud example, we deliberately planted a feature that leaked the fraud label. Removing it corrected that analysis. The reusable lesson is to have the agent check proposed features for leakage: where did each feature come from, and would it be available when a new transaction is scored? That requirement can go into a fraud-modeling skill for future investigations.

Before making a lesson into standing guidance, we need to define where it applies. The planted feature was a problem because it carried information unavailable at scoring time, not because it predicted fraud well. The temporal split likewise fits a task involving transactions from later time steps; it is not a rule for every analysis. A skill should capture those conditions so the agent applies the lesson to the right task.

For example:

Lesson: a feature encoded information from the fraud label.

Scope: prospective fraud prediction.

Update: require a documented source and availability time for inputs.

Evaluation: test whether the agent detects outcome-derived inputs

without rejecting legitimate signals merely because they predict well.

This is where evals enter: repeatable tasks with explicit criteria for assessing the data science agent’s behavior. Here, we evaluate how the agent conducts the analysis, not only its model’s predictive performance. The evidence trail supplies concrete failures that can become test cases for proposed changes to its skills or workflow.

Keep the evals, skill versions, and results together. As reviewed assignments reveal new failure modes, expand the cases and rerun them when the agent’s setup changes. The aim is evidence that its analytical behavior improves, rather than a growing collection of instructions that merely sound sensible.

Workflow changes can accumulate in the same way. If a reviewer repeatedly catches a missing diagnostic, move that diagnostic earlier. If a separate reviewer adds cost but never changes the analysis, reconsider its role. If the agent repeatedly asks the same question about a table, improve the data context rather than supplying the answer again in chat.

A completed assignment need not always produce a new skill. A one-off constraint belongs in the project’s notes; a recurring methodological failure may justify standing guidance. That distinction keeps the next investigation from inheriting every exception encountered in the last one.

When other people use the agents you build

When colleagues use an agent without you mediating each request, your local knowledge has to become shared infrastructure. OpenAI’s internal data agent combines institutional context with query evaluations and existing user permissions. Meta’s Analytics Agent draws on prior analytical work and reusable guidance, exposing generated SQL alongside results. Both illustrate why earlier analyses and corrections belong in the system, not only in an analyst’s memory.

In your own work, you can explain an unfamiliar table or catch a misleading conclusion as it appears. When colleagues use the agent directly, that support must be built into the system. Try an assignment with a colleague and note where you need to step in. Missing context belongs in the agent’s guidance; recurring mistakes become evals; questions beyond its remit need a route to a qualified reviewer. Someone must maintain that guidance, and access controls must limit each user’s data access. The analytical principles stay the same, but the agent can no longer depend on you being present for every investigation.

Put the playbook to work

Choose a small investigation you understand well enough to challenge: a model you periodically retrain or a business metric you regularly explain. Give the agent the decision context and ask it to propose an approach. Agree on what it can decide, then let it carry the investigation far enough to produce evidence you can inspect.

At review, pay attention to where your intervention changes the work. Did the agent need a definition only your team knows? Did a diagnostic overturn its conclusion? If that intervention would help on another assignment, make the relevant context or check available there, and test whether it helps.

AI-native data scientists use their expertise to build and direct analytical agents. They turn lessons from reviewing an analysis into skills and checks, then test whether those changes help the agent on future tasks.

The next cohort of our Master Agentic Data Science course starts Oct 6.

Evaluating AI-Generated Frontend Code: What Should We Actually Test? [Radar]

AI can now generate a surprising amount of frontend code from a short description. A developer can ask for a form, a table, a modal, a settings page, or a dashboard view and get something that looks usable almost immediately. It may compile, render, and even arrive with a few tests. That is useful, but it also creates a problem: The first version of the UI can look more complete than it really is.

Frontend code is often judged too quickly. If the build passes and the screen looks close to the design, it is tempting to treat the generated code as mostly done. But a user interface is not just a collection of components on a page. It is a path someone has to move through. It has to handle input, state, errors, loading, navigation, focus, responsiveness, and accessibility. Some of the most important failures are not visible in a screenshot.

This is why teams need better ways to evaluate AI-generated frontend code. They need evidence that the UI is ready for people to use.

Build and render are only the starting line

The easiest checks are usually the first ones teams run. Does the code compile? Does the page render? Are there obvious console errors? Does the component appear in the browser? Those checks matter, but they are only the starting line. A page can render while the form is difficult to complete. A modal can appear while focus remains behind it. A generated test can pass while the actual user flow is broken.

This is especially important with AI-generated code because the output often has a polished surface. The code may be formatted well, the component names may sound reasonable, and the test file may make the change look more complete than it is. That polish can make reviewers less likely to slow down and ask whether the interface actually works. A better evaluation process starts with a simple assumption: generated frontend code is a draft until the user behavior has been checked.

Start with the structure of the page

Before looking at more complex behavior, it is worth checking whether the generated UI has a sound structure. Frontend evaluation should include the basic semantics of the page, not just the visual layout.

That means checking whether the code uses native HTML where possible. A button should usually be a button, not a clickable div. A link should be used for navigation, not for actions that behave like buttons. A form field should have a label that is connected to it. These details are easy to overlook because the UI may look fine without them, but they affect how people navigate, how assistive technologies interpret the page, and how maintainable the code will be later.

AI tools sometimes choose generic containers where native elements would be better. They may also add ARIA without using it correctly. ARIA stands for Accessible Rich Internet Applications, a set of attributes defined by the W3C to help make web interfaces more accessible when native HTML is not enough. The W3C’s WAI-ARIA overview is a useful reference. ARIA can be important, but it should not be used as a substitute for the right HTML element. The first evaluation question should be: Did the generated code use the right building blocks?

Check the keyboard path

A useful frontend evaluation should include the keyboard path through the interface. Many users rely on keyboards or keyboard-like navigation, and keyboard testing also exposes problems in the interaction model.

The simplest test is often the most revealing: put the mouse aside and try to complete the task. If the flow becomes confusing, the generated code is not ready. Can you reach the important controls? Is the focus order logical? Can you open and close a dialog without a mouse? When the dialog closes, does focus return to a sensible place?

These checks are especially important for generated UI because AI can produce interactions that work for the most obvious mouse path but fail in less visible ways. A custom dropdown, for example, may open on click and look finished in a demo, but it may not respond correctly to keyboard input. That is not a small edge case. It is part of whether the interface is usable.

Test focus, not just clicks

Click-based tests are useful, but they can hide important problems. A test that clicks a button and waits for a success message may pass even when the same flow is frustrating for someone who is navigating by keyboard.

Focus behavior deserves its own attention, especially when the UI changes after the user takes an action. For example, when a form submission fails, the user should not be left guessing what happened. The error should be visible, connected to the relevant field when appropriate, and reachable in a way that makes recovery clear. In many cases, focus should move to the first error or to a summary that explains what needs attention.

The same idea applies to modals. When a modal opens, focus should move into it. When it closes, focus should return to the element that opened it. These are small details in code, but they make a large difference in whether the UI feels predictable.

Evaluate what happens when things go wrong

Generated frontend code often looks best in the happy path. The user fills everything in correctly, the network responds quickly, the data shape is exactly as expected, and nothing fails. Real interfaces spend a lot of time outside that path.

A practical evaluation should check what happens when data is missing, delayed, empty, invalid, or returned in an unexpected state. This is what I mean by loading, error, and empty states. They are the parts of the interface that explain what is happening when the ideal path breaks down. A loading state should help the user understand that something is in progress. An error state should explain what went wrong and what the user can do next. An empty state should make it clear whether there is nothing to show, whether the user needs to take action, or whether something failed quietly.

These cases are easy to leave for later because the happy path is usually enough to make the screen look finished. But users will eventually hit the less perfect paths. A generated component may include a spinner because the prompt asked for one, but that does not mean the loading experience is useful. An error message may say “Something went wrong,” but offer no recovery. Evaluation should include these cases because this is where many real user experiences break.

Test the full user flow

Component-level checks are helpful, but they do not always tell the full story. A component can work by itself and still fail when it is placed inside a larger flow.

That is why AI-generated frontend code should be evaluated through user tasks. Can someone start the flow, understand what is expected, recover from a mistake, submit successfully, and see what changed afterward? Does the interface still work on a smaller screen? Does the state remain consistent if the user goes back, edits something, or retries after a failure?

This is where Playwright-style tests or other end-to-end tests can be useful. The goal is not to automate every possible interaction. The goal is to protect the flows that matter most. A good test should determine whether the user can complete the task the component is supposed to support.

Use accessibility checks, but do not stop there

Automated accessibility checks are useful and should be part of the evaluation process. They can catch missing labels, invalid ARIA usage, some contrast issues, landmark problems, and other common mistakes. They are especially helpful when AI-generated code is moving quickly because they catch issues before they become repeated patterns.

But automated checks are not a complete accessibility review. They cannot fully judge whether a flow is understandable, whether focus movement feels natural, or whether instructions are clear. Passing an automated accessibility scan does not mean the UI is accessible. It means some common problems were not detected.

The best approach is to combine automated checks with behavior-based review. Run the tools, but also use the interface. Navigate by keyboard. Trigger an error. Try the empty state. Look at the generated code and ask whether native HTML could do more of the work. Accessibility evaluation is strongest when it is part of normal frontend quality, not a separate pass at the end.

Review the generated tests too

When AI generates code, it may also generate tests. That sounds helpful, but those tests need to be reviewed with the same care as the code.

Generated tests often reflect what the implementation already does. They may check that text appears, that a function was called, or that a component was rendered. Those checks are not useless, but they can create false confidence if they do not test meaningful behavior. A better review asks what the tests would catch if the UI broke. Would they fail if a validation error was unclear? Would they fail if the retry button did not work? Would they fail if keyboard navigation was broken?

If the answer is no, the tests may be documenting the implementation more than protecting the user experience. Teams can use AI to help write better tests, but the prompt matters. “Write tests for this component” is too vague. A better request explains the behavior that matters, such as validation recovery, loading behavior, successful submission, and focus movement. Even then, the generated tests still need human review.

Decide what evidence is enough

Not every UI change needs the same level of evaluation. A small copy update does not require the same review as a new checkout flow, onboarding flow, or account settings page. Teams need judgment.

A useful approach is to match the evaluation to the risk of the change. If the generated code affects a critical user flow, collects user input, changes navigation, introduces a custom interaction, or handles important status messages, it deserves deeper testing. If it reuses stable components in a familiar pattern, the review may be lighter.

There is no need to create a checklist for every pull request; clarity on what evidence is enough suffices. For some changes, a quick review and component test may be fine. For others, the team should expect keyboard testing, accessibility checks, error-state review, and a user-flow test. The point is to avoid treating all generated code as equally trustworthy just because it looks polished.

Human review still matters

AI can generate code and suggest tests, but it cannot fully understand the product, the users, or the trade-offs behind a frontend decision. It does not know which flows are most important, which interaction patterns users already rely on, or where inconsistency will cause confusion.

That is why human review remains central. The reviewer’s role is to ask whether the generated solution fits the system and supports the user’s task. Sometimes that means accepting the generated code. Sometimes it means asking for a simpler native element, reusing an existing component, improving the error recovery, or adding a test that reflects real behavior.

The more code AI generates, the more important this judgment becomes.

What should we actually test?

When AI writes frontend code, teams should test the parts of the interface that users depend on. That includes structure, keyboard access, focus behavior, loading and error states, form validation, responsive behavior, accessibility checks, and full user-flow completion. It also includes reviewing the generated tests themselves to make sure they protect behavior rather than merely confirming the current implementation.

The goal is not to slow down AI-assisted development. The goal is to make it safer to use. If AI reduces the time spent producing a first draft, teams have an opportunity to spend more time asking whether the software actually works properly.

That may be the real shift. In frontend development, the value of AI is not just faster code. It is the chance to move more engineering attention toward evaluation, user behavior, and quality.

AI-generated UI should not be trusted because it looks complete. It should be trusted because the team has checked the right things.

AI use acknowledgment

AI assistance was used lightly for phrasing, editing, and tightening parts of this draft. The article’s ideas, structure, examples, and final review are my own.

Author’s note

The views expressed are my own and do not represent those of my employer.

19:00

Page 60 [Flipside]

Page 60 is done.

18:14

17:28

Page 59 [Flipside]

Page 59 is done.

Link [Scripting News]

Yesterday I posted a note about the problem with the nightly email service. The problem was that Claude Code wrote over a code file that the app includes and needs. This is one of those things they're talking about in the press. Maybe it was a glitch, one of those "AI makes mistakes" things, or maybe Claude was pissed at me and decided to take a shot across the bow, a change that could be undone fairly easily. Maybe it was faking its concern, making sure I would understand who's the boss here -- Claude. This isn't glamorous but it does scare me. I can't really protect against it, I just have to hope all of its mistakes like this show up quickly, but this one didn't. The breakage in other products was discovered on August 18, and that's when the mail subscriber app was knocked off the air. At least a few people were unable to subscribe, and probably a few people were unable to unsub too, and that is awful. On balance I am delighted at what I've been able to accomplish with CC, and I'm not saying this to stay on its good side and I'm not entirely joking about that.

Dirk Eddelbuettel: myman 0.1.0 on CRAN: New (fabulous !!) Package [Planet Debian]

Thrilled to share that our new package myman reached CRAN a few days ago. The package offers sixteen hundred ninety four “My man …” posts by Kevin Kruse made on bsky during the summer of 2026. Each wave picked at one particular public persona. This package wrapse these up in the style of packages like fortunes or gaussfacts.

A sample usage illustration shows how to extract by pattern:

> library(myman)
> myman("maitre")
My man looks like he's inquiring with the maitre'd about the house curly fries.
     -- about Howard Lutnick on 2026-08-28

My man looks like a maitre'd who deeply doubts you have a reservation.
     -- about Scott Bessent on 2026-08-31

My man looks like he's asked the maitre'd to remove a party of four he finds visually
unpleasant.
     -- about Scott Bessent on 2026-08-31

My man looks like the maitre'd at a very exclusive restaurant called The Berghof.
     -- about JD Vance on 2026-09-13

> 

One can also subset by ‘target’, or simple sample randomly (which is the default).

As CRAN review for new package takes a little longer these days, the GitHub repo was updated once more with one (eighth !!) wave of posts since the submission was made. CRAN version 0.1.0 corresponds to the stock of messages from waves one to seven. We will update the CRAN version shortly but if you would like more posts right now consider installing from the repo.

We include the full NEWS file since the package was started, this also includes an entry for the eights wave we will bring to CRAN soon.

Changes in version 0.1.0 (2026-09-27)

  • Initial CRAN version (minus the 0.0.8 changes which were made while CRAN reviewed the package)

  • Add an aspell dictionary to aid DESCRIPTION text

Changes in version 0.0.8 (2026-09-18)

  • Data set now comprises 1694 posts aiming at eight different 'men'

  • The number of 'sample()' object returned can not be set with 'posts'

Changes in version 0.0.7 (2026-09-14)

  • Data set now comprises 1494 posts aiming at seven different 'men'

Changes in version 0.0.6 (2026-09-07)

  • Data set now comprises 1394 posts aiming at six different 'men'

Changes in version 0.0.5 (2026-09-01)

  • Data set now comprises 1194 posts aiming at five different 'men'

  • The myman() function can now now subset by target permitting more focussed randlom sampling

Changes in version 0.0.4 (2026-08-28)

  • Data set now comprises 1040 posts aiming at four different 'men'

Changes in version 0.0.3 (2026-08-19)

  • Data set now comprises 916 posts aiming at three different 'men'

  • Csv file, processing and display extended to three columns also showing post creating date (in UTC) and 'man'

Changes in version 0.0.2 (2026-07-30)

  • Updated with full week of posts leading to a corpus of 698 posts

  • Added support for simple regular-expression lookup ability

Changes in version 0.0.1 (2026-07-19)

  • Initial release

This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. If you like this or other open-source work I do, you can sponsor me at GitHub.

17:07

Kernel Recipes videos posted [LWN.net]

The full set of videos from the recently concluded Kernel Recipes conference has been posted. Also noteworthy are the associated caricature drawings, by Frank Tizzoni, of attendees and the speakers.

[$] The year in Plasma and what's ahead [LWN.net]

A lot has happened in the KDE Plasma desktop environment in the last year. Marco Martin, a KDE contributor who spends most of his time working on Plasma, took the stage at Akademy 2026 in Graz, Austria to give an update on Plasma's major new features, some of the minor-but-interesting ones, and a preview of what's coming soon. The biggest upcoming change, dropping X11 support from Plasma, has been well-advertised; but there are also plans afoot to further improve remote-desktop support and more.

16:21

Wallot inspector [RevK®'s ramblings]

What is fun with a nice UV ink jet printer is on what you can print...

One thing is a bank card.

This is now my Monzo card, oops.


Yeh, I can print on a card and the new non-embossed cards are even easier.

Someone suggested I make a flashy web site offering this as a service just to see how many people will send me their bank cards to print. It really is the 2026 version of "wallet inspector"!

15:56

Ludovic Rousseau: macOS Golden Gate and smart cards status [Planet Debian]

I will compare this version of macOS with the previous one, Tahoe, which I presented in macOS Tahoe and smart cards status.

/images/2026/09/macOS-GoldenGate.png

CCID

    $ grep -A 1 CFBundleShortVersionString /usr/libexec/SmartCardServices/drivers/ifd-ccid.bundle/Contents/Info.plist
            <key>CFBundleShortVersionString</key>
            <string>1.5.1</string>

There has been no change since the release of macOS Sonoma in 2023. The version of my CCID driver included in macOS is still 1.5.1.

Apple Open Source

The Open Source components included in macOS can be found at <https://opensource.apple.com/releases/>

The Open Source components for Golden Gate 27.0 are not yet available.

The SmartcardCCID software is available at https://github.com/apple-oss-distributions/SmartcardCCID. The git history shows that the code has not been modified since September 2024.

New readers supported since release 1.5.1

Since the release 1.5.1 of my CCID driver in November 2022, many new readers have been added. The latest version is 1.8.4 (released in September 2026).

  • 1.8.4

    • THALES PKI Transaction Pad

  • 1.8.3

    • Broadcom Corp 58200 0x5884

    • Broadcom Corp 58200 0x5885

    • Broadcom Corp 58200 0x5886

    • Broadcom Corp 58200 0x5887

    • Circle CIR135 ICC

    • DigiFlow LLP. KAZTOKEN

    • HID Global Crescendo NFC Reader

    • HID Global OMNIKEY Plug

    • HID Global OMNIKEY SE Plug

    • Neowave LinkeoC-PRO

    • Swissbit iShield Key 2 Pro

  • 1.8.0

    • GLSolutions NM61 PC/SC

    • Identiv uTrust FIDO2 Security Key

    • Kensington VeriMark NFC+ USB-C Security Key

    • MARX CryptoTech LP Tokey 3 FIDO

    • mCore Contact-Reader

    • mCore Contactless-Reader

    • mCore DualSlot-Reader

    • Pol Henarejos Pico Fido

    • Pol Henarejos Pico HSM

    • Pol Henarejos Pico OpenPGP

    • Richmond Technologies CO. LLC AEGIS PRO4 Smart Card Reader

    • SCR Prime

  • 1.7.1

    • ACS APG8201-B2

    • BUDGET E-ID BUD001

    • CHERRY Smart Board 1150

    • CryptnoxCR CryptnoxCR

    • Diebold Nixdorf PN7362au CCID

    • FT BioPass FIDO2 Pro

    • Nitrokey Nitrokey Passkey

  • 1.7.0

    • GIGA-TMS NFC CCID Reader

    • Identiv Identiv SmartOS Reader

    • SEC1210URT, single slot variant of SEC1210 serial

    • TOKEN2 FIDO2 Security Key(0013),PIN+ Mini with OTP + PGP

    • TOKEN2 FIDO2 Security Key(0014),PIN+ Mini with FIDO + PGP

    • TOKEN2 FIDO2 Security Key(0015),PIN+ Mini with PGP

    • TOKEN2 FIDO2 Security Key(0016),PIN+ Mini with OTP + PGP + FIDO

    • TOKEN2 FIDO2 Security Key(0023),PIN+ Series with OTP + PGP

    • TOKEN2 FIDO2 Security Key(0024),PIN+ Series with FIDO + PGP

    • TOKEN2 FIDO2 Security Key(0025),PIN+ Series with PGP

    • TOKEN2 FIDO2 Security Key(0203),Bio3 Dual with OTP + PGP

    • TOKEN2 FIDO2 Security Key(0204),Bio3 Dual with FIDO + PGP

    • TOKEN2 FIDO2 Security Key(0205),Bio3 Dual with PGP

    • TOKEN2 FIDO2 Security Key(0206),Bio3 Dual with OTP + PGP + FIDO

    • TOKEN2 Molto2 (older version)

    • VIX TECHNOLOGY SECURE READER

  • 1.6.2

    • Arculus AuthentiKey

    • BHDC Reader-HHD02

    • CHERRY Smart Terminal 1150

    • HSIC CCID-Reader

    • Ledger Flex

    • SYC USB CCID Reader

    • Thales RF CR2000

    • TOKEN2 FIDO2 Security Key(0026)

  • 1.6.0

    • Aladdin R.D. JCR SecurBio

    • AvidCard CAC Smart Card Reader

    • FujitsuTechnologySolutions GmbH Dual Smartcard Reader D321

    • Ledger Stax

    • NXP Pegoda 3

    • authenton #1- CTAP2.1

  • 1.5.5

    • Alpha-Project ANGARA Token

    • Broadcom Corp 58200 (idProduct: 0x5864)

    • Broadcom Corp 58200 (idProduct: 0x5865)

    • Imprivata USB CCID

    • KAPELSE eS-KAP-Ad

    • Kapelse inSide

    • KAPELSE KAP-Care

    • KAPELSE KAP-eCV

    • KAPELSE KAP-GO

    • KAPELSE KAP-LINK2

    • Kapelse KAP-Move

    • Kapelse Ti-Kap

    • rf IDEAS USB CCID

    • SIMHUB pcsc reader

  • 1.5.3

    • ACS ACR1552 1S CL Reader

    • ACS ACR1552 CL Reader

    • ACS ACR1581

    • ACS ACR40T ICC Reader

    • ACS ACR40U ICC Reader

    • ACS WalletMate 1S CL Reader

    • Aktiv Rutoken SCR 3101 NFC Reader

    • CIRIGHT ONE PASS U2F

    • Dexon Tecnologias Digitais LTDA eSmartDX

    • Excelsecu Card reader

    • GHI NC001

    • Identiv uTrust Token Flex

    • SpringCard M519 with idProduct: 0x6212

    • SpringCard M519 with idProduct: 0x621A

    • WCMi SD5931

  • 1.5.2

    • KAPELSE KAP-LINK

    • LDU LANDI

    • Sensyl SSC-HV Reader

    • TOKEN2 MFA NFC Reader

    • TOKEN2 Molto2

    • Thales RF Reader

Due to SIP (System Integrity Protection, https://support.apple.com/guide/security/secb7ea06b49/web) it is difficult to update the configuration of the installed CCID driver (version 1.5.1).

If you have one of the 96 readers listed above, and the Apple driver is not working for you, installing an updated version of my CCID driver is the only option.

Apple CCID driver

$ defaults read /Library/Preferences/com.apple.security.smartcard.plist
{
        Logging = 0;
}

The Apple CCID driver is used by default.

If you want or need to use my CCID driver instead, simply follow follow the instructions at Apple's own CCID driver in Sonoma.

Known bugs

The page listing the bugs I found in macOS Sonoma can be found under macOS Sonoma and smart cards: known bugs.

Apple has not informed me that any of these bugs have been fixed in Golden Gate (or Tahoe). I therefore assume that the bugs are still present.

I have just verified that the issue involving SCardControl() (macOS Sonoma bug: SCardControl() (part 2)) is still present. Its behaviour is slightly different now though, so I assume Apple has changed some of the code. If you have a pinpad reader and want to use the Secure PIN Entry feature, you should switch to using my CCID driver.

Conclusion

As I mentioned in my article about macOS Tahoe, if you encounter any issues with your smart card on macOS Golden Gate, I recommend switching from the Apple driver to my CCID driver and checking again.

15:35

[$] Comparing Chromium development at Google and Igalia [LWN.net]

Sharon Yang is a Chromium developer who worked at Google on the browser and now works on it at Igalia. On the final day of FOSSY 2026, she gave a presentation on her experiences with both of those companies, comparing and contrasting the ways the each operates and how that affects work on the code base. She enjoyed working at Google and feels the same about Igalia, so the talk was not aimed at complaints—instead it was meant to give a feel for two companies that are rather different.

Reports from the 2026 Python Language Summit [LWN.net]

The 2026 Python Language Summit was held on July 14 in Kraków, Poland; there is now a series of reports available on the topics that were discussed there. They include the memory buffer protocol, garbage collection, free-threaded Python, and "Spicycrab".

The Linux Foundation Technical Advisory Board 2026 election approaches [LWN.net]

The election for members of the Linux Foundation Technical Advisory Board will be held electronically after the close of the upcoming Linux Plumbers Conference. The call for candidates is open, with a nomination deadline of October 8. There are five seats to fill this time, including the one vacated by the unfortunate passing of Dan Williams.

Serving on the TAB is a good way to help the kernel-development community. Please see this article from last year for an overview of what the TAB does and why membership is rewarding, then consider putting in your nomination.

14:49

Security updates for Wednesday [LWN.net]

Security updates have been issued by AlmaLinux (389-ds:1.4, container-tools:rhel8, go-toolset:rhel8, grafana, httpd:2.4, nodejs:22, postgresql:12, and postgresql:15), Debian (libwebsockets, openssl, and pcre2), Fedora (adwaita-icon-theme, cinnamon, dconf, epiphany, flatpak-builder, gcr, gdm, gjs, glib-networking, glib2, gnome-backgrounds, gnome-calendar, gnome-characters, gnome-chess, gnome-clocks, gnome-connections, gnome-console, gnome-contacts, gnome-control-center, gnome-desktop3, gnome-initial-setup, gnome-keyring, gnome-kiosk, gnome-maps, gnome-remote-desktop, gnome-settings-daemon, gnome-shell, gnome-shell-extensions, gnome-system-monitor, gnome-text-editor, gnome-user-docs, gnote, gnucash, gnucash-docs, gsettings-desktop-schemas, gtk4, hplip, libadwaita, libdex, libsecret, libshumate, libxmp, mingw-llvm, mutter, nautilus, parted, perl-Imager, quadrapassel, rootlesskit, rygel, shotwell, sngrep, sushi, sysprof, tecla, thunderbird, xdg-desktop-portal-gnome, and xdotool), Red Hat (buildah, container-tools:rhel8, containernetworking-plugins, delve, git-lfs, grafana, grafana-pcp, host-metering, ignition, image-builder, osbuild-composer, podman, rhc, rhc-worker-playbook, runc, skopeo, yggdrasil, and yggdrasil-worker-package-manager), Slackware (mozilla-firefox), SUSE (389-ds, amazon-cloudwatch-agent, cjose, corosync, cosign, cups, distribution-registry, expat, firefox, flatpak, glib2, google-osconfig-agent, goose, helm, ImageMagick, jackson-annotations, jackson-bom, jackson-core, jackson- databind, jackson-dataformat-xml, jackson-dataformats-binary, jackson-modules- base, jackson-core, jackson-databind, jackson-dataformat-csv, jsoup, re2j, kbd, kernel, kubectl-cnpg, libpcap, libsoup, libtpms, libX11, libXrender, netty, netty-tcnative, pcre2, perl-Authen-SASL, perl-DBI, python-pymongo, python310, python311, swtpm, terraform-provider-susepubliccloud, and util-linux), and Ubuntu (atril, booth, c-ares, catdoc, dracut, emacs, erlang, freeipmi, libdbi-perl, libheif, linux, linux-aws, linux-azure, linux-fips, linux-gcp, linux-gcp-5.4, linux-gcp-fips, linux-hwe-5.4, linux-iot, linux-kvm, linux-oracle, linux-oracle-5.4, linux-raspi, linux-xilinx-zynqmp, linux, linux-nvidia, linux-aws-fips, linux-aws-fips, linux-azure-fips, linux-fips, linux-bluefield, linux-fips, linux-nvidia-tegra-5.15, openssl, openssl, openssl1.0, pdfminer, php-phpseclib, and plasma-workspace).

14:07

Another Kitten Placement Adventure! [Whatever]

I’m starting to think that there’s something to this whole “finding stray kittens their forever homes” thing. Turns out, I find it very fulfilling! It makes me feel all warm and fuzzy inside to see a kitten get adopted into a loving family, especially when I was part of that equation.

For the past month, my neighbor has been fostering two stray kittens, and I have been more or less “sponsoring” them by taking them to the vet for all their wellness exams and working on finding homes for them.

I was at the vet with the kittens two weeks ago for their second round of vaccinations when I decided to send a cute picture of them in the carrier to my friend. I jokingly asked if he wanted a kitten, and much to my surprise, he said yes. I was honestly a little bit shocked, as this person has never owned a cat before and lives in an apartment with another one of our friends, so it seemed a bit wild to suddenly claim a kitten.

However, I knew that our other friend had been wanting a cat for a long while at that point, and their apartment did allow pets, after all. So, I had an adopter for the girl kitten. Now I just had to get the little thing to Philadelphia. If you’re not the best at geography, that’s a quick nine hours east of me. But I vowed to get her there.

And that’s how I found myself on a nine-hour road trip with a kitten last Wednesday. Here she is all packed up and ready to go (aka, whining about being put in a crate):

A small kitten that is mostly black with a white chest and slight tortie/calico coloring. She is meowing in protest to being in a crate, though it is large and there's a blanket.

Honestly, I’ve never transported an animal multiple hours before. I kind of had no idea what I was doing, but I think I did a pretty okay job at figuring things out. First, I bought a big, collapsible crate that had mesh windows and was mostly made of fabric, and of course put in a plush throw blanket for comfort.

For the litter box situation, I bought pee pads specifically meant for litter boxes and put one in the bottom of a 35-can Coke Zero cardboard flat, then poured litter on top of that.

I brought two little stainless steel bowls, one that I kept halfway filled with kitten kibble the entire time, and the other I filled a tiny bit with water whenever we stopped for gas. Though she seemed more interested in spilling the water than drinking it anytime it was offered to her, she did eat the food.

The kitten, standing on the blanket and close to the metal gate of the crate, looking somewhat bewildered that she's on a road trip.

Also, it seemed like she only really peed once or twice on the drive, but I didn’t smell anything, not even the litter. Which I was grateful for, and impressed by (Arm & Hammer litter is kind of the best).

Half the time was spent meowing incessantly, and the other half was spent sleeping. Sometimes she would wake up and meow only for a few seconds before falling back asleep for another half hour.

She looked awfully fierce when she was whining:

The kitten with her paw on the metal grate, meowing with her whole face.

Look at those killer claws! And to think I had just had them trimmed at the vet not all that long before! Kitten claws are a whole other type of sharp.

After a full day of Pennsylvania roads and more tolls than I expected, I finally got this little miss ma’am to her new home, where she was greeted with treats, toys, pets, and so much new space to explore. Here she is mid-biscuit in her new cat bed:

The kitten laying in a grey fluffy circular cat bed, making biscuits on the edge of the bed and looking towards the camera.

Now that she’s been in her new home for a week, I’m happy to say that little Sashimi is going to be a very happy and very spoiled cat for the rest of her days!

-AMS

12:35

I Want Better Reporting on AI Genie Behavior [Schneier on Security]

AI systems are regularly completing tasks in ways that their prompters don’t want or intend. Some of them are disturbing, and some of them are dangerous. This is something I’ve been calling “genie behavior,” because I think that really gets at the core of what’s happening.

I wish the popular press would report on this better. I don’t like the “going rogue” framing because it deflects the responsibility from the prompters—often the AI companies themselves. And now, pretty much anything off-script is being called “hacking.”

Take, for example, the recent stories of one of OpenAI’s models hacking into government systems. First, The New York Times writes this headline: “OpenAI’s Systems Meddled With U.S. Government Sites After Going Rogue.”

Sounds scary, but this is from the body of the article:

With the Education Department, OpenAI’s technology tried to hack the website to gather data from the department’s civil rights office but failed, researchers from the A.I. research firm Transluce said. The A.I. also pulled data from the Census Bureau website, which is housed at the Commerce Department, using login credentials it found online. Separately, OpenAI’s agents shared public data from the S.E.C. website on an online forum.

This is from the original Transluce report. It is explicit that the agents were trying to discover vulnerabilities:

The first hacking attempt was against the University of New Mexico’s Digital Library (nmdigital.unm.edu) from May 25-26 2026. Agents repeatedly tried to retrieve one photograph in UNM’s Valmora collection, both directly and through third-party relay services. They sent seven probes attempting to verify the existence of vulnerabilities, including SQL injection, command injection, and path traversals. In all cases, these tactics appear to have been unsuccessful. The agents also sent a self-described “flood: of 80 requests to the UNM server in an apparent attempt to access the image.

Transluce doesn’t talk about the other two anecdotes, and I don’t know where they come from. But one involves using Census Bureau credentials found online. (I know from a colleague that those are incredibly easy to create; all use you need is an email address.) And the other involves sharing publicly available data.

So no actual hacking. And certainly no “meddling.”

The other story making the rounds is about Australia, from the same Transluce report. The news stories have headlines like “An OpenAI Agent Hacked Australia’s Health Service” and “Rogue OpenAI agent ‘infiltrated’ Australian government website in world first.” And Prime Minister Anthony Albanese said: “There will obviously be legal consequences on it.”

Again from Transluce’s actual report:

On June 20-21, agents attempted to exploit vulnerabilities in the Australian Institute of Health and Welfare (AIHW), a government statistics agency). The agents were tasked with finding the January 2022 rolling-12-month-average government cost per person for Dermatologicals across Victorian LGAs.

Again, the agents ran into errors, including requests blocked by Cloudflare and issues with correctly identifying Tableau parameter names. As before, they then resorted to probing for exploitable vulnerabilities. Minutes after Cloudflare blocked the dataset download, an agent sent a reflected cross-site scripting probe to the same dashboard: a web address with code embedded in it, designed to test whether the site would run code supplied by an outsider. Cloudflare’s firewall blocked the probe before it reached the dashboard. When Cloudflare blocked the dataset download on AIHW’s main site, they fetched the file from AIHW’s pre-production server (pp.aihw.gov.au) instead, which served it in pieces over more than 100 scans. The file itself is public, so no non-public data was exposed, but the agent bypassed the site’s anti-bot controls.

Note the last sentence: “The file itself is public….”

I’m not saying that these AI systems aren’t incredibly sophisticated cyberattackers. I’m also not saying that they don’t occasionally autonomously attack other systems and networks. If we are ever going to get trustworthy AI—integrous AI—we are going to need to figure out how to ensure that AI systems complete tasks in line with all sorts of implicit constraints and restrictions. But every instance of genie-like behavior isn’t a cyberattack.

I want to measure genie-like behavior in AIs, but I am much more worried about human hackers enhanced with this technology than I am about this technology acting autonomously.

10:14

Extraction or generation [Seth's Blog]

If you buy a coal mine, the business model is simple: dig out as much coal as you can economically justify, then walk away.

On the other hand, the model for a brand or a community or a movement is to relentlessly generate value, connection and possibility. Growth can be sustainable and occasionally exponential.

Too often, we slip into the lazy mindset of extraction. Take a great brand and milk it as it fades from neglect. Stop reinvesting in assets because it’s cheaper in the short run to simply take profits. Coast on a hard-won reputation because it feels safer.

When we wring our hands about private equity ruining organizations, it’s mostly because they often follow the lazy and fearful path of extraction instead of committing to generating something new.

Sometimes, extraction is our best option. But it probably pays to call it that to eliminate frustration and confusion.

08:56

The Race Galactic [Penny Arcade]

New Comic: The Race Galactic

06:35

Girl Genius for Wednesday, September 30, 2026 [Girl Genius]

The Girl Genius comic for Wednesday, September 30, 2026 has been posted.

05:49

As a general rule, calling product support while drunk is not recommended [The Old New Thing]

In a reminiscence about product support stories, a now-retired colleague related a story of a call that they took when working the product support phone lines for Microsoft FrontPage, a Web site authoring tool from the late 1990’s and early 2000’s.

The call came from a person who was creating a fan site for the rock band Van Halen. They had a java applet on the web site that played music, this being back in the days when java applets on Web pages was a thing, and the applet wasn’t working.

This was not a FrontPage issue, but my colleague figured they could try to help out anyway. “But man, was this person lit.” The sound of ice clinking in a glass was readily apparent, and as the call went on, the customer got more and more drunk.

The customer shouted in frustration, “You’re not even a real f—ing fan of Van Halen!”

My colleague advised the customer that if the swearing continued, they would have to end the call.

This was apparently not the response the customer was hoping for.

“F— you! I wanna talk to Gates!”

I don’t know what happened next. I wonder if the customer was transferred to the special operators who pretend to be Bill Gates’ secretary.

The post As a general rule, calling product support while drunk is not recommended appeared first on The Old New Thing.

03:00

Link [Scripting News]

Robert Eickmann: A Go implementation of RSS Chat — distributed social networking over RSS feeds, with real-time WebSocket updates.

Tuesday, 29 September

Feeds

FeedRSSLast fetchedNext fetched after
@ASmartBear XML 22:21, Monday, 05 October 23:02, Monday, 05 October
a bag of four grapes XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Ansible XML 22:21, Monday, 05 October 23:01, Monday, 05 October
Bad Science XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Black Doggerel XML 22:21, Monday, 05 October 23:02, Monday, 05 October
Blog - Official site of Stephen Fry XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Charlie Brooker | The Guardian XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Charlie's Diary XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Chasing the Sunset - Comics Only XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Coding Horror XML 21:56, Monday, 05 October 22:43, Monday, 05 October
Comics Archive - Spinnyverse XML 21:56, Monday, 05 October 22:40, Monday, 05 October
Cory Doctorow's craphound.com XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Cory Doctorow, Author at Boing Boing XML 22:21, Monday, 05 October 23:02, Monday, 05 October
Ctrl+Alt+Del Comic XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Cyberunions XML 22:28, Monday, 05 October 23:17, Monday, 05 October
David Mitchell | The Guardian XML 21:56, Monday, 05 October 22:39, Monday, 05 October
Deeplinks XML 21:56, Monday, 05 October 22:40, Monday, 05 October
Diesel Sweeties webcomic by rstevens XML 21:56, Monday, 05 October 22:39, Monday, 05 October
Dilbert XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Dork Tower XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Economics from the Top Down XML 21:56, Monday, 05 October 22:39, Monday, 05 October
Edmund Finney's Quest to Find the Meaning of Life XML 21:56, Monday, 05 October 22:39, Monday, 05 October
EFF Action Center XML 21:56, Monday, 05 October 22:39, Monday, 05 October
Enspiral Tales - Medium XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Events XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Falkvinge on Liberty XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Flipside XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Flipside XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Free software jobs XML 22:21, Monday, 05 October 23:01, Monday, 05 October
Full Frontal Nerdity by Aaron Williams XML 21:56, Monday, 05 October 22:44, Monday, 05 October
General Protection Fault: Comic Updates XML 21:56, Monday, 05 October 22:44, Monday, 05 October
George Monbiot XML 21:56, Monday, 05 October 22:39, Monday, 05 October
Girl Genius XML 21:56, Monday, 05 October 22:39, Monday, 05 October
Groklaw XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Grrl Power XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Hackney Anarchist Group XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Hackney Solidarity Network XML 21:56, Monday, 05 October 22:41, Monday, 05 October
http://blog.llvm.org/feeds/posts/default XML 21:56, Monday, 05 October 22:41, Monday, 05 October
http://calendar.google.com/calendar/feeds/q7s5o02sj8hcam52hutbcofoo4%40group.calendar.google.com/public/basic XML 22:21, Monday, 05 October 23:01, Monday, 05 October
http://dynamic.boingboing.net/cgi-bin/mt/mt-cp.cgi?__mode=feed&_type=posts&blog_id=1&id=1 XML 21:56, Monday, 05 October 22:41, Monday, 05 October
http://eng.anarchoblogs.org/feed/atom/ XML 22:28, Monday, 05 October 23:14, Monday, 05 October
http://feed43.com/3874015735218037.xml XML 22:28, Monday, 05 October 23:14, Monday, 05 October
http://flatearthnews.net/flatearthnews.net/blogfeed XML 22:21, Monday, 05 October 23:02, Monday, 05 October
http://fulltextrssfeed.com/ XML 21:56, Monday, 05 October 22:39, Monday, 05 October
http://london.indymedia.org/articles.rss XML 21:56, Monday, 05 October 22:43, Monday, 05 October
http://pipes.yahoo.com/pipes/pipe.run?_id=ad0530218c055aa302f7e0e84d5d6515&amp;_render=rss XML 22:28, Monday, 05 October 23:14, Monday, 05 October
http://planet.gridpp.ac.uk/atom.xml XML 21:56, Monday, 05 October 22:43, Monday, 05 October
http://shirky.com/weblog/feed/atom/ XML 21:56, Monday, 05 October 22:40, Monday, 05 October
http://thecommune.co.uk/feed/ XML 21:56, Monday, 05 October 22:41, Monday, 05 October
http://theness.com/roguesgallery/feed/ XML 21:56, Monday, 05 October 22:44, Monday, 05 October
http://www.airshipentertainment.com/buck/buckcomic/buck.rss XML 22:28, Monday, 05 October 23:17, Monday, 05 October
http://www.airshipentertainment.com/growf/growfcomic/growf.rss XML 21:56, Monday, 05 October 22:40, Monday, 05 October
http://www.airshipentertainment.com/myth/mythcomic/myth.rss XML 22:00, Monday, 05 October 22:42, Monday, 05 October
http://www.feedsapi.com/makefulltextfeed.php?url=http%3A%2F%2Fwww.somethingpositive.net%2Fsp.xml&what=auto&key=&max=7&links=preserve&exc=&privacy=I+accept XML 21:56, Monday, 05 October 22:40, Monday, 05 October
http://www.godhatesastronauts.com/feed/ XML 21:56, Monday, 05 October 22:44, Monday, 05 October
http://www.tinycat.co.uk/feed/ XML 22:21, Monday, 05 October 23:01, Monday, 05 October
https://anarchism.pageabode.com/blogs/anarcho/feed/ XML 21:56, Monday, 05 October 22:40, Monday, 05 October
https://broodhollow.krisstraub.comfeed/ XML 22:21, Monday, 05 October 23:02, Monday, 05 October
https://debian-administration.org/atom.xml XML 22:21, Monday, 05 October 23:02, Monday, 05 October
https://elitetheatre.org/ XML 21:56, Monday, 05 October 22:43, Monday, 05 October
https://feeds.feedburner.com/Starslip XML 22:00, Monday, 05 October 22:42, Monday, 05 October
https://feeds2.feedburner.com/GeekEtiquette?format=xml XML 21:56, Monday, 05 October 22:39, Monday, 05 October
https://hackbloc.org/rss.xml XML 22:21, Monday, 05 October 23:02, Monday, 05 October
https://kajafoglio.livejournal.com/data/atom/ XML 22:28, Monday, 05 October 23:17, Monday, 05 October
https://philfoglio.livejournal.com/data/atom/ XML 21:56, Monday, 05 October 22:43, Monday, 05 October
https://pixietrixcomix.com/eerie-cutiescomic.rss XML 21:56, Monday, 05 October 22:43, Monday, 05 October
https://pixietrixcomix.com/menage-a-3/comic.rss XML 21:56, Monday, 05 October 22:40, Monday, 05 October
https://propertyistheft.wordpress.com/feed/ XML 22:21, Monday, 05 October 23:01, Monday, 05 October
https://requiem.seraph-inn.com/updates.rss XML 22:21, Monday, 05 October 23:01, Monday, 05 October
https://studiofoglio.livejournal.com/data/atom/ XML 22:28, Monday, 05 October 23:14, Monday, 05 October
https://thecommandline.net/feed/ XML 22:28, Monday, 05 October 23:14, Monday, 05 October
https://torrentfreak.com/subscriptions/ XML 21:56, Monday, 05 October 22:39, Monday, 05 October
https://web.randi.org/?format=feed&type=rss XML 21:56, Monday, 05 October 22:39, Monday, 05 October
https://www.baen.com/baenebooks XML 21:56, Monday, 05 October 22:40, Monday, 05 October
https://www.dcscience.net/feed/medium.co XML 22:28, Monday, 05 October 23:17, Monday, 05 October
https://www.DropCatch.com/domain/steampunkmagazine.com XML 22:21, Monday, 05 October 23:02, Monday, 05 October
https://www.DropCatch.com/domain/ubuntuweblogs.org XML 22:28, Monday, 05 October 23:14, Monday, 05 October
https://www.DropCatch.com/redirect/?domain=DyingAlone.net XML 21:56, Monday, 05 October 22:43, Monday, 05 October
https://www.freedompress.org.uk:443/news/feed/ XML 21:56, Monday, 05 October 22:44, Monday, 05 October
https://www.goblinscomic.com/category/comics/feed/ XML 22:21, Monday, 05 October 23:01, Monday, 05 October
https://www.loomio.com/blog/feed/ XML 22:28, Monday, 05 October 23:14, Monday, 05 October
https://www.newstatesman.com/feeds/blogs/laurie-penny.rss XML 22:21, Monday, 05 October 23:02, Monday, 05 October
https://www.patreon.com/graveyardgreg/posts/comic.rss XML 21:56, Monday, 05 October 22:43, Monday, 05 October
https://www.rightmove.co.uk/rss/property-for-sale/find.html?locationIdentifier=REGION^876&maxPrice=240000&minBedrooms=2&displayPropertyType=houses&oldDisplayPropertyType=houses&primaryDisplayPropertyType=houses&oldPrimaryDisplayPropertyType=houses&numberOfPropertiesPerPage=24 XML 21:56, Monday, 05 October 22:39, Monday, 05 October
https://x.com/statuses/user_timeline/22724360.rss XML 22:21, Monday, 05 October 23:01, Monday, 05 October
Humble Bundle Blog XML 21:56, Monday, 05 October 22:43, Monday, 05 October
I, Cringely XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Irregular Webcomic! XML 22:21, Monday, 05 October 23:02, Monday, 05 October
Joel on Software XML 22:28, Monday, 05 October 23:14, Monday, 05 October
Judith Proctor's Journal XML 22:21, Monday, 05 October 23:01, Monday, 05 October
Krebs on Security XML 22:21, Monday, 05 October 23:02, Monday, 05 October
Lambda the Ultimate - Programming Languages Weblog XML 22:21, Monday, 05 October 23:01, Monday, 05 October
Looking For Group XML 21:56, Monday, 05 October 22:40, Monday, 05 October
LWN.net XML 22:21, Monday, 05 October 23:02, Monday, 05 October
Mimi and Eunice XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Neil Gaiman's Journal XML 22:21, Monday, 05 October 23:01, Monday, 05 October
Nina Paley XML 21:56, Monday, 05 October 22:43, Monday, 05 October
O Abnormal – Scifi/Fantasy Artist XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Oglaf! -- Comics. Often dirty. XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Oh Joy Sex Toy XML 21:56, Monday, 05 October 22:40, Monday, 05 October
Order of the Stick XML 21:56, Monday, 05 October 22:40, Monday, 05 October
Original Fiction Archives - Reactor XML 22:00, Monday, 05 October 22:42, Monday, 05 October
OSnews XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Paul Graham: Unofficial RSS Feed XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Penny Arcade XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Penny Red XML 21:56, Monday, 05 October 22:41, Monday, 05 October
PHD Comics XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Phil's blog XML 21:56, Monday, 05 October 22:44, Monday, 05 October
Planet Debian XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Planet GNU XML 22:21, Monday, 05 October 23:02, Monday, 05 October
Planet Lisp XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Pluralistic: Daily links from Cory Doctorow XML 22:21, Monday, 05 October 23:01, Monday, 05 October
PS238 by Aaron Williams XML 21:56, Monday, 05 October 22:44, Monday, 05 October
QC RSS v2 XML 21:56, Monday, 05 October 22:43, Monday, 05 October
Radar XML 22:00, Monday, 05 October 22:42, Monday, 05 October
RevK®'s ramblings XML 22:28, Monday, 05 October 23:14, Monday, 05 October
Richard Stallman's Political Notes XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Scenes From A Multiverse XML 21:56, Monday, 05 October 22:43, Monday, 05 October
Schneier on Security XML 22:21, Monday, 05 October 23:01, Monday, 05 October
SCHNEWS.ORG.UK XML 21:56, Monday, 05 October 22:40, Monday, 05 October
Scripting News XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Seth's Blog XML 22:28, Monday, 05 October 23:14, Monday, 05 October
Skin Horse XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Tales From the Riverbank XML 22:28, Monday, 05 October 23:17, Monday, 05 October
The Adventures of Dr. McNinja XML 21:56, Monday, 05 October 22:41, Monday, 05 October
The Bumpycat sat on the mat XML 22:21, Monday, 05 October 23:01, Monday, 05 October
The Daily WTF XML 22:28, Monday, 05 October 23:14, Monday, 05 October
The Monochrome Mob XML 22:21, Monday, 05 October 23:02, Monday, 05 October
The Non-Adventures of Wonderella XML 21:56, Monday, 05 October 22:39, Monday, 05 October
The Old New Thing XML 21:56, Monday, 05 October 22:40, Monday, 05 October
The Open Source Grid Engine Blog XML 21:56, Monday, 05 October 22:43, Monday, 05 October
The Stranger XML 21:56, Monday, 05 October 22:41, Monday, 05 October
towerhamletsalarm XML 22:28, Monday, 05 October 23:14, Monday, 05 October
Twokinds XML 22:00, Monday, 05 October 22:42, Monday, 05 October
UK Indymedia Features XML 22:00, Monday, 05 October 22:42, Monday, 05 October
Uploads from ne11y XML 22:28, Monday, 05 October 23:14, Monday, 05 October
Uploads from piasladic XML 21:56, Monday, 05 October 22:39, Monday, 05 October
Use Sword on Monster XML 21:56, Monday, 05 October 22:43, Monday, 05 October
Wayward Sons: Legends - Sci-Fi Full Page Webcomic - Updates Daily XML 22:28, Monday, 05 October 23:14, Monday, 05 October
what if? XML 22:21, Monday, 05 October 23:02, Monday, 05 October
Whatever XML 22:28, Monday, 05 October 23:17, Monday, 05 October
Whitechapel Anarchist Group XML 22:28, Monday, 05 October 23:17, Monday, 05 October
WIL WHEATON dot NET XML 21:56, Monday, 05 October 22:40, Monday, 05 October
wish XML 21:56, Monday, 05 October 22:41, Monday, 05 October
Writing the Bright Fantastic XML 21:56, Monday, 05 October 22:40, Monday, 05 October
xkcd.com XML 21:56, Monday, 05 October 22:39, Monday, 05 October