Posts in Programming (20 found)

Front page

I have not had a huge success with my writing in this blog so far. I don’t collect any analytics 1 , so I am not sure exactly how each post fares, but based on the amount of comments I have received in the various platforms I have shared my posts, the traffic must not be very high. This has not discouraged me from writing. My goal is to share projects that I am developing and to express some of my thoughts when I feel like it. I am glad if someone finds the content interesting and I am even happier to engage anyone who sends a comment my way. In any case, I think that the mere act of sitting down and writing your thoughts is beneficial by itself and it is worth doing, even if the audience is not there. It is true, though, that it feels nice when people accept what you are offering. And two days ago, I finally got my first breakthrough. I shared my last post on Hacker News and it reached the front page . I admit it was satisfying to see that a lot of people approved of the message and wanted to share their own opinions. I spent the next day or so participating in the discussion and digesting all the new information that was available. One of the main themes of the discussion was user interface accessibility. It is a part of software development that is of great interest and importance and I hope to more systematically approach it in my programming and my writing in the near future. The whole experience was positive. I do not claim to be a great writer, nor that my post proclaimed some profound truth, but it was gratifying to see that an extended discussion was started because of something I wrote. As a result, I now am a little braver to share my thoughts with the world. I think they can create bad incentives for content creation.  ↩ I think they can create bad incentives for content creation.  ↩

0 views
Sean Goedecke Yesterday

You have to beat the models at something

In 2025, I wrote that software engineers ought to be assessed by “value over replacement” : not how much money they made for their company, but how much they would have made compared to the average engineer in their position. I’ve always found it vaguely silly when engineers put “built a product that made $X” on their resumes, when they just did the JIRA tickets that came across their desk. Today, value over replacement is even more important. A replacement-level engineer in the 2010s was fine : maybe not worth promoting, but still worth paying , because writing code had a high fixed cost. Now writing code costs a hundred bucks a month . What are you doing that GPT-5.6-Sol or Claude Opus 5 wouldn’t do in your position? Why is it worth paying an extra two or three orders of magnitude for? This is a scary thought. But you’re not doing yourself any favors by pretending that LLMs can’t actually write code and it’s all just a scam, or that LLM-written code is inherently so bad as to cause companies using it to collapse next year. We are not going to wake up in 2027 to find that the AI craze is over and everyone is writing code by hand again. You ought to put some serious thought into what you can do better than the models in the medium and long term. Staying ahead of the models is a moving target. At the start of 2026, “make working changes to large codebases” was in this category , but now it’s not. For this reason, I doubt that you can retreat to some “hard engineering” area that requires deeper expertise. That might work in the short term, but not forever. If LLMs can find a better lower bound on the Riemann hypothesis, they will soon 1 be able to write solid high-performance kernel drivers or GPU shaders or whatever. I think it’s more useful to look at the tasks models haven’t gotten better at over time, and the tasks that are hard for them get better at in principle. The two best examples of these are: What do frontier LLMs get wrong? What kind of coding mistakes do they make? It’s been a long time since I’ve seen a straight-up hallucination from a coding agent, or a simple logic error like an off-by-one. The mistakes they make tend to be errors of ignorance : Other times they’re errors of paranoia : What do these errors have in common? They’re the kind of errors a smart engineer might make if they had no context on the system: they’re competent enough to be able to solve the problem, but they haven’t been around long enough to confidently say “yes, we can take this risk to avoid an extra three thousand lines of code”. Until someone cracks continuous learning or truly massive context windows, this is just an inherent feature of how AI agents operate. If you can catch these errors, you’ll be providing real value. The only way to catch these errors is to be familiar with the codebase and familiar with the system in general. For much more on this, see my post You can’t design software you don’t work on . But there’s also a psychological component to it. You have to be willing to confidently disagree with the agent. AI agents can be very convincing. Often they can get “stuck” on some error above where they’re not willing to take a particular risk, so they keep going back and sneaking in code to cover that case (or writing persuasive arguments about why that case is important). To add value, you need to be willing to say “this sucks, I don’t think we need X and Y at all, why can’t we do Z in a much simpler way?” It takes courage . You can’t rely on other AI agents to review each other’s work. If you use the same model, it’ll reliably make the exact same assumptions and mistakes. But even if you use different models, they’ll also tend towards the same kinds of mistakes — ignorance and paranoia — for the same structural reasons. AI-driven review loops are in fact more likely to get these things wrong, because modern AIs have been RL-ed to try to find a few nitpicks no matter what. Having a critic AI and a worker AI bounce off each other is a really good way to end up with ten thousand lines of paranoid slop. Another area where you can add value on top of AI is communication . Newer models are better at coding, but are paradoxically getting worse at writing. GPT-3.5 and GPT-4 had a human-like writing style at times. GPT-4o introduced the modern slop idiolect, and the newer Anthropic models speak “Claudish” : a bizarre semi-baroque semi-truncated way of communicating that nobody enjoys. There have been a few bright spots — GPT-4.5 was okay, and I quite liked o3 2 — but in general LLMs are not good at this. Here’s two reasons why. First, good writing is not a verifiable domain . If you want a model to get good at mathematics or coding, you can generate problems for it and automatically grade them. You can’t grade good writing. If you try to get humans to grade it — for instance, via the early OpenAI RLHF attempts — you get the kind of writing that sounds impressive to the average person when consumed in single-paragraph form. This is the origin of the “stick three hundred writing devices into every sentence” style. I think it’d be possible in principle to hand-pick some people with good taste and have them do it, but there are some obvious problems 3 that prevent this from happening. Second, the labs have been monomaniacally focused on capability instead of communication . When you’re trying to train a model that can break new scientific ground or replace a software engineer, you might trade off some communication ability. In fact, I think we can identify exactly how this has been happening. If you look at internal model reasoning tokens , they tend to have strange word choices and oddly truncated grammar: RESOLUTION: charge the current-leg’s OWN saved-prefix occupancy EAGERLY: when leg i saves e 1..e t: ALSO commit their occupancy AT LEG i If you were to translate this into proper English, you would probably end up with something that reads like Claudish: Charge the current-leg’s saved-prefix occupancy on a clean, eager path: when leg i saves e 1..e t, commit the occupancy at leg i. I suspect that the weirdly alien writing style of some LLMs is because you’re reading a semi-literal translation of that model’s internal chain-of-thought, which has become nearly incomprehensible in pursuit of better problem-solving abilities. It is surprisingly hard to translate Claudish to good English: not only do you need to follow the convoluted, compressed language of the original, but you need the technical ability to understand the problem the model is solving. Because of all this, technical communication may be a surprisingly durable skill. In Peter Watts’ novel Blindsight , the world is full of cognitively augmented humans. The main character is a “synthesist”: someone whose job is to be a translation layer between these geniuses (who speak in abbreviations and gestures) and everyone else. Watts’ idea is that communication ability may be largely independent from — or even negatively correlated with — intelligence. A “country of geniuses” may still need a bunch of ordinary smart people to translate their insights for everyone else. If you’re trying to communicate to humans, there are also huge advantages to having a human write the content. Many of us are becoming AI-blind : developing an instinctive reflex that stops us reading when we encounter AI-generated content. It’s like the reflex that allows people to ignore flashing billboards or sidebar advertisements on websites. If you circulate some planned technical strategy as an AI-written document, most of your colleagues will have to physically force themselves to read it word-by-word. Whatever you do, don’t be a meat proxy : someone who simply copies requests into an AI agent and submits their output as your own work product. Doing that is just begging to be fired, since you’re definitionally not adding any value yourself. Even if you have a cunning system of multiple agents — the so-called “software factory” — you’re still on dangerous ground. When the features of your system work their way into enterprise AI tooling (and they will), you’ll be disposable. You need to find some way to leverage your expertise to do what the models can’t. Simply not using AI at all is better than being a meat proxy, since you’ll probably do some things better than the model would have, but it’s far better to figure out what AI can do and position yourself to fill those gaps. Right now, there are two main gaps: familiarity with the technical details of the system, and the ability to clearly and persuasively write about those details. If you’re thinking “but LLMs can do these things now!”, substitute your preferred example of high-difficulty software engineering. Although this was probably a “thank God it doesn’t speak like 4o” reaction. Defining good taste is hard, there’s no guarantee that AI lab researchers have good taste to start with, nobody will agree on examples, the bulk of users might not even like it, you won’t be able to get enough people to produce the volume of data you need, and so on. Deep familiarity with the codebase Technical communication Not knowing that there’s a module in the codebase they could use instead of reimplementing some logic Making the change in the wrong system because they didn’t know System X was the standard place for this functionality Adopting a coding style that’s inconsistent with the company’s standard practice Implementing triply-redundant checks for a value that technically could be wrong but practically is set once from config and never updated Assuming that ten milliseconds of stale data is unacceptable and designing a complex, unnecessary system to keep it always up to date Building in fallbacks and “graceful” degradation into some code that ought to simply crash on error (e.g. a CLI tool, or a restartable k8s service) If you’re thinking “but LLMs can do these things now!”, substitute your preferred example of high-difficulty software engineering. ↩ Although this was probably a “thank God it doesn’t speak like 4o” reaction. ↩ Defining good taste is hard, there’s no guarantee that AI lab researchers have good taste to start with, nobody will agree on examples, the bulk of users might not even like it, you won’t be able to get enough people to produce the volume of data you need, and so on. ↩

0 views

An audience of one

Every so often I hit some small, specific annoyance, and instead of living with it I spend an afternoon vibe coding my way to a fix. The results are niche beyond belief, held together with optimism, and exactly what I wanted. They work for me. They’re all on one page now at apps.dmcc.io . I’m not selling anything. I’ve put them up for the fun of it, on the off chance one of these itches is one you’ve got too. Every one of them started as my own problem. I build the thing, live with it for a bit, and open it up once I trust it not to embarrass me. Mostly. AudioLock is the one I actually use every day. Android decides where your audio goes and it decides wrong constantly. Media drifts back to the phone speaker mid-ride while my helmet headset sits there connected and silent. AudioLock overrules it. Pick a Bluetooth device, a Cardo or a pair of hearing aids, and it stays the output for media, calls and notifications until that device is genuinely gone. It watches where the audio is actually playing rather than where Android claims, and drags it back when it wanders off. No account, no store listing, a debug-signed APK you sideload. Deeply unglamorous. Fixes the exact thing that was driving me up the wall. whoop-tasker is more of the same, smaller. A screen you have to look at is useless when you’re riding, in a meeting, or asleep, so this gives Tasker a new trick: buzz my WHOOP strap. Front door opens, calendar fires, washing machine finishes, and my wrist knows. The one rule I gave myself was that it must not cost me a single day of the stats WHOOP has been nagging me to keep for months. (Remind me why I paid to be nagged?) So it doesn’t fight the official app for the connection or re-pair anything — it just leans on the same link for the two seconds a buzz takes, then clears off. It physically can’t do anything except buzz. Not a byte of recovery data goes missing. CityWall is completely pointless and I love it. It draws the streets and rivers around you and makes them your phone wallpaper, then quietly redraws it as you travel. Wake up somewhere new and so has your phone. It only ever knows your rough city, never the exact spot, because a wallpaper has no business tracking anyone. Does it need to exist? No. Do I like it? Enormously. May is the oldest itch: where does my money actually go on these vehicles. A self-hosted dashboard for fuel, expenses, maintenance and reminders across every car and bike, with DVLA lookups for MOT and tax so I don’t have to remember either. It’s named after James May, purely so the Top Gear set is complete now that Clarkson and Hammond already exist. Somehow it’s the one strangers have taken to most. Didn’t expect that at all. feedstand is my answer to feeds that decide what you see. An RSS reader that sets articles like a book instead of dumping them in a web view — proper typography, offline caching, full-text search over everything you’ve read, all of it staying on the phone. Paste any URL and it finds the feed, including the ones lurking behind a YouTube channel or a Mastodon profile. No server, so nothing to sign into. It’s the most finished-feeling of the lot, which tells you how long the feed thing has been bothering me. Two of these exist to make a point rather than fix a chore. Bluehood was a weekend thing that got a bit out of hand. It passively watches the Bluetooth devices around you — phones, cars, headphones, delivery vans — and tracks the patterns. With enough data you can tell what time someone walks the dog purely from the metadata their gadgets leak. That was the whole point of building it. It’s firmly alpha, so treat anything it tells you as a rough draft. It’s also, bafflingly, the thing I’ve made that most people have actually looked at. Loyalty Roulette is the same gripe, lighter. I’ve moaned before about privacy poverty — discounts quietly becoming a tax on your data — so this goes after the loyalty-card version. It pools shared card numbers so anyone can take the member price to the till: the savings without handing over your weekly shop, and no single number left tied to a person. mydronefeed is the odd one out because it’s genuinely quite polished, which wasn’t the plan. Point a DJI controller at it and you get a private, sub-second live view of every flight on the web, plus a link you can hand to anyone. Type it into a car screen and there’s your drone, full-screen, no sign-in. There’s a proper free tier for search and rescue teams, too, which is the bit I’m actually pleased with. And GRID/OS , which isn’t solving anything at all. It’s a browser game — Uplink by way of Spooks (MI5: 9 to 5, for my American friends), where you run cyber ops for the Service from a classified terminal at Thames House. Real Cisco, Ubiquiti and MikroTik consoles you work by hand, a trace clock narrowing on you the whole time, and a campaign that ends by asking you to name the mole. No install, nothing to sign in to. I built it because I wanted to, and that’s the entire justification. Not all of them last. I built one called Casey , used it every morning for a while, then quietly stopped. Building this way is cheap and fast, which is the appeal, but it’s also how you end up with a pile of half-used tools and a nagging sense that the time and the energy weren’t free. Worth keeping in mind before the next one. None of these are trying to be a product. They’re specific, slightly janky, and built for an audience of one who happens to be me. If any of them happen to scratch the same itch for you, brilliant, they’re all up there. If not, no harm done — I was going to build them anyway.

0 views

Mounts Krn and Batognica

I lost count of how many times I looked—and photographed—Mount Krn over the past decade. It’s impossible to miss, standing right there in the background of almost all my walks around the valleys, with its 2245 meters and its characteristic gently sloping side. And yet, I never hiked it. The reason why I never hiked it was that it’s a long hike and past me wasn’t a fan of long hikes. But past me is, well, in the past. Current me, on the other hand, loves long hikes. And to be fair, “long” is a relative term. How long does a hike need to be to be considered long anyway? The route for this hike has been sitting on my watch ready to go for weeks, waiting for the summer heat to come down a little bit. I’m a stupid hiker, but going up a mountain in 35°C is too stupid, even for me. And down it finally went. Randevouz with a friend was set for 7 am. The plan was straightforward: drive together about an hour, crossing the border with Slovenia, going through Kobarid, to then reach a parking spot inside the Triglav National Park . The whole area around mount Krn is a lovely plateau, gently sloping uphill, with cows and sheep free to roam around. The scenery is wonderfully relaxing. There are many ways to go up these mountains, our plan is to summit Krn first, and once there, do a long loop touching Mount Batognica on our way back to the start. Climbing up is surprisingly easy. It’s a long, easy walk that takes us from roughly 1000 meters above sea level to the 2254 of the summit in just a bit less than 3 hours. I can’t remember the last time I gained this much elevation this easily. And getting to the top is rewarding. The view is stunning. On one side, facing south, a mix of mountains, hills, and the sea far in the distance. On the other side, facing north, a gorgeous assembly of peaks and valleys, trees and rocks. Also from up here, an unusual angle of Mount Matajur. Funny how a place can look almost unrecognisable when seen from a different perspective. Going from the Krn to the Batognica doesn’t take much. The two peaks are near each other. What I didn’t know is how much history is still up there, laying on a ground that carries the scars of the First World War. Rusted barbed wire is everywhere. So are chunks of metals, and rusted shovels and other tools. A few artillery shells are also there, now a memorial. Also bunkers. So many bunkers dug into these mountains. Walking through these places never fails to remind you of the insanity that those wars were. So many lost lives and for what? The clouds decide to pay a visit, the scenery turns grayscale in the blink of an eye. I’m glad the sun is not hammering us on the way down. It’s less hot than it was a few weeks ago, but it’s still summer, and it’s definitely not cold, even up here. The descent to the starting point is long, and mostly enjoyable. A couple of rough kilometres towards the end, the humidity comes back, and the trail is all of a sudden slippery and a bit overgrown. We lose almost 700 meters of elevation in just a couple of kilometres. The view from the other end of the loop is still magnificent. And the cows are still here, still minding their own business. What else are they supposed to do after all? A bit less than 7 hours have passed , and we’re back at the car. Krn didn’t disappoint. Batognica is worth hiking. The whole Triglav National Park is worth exploring. Slovenia is a beautiful country, and if you’re an outdoorsy person, it won’t disappoint you. You love the outdoors and RSS. You're one of the special ones.

0 views

Premium: The Hater's Guide To Circular Financing (Part One)

[NVIDIA Company Meeting, the present day, YMCA playing] JENSEN HUANG : We love NVIDIA, don’t we folks? We’re the biggest, most-beautiful semiconductor company, we make the biggest, hottest GPUs for Clammy Sammy and Wario Amodei ’s huge, beautiful AI labs, but they can’t afford them because they’re losing so much money! [crowd booing] It’s okay! It’s okay! Big strong men, the biggest muscles, big, beautiful, strong men like Satya Nadella are calling me, begging — they’re begging, can you believe it? — they’re begging me, “Sir, Sir, please ship me Vera Rubin sir! I can’t get enough!” [crowd braying] they can’t get enough of Vera Rubin! They’re begging me to get Vera over there! Vera! Where’s Vera! [scanning crowd] get her up here! No, no, don’t do it, she’s too shy! We love Grace too, [voice turning gravely] Grace Blackwell , what a gal! I told them all we’re going to ship a trillion dollars of Grace Blackwell and Vera Rubin by the end of 2027 , our beautiful girls Grace and Vera , they’re our biggest and most-expensive girls yet, our Gee-Pee-Yous , the media says “we don’t believe you sir!” but I’m gonna make everyone buy ‘em, hell I’m gonna give ‘em the money to do it like I did with CoreWeave and then I’m gonna tell  Clammy Sammy and say “Samuel, give ‘em a few billion like you gave to Michael Intrator ,” and he’ll say “yes sir!”  Now, people are saying to me — “Sir! Sir! Your customers can’t afford your semiconductors! Sir, they’re too expensive!” and I say they’re not expensive enough! We’re gonna charge ‘em 17% more! [crowd braying] Should we up the price? Should we do it? We’re gonna do it!  In my mind, this is how Jensen Huang speaks to his workers, more than 70% of whom are millionaires as a result of NVIDIA’s remarkable stock growth, and from what I’m told by insiders, there’s a near-manic attention paid to stock movements as a result. I imagine working there must feel a little insane. Assuming you arrived before the stock went parabolic in 2024, you’ve seen your RSUs explode 10x in the space of a few years, all based on the back of everybody talking about how big and huge AI is… … all as it becomes blatantly obvious that NVIDIA’s biggest customers are, for the most part, funded by NVIDIA . While NVIDIA still ostensibly sells things other than AI GPUs (like autonomous cars , laptop graphics cards, and simulation technology for robotics ), more than 90% of its revenue comes from data center hardware. As a result, the company has become almost-entirely valued on whether or not it can continually come up with rationalizations for its largest customers to spunk tens of billions of dollars a quarter.  Why else would NVIDIA invest even an iota of effort into making an NVIDIA-branded Openclaw or build a platform for LLMs to do “agentic” things , or give $6 billion to Poolside (while investing another $1 billion) and hire away most of its staff? Why else would it plan to invest billions of dollars in Perplexity at a $30 billion valuation that lands somewhere between “fucking stupid” and “laughable”?  Sorry, I’m being a little vague. Everything NVIDIA has done for the last three years has existed to do two things: NVIDIA has succeeded in doing the first primarily by selling these GPUs to hyperscalers like Amazon, Google, Microsoft, Oracle, and Meta, who make up somewhere between 50% and 60% of its GPU sales depending on which analyst you ask.  The rest comes from a mixture of unnamed “sovereign AI customers” and “neoclouds” — companies that exist to raise debt, buy NVIDIA GPUs, and put them in data centers to rent to theoretical AI customers. Per Vivek Arya of Bank of America (at the BoFA Global Technology Conference in June), sales to “neocloud/sovereign/on-premise” were about the same as those to hyperscalers, and while it’s tempting to dither here and say “there could be large sovereign buildouts!” I can’t find compelling evidence that these actually exist outside of a theoretical 75 billion Euro investment in AI infrastructure in France by SoftBank , which doesn’t have that much money to spend. In any case, NVIDIA’s entire strategy has become a case of either convincing the largest companies in the world to give Jensen Huang $100 billion a year or artificially inflating its revenues through circular financing, which is obviously what I’m talking about today. This is the first part of my Hater’s Guide To Circular Financing, a comprehensive analysis of the current state of NVIDIA’s massive circular financing operation, why it has yet to break, its limitations, and the material concerns that were raised in its latest quarterly earnings. The second part, coming next week, will cover the history of circular financing, where we’ve seen it before, and what we can learn from its horrible past. Create sales for its AI GPUs and associated hardware. Create demand for AI compute for its customers.

0 views
Unsung 2 days ago

“They had no concept of a duty of care to their users.”

A Mastodon post by computer scientist David Chisnall has a very Unsung opener: I have used vim since around 2000. I have written five books, a PhD thesis, a few dozen papers and over 150 articles with it. At this point, my higher brain functions are not engaged at all when I use a bunch of common vim commands, they just happen. Documents I wrote with anything else have random :w in the middle. Chisnall goes on to talk about one specific vim feature: Persistent undo is one of my favourite features of vim. […] I don’t often need the persistent undo. But on the few occasions when I have needed it, it’s been invaluable: ooops, I deleted something from this file, maybe last week and one reboot ago, what was it? Undo until I find it, copy it, paste it into the current version. Or, a bit more commonly: I had this working, then I tidied it up ready to commit, now it isn’t working, what did I do? Vim has kept this working across major version upgrades over a period of about 20 years. I don’t even think about it, it’s just part of Raskin’s First Law: A program may not harm a user’s data or, through inaction, allow a user’s data to come to harm. If vim or the computer crash, or if I close a file and come back to it six months later, my undo history is still there. NeoVim is a fork of vim (in news for other reasons ): So I tried NeoVim when it was quite new. Vim that you are familiar with, but better? Great! The first thing I noticed in NeoVim was that undo didn’t work. I tried opening the file in vim and undo didn’t work there either . Neovim had changed the format of the undo files. It hadn’t upgraded the old one. It hadn’t used a different name for its undo files. It had just noticed the existence of a vim undo file, deleted it (losing all of the data in it) and replaced it with one that vim couldn’t read. I raised an issue about this and was told that the persistent undo format was unstable and users should not rely on data being preserved in a feature explicitly called persistent undo. It had changed once and would probably change again. And that ended my experience with NeoVim. The authors showed immediately that they absolutely could not be trusted with any of my data. Breaking persistent undo is something I could forgive as a bug, but the attitude that just because something is a persistent file on your filesystem that contains data that you might want is no reason for their program not to delete it meant they had no concept of a duty of care to their users. I liked this post (which I quoted almost in its entirety), because it covers a few important things: I also loved it for the appearance of Raskin’s First Law. Jef Raskin, of Macintosh and Canon Cat fame, put together the three laws in his 2000 book The Humane Interface , and they go as follows: It was a very important and formative book for me to encounter as a young designer. I have no idea how these laws haven’t made it to Unsung before today. #principles #text editing #undo I have never heard of the persistent undo like that, and it seems kind of amazing. People do remember when software loses their hard work or disrespects them. I can see how “It had changed once and would probably change again” can be such a powerful feeling. A computer shall not harm your work or, through inaction, allow your work to come to harm. A computer shall not waste your time or require you to do more work than is strictly necessary. An interface is humane if it is responsive to human needs and considerate of human frailties.

1 views

The AtomicLog + LogDrive: Strong vs weak sealing

This post continues to elaborate on Virtual Consensus and the new Loglet abstractions of the AtomicLog and LogDrive . My last post focused on composability, this post looks at log sealing. A common aspect of segmented logs is that when extending the log with a new segment, the current active segment needs to be sealed to prevent further appends. This post discusses segment sealing in the AtomicLog and LogDrive abstractions. I’m going to treat Virtual Consensus here less as a fixed API and more as a collection of architectural patterns and correctness requirements. The Delos paper defines a specific Loglet API, and the AtomicLog in the LogDrive paper implements that API verbatim. But the implementer has some flexibility regarding where the responsibilities related to seal enforcement live (which is discussed in this post).  The LogDrive is an unusually weak abstraction. It is an abstraction over single-value linearizable registers (an address is only required to behave linearly if the caller(s) ensures that only one distinct value is ever written to it). Should callers attempt to write two different values to the same register, we get undefined behavior. This means we can write value V1 to register R1 once, twice, as many times as we want, but never V1 and V2. It’s kind of like write-once semantics, where the caller guarantees the write-once property not the register (so significantly weaker than a WOR). So who guarantees that a given address (a given register) will only ever have one value proposed to it? The sequencer in the AtomicLog. Fig 1. The sequencer ensures the single-value register property. Delegating this responsibility to the AtomicLog rather than the storage service weakens the storage primitive we need. It also makes quorum composition cheaper. A general multi-writer ABD-style atomic register requires two rounds for a write: one to discover the latest version and another to install a newer value. With a single-value register there can be no competing value to discover, so a QuorumLogDrive can simply write the value to a write quorum in one round trip.  Multi-Paxos also achieves one quorum round-trip per append in the steady state, but for a different reason. Its leader has already completed Phase 1 for its ballot, amortizing that round across subsequent log entries. Crucially, Paxos cannot assume that only one value will ever be proposed for a slot: after a leader change, different leaders operating in different ballots may have proposed different values to the same slot. The acceptors therefore need ballot-aware semantics to reconcile these proposals. AtomicLog avoids this problem entirely as its sequencer is never replaced within an AtomicLog. If it fails, the AtomicLog is sealed and the VirtualLog moves to a new one. From a system builder's perspective this is quite nice as we can use more types of backing storage service. Simply put, a Primitive LogDrive is not constrained to use a storage service that provides conditional writes or one with awareness of ballots or fencing mechanisms. Conditional writes are becoming more and more commonly supported across cloud services such as object storage and databases, but it's not everywhere. So to recap, the LogDrive needs only single-value register semantics, not write-once registers (WOR) or conditional registers (CR). The Virtual Consensus in Delos paper provides some flexibility regarding seal semantics. At the architectural level, what Virtual Consensus really needs from sealing is an acknowledgement fence . Once the seal has taken effect, an append must not be allowed to return successfully. Importantly, that does not mean the append must be prevented from reaching storage. Delos explicitly allows a failed append to nevertheless become durable. Within this space we have two broad types of seal that we can call strong and weak seals. Strong seal : the seal is enforced inline by the append path Weak seal: the data write operation is oblivious to sealing. The write can complete normally, but its success is conditional on a subsequent check of an independent seal register. The seal in the LogDrive paper is the weak seal. The NativeLoglet in the Delos paper uses a strong seal. The seal operation contacts the Log Servers of the Loglet, to set their seal bit. The seal bit is essentially, unset or set, idempotent and monotonic. Once set, it is forever set. Fig 2. Example strong seal, where Log Servers cooperate in the sealing protocol and reject writes once the seal bit is set. After sealing a NativeLoglet, a quorum-write by the sequencer will fail as the Loglet has been quorum-fenced. This approach is basically the same as what Apache BookKeeper does. Active, inline fencing is possible because the Log Servers are active participants in the protocol: they durably store the seal state and consult it when processing writes. I defined active storage as storage processors that can apply custom logic as part of the write. Likewise, I define passive storage as simply a (third party) storage service that has more limited semantics (such as S3, DynamoDB, etc, etc). Passive storage services generally lack the ability to fence an entity’s address space. Many support conditional writes which are fundamentally limited to specific individual addresses (or keys). Other services may not support conditional writes at all. This is where the weak seal becomes useful. The weak seal is based on a seal register (another single-value register—you can only set it to True). The AtomicLog can represent the seal register as a LogDrive with a single address (0), configured the same way or differently to the Loglet address space (i.e. based on the same or different composition, with same or different backing storage service). Fig 3. Checking the seal becomes the last step of an append. The data and seal registers can exist in the same backing storage or separate storage services. With a weak seal, the seal register is checked post-write. To strictly comply with the Loglet API, the AtomicLog append must perform the seal check before returning. The downside of this approach is that it involves one more round-trip to check the seal register. However, we could also split seal validation into its own Loglet API operation. This allows the VirtualLog to amortize the cost of the seal check by group-commit behavior (basically checking the seal status after a group of appends has completed). Implementing a strong seal over passive storage, within the AtomicLog/LogDrive context, starts getting a bit more involved. For example, the sealer can potentially install barrier values ahead of the tail, such that once the seal is complete, we guarantee no further writes can succeed. Fig 4. Barrier values written to the loglet tail to fence further writes This requires a backing storage service with conditional writes. Correctness depends on placing that barrier safely relative to the in-flight write window. But it's more than that, for one, we have lost the guarantee that each address will only ever have one value written to it. Now, for any given address, we can have a normal value competing with a barrier value. Suddenly we’re entering the consensus arena. For singleton Primitive Log Drives, this can be solved by Write-Once-Register semantics (we delegate consensus to the backing service). Whoever writes first wins. The barrier writer just needs to ensure that the normal value tail cannot advance. At least one barrier entry must be written ahead of the normal value tail, and no additional addresses below the barrier can remain writable. After that, we guarantee normal writers cannot progress. For QuorumLogDrives it gets more complicated, as we’ll need conditional register semantics in the backing storage service coupled with a Paxos style of protocol. Importantly: regular writes can use ballot 0 as a fast path, skipping Phase 1 because the AtomicLog guarantees that no competing normal value will be proposed. But barrier entries will require the full two-phase protocol with ballot 1+, where the barrier writer will end up either completing a partial normal value write or completing its own barrier write. There’s a bit to it and I don’t want to get bogged down here further, just know it starts getting deep into distributed systems consensus stuff. The LogDrive paper doesn’t discuss strong seals (or even define the terms strong and weak seal). It explores the LogDrive as the weakest, most reduced set of behaviors possible. But the implementer can take these ideas and apply strong seals all the same. There are a few interesting insights to remark on. First, the extremely weak semantics of the LogDrive are possible because AtomicLog ensures that normal values never compete for an address. This is also why the AtomicLog sequencer is not recovered: if it fails, the VirtualLog seals that AtomicLog and moves to another one. If instead a new sequencer with a new epoch were to take over the same Loglet address space, we’d have competing values for the same address.  Weak sealing preserves this single-value invariant. The seal lives out-of-band, so the data addresses remain simple single-value registers. The cost is that acknowledgement must be gated by a post-write seal check, either per append or amortized over a group of appends. Strong sealing avoids this post-write check cost. With active storage, fencing can be performed directly by the storage processes. This is where a strong seal is close to a no-brainer. With passive storage, an in-band barrier introduces a second possible value for an address and therefore breaks the single-value assumption, making things non-trivial for the QuorumLogDrive implementer. So choose your trade-off. A weak seal keeps the storage abstraction as weak and portable as possible, at the cost of extra work pre-acknowledgement. A strong seal can avoid that cost, but may require more active participating storage or potentially adding more complexity to the storage protocol for more passive storage. Strong seal : the seal is enforced inline by the append path Weak seal: the data write operation is oblivious to sealing. The write can complete normally, but its success is conditional on a subsequent check of an independent seal register.

0 views
matduggan.com 2 days ago

You Know GDPR Is Good Based on Who Hates It

FDR has always been one of my favorite presidents, second maybe to Lincoln. Both were men the establishment assumed were one of them until, to their horror, they governed like they weren't. Both could trash the opposition in one breath and take the moral high ground in the next. One of my favorite Roosevelt lines growing up came from Madison Square Garden, October 1936, standing before a crowd that included plenty of people who wanted him dead: What I love is that he doesn't argue with the hate. He doesn't say they're wrong to hate him, or that the hate is unfair. He says the hate is evidence. The process is working. I've always treated it as a metric: if you're doing something hard and nobody hates it, you probably aren't doing it. If the right people hate it and those people happen to be some of the worst people alive, so much the better. By that standard, the GDPR (Europe's General Data Protection Regulation) is doing beautifully. It is impossible to go anywhere in a technology space online without hitting a wave of commentary about how stupid GDPR is. It was written by bureaucrats who don't understand the amazing potential of unrestricted technology. These US-based critiques almost always lean on the oldest trick in cyberlibertarianism: we don't have time to regulate, we must simply adapt and ride the wave. Nobody has time for government. Of all GDPR's consequences, none gets more attention than the cookie banner, which critics present as the inevitable result of government meddling. Blaming GDPR for the cookie banner is like blaming the health inspector for the roaches. The banner is deliberate vandalism, a dark pattern engineered to exhaust you before you can learn anything about the surveillance apparatus humming behind the "OK." Ironically the banner designed to hide the machine has taught the public more about the machine than a thousand podcasts ever will. Even non-technical people stop at "your data is shared with 996 partners." "For a sports score website?" So why is the tech commentary community so loud about this? Because they understand what's at stake. If consent must be freely given and easy to refuse, the industry's power shrinks exponentially. People might decide who has their data, how long it's kept, and what it was collected for. You can only imagine how that thought keeps a Meta executive up at night when he's not eating endangered animals, or ignoring calls from his children whose names he has forgotten while on a tacky yacht. Consider the following example. Did you know Google was doing this every single time you searched on Google ? Did your dad? So even in the most maliciously compliant form the regulation does provide value and information. Remember the hatred is the metric. How did we get here, where US tech companies end up regulated by Brussels? Why isn't the US government regulating US corporations anymore? If the rules are so terrible, why did nobody choose market exit? Has the EU become the world's "privacy cop" or, in the inverse, the biggest player to protect a fundamental human right to privacy? Ah who doesn't remember where they were on GDPR Day. Since we're all socialists in the EU, we stood up from our government issued desks and gave the required three cheers for regulation, then resumed being on vacation for 6 weeks. Obviously after stopping by my free doctor on my way to the airport. On May 25th, 2018, GDPR took effect to the sound of American commentary, the way fireworks take effect to the sound of dogs. You can tell American CEOs were aware of the regulation based on the speed by which they copied the language from it. Right before it took effect Brad Smith, the president of Microsoft, tweeted "We believe privacy is a human right." Tim Cook was right behind, telling CNN that "privacy is a fundamental human right". The framing of privacy as a human right is one of the key elements of the EU approach with GDPR. This is in stark contract with the US legal system which views information privacy as more of a market problem. You are all informed individuals in the wide marketplace of data exchanges and are left mostly to your own devices. In theory there should be regulations by the US of things like unfairness, deceptions and other market failures but in practice that doesn't happen. Europe is no stranger to this fight. The German state of Hesse passed the world's first data protection law in 1970, also known as the year the Beatles broke up, and set a standard we still fail to meet today: At the launch of GDPR there were 126 countries with data privacy laws of some sort. What you see with this sea of legislation is an overwhelming consensus that what GDPR was attempting to do was correct. In fact you see a pretty high level of global convergence of standards. All 126 laws descend from the same commandments the OECD carved in 1980: collect only what you need, say what it's for, keep it safe, let people see and correct it, and don't be a creep about any of this. Fifty years later, the American internet industry is still stuck on commandment one. So first the often-repeated sentiment that this is a flight of EU fancy is straight up incorrect. Something you could describe as the "European standard" for data privacy quickly became a global standard. Anu Bradford calls it the Brussels Effect: Europe regulates, the world complies, because despite American bluster, Europe is a market nobody can leave. In the US financial market, companies who surrendered the EU market would have quickly found themselves with new CEOs as their previous leaders suddenly discovered health problems or a deep love of their families that they had ignored for years. Especially given the increasingly frosty relationship between the US and China, companies that were pushed out of China due to regulation and increased domestic competition cannot lose the EU market. It's also difficult to screen a lot of services for EU customers, especially because the laws follow the personal data of EU residents whenever and wherever the information is transferred outside of the EU. Think of it like trying to sort luggage based on what stickers are on the outside. The EU is also set up in such a way where enforcement of such a law becomes possible. Every member state has a Data Protection Authority, who are charged with assisting individuals in protecting their rights, advising domestic legislatures on the functioning of existing regulation and finally enforcing the law. The EU is also different from the US in that it is open to exploring precautionary regulatory action. We see this with the EU Artifical Intelligence Act which tried almost immediately to get some controls on the industry right at the beginning. Link So the combination of a robust option for enforcement combined with an increased appetite for regulation in general and a high level of respect for personal privacy made the EU the logical source for this legislation. Want to know what the teeth look like? In 2011, an Austrian law student named Max Schrems asked Facebook for everything it had on him and got back 1,200 pages, much of it stuff he'd never volunteered. He filed a complaint from a dorm room. Four years later, the Court of Justice of the EU had voided Safe Harbor, the transatlantic data treaty, on the strength of it. A college student complaint killed an international agreement signed by presidents and prime ministers....multiple times. The best proof of GDPR's power is what it did to Japan. On January 23rd, 2019 the EU and Japan reached a deal which allowed for the free flow of personal data between the two economies. It established an overarching privacy law with a core set of individual rights and enforcement by independent supervisory authorities. The process took 2 years, which isn't a surprise because before this process Japan had very weak, swiss-cheese regulations. In 2014 Graham Greenleaf chose the title "The Illusion of Protection" for his chapter about Japan in an overview of Asian privacy laws. The private sector was basically unregulated, it has "easily manipulated exceptions" to its rules concerning the use and disclose of personal data, its absence of provisions for sentivie information and had no restrictions on data exports. It was nowhere near the level of protections that the EU would expect for information sharing. You can draw a straight line from a bargaining table in Brussels to new rights for a retiree in Osaka. Japanese data brokers hate it, which again is the point . Why hasn't the US sought the same arrangement? Because everyone involved knows the application would be denied. Which raises the real question: why can't the country that invented most of this technology produce a rule for it? Why is the US stuck pretending there's no reason to regulate while the rest of the world moves on? There's no better text on what has happened in the US with the data economy than The Age of Surveillance Capitalism by Shoshana Zuboff. It's a good read and I won't ruin it for you. First, the definition of Surveillance Capitalism from the book. Didn't read that? I don't blame you. In English they found oil and the oil was us. And as us Americans love to do, we immediately discovered a sudden love of freedom in the presence of oil. Effectively here's what happened. The 9/11 terrorist attacks had a ripple effect in US regulations, effectively derailing the momentum that the domestic US regulatory organizations had about starting to build frameworks around personal data. The focus became on security and not privacy. Quickly public intelligence agencies and the fledgling surveillance capitalist business in Silicon Valley found each other and carved out the concept of "surveillance exceptionalism". If you were spying to keep us safe, then it's not spying it's public service. As time went on, the corruption of American politics rendered the possibility of regulation less and less feasible at the federal level. Google and Facebook poured tens of millions into lobbying (for non-US readers, lobbying is a nice way of saying bribery that is legal). There also became an "open door" between government and tech, with 197 people moving back and forth from Washington to the Googleplex. What these companies learned is that by studying our behavioral data during periods of relaxation or play was the most value, allowing them to accurately and reliably push people towards profitable outcomes. US consumers were aware of this to some extent, often repeating phrases like "If it's free, then you are the product". That was true before, but in the new digital economy it is no longer true. We're not even the product anymore, which is why all these companies no longer give a solitary shit about whether their stuff is good or fun to use. We're the raw material that they mine. They know every single thing about us and we don't know anything about them. They accumulate all the data from us but not for us or for our benefit. Behavioral modification though the accumulation and manipulation of this data is now the wealth engine of the United States. Now we are in a market failure scenario. No individual company will disarm first, and any regulator can be captured for what these companies spend on catering. Only law with real enforcement teeth changes the math. And the US cannot pass laws with teeth anymore, not because voters don't want them (polling says they do), but because the pipeline is purchased. When someone says America "can't" regulate tech, they mean it the way a hostage "can't" reach the phone. GDPR didn't spread because Europe is heroic. GDPR spread because Brussels is what fills the room when Washington leaves it. Thanks to the amazing https://decryptads.com you can see what it looks like in real time. Let's take The Verge. This is a relatively straightforward tech commentary website that has a paywall. It should have a very simple supply chain in terms of advertising. What we see is the opposite. There is a giant network of companies trading, selling and bidding on the information from this website. This isn't the fault of The Verge, this is just how the machine operates. Your data is like fish at a fish market. Everyone gets to inspect the merchandise and decide whether they want to buy without you being involved. Ironically we only get this information because of the and which exist to combat advertising fraud. Even a well-run website operated by technologically literate people geared towards tech enthusiasts behind a paywall is not immune to this system. What you see here is that there is no fucking escape . If you want to have a large web presence and have bills to pay, you need to participate. And The Verge is doing it right! 59 declared partners and none of them are actively preparing for war with the US. In the US though this is not a new problem. The economic historian Karl Polanyi came up with the concept of "Double Movement". You can read it here. Think of it like this. Imagine you strip American Capitalism down to a tug of war. In the first round, the businesses go first. This is the "let the invisible hand decide" part. Everything becomes a product you can buy and sell including land, work, even money itself. Think of this like removing all the referees from a game and letting players do whatever they want. Then there is round 2, which is "wait this is hurting people". People demand protections like minimum wage laws, workers rights and trade regulations because the "free market" never seems to regulate itself but is causing real harm. The referees come back, but now with a rulebook written by the players who got hurt. His argument is that a totally free market is a fantasy. It's a fantasy because markets need government support to operate. Even people who pretend they despise government interference rely on them. Copyright and trademark doesn't matter when you need to train an LLM, but it matters a whole lot when I start marketing my iWatch smart watch. That rhythm of abuse, then correction governed American capitalism for a century. For surveillance capitalism, round two never comes. There is no functional federal counterweight as I write this in 2026. Some states are trying, and good for them, but regulating the internet state by state isn't progress it's just stopping the bleeding. Meanwhile the brokers buying and selling your life exist entirely outside your scrutiny, have no fear of comprehensive reform, and can swing a statehouse election for what they spend on a quarter's catered lunches. Entities this powerful cannot coexist with a functional democracy — a fact they seem to have considered, given the rise of the "Nerd Reich". https://www.npr.org/2026/08/10/nx-s1-5925350/the-nerd-reich-tracks-the-unmasking-of-silicon-valleys-true-politics So the order of operations is simple. Washington can't act, so Brussels does. Brussels acts, and the world follows, because the market is too big to leave. That is the whole story of GDPR: not European ambition, but American absence. A sign of the declining empire if you will. Roosevelt gave that speech at Madison Square Garden on the last night of October 1936, and a week later he won forty-six states. The people who hated him got Maine, Vermont, and the next ninety years of being wrong about everything they hated. That's the thing about being hated by the right people which is it is a currency and a valuable one. Privacy regulation will get there too. Not because the industry repents because industries don't, but because this is how every one of these stories ends: seatbelts, smoking sections, lead paint. Normal, then scandalous, then unthinkable. Someday someone will ask what an ad network for children was and refuse to believe the answer. And the executives who fought this will be on a boat somewhere, explaining that they were for it all along. Accept all.

0 views

How big are factorials?

The other day, I found myself wondering how big 52! (52 factorial) is, and that led me to ponder how these could be estimated without a calculator or a computer. It turns out there’s some fairly interesting math behind being able to estimate the size (number of digits) of a factorial reasonably accurately. This post will start by stating how to do the estimate, and if you’re curious you can read on for the math background. Without further ado, the approximation is: As an example, let’s use my original question, by estimating this for 52! Well, 52 divided by e is... 20-ish? And \log_{10}(20) is about 1.3 [1] ; therefore our estimate comes out to: The real answer is 68, so this is very close! In estimates like this - when you’re dealing with enormous numbers - being off by a couple of digits usually isn't a big deal. The Gamma function for real n>0 is defined [2] as: This integral does not have an analytic expression in the general case, but it does have a very useful property that we can take advantage of. Let’s see what \Gamma(n+1) is: And now use integration by parts with: But notice that the last integral is just \Gamma(n) ; therefore, we’ve shown that: Let’s also calculate \Gamma(1) - it’s a special case that has an analytical solution: This helps establish an induction argument: In other words - the Gamma function is an interpolation of the factorial over all positive reals. Here’s a plot of the Gamma function over a small range; note that the y axis is log-scale because of the function’s fast growth: You may have encountered Stirling’s approximation before: It’s a great approximation that works reasonably well even for small . This section is a brief overview of how Stirling’s formula is derived from the Gamma function. We’ll start by massaging the integrand a bit: And making a change of variables x=ny , which means that dx=ndy : These steps make the integral amenable to applying Laplace’s method , which allows us to approximate definite integrals of the form: Where is a twice-differentiable function and some large number. By Laplace’s method, such integrals can be approximated by: Where is the global maximum of . Let’s see how to apply this method [3] to the latest equation we have for n! (renaming the dummy integration variable back to x ): In our case, f(x)=\ln x - x . It’s easy to show that this function is twice differentiable and has a global maximum at . Moreover: Substituting these into the proper places in Laplace’s approximation, we get: We can calculate the number of digits in n! by taking the base-10 logarithm of Stirling’s formula: Note that the first term is not multiplied by itself; therefore, as grows, it will become less and less noticeable. That said, it still adds a couple of digits - so you should take it into account if you want a more accurate approximation [4]

0 views

GUIs should be fully keyboard-driven

This post has made it to the Hacker News front page. See the discussion . Last week I came across a post on Hacker News that encouraged application developers to stop making terminal user interfaces 1 (a.k.a. TUIs) and instead focus on graphical user interfaces (a.k.a. GUIs). The post reached the HN front page and sparked a lively debate in the comments section. I think there is merit in both sides of the debate. On one hand, I understand the GUI-positive arguments of the post author. In theory, the capabilities of GUI application frameworks are a superset of the capabilities of their TUI counterparts, so they should be preferred. On the other hand, as a heavy terminal user , I also greatly appreciate all TUIs that allow me to “stay” in the terminal and fulfill all my needs. But I want to oppose a recurring argument in favor of TUIs that in my opinion does not have a solid foundation 2 . To paraphrase various commenters: TUIs should be preferred because they are keyboard-driven. While it’s true that if you randomly pick a GUI and a TUI application, the latter is more probable to be fully keyboard-driven, this does not tip the scale in favor of developing TUIs over GUIs 3 . What it does is highlight the inadequacies of keyboard navigation in many GUI applications. There is nothing preventing a GUI from being fully keyboard-driven 4 just like — or even better than — a TUI. In fact, many GUI framework application guidelines explicitly encourage GUI application developers to provide support for keyboard-driven navigation that covers the whole functionality of the application. For example, the GNOME Human Interface Guidelines state that just as it should be possible to perform every action with a pointing device, every action should also be possible with the keyboard and that it should be possible to move around and interact with every part of your user interface using the keyboard . This resonates with me as a user. Being able to intuitively — and predictably — navigate around a GUI application with only my keyboard gives me more incentive to choose it compared to its alternatives. Knowing that, and when wearing my developer hat, I have to make sure that my applications are keyboard-friendly. For my first ever GUI application, Klisi , I invested some time to implement keyboard shortcuts targeting the whole range of available actions. Keyboard navigation is not that hard to achieve in most cases and results in an overall better user experience. It is not a matter of feasibility, but a matter of will on the application developer’s part. The takeaway is simple. Do not compromise on the user experience you provide with your application. Strive to make it as intuitive as possible. To that end, enabling full keyboard navigation should not be ignored. Or alternatively text-based user interfaces .  ↩ The original post author mentions this as well.  ↩ There are other more compelling arguments towards tipping that scale, for example ease of portability.  ↩ Of course, the dexterity achieved via mouse is still preferred — or even required — for some tasks.  ↩ Or alternatively text-based user interfaces .  ↩ The original post author mentions this as well.  ↩ There are other more compelling arguments towards tipping that scale, for example ease of portability.  ↩ Of course, the dexterity achieved via mouse is still preferred — or even required — for some tasks.  ↩

0 views

Verifying (simple) C in Isabelle/HOL with AutoCorres

This post details the first steps of verifying a C function in Isabelle/HOL using AutoCorres. It'll go over the basic setup for Isabelle and AutoCorres, what AutoCorres gives you, and how we can verify some basic properties of a function (here, the sum of a list.) I use the latest versions of Isabelle and AutoCorres available at time of writing (24/08/26). This is not official documentation for AutoCorres, nor may it be 100% correct in all places. All the proofs go through, but I do not work on AutoCorres, nor have I used it professionally; most of my experience is hobby verification. However, I have found resources on it are woefully lacking, so I wished to introduce some more. Much of this information has been gleaned from the official documentation (which can be found at the aforementioned,) and this course . When it runs, the slides/similar may be removed for some time - they exist on the internet archive also. This article assumes a little either Isabelle/HOL or general verification knowledge, but I try to explain wherever feasible. A bit of C knowledge is required as well, and so is a little knowledge about program verification - a little Hoare logic, and the like. I'll try to explain as much as I can without being excessively verbose, and much of it is very searchable. You can find Isabelle here . Install as is appropriate for your platform. You can decide whether to put in your path or not. First, pick a directory for your project. It may be feasible to use AutoCorres globally, but I wouldn't recommend it for versioning reasons. Then, AutoCorres can be found by scrolling down here . (It will probably take you here , whereupon you should scroll down to the latest AutoCorres release). You only need the AutoCorres download, as it bundles the C parser. Take that file, and extract it in your directory of choice. Then we need to build AutoCorres. From the directory where you unpacked it, run This'll take a second. Replace paths as appropriate. Note that is not the architecture you are on - it determines how the tool translates various C sizes into Isabelle. This will need to be the same architecture you launch with later. There are some others, and on a related note: It may not all make sense yet, but it may answer some questions. I recommend making a little shell script for this step ( perhaps.) You'll need to invoke Isabelle in a manner similar to the following: Remember that needs to be the same as earlier. Now that we have Isabelle running, we can use AutoCorres to generate Isabelle versions of C files. The manner in which this is performed is long, complex, and interesting - I recommend a rabbit hole evening - but in short, the C parser first translates it to a deep embedding in a language called Simpl, and then AutoCorres takes this Simpl representation and turns it into a monadic shallow embedding best it can. Here's what we'll be verifying today: The use of unsigned will be expanded on later. We'll put it in a C file named . At this point, your directory should look something like or similar. Begin a regular Isabelle theory, importing AutoCorres (and whatever else you want): We then need to let AutoCorres perform its magic. First, we "install" the C file with the C parser: You can use to see what this defines. Of particular interest is . Then, AutoCorres: The idea the C parser and AutoCorres use for verifying C is that it is reasonable to do a very direct translation of C to Simpl, and then a refinement to the monadic representation. The C parser is correct through inspection, and extensive testing. However, the translation of Simpl to the monadic form is verified - there exist Isabelle-checkable proofs that show that the monadic representation, however different it may be, behaves identically to the Simpl equivalent. This makes the monadic form a refinement of Simpl, and is why the things we prove about the monadic forms translate back down to C. This might also take a second. We choose to use . This makes unsigned integers perform modular arithmetic instead of using overflow checks; this has positives and negatives. Read the README for more. Again, you can use to see what this gives you. Of particular interest is . This is the monadic embedding of our function. We then need to enter the locale (think of it as an environment) defined by the C parser and AutoCorres, so your file should look something like: We do our work in this locale. If you haven't already, and . The former is the deep embedding produced by the C parser, and the latter is the result of AutoCorres's shallow embedding. These can be unfolded with and respectively. You can examine the types of things with ctrl-hover (cmd-hover on Macs.) Also examine . The type for the monadic state used by AutoCorres is called (You may see it displayed as in some places.) It's a record containing fields for each type of pointer used; ours only uses , so it only contains information for 32 bit words. We can examine the extract and update functions used in with and . Which monad AutoCorres chooses to embed a function into depends on the function, and can also be configured. This function is simple enough that it can be encoded with purely , but others include , (option with state,) and . Yes, this is a reasonable question to ask. What does it actually mean to verify this function? Generally, there are a few reasons to verify something: We'll look at all three. The not failing example will go into quite a lot of depth, whereas the correctness example will go into much less, only covering broad strokes. This is because the proofs are quite similar, and if you find the former excessively verbose, you might want to skip to the latter. Let's consider what we need for this function to not fail in C. The obvious constraint is that must be defined for all . Using , we can state this as a definition: Now we have our suitable precondition, let's set up our "doesn't fail" lemma. We expect that: We can state this using the combinator . The NF stands for , and it adds the additional condition that our program does not fail in some way during execution. Precisely what we want! It takes three arguments: The arguments are: Here, is used because of the choice of state monad AutoCorres makes. Note that our postcondition takes both the state and the return value . When we use with , will be our state . So, our lemma then becomes: If we have our list defined properly and some property Q, and we run our program, then it does not fail and Q is still true. If you're familiar with Hoare logic, you'll know we probably want to use some sort of weakest precondition reasoning. A weakest precondition is roughly "what is the smallest amount of information we need to know for this to be true", which allows us to simplify our proof obligations. Indeed, AutoCorres provides us with a family of tactics such as and . However, I find it nice to start these proofs by unfolding the function at hand, and applying or similar to get some simplification going. Then we can apply to apply relevant weakest precondition rules automatically. This should leave you with a state something like: I highly recommend using the Query tab of jedit throughout (or equivalents like .) will come in handy, and it's a nice fuzzy search. will do what's on the tin, and will find theorems that could apply. As we have a while-loop, a reasonable step is to add an invariant. An invariant is something that is invariant over the loop - it is always true, at the top of every loop cycle, and right after the loop finishes. This is how we conclude things about what a loop does. Indeed, we can see a theorem of use: Most of the time, the prefixes can be omitted. So first, we add an invariant, and then we can use to transform our into theorems we can work with. The invariants we care about right now are: We also care that this loop terminates, so let's add a suitable measure. is our invariant, and is the termination measure for the loop. Note the type conversion in . Then, again: This'll probably give you a few more normal looking goals. can come in handy again to chunk these down. This leaves me with: The first we talked about earlier - we can solve it by unfolding via , and basic reasoning. For the second, it seems obvious - why hasn't solved it? (If it were on s, it certainly would have.) Alas, it's on s, which as we have chosen to use modular arithmetic, are slightly less nice. It's hard to search for theorems involving as it's so overloaded, but luckily here finds . The final goal is more interesting. The initial intuition might be to use again, but this leaves us with nasty metavariables because of the chaining nature of the binds, and the fact that AutoCorres here is slightly too general. With a little searching we have the following: But we only really need to be identical to . We could instantiate manually each time, or we can make a little helper lemma (which I will do.) Above this lemma, we add Then we can apply twice (There are two binds.) We hence have: It's finally time to start unfolding the definition of so we can crunch down these last few goals. If we hit the first goal with: we're left with which takes out nicely. Tip: I fiddled with this for a bit manually, but if it looks fiddly and obvious, there's a good chance sledgehammer can do it. Finally, the last two are easy: We have a proof of non-failure! This is very exciting. The final proof is as follows: Then, what does it mean for our function to be correct? Well, a reasonable definition is that it produces the output we expect. We then need to figure out what "output we expect" means. We could have also defined the sum of a list as similar to the following recursive function: Note that we explicitly check for 0 to ensure the recursion terminates (We can't pattern match on 0/Suc, as we're working with s.). Unfortunately, this function's termination can't be proven automatically due to the use of a , which Isabelle isn't as good with as its own s. This is why we use a instead of , and we must do the termination proof ourselves: We also delete from the default simp set because it seems to cause some solvers to loop. We can set up our lemma like before, but this time, we use the parameter: We want the result to be equivalent to summing the entire list with our recursive function. We also don't bother to prove that it doesn't fail here. We can begin as before, omitting the step. We then need to annotate with a suitable invariant. A hint: We want the sum at the end to be correct, so a good invariant should capture correctness at every step , which gives us full correctness when the loop finishes. We have to manually a few times as we deleted it from the simp set, but otherwise the proof is very straightforward. Mine came out to be: We could take this one step further if we wanted, and define a bijection between and assuming our precondition, and then also show that our own spec is equivalent to the sum of that list. That'd give us even more confidence our function is correct. I'm personally pretty convinced, but you're welcome to try this yourself! We could then finally prove that if some property is true for the sum of a list, it's true for the result of our program. I won't detail this one; it should follow reasonably easily from the former two. So, what have we (hopefully) learnt? I hope this has been informative! (With less fixing of indentation for the web, sorry): A bit underwhelming for how long it took to explain, perhaps! To prove it never "fails" (what failing means is another question) To prove it produces some desired output To prove it holds some desired property is a , as perhaps expected. has been converted into a . is our state for the function, and carries information as mentioned about the heap. We use the perhaps confusing for pointer addition. You can write this in jedit with . We also need an explicit type conversion , as you can add negatives to a pointer, so the argument is an (this is not unsigned int; that's . here corresponds to Isabelle , which is signed.) If: The list is defined properly Some other property Q is true Then: Our function does return successfully, so That property is still true. A precondition function. A computation function. A postcondition function. (for termination) (for no-failure) (for simplified correctness) How to install and setup Isabelle and AutoCorres What it means to prove things about programs How to set up appropriate proofs How to prove them What tools we have available and how we can search for more

0 views

Two Alleged ‘TeamPCP’ Hackers Arrested in Australia

Authorities in Australia have arrested two men believed to be members of TeamPCP , a prolific cybercrime and data extortion group blamed for perpetrating the longest running spree of software supply chain attacks ever. In a statement released today, the Australian Federal Police (AFP) said two men from Western Australia, aged 21 and 23, were arrested in connection with a “sophisticated cybercrime syndicate that allegedly created malicious open-source software to rob thousands of global businesses.” The AFP did not name the defendants, but KrebsOnSecurity learned the 21-year-old suspect’s real identity in June, and has been communicating with him ever since. This story includes interviews with TeamPCP’s self-described spokesperson, and examines clues left behind by the TeamPCP leader that likely led to his undoing. TeamPCP vaulted onto the cybercrime scene in late 2025, embedding malicious code in hundreds of open source software tools and extorting victims for profit. Members of the group made headlines by compromising corporate cloud environments using a self-propagating worm dubbed  Shai-Hulud , which added malicious code to open source programs maintained by developers whose credentials at public code repositories like GitHub or NPM were phished or stolen. Writing for Wired , journalist Andy Greenberg described TeamPCP’s core tactic as a kind of cyclical exploitation of software developers. “The hackers gain access to a network where an open source tool commonly used by coders is being developed,” Greenberg wrote in May . “The hackers plant malware in the tool that ends up on other software developers’ machines, including some who are writing other tools intended to be used by coders. The malware allows TeamPCP’s hackers to steal credentials that let them publish malicious versions of those software development tools, too. The cycle repeats, and TeamPCP’s collection of breached networks grows.” TeamPCP also has practiced something akin to cyclical recruitment. In May, the source code for the third iteration of Shai-Hulud was published online, and TeamPCP soon after launched a contest offering $1,000 in virtual currency to whichever participant could conduct the largest supply chain operation using the worm’s code. According to the contest rules, participants were scored based on the number of weekly and monthly downloads of packages they compromised — directly incentivizing them to target the most popular code libraries. A screenshot of a message from TeamPCP’s Telegram account, announcing the supply chain hacking contest. Image: dataminr.com. “TeamPCP has stated the competition is a recruiting opportunity and they intend to purchase all meaningful access harvested from participants’ campaigns,” the security firm Dataminr wrote . “The $1,000 XMR (Monero) prize is a recruitment floor and has been dismissed by the actor as ‘just like participation trophy,’ adding ‘if you find something good you will be paid way more,’ confirming the contest’s true function as talent identification and malicious access acquisition at scale.” In March, TeamPCP executed a supply chain attack targeting AI infrastructure by compromising the code for LiteLLM , an open source AI gateway that connects users to more than 100 different large language models. A recent analysis by the security firm CloudSEK found TeamPCPs attack on LiteLLM harvested cloud service keys and other secrets from more than 2,500 organizations, including many of the world’s top technology companies. In May, TeamPCP claimed credit for compromising at least 3,800 code repositories at the Microsoft-owned GitHub , after a GitHub developer installed a code extension that was compromised by TeamPCP’s malware. Security experts say TeamPCP is less of a hacker group than an amalgamation of threat actors from multiple cybercriminal gangs who sometimes work together toward similar goals. “It is not a structured criminal crew with a single operator,” said Austin Larsen , a principal threat analyst with the Google Threat Intelligence Group . “It is a peer community of individually-skilled actors, with one clear center of gravity.” That center of gravity is George Prepakis , an accomplished security researcher and self-described exploit developer who operates the Twitter/X profile @kernelstub . Earlier this year, @kernelstub tweeted a public invite link to a Matrix chat server he created and dubbed “Cybercats,” and TeamPCP and several other cybercrime entities have been using this server to communicate daily for the past several months. A screenshot of the Matrix chat server “Cybercats,” whose members used hacker handles associated with multiple distinct cybercrime groups that have occasionally collaborated on a series of supply chain and data ransom attacks over the past nine months. Kernelstub, like other administrators in the Cybercats chat, has been using his Twitter/X profile name as his handle in these Matrix communications, frequently tweeting references to other members and to conversations taking place in the Cybercats chat. In a number of cases, the corresponding X accounts for members of the Cybercats chat taunted cybercrime victims publicly before the incidents were reported in the news media. The Cybercats administrator listed at the top of the screenshot above — “ Boxturtle ” — is a close associate of TeamPCP who has been tweeting about the group’s conquests under the name @xpl0itrsturtle . This handle corresponds to a data breach broker active on Breachforums and Darkforums who has been selling data stolen in a wave of recent breaches at automobile manufacturers, including BMW Group , Audi , Honda , Mercedes-Benz , Volvo and Toyota , as well as data allegedly taken from Snapchat and SportRadar . The data leak site for the extortion group or handle “xpl0itrs.” The Cybercats administrator “ SeesawSec ” in the screenshot above is the alias of whoever is behind the cybercrime group known as Fulcrumsec , which recently claimed credit for data extortion attacks against the pharmaceutical giant Novo Nordisk , the data broker LexisNexis , and Avnet , a Fortune 500 distributor of electronic components. The data leak site of Fulcrum Security, a.k.a. Fulcrumsec. The Cybercats administrator “ @pcpcasper ” also has been using a similar name on X to discuss TeamPCP’s attacks and victims. This person has an extensive message history on Telegram, where their messages and shared videos show @pcpcasper is an active and vocal member of the National Socialist Network, a neo-Nazi political organization based in Australia. At one point in these chats, @pcpcasper shared videos and images of what they claimed was their cat, and several of those videos place this user in Western Australia. One source close to the investigation told KrebsOnSecurity that @pcpcasper was one of the two arrested, a claim supported by messages that @kernelstub posted online this morning. The Cybercats member roster pictured above also features an administrator with the username “ T ,” which is short for the now-banned Twitter/X profile @pcpcats , the account operated by the self-described TeamPCP spokesperson who was arrested today. As we’ll see in a moment, @pcpcats also is from Western Australia. By the time @kernelstub tweeted a public invite link to the Cybercats Matrix server, T/@pcpcats was posting only infrequently to the group chat, with other members often inquiring as to his whereabouts and well-being. The group’s collective concern related to @pcpcats’s tendency to blame his increasingly extended absences on the use of hallucinogens and other narcotics that kept him awake for days on end, but also caused him to crash in bed for several days after the highs wore off. The Cybercats member @pcpcats has used multiple nicknames on the cybercrime forums, including EllisD25/LSD on Darkforums, BulkDMT on Breachstars, and Express on Breachforums. These accounts are linked because they all advertised the same Tox ID and/or Session ID as instant message contact handles in their cybercrime forum posts. BulkDMT was also known on the forums as DMT Host , which was a virtual private server (VPS) hosting service that was peddled on Darkforums and Breachstars. DMT Host/EllisD25, posting on the English-language cybercrime community DarkForums in September 2025. Image: ke-la.com. According to the cyber intelligence firm Intel 471 , Express registered on Breachforums using the email address [email protected] . Intel 471 finds Express posted on Breachforums across a two-month period in 2025 using four different Internet addresses located in South Africa . On July 30, 2025, Express announced on Breachforums they were selling access to 14 gigabytes of data stolen from South Africa’s State Information Technology Agency. The threat intelligence platform Flashpoint recorded more than a year’s worth of messages from the TeamPCP leader’s alter ego on Telegram — Persy_PCP —  who claimed they split their life living between two countries [full disclosure: Flashpoint is an advertiser on this blog]. “I have these [files] as well, problem is these are in another country,” Persy_PCP explained to another user inquiring about a stolen data set in November 2025. Later that month, Persy_PCP complained, “My whole country is racist and they want people like me dead.” Flashpoint records show BulkDMT shared in September 2025 that “this country is going to fucking starve when they take the farmers land,” a likely reference to white landowners in South Africa who claim to be targeted by an ongoing genocide campaign . This tracks with public reporting on TeamPCP. Cyberscoop reported in June that Google had traced TeamPCP’s residential and mobile Internet address connections to South Africa, “indicating the primary operator was located there during at least some of its attacks.” BulkDMT also shared on the group chat at Breachforums that they were recovering from an addiction to methamphetamine. “My life is kinda fucked rn [right now], but that’s fine and there isn’t really a point in pouring so much emotional energy into that fact, my parents had money but I unfortunately got really addicted to some things so I don’t get to benefit from that. As long as I continue to survive, stay sober, and move closer towards my goals that’s enough drive and meaning.” The identity threat protection company SpyCloud finds [email protected] shows up in the registration of an account called ChristmasSnow on the cybercrime community Raidforums in 2022. Nearly all of the Internet addresses used to access that account came from ISPs in Perth, Australia, SpyCloud found. KrebsOnSecurity looked up all of those Perth IP addresses in passive DNS records maintained by DomainTools.com , and found one of them — 211.27.196.111 — for several years was used as a private file server by a family in Perth with the last name of Thomson . Those records show at least three hosts — ithomson.direct.quickconnect.to (a remote Synology server), kthomson0061.direct.quickconnect.to, and joshuawthomson39.myqnapcloud.com (a QNAP network storage device) — persisted at that address between 2022 and 2025. Searching on “ joshuathomson39 ” in the breach tracking service Constella Intelligence reveals an account at the freight forwarding company kwe.com created in the name of Joshua Thomson from Perth, Australia. The open source intelligence platform Epieos finds the phone number attached to that kwe.com account was used to register a Facebook profile for Josh Thomson, which says his family includes a brother named Ruben , his father Ian , and his mom Cindy. That Facebook profile also says Josh and his family are originally from Pietermaritzburg , in KwaZulu-Natal, South Africa, but currently living in Cottesloe , a beach-side suburb of Perth. A search in DomainTools for Ian Thomson and Australia unearthed five domains by the same registrant, including securecomputing.au , thomson.org.au , and thomsonfamily.net.au . Ian Thomson is a dentist in Cottesloe, and a biography says he graduated from The University of the Witwatersrand in Johannesburg, South Africa. Constella finds a [email protected] registered a number of accounts online, but Josh doesn’t seem to have much of a connection to dodgy cybercrime forums. His brother Ruben, on the other hand, has quite the presence on these communities, dating back to at least 2018. Constella reports [email protected] frequently reused the password “joshuathomson1,” and Constella further finds that password was used by just a handful of accounts, including [email protected] and [email protected] . According to Intel 471, [email protected] was used to register the user Yolosolo17 on the crime forum Altenen in 2018, and that user account was registered from the Perth address 110.141.230.15 . On Altenen, Yolosolo17 advertised free web proxies, as well as the domain rubenthomson.com, which was at one point used to sell steeply discounted iPhones. DomainTools says rubenthomson.com was hosted at 110.141.230.15 and registered to [email protected]. A cached copy of the domain rubenthomson.com from 2017 shows a login page underneath a banded stack of money. Image: archive.org. SpyCloud reports 10.141.230.15 was used by the email address [email protected] on Raidforums and [email protected] on Nulled, and that the same IP was used by the email addresses [email protected], [email protected], and [email protected]. SpyCloud also shows that sheepstealing Gmail address is tied to the accounts Sheep420 , YoloSolo117 and Yakuza.cc on Raidforums, and to the account “Sheep Stealing” on Hackforums. Intel 471 says [email protected] was used to register the account DingoFlour on Breachforums in October 2023, as well Sheepx on Altenen. Epieos reports that [email protected] is tied to an Airbnb account for Ruben, who described himself as a Web developer who went to school at the University of Western Australia and was living outside the country. “Hey, I’m Ruben, my friends call me Ellis . I’m a Perth creative who occasionally books rooms when visiting family and for photography.” Epieos also finds [email protected] registered an upwork.com profile under the name Ruben, who said his main skills are setting up secure server hosting solutions and PHP full-stack Web development. “I’m familiar with Linux, working with relational databases (SQL),” the Upwork profile reads. “I also script in Python mainly for writing social media bots.” The Upwork profile for Ruben Thomson in Cottesloe, Australia. Epieos further discovered [email protected] is connected to a Microsoft account for Ruben Thomson, and to a now-defunct GitHub account called XmasSnow/XmasSnowisBack that scammed people on the forums in 2022 by claiming to sell exclusive exploits for recently-released software patches (recall that [email protected] was used to register a forum account named ChristmasSnow). This same sheepstealing email address registered a Twitter/X account in 2026 called “Gone Fishing” that lists its location as South Africa. That Gmail account also left several reviews for businesses listed on Google Maps over the past seven years, but all of those establishments are located on the west coast of Australia. Business reviews in Western Australia left by the Google account sheepstealing at gmail.com. The people search service Pipl finds a 21-year-old Ruben Thomson in Western Australia who has a phone number ending in 979. A lookup on that number at Epieos reveals it is connected to a TikTok account under the name Ellis, and to a PayPal account in the name of Ruben Thomson. Finally, a search on the name Ruben Thomson from Cottesloe at the Australian government’s record of registered businesses finds he has incorporated or served as an official in multiple companies created since 2024, including Secure Computing Solutions , Tensor Industries , and another entity ironically named OPSEC Express . Recall that Express was BulkDMT’s nickname on Breachforums. Australian companies connected to Ruben Thomson. Image: abr.business.gov.au. It’s ironic because OPSEC is short for the term “operational security,” which refers to techniques and behaviors used to obfuscate and compartmentalize one’s real-life identity online, and using your cybercrime handle as part of your own company name is very much the antithesis of that practice. There is at least one other major opsec failure by Ruben that exposed a link to TeamPCP. In June 2025, someone using the name Ruben Thomson registered on HackerOne , a popular “bug bounty” program that seeks to reward and recognize researchers who agree to work with affected software vendors to help fix the flaws before publishing about their findings. What was Ruben Thomson’s chosen HackerOne username? Deadcatx3 , a nickname that has been flagged by multiple security firms as an alias used by TeamPCP. The HackerOne profile for “Ruben Thomson” uses the nickname Deadcatx3, which multiple security firms have concluded is an alias used by TeamPCP. Image credit: flare.io. In early July 2026, not long after having discovered clues about Ellis’s real life identity, KrebsOnSecurity interviewed the TeamPCP leader via Signal, where he was remarkably open about his activities and personal struggles [for the sake of simplicity, the TeamPCP spokesperson will be referred to from here on as Ellis]. Ellis claims he stopped doing cybercrime for TeamPCP in March 2026 — just before the attacks that compromised LiteLLM — and that at least one other individual has taken over the group’s leadership since then. Ellis shared that a year earlier he had just completed the latest in a series of detox and sobriety programs, and was two months sober when he reconnected with some old friends from the malware development scene. “One year ago I needed help monetizing some [GitHub credentials], I was two months sober and needed a distraction and something to keep busy as well as people to speak to,” Ellis said. “I had largely disconnected from my old circle, they had become very toxic and I needed to get away from the substances. Previously I had done some mass exploitation campaigns and grew up doing [malware development] and [capture the flag] contests. There were some friends who were also vending but had stopped a while, and one of them introduced me to some chats where I posted access for sale.” Prior to that, Ellis said, he was homeless and hopping between “some very unstable places.” “Blackhatting is fun,” he said. “There are actual rewards and incentives to learn and you grow with your team. Without qualifications, no employer will even take the time to hear you out.” Ellis claims he’s earned a grand total of about $20,000 for his activities with TeamPCP, and that it was never about the money or fame for him. Asked whether his experiences with TeamPCP might prepare him for gainful employment in a legitimate IT job, Ellis said he doubted it. “I am nowhere close to a skill level where I am comfortable, and this would take maybe half a decade of further experience,” he said. “I no longer have to choose between rent and food for that I’m grateful and so are the team members.” Ellis expressed no remorse over his cybercrime activities, and said he was grateful for the friendships and relationships built throughout his engagement with TeamPCP. The young hacker also seemed resigned to his fate, and told KrebsOnSecurity that he’ll accept the consequences if he’s ever arrested. “If I’ve already been found out then its out of my control, I’ll make peace with that,” he said. “Honestly, I think someone like me needs a lot of help that prison just can’t provide. If I had the funds to study different parts of the field and closer guidance, this would have turned out differently. But that’s a pipe dream and we both know this.” It is clear from reading Ellis’s posts to the group’s Matrix server chats that his struggles with sobriety are ongoing. On Thursday, June 25, Ellis told @kernelstub he was about to “trip” with his “homie.” “What kind,” @kernelstub inquired. “Ketty and some DMT,” Ellis replied, referring to the dissociative anesthetic ketamine and dimethyltryptamine (DMT), a powerful psychedelic compound that is found naturally in some plants but is also synthetically produced in underground lab environments. “There’s a little 2cb so we might throw that in the mix,” he continued, referring to another psychedelic compound by its chemical shorthand. Roughly two weeks before his arrest, Ellis told KrebsOnSecurity he was ready to leave his life of crime behind and was prepared to turn himself in, but that in the meantime he was making plans to tie up loose ends. Less than 24 hours later, the TeamPCP leader posted an image on Telegram showing a yellowish powdered substance in a baggie and on a scale, possibly synthetic DMT. The image shows the powder being weighed next to a series of small vape cartridges, two of which are open on the table in front of the photographer. An image posted by the TeamPCP leader to Telegram, advertising his acquisition of some type of psychoactive substance, most likely a synthetic version of the powerful hallucinogen known as DMT. The two defendants were arrested Wednesday morning. The AFP said the men face a combined 14 cybercrime offenses and are scheduled to appear in Perth Magistrates Court today. Charlie Eriksen is a security researcher at Aikido Security who has closely followed TeamPCP’s cybercrime campaigns. Eriksen said TeamPCP are a good example of a new kind of threat actor that does not fit neatly into the usual categories. “They are not a state actor, not quite organized cybercrime, and not purely ideological,” he said. “Their motivations seem to mix money, disruption, attention, and ideology.” Eriksen said that historically there has always been a meaningful gap between reading about an attack technique and being able to reliably turn it into an operational campaign, but that large language models (LLMs) and artificial intelligence increasingly are helping threat actors to bypass that knowledge gap. “You had to understand the research, adapt the code, troubleshoot it, build infrastructure around it, and then repeat that process across different targets,” he said. “LLMs have compressed that gap significantly.” According to Eriksen, this creates an environment where threat actors suddenly have the ability to operate at significant scale without having developed the operational discipline that traditionally accompanies that level of capability. Put another way, it sets the stage for cybercriminals who are capable enough to cause significant damage, but not necessarily careful enough to understand or care about the consequences. “They can be noisy, they can make mistakes,” he said. “They can leave evidence everywhere. They can take risks that a professional criminal group or intelligence service would consider completely unacceptable. But that does not necessarily make them less dangerous. In some ways, it can make them more dangerous.” In a recent blog post , Eriksen called TeamPCP’s Shai-Hulud worm the “best thing to happen to supply chain security,” because it forced GitHub and other public coding platforms to erect new security safeguards. In direct response to TeamPCP’s broad success at pushing poisoned versions of popular software packages, GitHub in late July introduced a three-day “cooldown” mechanism for Dependabot, the platform’s tool for auto-fetching newly shipped updates for any package dependencies. Cooldown periods are designed to help buy time for security tools and package maintainers to identify and remove any compromised versions. Other coding ecosystems like Python and various JavaScript platforms also added support for cooldown periods this year amid growing calls from security experts about the need for more widespread adoption of the safety feature. Eriksen said TeamPCP’s legacy is that they achieved in the span of a few months what the supply chain security community has been unable to do for years. “They managed to wake up Microsoft to the fact that they had become negligent in terms of security,” Eriksen said. “By compromising GitHub and stealing their source code, they humiliated Microsoft into action, making them finally act on what we had been asking them to do and take seriously for a while now.” Update, 10:08 a.m. ET: A story this morning from ABC News in Australia confirms Ruben Ian Thomson of Cottesloe was one of the two arrested. The 23-year-old suspect thought to be @pcpcasper, Michael Gaebler, also was arrested in Perth. ABC News reports that Thomson was denied bail (Mr. Gaebler’s attorney reportedly did not request bail for his client), and that both men will be held in custody until their next court appearance on September 18.

0 views
codedge 4 days ago

Prevent deploying broken links to your blog

Having your own blog is fun. Checking internal links or also having an eye on all old URLs you ever linked is not. Fortunately you can automate link checking every time you deploy your website. I recently read about how links you once posted on your personal page or block become outdated. They are either put in private (403), they vanish completely (404) or they get a proper redirect (302). Whatever the case is, it would be cool the get all your links checked automatically when deploying your site, so you can either start fixing or removing them. For my Hugo site I wanted to do exactly, without going the write a scraper to extract links from my site and letting them run through curl . I wanted something to be run against my static HTML files, than I generate before deploying a new version of my page. I came across a very handy tool called lychee , that does exactly that. On their website they advertise it with Catch broken links in seconds Async, rust-powered simplicity for docs, sites, and codebases The cool stuff is, lychee works with I implemented it into my deployment workflow, scanning a folder , where my newly generated files are - and voila, I get a list of URLs with all their HTTP status codes. Of course you can configure ( see documentation ) which status codes are treated as good or errors. For example, I consider a not an error per se. You can also exclude specific URLs (or via regexp) to not being checked. I run this now on PR and on new deployments of my main branch. Works very well! Markdown files Websites (scraping all links)

0 views
Jason Fried 4 days ago

$5300 in $100s

$5300 in $100s please. That's what we said at the bank this morning. See, whenever you have a free IRL event, you tend to get a lot of no-shows. Plenty of people sign up and RSVP, but ultimately far fewer show up. This isn't a big deal if you have unlimited space and anyone can just show, but if you want to have an event at a small venue, and you can only host, say, 80 people, you need to have a sense of how many are coming. That's why you require signup, that's why you require RSVPs. But still, many don't show. We've run into this with our own free events over the years. You "sell out" of space, but you end up with 15 extra seats because people don't show. Those seats could have been filled with people who really wanted to be there. We could charge, which might help, but we really want this to be a free event. So what to do? How can we find a way to bridge the gap between who says they'll show, and who will really show? How do we shrink the delta? We're trying something tomorrow. Tomorrow we have a Breakfast with Basecamp IRL event in Chicago. It's entirely free. As long as you show up. When you register you're charged $100. And if you show up, you get a crisp $100 bill at the door. So you're made whole, it's entirely free. If you don't show, you forfeit your $100. It costs you money not to be there. A little incentive. This isn't a new idea. Some restaurants have been charging for reservations, essentially asking you put down a deposit which can be applied to the meal if you show. If not, you lose the deposit. So we're doing the same thing, except for a free event, and rather than apply the deposit to the meal or ticket price (which we don't have), we just literally hand you back the cost of admission, with a smile. I'm really curious to see if it works. I'll comment back with the no-show rate and compare it to our last event where we didn't do this at all. -Jason

0 views
Farid Zakaria 5 days ago

Stamping build info in constant memory

This is a fun little trick I came across at . I did not invent it, but I thought it was interesting enough to understand better and share. At we build with buck2 and we stamp our executables with build information: build-id, timestamp, author, the usual suspects using as a step after the link. The reason it is a separate step is caching. If the build info was generated at link time then everytime we link the binary it would produce different bytes causing it to not be bit-reproducible. When something is bit-reproducible, it is safe to cache it, and the build system can apply early cut-off optimizations. That works, until the binaries get big. We noticed that ’s memory use scales with the size of the file it is stamping. Stamping is exactly the kind of step that runs massively parallel at the end of a build so this can cause a lot of memory pressure. Why is the stamping step reading the binary at all? 🤔 Let’s measure the claim that the memory use of scales with the size of the file. We will attach a JSON build info blob to an increasingly large synthetic executable and measure the peak RSS of the stamping step. The graph confirms the claim. The memory use of scales linearly with the size of the file being stamped. Surprisingly, the slope is two . The peak RSS is roughly twice the size of the file being stamped irrespective of the size of the build info being attached. 1 I am helping to shepherd a PR open against LLVM to stream the ELF output rather than materialize it, which roughly halves the peak. That is a definite improvement but the problem remains that in order to add a tiny section to a large binary, the whole binary has to be read into memory. The memory use is still linear in the size of the file. The problem is not poor implementation on the part of . Adding a section to an ELF touches three separate things: 0x78 bytes in .interp\0 .buildinfo\0 .rela… 0x70 0x78 0x83 the section's bytes 88 bytes of JSON, at 0x401021 The entry holds neither the name nor the bytes. It only refers to them — and only one of those two references can be repointed in place. In order to account for the new section, the section header table has to grow by one entry, and has to grow by the length of the new name. The current model for is to read the whole file into memory, add the new section, and write the whole file back out. That is why the memory use scales with the size of the file. How can we avoid having to rebuild the whole file just to add a tiny section? The trick is to pay the cost at link time rather than at stamp time. We can have the linker emit a placeholder section with the right name and a single byte of content. The section header table entry is already there, and the name is already in . The post-link stamping step can then append the payload to the end of the file and update the section header entry to point to it. 💡 We make linker emit the section during the normal build. It does not need to hold anything; it just needs to exist so that it owns a name and a header. Our “stamp” step is now incredibly simple. It does not need to read the file at all, it just needs to write the new payload and update the section header entry. Nothing that already exists moves. does not move, the section header table does not move, no other changes. The edit is sixteen bytes , at a file offset you can compute from the ELF header, plus a . read into a model, serialized again — 1,238 bytes of it actually differ reserve one byte, then append 16 bytes + a tail ehdr phdrs .text .rodata … .shstrtab section headers payload the reserved byte sh_offset, sh_size Note Why 1 byte? Turns out that and GNU disagree on whether an empty section is a valid ELF. The one byte is a cheap way to make both linkers happy. The payload lands after the section header table, which looks alarming the first time you see it but is completely legal. Nothing in ELF says section contents must precede the section header table. The kernel also never looks at section headers also, it loads segments out of the program headers, which we do not touch. I wrote a small C version to benchmark it in contrast, please be mindful that this graph is log-log. Our trick works! The “append + 16 bytes” approach is constant memory. Not only is the peak RSS constant, but the wall time is also constant and much faster by avoiding the read and write of the whole file. It is often easy to reach for general-purpose tools like as they are a swiss-army knife for manipulating object files. What I like about this trick though is that there are meaningful improvements to be made by writing special purpose tools and that does not mean we have to accrue large maintenance costs. In this case it was a tiny 200-line C program. The economics of these tools is also changing with the rise of LLMs in our workflow. While many are concerned about the influx of generated code, I remain optimistic that we we can use them to find such opportunities. Don’t be afraid to write a small tool to solve a specific problem. For those thinking this is an LLVM specific issue, GNU exhibits the same behavior.  ↩ the section’s bytes , somewhere in the file a 64-byte entry in the section header table describing where those bytes are the section’s name , which is not in the entry itself but rather the entry holds a offset into , so the name has to be appended to that string table 0x70 0x78 0x83 the section's bytes 88 bytes of JSON, at 0x401021 The entry holds neither the name nor the bytes. It only refers to them — and only one of those two references can be repointed in place. In order to account for the new section, the section header table has to grow by one entry, and has to grow by the length of the new name. The current model for is to read the whole file into memory, add the new section, and write the whole file back out. That is why the memory use scales with the size of the file. Pay the byte at link time How can we avoid having to rebuild the whole file just to add a tiny section? The trick is to pay the cost at link time rather than at stamp time. We can have the linker emit a placeholder section with the right name and a single byte of content. The section header table entry is already there, and the name is already in . The post-link stamping step can then append the payload to the end of the file and update the section header entry to point to it. 💡 We make linker emit the section during the normal build. It does not need to hold anything; it just needs to exist so that it owns a name and a header. Our “stamp” step is now incredibly simple. It does not need to read the file at all, it just needs to write the new payload and update the section header entry. append the payload to the end of the file write the new and into the placeholder’s section header entry the reserved byte sh_offset, sh_size Note Why 1 byte? Turns out that and GNU disagree on whether an empty section is a valid ELF. The one byte is a cheap way to make both linkers happy. The payload lands after the section header table, which looks alarming the first time you see it but is completely legal. Nothing in ELF says section contents must precede the section header table. The kernel also never looks at section headers also, it loads segments out of the program headers, which we do not touch. Benchmark I wrote a small C version to benchmark it in contrast, please be mindful that this graph is log-log. elfstamp.c Our trick works! The “append + 16 bytes” approach is constant memory. Not only is the peak RSS constant, but the wall time is also constant and much faster by avoiding the read and write of the whole file. One trick pony It is often easy to reach for general-purpose tools like as they are a swiss-army knife for manipulating object files. What I like about this trick though is that there are meaningful improvements to be made by writing special purpose tools and that does not mean we have to accrue large maintenance costs. In this case it was a tiny 200-line C program. The economics of these tools is also changing with the rise of LLMs in our workflow. While many are concerned about the influx of generated code, I remain optimistic that we we can use them to find such opportunities. Don’t be afraid to write a small tool to solve a specific problem. For those thinking this is an LLVM specific issue, GNU exhibits the same behavior.  ↩

0 views
Xe Iaso 5 days ago

How to make VS Code go back to the old UI

Someone on the VS Code team decided to do a redesign of the product. This makes VS Code look like this: A picture of the VS Code UI that has a design I don't like. This design is fine, I guess? It's got some rounded corners that look nice, I guess, but I really just want it to look like what I'm used to. I can tolerate this kind of redesign in my chat app, but I use VS Code professionally and kinda want things to look the same so I don't have to think as much. You can revert the design change by setting in your settings.json. Then it looks like this: A picture of the VS Code UI that has the design I'm used to. Who knows how long this is going to last, but at the very least it should work for now. Thanks to jtagcat and Hugo ARNAL for letting me know about this setting.

0 views
Xe Iaso 5 days ago

If your VS Code remotes stopped working, downgrade to v1.124.x

Today I woke up and saw a brand new VS Code error when I remoted into my coding box: A VS Code error saying that the remote SSH extension can't use the API terminalRemoteResolver and that I have to start VS Code with some weird command line flag or something. Trying to update my extensions didn't work. I still got that error. The only thing that worked was to downgrade to VS Code v1.124.2. This also undid the redesign that I kinda hate. The VS Code UI drafting an early version of this post. Obviously keeping things at this older version of VS Code is not a viable strategy, so maybe this issue will actually be fixed instead of just being closed for no reason. Quality software reigns again!

0 views
Jeff Geerling 5 days ago

Debugging Ubiquiti's 5G Backup on AT&T

For a mobile 'mini homelab' project I'm working on, I wanted to use 5G Internet as a primary 'on-to-go' connection, but still have the ability to plug in another Internet connection when I put the mobile rack I'm building in a fixed location. I'll have more on that project soon, but I figured this project was a good way to check out Ubiquiti's solutions, especially considering many of their smaller gear fits nicely within the dimensions of a mini rack (the 3U DeskPi RackMate TT is pictured above).

0 views
Jim Nielsen 5 days ago

Have You Heard the Good News About Microlighter?

Dave Rupert wrote about shipping microlighter : a tool for handling syntax highlighting using the CSS Custom Highlights API . I saw his post the day he released it, and I had an implementation PR up for my blog by end of day. Then, like I do with so many things, I let it sit there. This is the period where my subconscious takes over. It does the work of, “How do I actually feel about that? Do I want to merge it? Do I have any regrets about what I did?” If I still want to merge it after a few days, that’s usually a good sign that I’ll be happy with the work. (Sometimes after a few days I say, “What the hell was I thinking?” and then it’s easy to simply close the PR with zero regrets.) Well it’s a few days later and I still feel good about it, so time to ship! My PR for this is pretty straightforward: Granted, there are trade-offs to this approach. I get it. Dave’s explainer for this tool on The ShopTalk Show vibed with me because I’ve been in his shoes many times: “Whoops, somehow syntax highlighting on my blog is broken again. Guess I need to fix it. Ugh. I’ve done prism , I’ve done highlight.js , I’ve done shiki . What should I do this time? Could I do this in a way that’s just less ?” He clarifies: I’m not coming at this like, “Everyone is doing it wrong!” I was just kind of like, “Could I do this in a way that suited me?” Well, this approach suites me. There’s a kind of conceptual elegance to it where syntax highlighting lives in the realm of a styling operation rather than a content transformation plus styling. In short: syntax highlighting, i.e. styling text, is a styling concern so solve it with CSS — no DOM manipulation required! Plus, I mean, how cool is it that the code on the website is the same as the code in the DOM?!? I guess this is how I know I still like working on the web, because seeing browsers do stuff like this that they couldn’t do before still feels really cool! Reply via: Email · Mastodon · Bluesky Remove dependency (and related plumbing) On paths that 1) match my post pages (i.e. ), and 2) have code on them, pull microlighter deps from a CDN and run it.

0 views
Unsung 5 days ago

Two previews for the price of one

A thoughtful moment in the Arrange Displays pop-up in macOS’s Settings when you choose to designate a screen to be a primary screen: It not only shows a nice preview of where your menu is going to land, but also changes the shape of the menu proxy you’re holding , to help you connect the two. (The whole interaction is probably obsolete, though – if I remember correctly, it used to be that you could only have a menu on one designated screen. Today, the menu is replicated on all screens.) #above and beyond #mouse #preview

0 views