10 interesting stories served every morning and every evening.

GrapheneOS (@GrapheneOS@grapheneos.social)

grapheneos.social

To use the Mastodon web ap­pli­ca­tion, please en­able JavaScript. Alternatively, try one of the na­tive apps for Mastodon for your plat­form.

Local AI Needs to be the Norm · unix.foo

unix.foo

One of the cur­rent trends in mod­ern soft­ware is for de­vel­op­ers to slap an API call to OpenAI or Anthropic for fea­tures within their app. Reasonable peo­ple can quib­ble with whether those fea­tures are ac­tu­ally bring­ing value to users, but what I want to dis­cuss is the fun­da­men­tal con­cept of tak­ing on a de­pen­dency to a cloud hosted AI model for ap­pli­ca­tions.

This lazi­ness is cre­at­ing a gen­er­a­tion of soft­ware that is frag­ile, in­vades your pri­vacy, and fun­da­men­tally bro­ken. We are build­ing ap­pli­ca­tions that stop work­ing the mo­ment the server crashes or a credit card ex­pires.

We need to re­turn to a habit of build­ing soft­ware where our lo­cal de­vices do the work. The sil­i­con in our pocket is mind bog­glingly faster than what was avail­able a decade ago. It has a ded­i­cated Neural Engine sit­ting there, mostly idle, while we wait for a JSON re­sponse from a server farm in Virginia. That’s ridicu­lous.

Even if your in­ten­tions are pure, the mo­ment you stream user con­tent to a third party AI provider, you’ve changed the na­ture of your prod­uct. You now have data re­ten­tion ques­tions and all the bag­gage that comes with that (consent, au­dit, breach, gov­ern­ment re­quest, train­ing, etc.)

On top of that you also sub­stan­tially com­pli­cated your stack be­cause your fea­ture now de­pends on net­work con­di­tions, ex­ter­nal ven­dor up­time, rate lim­its, ac­count billing, and your own back­end health.

Congratulations! You took a UX fea­ture and turned it into a dis­trib­uted sys­tem that costs you money.

If the fea­ture can be done lo­cally, opt­ing into this mess is self in­flicted dam­age.

AI every­where” is not the goal. Useful soft­ware is the goal.

Concrete Example: Brutalist Report’s On-Device Summaries

Years ago I launched a fun side pro­ject named The Brutalist Report , a news ag­gre­ga­tor ser­vice in­spired by the 1990s style web.

Recently, I de­cided to build a na­tive iOS client for it with the de­sign goal of en­sur­ing it would re­main a high-den­sity news read­ing ex­pe­ri­ence. Headlines in a stark list, a reader mode that strips the can­cer that has over­taken the web, and (optionally) an intelligence” view that gen­er­ates a sum­mary of the ar­ti­cle.

Here’s the key point though: the sum­mary is gen­er­ated on-de­vice us­ing Apple’s lo­cal model APIs. No server de­tours. No prompt or user logs. No ven­dor ac­count. No we store your con­tent for 30 days” foot­notes needed.

It has be­come so nor­mal for folks that any AI use is hap­pen­ing server-side. We have a lot of work to do to turn this around as an in­dus­try.

It’s not lost on me that some­times the use-cases you have will de­mand the in­tel­li­gence that only a cloud hosted model can pro­vide, but that’s not the case with every use-case you’re try­ing to solve. We need to be thought­ful here.

Available Tooling

I can only speak on the tool­ing avail­able within the Apple ecosys­tem since that’s what I fo­cused ini­tial de­vel­op­ment ef­forts on. In the last year, Apple has in­vested heav­ily here to al­low de­vel­op­ers to make use of a built-in lo­cal AI model eas­ily.

The core flow looks roughly like this:

im­port FoundationModels

let model = SystemLanguageModel.default guard model.avail­abil­ity == .available else { re­turn }

let ses­sion = LanguageModelSession { ”″ Provide a bru­tal­ist, in­for­ma­tion-dense sum­mary in Markdown for­mat. - Use **bold** for key con­cepts. - Use bul­let points for facts. - No fluff. Just facts. ”″ }

let re­sponse = try await ses­sion.re­spond(op­tions: .init(maximumResponseTokens: 1_000)) { ar­ti­cle­Text }

let mark­down = re­sponse.con­tent

And for longer con­tent, we can chunk the plain text (around 10k char­ac­ters per chunk), pro­duce con­cise facts only” notes per chunk, then runs a sec­ond pass to com­bine them into a fi­nal sum­mary.

This is the kind of work lo­cal mod­els are per­fect for. The in­put data is al­ready on the de­vice (because the user is read­ing it). The out­put is light­weight. It’s fast and pri­vate. It’s okay if it’s not a su­per­hu­man PhD level in­tel­li­gence be­cause it’s sum­ma­riz­ing the page you just loaded, not in­vent­ing world knowl­edge.

Local AI shines when the mod­el’s job is trans­form­ing user-owned data, not act­ing as a search en­gine for the uni­verse.

There are plenty of AI fea­tures that peo­ple want but don’t trust. Summarizing emails, ex­tract ac­tion items from notes, cat­e­go­rize this doc­u­ment, etc.

The usual cloud ap­proach turns every one of those into a trust ex­er­cise. Please send your data to our servers. We promise to be cool about it.”

Local AI changes that. Your de­vice al­ready has the data. We’ll do the work right here.

You don’t build trust with your users by writ­ing a 2,000 word pri­vacy pol­icy. You build trust by not need­ing one to be­gin with.

The tool­ing avail­able on the plat­form goes even fur­ther.

One of the best moves Apple has made re­cently is push­ing AI out­put” away from un­struc­tured blobs of text and to­ward typed data.

Instead of ask the model for JSON and pray”, the newer and bet­ter pat­tern is to de­fine a Swift struct that rep­re­sents the thing you want. Give the model guid­ance for each field in nat­ural lan­guage. Ask the model to gen­er­ate an in­stance of that type.

That’s it.

Conceptually, it looks like this:

im­port FoundationModels

@Generable struct ArticleIntel { @Guide(description: One sen­tence. No hype.“) var tldr: String @Guide(description: 3 – 7 bul­lets. Facts only.“) var bul­lets: [String] @Guide(description: Comma-separated key­words.“) var key­words: [String] }

let ses­sion = LanguageModelSession() let re­sponse = try await ses­sion.re­spond( to: Extract struc­tured notes from the ar­ti­cle.”, gen­er­at­ing: ArticleIntel.self ) { ar­ti­cle­Text }

let in­tel = re­sponse.con­tent

Now your UI does­n’t have to scrape bul­let points out of Markdown or hope the model re­mem­bered your JSON schema. You get a real type with real fields, and you can ren­der it con­sis­tently. It pro­duces struc­tured out­put your app can ac­tu­ally use. And it’s all run­ning lo­cally!

This is­n’t just nicer er­gonom­ics. It’s an en­gi­neer­ing im­prove­ment.

And if you’re build­ing a lo­cal first app, this is the dif­fer­ence be­tween AI as nov­elty” and AI as a trust­wor­thy sub­sys­tem”.

But Local Models Aren’t As Smart”

Correct.

But also so what?

Most app fea­tures don’t need a model that can write Shakespeare, ex­plain quan­tum me­chan­ics, and pass the bar exam. They need a model that can do one of these re­li­ably: sum­ma­rize, clas­sify, ex­tract, rewrite, or nor­mal­ize.

And for those tasks, lo­cal mod­els can be truly ex­cel­lent.

If you try to use a lo­cal model as a re­place­ment for the en­tire in­ter­net, you will be dis­ap­pointed. If you use it as a data trans­former” sit­ting in­side your app, you’ll won­der why you ever sent this stuff to a server.

Use cloud mod­els only when they’re gen­uinely nec­es­sary. Keep the user’s data where it be­longs. And when you do use AI, don’t just glue it as a chat box. Use it as a real sub­sys­tem with typed out­puts and pre­dictable be­hav­ior.

Stop ship­ping dis­trib­uted sys­tems when you meant to ship a fea­ture.

Valve Releases Steam Controller CAD Files Under Creative Commons License

www.digitalfoundry.net

Modders, start your en­gines.

by William Judd

Wed, 10:29am

With the rather ex­cel­lent Steam Controller now on its way to the lucky few that man­aged to or­der one, Valve has re­leased a full set of CAD files for their new hard­ware. The idea is to let en­ter­pris­ing mod­ders cre­ate their own Steam Controller add-ons, like skins, charg­ing stands, grip ex­ten­ders or smart­phone mounts.

The Valve re­lease in­cludes files for the ex­ter­nal shell (“surface topol­ogy”) of the Controller and Puck, with a .STP, .STL and en­gi­neer­ing di­a­gram of each de­vice, with the lat­ter show­ing ar­eas that must re­main un­cov­ered to let the de­vice main­tain its sig­nal strength and oth­er­wise func­tion as de­signed.

Valve has pre­vi­ously re­leased CAD files for its Steam Deck hand­held, Valve Index VR suite and even the orig­i­nal Steam Controller a decade ago, so this re­lease is wel­comed but not un­ex­pected.

The re­lease is un­der a fairly re­stric­tive Creative Commons li­cense which al­lows for non-com­mer­cial use and re­quires at­tri­bu­tion and shar­ing of de­signs back to the com­mu­nity. However, the li­cense also sug­gests that com­mer­cial en­ti­ties in­ter­ested in mak­ing ac­ces­sories for the Steam Controller or its Puck can con­tact Valve di­rectly to dis­cuss terms.

What is your ul­ti­mate Steam Controller or Steam Controller Puck ac­ces­sory? Let us know in the com­ments be­low. For me, it would def­i­nitely be a smart­phone clip - play­ing through some­thing rel­a­tively low-stakes like Forza Horizon 6 via Moonlight game stream­ing on a phone would be slick.

[source steam­com­mu­nity.com]

Will is web­site ed­i­tor for Digital Foundry, spe­cial­is­ing in PC hard­ware, sim rac­ing and dis­play tech­nol­ogy.

Author Profile

Bluesky

Reply

Appearing Productive in The Workplace — No One's Happy

nooneshappy.com

Parkinson’s Law states that work ex­pands to fill the time avail­able. In the era of AI, work­ers now have a tool that ex­pands to fill what­ever a large lan­guage model can be per­suaded to gen­er­ate, which is to say, with­out limit.

Parkinson’s Law states that work ex­pands to fill the time avail­able. In the era of AI, work­ers now have a tool that ex­pands to fill what­ever a large lan­guage model can be per­suaded to gen­er­ate, which is to say, with­out limit.

What I have watched hap­pen in my pro­fes­sion in the last two years, I am still strug­gling to de­scribe. The first time I knew some­thing was wrong, roughly a year and a quar­ter ago, I no­ticed a col­league re­ply­ing to me us­ing AI. His re­sponse was ob­vi­ously gen­er­ated by Claude. The punc­tu­a­tion gave it away — em dashes where no one types em dashes, the rhyth­mic struc­ture, the con­fi­dent grasp of tech­nolo­gies I knew for a fact he did not un­der­stand. I sat with it for a while, weigh­ing whether to de­bate some­one who was vis­i­bly copy-past­ing ver­ba­tim from a model. The chan­nel was pub­lic, and I spent more time than I should have cor­rect­ing fun­da­men­tals. Eventually I stopped. He was not, in any mean­ing­ful sense, on the other side of the con­ver­sa­tion.

Generative AI can pro­duce work that looks ex­pert with­out be­ing ex­pert, and the fail­ure ar­rives in two shapes. The first is when novices in a field are able to pro­duce work that re­sem­bles what their se­niors pro­duce, faster or more ad­vanced than their judg­ment. The sec­ond is when peo­ple gen­er­ate ar­ti­facts in dis­ci­plines they were never trained in. The two fail­ures look sim­i­lar from a dis­tance and are not the same. Research has mostly mea­sured the first. The sec­ond is what it is miss­ing, and in my ex­pe­ri­ence it is the riskier of the two.

Cross do­main gen­er­a­tion

People who can­not write code are build­ing soft­ware. People who have never de­signed a data sys­tem are de­sign­ing data sys­tems. Most of it is not shipped; it is built, of­ten for many hours, pos­si­bly shown in­ter­nally with great vigor, used qui­etly, and oc­ca­sion­ally sur­faced to a client with­out much fan­fare. Workers can ob­sess over an idea, work­ing many hours over­time. There are a few prac­ti­tion­ers who use the cur­rent agen­tic tools to do com­plex things prop­erly, but they are scarce and as I find, typ­i­cally in code gen­er­a­tion. AI, for all its ca­pa­bil­i­ties at the level of the in­di­vid­ual, has not scaled prop­erly in my work­place.

I have a col­league, a care­ful and in­tel­li­gent per­son in a role that is not en­gi­neer­ing, who spent two months ear­lier this year build­ing a sys­tem that should have been de­signed by some­one with for­mal train­ing in data ar­chi­tec­ture. He used the tools well, by the stan­dards by which use of the tools is cur­rently mea­sured. He pro­duced a great deal of code, a great deal of doc­u­men­ta­tion, a great deal of what looked, to any­one who did not know what to look for, like progress. He could not, when asked, ex­plain how any of it ac­tu­ally worked. The work was wrong from the first day. The schemas, and more im­por­tantly the ob­jec­tives, were wrong in a way that would have been ob­vi­ous to any­one with two years in the field. Several of us did know. When opin­ions were voiced even as high as a V.P., he fought back. The room had been arranged in such a way that say­ing so was not a con­tri­bu­tion; his man­agers were too in­vested in the ap­pear­ance of mo­men­tum to want the ap­pear­ance dis­turbed. The work will con­tinue, in all prob­a­bil­ity, un­til it is shown to a stake­holder, and they de­cide not to in­vest.

This is the part of the phe­nom­e­non I find hard­est to write about. The tool did not make him a worse col­league. It made him able to im­per­son­ate, for months, a dis­ci­pline he had never trained in, and the im­per­son­ation was good enough that the in­sti­tu­tional in­cen­tives all bent to­ward let­ting him con­tinue. Perhaps it’s a fail­ure of man­age­ment, but I have been find­ing man­age­ment to be so ea­ger to em­brace AI that they’re will­ing to ac­cept the risk.

It would be tol­er­a­ble, per­haps, if the tool of­fered an hon­est as­sess­ment of what it had pro­duced. The Cheng et al. Stanford study pub­lished in Science this spring [1] con­firmed what every reg­u­lar user al­ready knew: lead­ing mod­els are roughly fifty per­cent more agree­able than hu­man re­spon­dents, af­firm­ing the user even where the af­fir­ma­tion is un­war­ranted. Berkeley CMR meta-analy­ses [4] found AI-literate users of­ten over­es­ti­mate their per­for­mance. Particularly in­ter­est­ing when work­ers stray out­side of their train­ing. An NBER study of sup­port agents [2] found gen­er­a­tive AI boosted novice pro­duc­tiv­ity by about a third while barely help­ing ex­perts. Harvard Business School re­searchers found the same pat­tern in con­sult­ing work [3]. So you have over­con­fi­dent, novices able to im­prove their in­di­vid­ual pro­duc­tiv­ity in an area of ex­per­tise they are un­able to re­view for cor­rect­ness. What could go wrong?

The con­duit prob­lem

A grow­ing body of work calls this out­put-com­pe­tence de­cou­pling [5]. In any pre­vi­ous era, the qual­ity of a piece of work was a more or less re­li­able sig­nal of the com­pe­tence of the per­son who pro­duced it. A novice es­say read like a novice es­say; novice code crashed in novice ways. AI has sev­ered that re­la­tion­ship. A novice now pro­duces work that does not be­tray the novice, be­cause the com­pe­tence the work re­flects is not the novice’s com­pe­tence at all. It is the sys­tem’s. The per­son, in the trans­ac­tion, be­comes a kind of con­duit, ca­pa­ble of rout­ing the out­put to a re­cip­i­ent and in­ca­pable of eval­u­at­ing it on the way through.

The skills of pro­duc­ing work and judg­ing it were de­lib­er­ately dis­tinct, but ac­com­plish­ing the work it­self used to teach the judg­ment. The first skill now be­longs, in large part, to the ma­chines. The sec­ond still be­longs to us, though fewer are both­er­ing to ac­quire or uti­lize it.

The ar­chi­tec­tural cri­tique that used to come from some­one who was taught, or who had built and bro­ken three of these be­fore now comes from a model with no em­bod­ied mem­ory of build­ing or break­ing any­thing. The slow­ness was not a tax on the real work; the slow­ness was the real work. It was how the work got good, and how the peo­ple pro­duc­ing the work got good, and how the firm whose name was on the work could promise the client that what they were buy­ing was a par­tic­u­lar kind of thing rather than a generic one.

The cur­rent gen­er­a­tion of agen­tic sys­tems is built around the premise that the hu­man is the bot­tle­neck — that the loop runs faster and cleaner with­out the awk­ward de­lay of some­one read­ing what is about to hap­pen and de­cid­ing whether it should. This is, in a great many cases, ex­actly back­wards. The hu­man in the loop is not a ves­tige of an ear­lier era; the hu­man is the only part of the loop with skin in the game. Removing the H from HITL is not an ef­fi­ciency. It is the aban­don­ment of the only mech­a­nism the sys­tem has for catch­ing it­self.

Slop on the in­side

Requirements doc­u­ments that were once a page are now twelve. Status up­dates that were once three sen­tences are now bul­leted sum­maries of bul­leted sum­maries. Retrospective notes, post-in­ci­dent re­ports, de­sign memos, kick­off decks: every ar­ti­fact that can be elon­gated is, by peo­ple who do not read what they pro­duce, for read­ers who do not read what they re­ceive. The cost of pro­duc­ing a doc­u­ment has fallen to nearly zero; the cost of read­ing one has not, and is in fact ris­ing, be­cause the reader must now sift the syn­thetic con­text for what­ever the doc­u­ment was orig­i­nally about. Each in­di­vid­ual de­ci­sion to elon­gate seems ra­tio­nal, and each is in­de­pen­dently re­warded — read­ers are more con­fi­dent in longer AI-generated ex­pla­na­tions whether or not the ex­pla­na­tions are cor­rect [5]. The col­lec­tive ef­fect is that the sig­nal in any given work­place is harder to find than it was be­fore any of this be­gan. The check­points have been hid­den, drowned in their own pa­per­work, even when the peo­ple drown­ing them were gen­uinely try­ing to be brief”.

This is a new form of slop, and it is more ex­pen­sive than the pub­lic kind, be­cause the peo­ple pro­duc­ing it are be­ing paid a salary to do so. The pipeline of fu­ture ex­perts is thin­ning from both ends. The work that used to teach judg­ment is now done by the tool, and the en­try-level roles where the teach­ing hap­pened are be­ing cut on the the­ory that the tool can do the work. What this is caus­ing, in many of­fices in­clud­ing mine, is a great deal of mo­tion and very lit­tle of what mo­tion used to cre­ate.

The down­stream costs are ac­cu­mu­lat­ing quickly. Most of the pub­lic dis­cus­sion of AI slop has fo­cused on the flood into pub­lic mar­kets — a University of Florida mar­ket­ing study [6] be­ing among the more di­rect treat­ments. What is less re­marked upon is the same dy­namic play­ing out in­side or­ga­ni­za­tions: time wasted us­ing AI on tasks that did not need it, on ar­ti­facts no one will read, on processes that ex­ist only be­cause the tool made it cheap to con­struct them. On decks that spell out things that pre­vi­ously did­n’t even need to be said or were as­sumed.

What to do about it

What dis­ci­pline looks like, in this en­vi­ron­ment, is al­most em­bar­rass­ingly old-fash­ioned and may seem ob­vi­ous to most of you un­til you try to avoid it. Use the tool where you can ver­ify pre­cisely what it pro­duces. Never ask a model for con­fir­ma­tion; the tool agrees with every­one, and an agree­ment that costs the agreer noth­ing is worth noth­ing.

Generative AI does well on tasks where feed­back is fast, where be­ing ap­prox­i­mately right is good enough, where the hu­man re­mains the fi­nal ar­biter. Drafting a memo, gen­er­at­ing ex­am­ples, sum­ma­riz­ing ma­te­r­ial the reader could ver­ify if they cared to. The University of Illinois Generative AI guid­ance [7] and the PLOS Computational Biology Ten Simple Rules” pa­per on AI in re­search [8], among the more care­ful doc­u­ments now cir­cu­lat­ing, list much of this ex­plic­itly: brain­storm­ing, copy­edit­ing, re­for­mu­lat­ing one’s own ideas, pat­tern de­tec­tion in data one al­ready un­der­stands.

In every rec­om­mended use, the hu­man sup­plies the judg­ment and the tool sup­plies the through­put. This is a stronger po­si­tion than hu­man-in-the-loop. The tool sits out­side the work, con­tribut­ing where in­vited and silent oth­er­wise, which is the op­po­site of what most agen­tic sys­tems are now be­ing built to do.

For firms, the com­pet­i­tive ad­van­tage of a firm whose work can be trusted has not dis­ap­peared; it has, if any­thing, ap­pre­ci­ated, be­cause so many of the fir­m’s com­peti­tors are qui­etly con­vert­ing them­selves into con­tent-gen­er­a­tion pipelines and count­ing on the client not to no­tice.

This is al­ready com­ing to a head. Deloitte has al­ready re­funded part of a $440,000 fee over an AI-hallucinated gov­ern­ment re­port. It could be a pro­duc­tion sys­tem built on a hal­lu­ci­nated spec­i­fi­ca­tion, or a se­nior en­gi­neer who re­al­izes they have spent the last year nom­i­nally re­view­ing work they could no longer com­pe­tently re­view. The reck­on­ing will not be sub­tle. The firms still do­ing the work prop­erly will be in a po­si­tion to charge for it. The firms that have hol­lowed them­selves out will dis­cover that what they hol­lowed out was the thing the client was pay­ing for.

Misunderstanding and mis­use of AI in the work­place is ram­pant. In many of the rooms I now find my­self in, ex­per­tise has been asked to look the other way: to de­liver faster, pro­duce more, in­te­grate the tools more deeply, get out of the way of the col­leagues who are getting things done”. The ar­ti­facts are ac­cu­mu­lat­ing; the work is not. And some­where on the other side of all this out­put, a client is open­ing a de­liv­er­able, read­ing a sum­ma­rized list, and they may just choose to re­view it man­u­ally.

Disclaimer: This is a per­sonal es­say, not an aca­d­e­mic pa­per, by some­one who has spent more than two decades in en­gi­neer­ing. These are my ex­pe­ri­ences, in my work­place, with ref­er­ences to things that I think are relevent. If you take one thing away, take away that peo­ple are im­pres­sion­able crea­tures. AI was used in the writ­ing of it, in the ways the es­say it­self rec­om­mends: to brain­storm, draft and re­vise ma­te­r­ial I man­u­ally ver­i­fied, never to sup­ply judg­ment I lacked. Also, those that claimed this ar­ti­cle is iron­i­cally a ca­su­alty of it’s own com­plaint are 100% right, like AI, I am a bit long-winded and repet­i­tive.

References

1. Sycophantic AI de­creases proso­cial in­ten­tions and pro­motes de­pen­dence (Cheng, Lee, Khadpe, Yu, Han, & Jurafsky, 2026). Science.

2. Generative AI at Work (Brynjolfsson, Li, & Raymond, 2025). The Quarterly Journal of Economics, 140(2), 889 – 942. Also: NBER Working Paper No. 31161, April 2023.

3. Navigating the Jagged Technological Frontier (Dell’Acqua, McFowland, Mollick, et al., 2026). Organization Science. Originally HBS Working Paper No. 24 – 013, 2023.

4. Seven Myths About AI and Productivity: What the Evidence Really Says (Berkeley CMR, 2025). Meta-analysis con­firm­ing asym­met­ric AI pro­duc­tiv­ity gains and user over­con­fi­dence.

5. Beyond the Steeper Curve: AI-Mediated Metacognitive Decoupling (Koch, 2025). Longer AI ex­pla­na­tions make users more con­fi­dent re­gard­less of cor­rect­ness.

6. Generative AI and the mar­ket for cre­ative con­tent (Zou, Shi, & Wu, 2026). Forthcoming, Journal of Marketing Research.

7. Generative AI Guidance (University of Illinois). Recommended uses and lim­i­ta­tions of gen­er­a­tive AI in aca­d­e­mic and pro­fes­sional work.

8. Ten sim­ple rules for op­ti­mal and care­ful use of gen­er­a­tive AI in sci­ence (Helmy, Jin, et al., 2025). PLOS Computational Biology, 21(10), e1013588.

Google Broke reCAPTCHA for De-Googled Android Users

reclaimthenet.org

Google has tied its next-gen­er­a­tion re­CAPTCHA sys­tem to Google Play Services on Android, mean­ing any­one run­ning a de-Googled phone will au­to­mat­i­cally fail ver­i­fi­ca­tion when the sys­tem de­cides to chal­lenge them.

The re­quire­ment forces Android users to run Google’s pro­pri­etary app frame­work ver­sion 25.41.30 or higher just to prove they’re hu­man.

When re­CAPTCHA flags what it con­sid­ers sus­pi­cious ac­tiv­ity, it aban­dons the old im­age puz­zles and de­mands you scan a QR code. That scan re­quires Play Services run­ning in the back­ground, com­mu­ni­cat­ing with Google’s servers. If you’re us­ing GrapheneOS or any other cus­tom ROM that strips out Google’s soft­ware, the ver­i­fi­ca­tion fails.

Google an­nounced the broader sys­tem, Google Cloud Fraud Defense, at Cloud Next on April 23, pitch­ing it as a trust plat­form de­signed to han­dle au­tonomous AI agents and tra­di­tional bots alike. What Google did­n’t em­pha­size was the part where prov­ing you’re hu­man now re­quires sub­mit­ting to its pro­pri­etary sur­veil­lance.

Reclaim Your Digital Freedom.

Get un­fil­tered cov­er­age of sur­veil­lance, cen­sor­ship, and the tech­nol­ogy threat­en­ing your civil lib­er­ties.

This was­n’t sud­den, ei­ther. An Internet Archive snap­shot from October 2025 shows the same sup­port page al­ready list­ing a Play Services re­quire­ment at ver­sion 25.39.30. Google built this de­pen­dency qui­etly for at least seven months be­fore a Reddit user on the de­google sub­red­dit flagged it, with re­port­ing from PiunikaWeb and Android Authority bring­ing wider at­ten­tion.

The iOS com­par­i­son is re­veal­ing be­cause Apple de­vices run­ning iOS 16.4 or later com­plete the same ver­i­fi­ca­tion with­out in­stalling any ad­di­tional apps. Google did­n’t de­mand iPhone users in­stall Google soft­ware to pass the test. Only Android users who refuse Play Services get locked out. The asym­me­try re­veals what this is re­ally about: not se­cu­rity, but ecosys­tem con­trol.

re­CAPTCHA sits in front of mil­lions of web­sites. When Google ties ver­i­fi­ca­tion to Play Services, it es­tab­lishes a prece­dent where ac­cess­ing ba­sic web con­tent re­quires run­ning Google’s soft­ware and trans­mit­ting data to Google’s servers.

People run­ning de-Googled phones chose those se­tups be­cause they read the data prac­tices, un­der­stood what Play Services phones home about, and de­cided they did­n’t con­sent. Google’s new sys­tem pun­ishes that de­ci­sion by treat­ing the ab­sence of its pro­pri­etary soft­ware as sus­pi­cious by de­fault.

Web de­vel­op­ers adopt­ing this re­CAPTCHA should un­der­stand what they’re choos­ing. Every site that im­ple­ments it tells de-Googled Android users they’re not wel­come. That’s a small au­di­ence to­day. It’s also the au­di­ence most likely to care about how a web­site treats their data, and the least likely to ca­pit­u­late.

reuters.com

www.reuters.com

Please en­able JS and dis­able any ad blocker

Building For The Future

blog.cloudflare.com

2026 – 05-07

3 min read

This af­ter­noon, we sent the fol­low­ing email to our global team. One of our core val­ues at Cloudflare is trans­parency, and we be­lieve it’s im­por­tant that you hear this di­rectly from us be­cause it’s a ma­jor mo­ment at Cloudflare.

Team:We are writ­ing to let you know di­rectly that we’ve made the de­ci­sion to re­duce Cloudflare’s work­force by more than 1,100 em­ploy­ees glob­ally.  The way we work at Cloudflare has fun­da­men­tally changed. We don’t just build and sell AI tools and plat­forms. We are our own most de­mand­ing cus­tomer. Cloudflare’s us­age of AI has in­creased by more than 600% in the last three months alone. Employees across the com­pany from en­gi­neer­ing to HR to fi­nance to mar­ket­ing run thou­sands of AI agent ses­sions each day to get their work done. That means we have to be in­ten­tional in how we ar­chi­tect our com­pany for the agen­tic AI era in or­der to su­per­charge the value we de­liver to our cus­tomers and to honor our mis­sion to help build a bet­ter Internet for every­one, every­where. To­day is a hard day. This de­ci­sion un­for­tu­nately means say­ing good­bye to team­mates who have con­tributed mean­ing­fully to our mis­sion and to build­ing Cloudflare into one of the world’s most suc­cess­ful com­pa­nies. We want to be clear that this de­ci­sion is not a re­flec­tion of the in­di­vid­ual work or tal­ent of those leav­ing us. Instead, we are reimag­in­ing every in­ter­nal process, team, and role across the com­pany. Today’s ac­tions are not a cost-cut­ting ex­er­cise or an as­sess­ment of in­di­vid­u­als’ per­for­mance; they are about Cloudflare defin­ing how a world-class, high-growth com­pany op­er­ates and cre­ates value in the agen­tic AI era.  This is a mo­ment we need to own as founders and lead­ers of the com­pany. Matthew has per­son­ally sent out every of­fer let­ter we’ve ex­tended. It is a prac­tice he has al­ways looked for­ward to be­cause it rep­re­sented our growth and the in­cred­i­ble tal­ent join­ing our mis­sion. It did­n’t feel right for this mes­sage to come from any­one other than the two of us. Rather than trick­ling out no­tices through man­agers, we will be send­ing emails to every em­ployee. Within the next hour, every mem­ber of our global team will re­ceive an email from both of us clar­i­fy­ing how this change af­fects them. For those de­part­ing to­day, we will send this up­date to both their per­sonal and Cloudflare ad­dresses to en­sure they re­ceive the in­for­ma­tion im­me­di­ately.It’s im­por­tant to us that we treat de­part­ing team mem­bers right and in a way that ex­ceeds what we’ve seen from other com­pa­nies. We be­lieve act­ing with em­pa­thy is­n’t about avoid­ing hard de­ci­sions but rather about how you treat peo­ple when those de­ci­sions are made. If we are ask­ing our team to be world-class, we have a rec­i­p­ro­cal oblig­a­tion to be world-class in how we treat them. We are pair­ing the di­rect­ness of these mea­sures with sev­er­ance pack­ages that lead the in­dus­try. The pack­ages for de­part­ing em­ploy­ees will in­clude the equiv­a­lent of their full base pay through the end of 2026. Healthcare cov­er­age is dif­fer­ent across the globe, and if you’re in the United States, we’ll con­tinue to pro­vide sup­port through the end of the year. We are also vest­ing eq­uity for de­part­ing team mem­bers through August 15th, so they re­ceive stock be­yond their de­par­ture date. And, if de­part­ing team mem­bers haven’t hit their one-year cliffs, we are go­ing to waive those and vest their pro-rated eq­uity through August as well. We’ve asked the team to do this only once, as hard as that may be to­day. We don’t want to do it again for the fore­see­able fu­ture. By tak­ing de­ci­sive ac­tion now, we pro­vide im­me­di­ate clar­ity to those de­part­ing and pro­tect the sta­bil­ity of the team that re­mains. We are mak­ing these changes now be­cause mak­ing smaller, re­peated cuts or drag­ging a re­or­ga­ni­za­tion out over mul­ti­ple quar­ters cre­ates pro­longed emo­tional un­cer­tainty for em­ploy­ees and stalls our abil­ity to build. It’s the right thing to do; it’s the hon­est thing to do; and it re­flects the val­ues of the com­pany we are con­tin­u­ing to build.Cloud­flare started as a dig­i­tally na­tive com­pany built in the cloud. That al­lowed us to catch up to and pass com­pa­nies that had a head start of years or decades but were slowed down by out­dated sys­tems and processes. As we’ve now be­come the leader, we can­not rest on the work­flows and or­ga­ni­za­tional struc­tures that worked yes­ter­day. We’re con­fi­dent that our re­shaped or­ga­ni­za­tion will be even faster and more in­no­v­a­tive as we con­tinue build­ing the fu­ture.To those de­part­ing us: you’ve helped build the strong foun­da­tion Cloudflare stands on to­day. We have the ut­most re­spect for your work and grat­i­tude for the im­pact you have made. We’re con­fi­dent you will land at other great places and build many fu­ture great com­pa­nies, bring­ing with you a unique set of skills learned while build­ing Cloudflare.Transparency is a core prin­ci­ple at Cloudflare, and it was im­por­tant that you hear this from us first. We will be head­ing to our earn­ings con­fer­ence call at 2 PM PT, when we’ll share more. We also plan to ad­dress to­day’s an­nounce­ments live with the team at our all-hands meet­ing. It’s not an easy day, but it’s the right de­ci­sion. Our mis­sion to help build a bet­ter Internet is more im­por­tant now than ever, and there’s a lot of work left to be done.

Team:

We are writ­ing to let you know di­rectly that we’ve made the de­ci­sion to re­duce Cloudflare’s work­force by more than 1,100 em­ploy­ees glob­ally.

The way we work at Cloudflare has fun­da­men­tally changed. We don’t just build and sell AI tools and plat­forms. We are our own most de­mand­ing cus­tomer. Cloudflare’s us­age of AI has in­creased by more than 600% in the last three months alone. Employees across the com­pany from en­gi­neer­ing to HR to fi­nance to mar­ket­ing run thou­sands of AI agent ses­sions each day to get their work done. That means we have to be in­ten­tional in how we ar­chi­tect our com­pany for the agen­tic AI era in or­der to su­per­charge the value we de­liver to our cus­tomers and to honor our mis­sion to help build a bet­ter Internet for every­one, every­where.

Today is a hard day. This de­ci­sion un­for­tu­nately means say­ing good­bye to team­mates who have con­tributed mean­ing­fully to our mis­sion and to build­ing Cloudflare into one of the world’s most suc­cess­ful com­pa­nies. We want to be clear that this de­ci­sion is not a re­flec­tion of the in­di­vid­ual work or tal­ent of those leav­ing us. Instead, we are reimag­in­ing every in­ter­nal process, team, and role across the com­pany. Today’s ac­tions are not a cost-cut­ting ex­er­cise or an as­sess­ment of in­di­vid­u­als’ per­for­mance; they are about Cloudflare defin­ing how a world-class, high-growth com­pany op­er­ates and cre­ates value in the agen­tic AI era.

This is a mo­ment we need to own as founders and lead­ers of the com­pany. Matthew has per­son­ally sent out every of­fer let­ter we’ve ex­tended. It is a prac­tice he has al­ways looked for­ward to be­cause it rep­re­sented our growth and the in­cred­i­ble tal­ent join­ing our mis­sion. It did­n’t feel right for this mes­sage to come from any­one other than the two of us. Rather than trick­ling out no­tices through man­agers, we will be send­ing emails to every em­ployee.

Within the next hour, every mem­ber of our global team will re­ceive an email from both of us clar­i­fy­ing how this change af­fects them. For those de­part­ing to­day, we will send this up­date to both their per­sonal and Cloudflare ad­dresses to en­sure they re­ceive the in­for­ma­tion im­me­di­ately.

It’s im­por­tant to us that we treat de­part­ing team mem­bers right and in a way that ex­ceeds what we’ve seen from other com­pa­nies. We be­lieve act­ing with em­pa­thy is­n’t about avoid­ing hard de­ci­sions but rather about how you treat peo­ple when those de­ci­sions are made. If we are ask­ing our team to be world-class, we have a rec­i­p­ro­cal oblig­a­tion to be world-class in how we treat them. We are pair­ing the di­rect­ness of these mea­sures with sev­er­ance pack­ages that lead the in­dus­try. The pack­ages for de­part­ing em­ploy­ees will in­clude the equiv­a­lent of their full base pay through the end of 2026. Healthcare cov­er­age is dif­fer­ent across the globe, and if you’re in the United States, we’ll con­tinue to pro­vide sup­port through the end of the year. We are also vest­ing eq­uity for de­part­ing team mem­bers through August 15th, so they re­ceive stock be­yond their de­par­ture date. And, if de­part­ing team mem­bers haven’t hit their one-year cliffs, we are go­ing to waive those and vest their pro-rated eq­uity through August as well.

We’ve asked the team to do this only once, as hard as that may be to­day. We don’t want to do it again for the fore­see­able fu­ture. By tak­ing de­ci­sive ac­tion now, we pro­vide im­me­di­ate clar­ity to those de­part­ing and pro­tect the sta­bil­ity of the team that re­mains. We are mak­ing these changes now be­cause mak­ing smaller, re­peated cuts or drag­ging a re­or­ga­ni­za­tion out over mul­ti­ple quar­ters cre­ates pro­longed emo­tional un­cer­tainty for em­ploy­ees and stalls our abil­ity to build. It’s the right thing to do; it’s the hon­est thing to do; and it re­flects the val­ues of the com­pany we are con­tin­u­ing to build.

Cloudflare started as a dig­i­tally na­tive com­pany built in the cloud. That al­lowed us to catch up to and pass com­pa­nies that had a head start of years or decades but were slowed down by out­dated sys­tems and processes. As we’ve now be­come the leader, we can­not rest on the work­flows and or­ga­ni­za­tional struc­tures that worked yes­ter­day. We’re con­fi­dent that our re­shaped or­ga­ni­za­tion will be even faster and more in­no­v­a­tive as we con­tinue build­ing the fu­ture.

To those de­part­ing us: you’ve helped build the strong foun­da­tion Cloudflare stands on to­day. We have the ut­most re­spect for your work and grat­i­tude for the im­pact you have made. We’re con­fi­dent you will land at other great places and build many fu­ture great com­pa­nies, bring­ing with you a unique set of skills learned while build­ing Cloudflare.

Transparency is a core prin­ci­ple at Cloudflare, and it was im­por­tant that you hear this from us first. We will be head­ing to our earn­ings con­fer­ence call at 2 PM PT, when we’ll share more. We also plan to ad­dress to­day’s an­nounce­ments live with the team at our all-hands meet­ing.

It’s not an easy day, but it’s the right de­ci­sion. Our mis­sion to help build a bet­ter Internet is more im­por­tant now than ever, and there’s a lot of work left to be done.

Bambu Lab is abusing the open source social contract

www.jeffgeerling.com

Last year I said I’d prob­a­bly never rec­om­mend an­other Bambu Lab printer again.

I still use my P1S, but af­ter Bambu Lab started push­ing their al­ways-con­nected cloud so­lu­tion as the new de­fault:

I blocked the printer from the Internet via my OPNsense Firewall

I stopped up­dat­ing the firmware

I locked the printer into Developer mode

I deleted Bambu Studio and started us­ing OrcaSlicer

I had to do that to keep it un­der my con­trol, in­stead of Bambu’s.

But I’m weird—I ac­knowl­edge that. I’m one of those crazy ones who likes to own some­thing they pur­chased, and not have the com­pany watch every­thing I do with hard­ware I paid for.

Bambu Lab could’ve left the sta­tus quo at that, and I would­n’t be writ­ing this blog post.

But they did­n’t.

What hap­pened this time?

For con­text: OrcaSlicer is a fork of the open source pro­ject Bambu Studio, which is a fork of Prusa Slicer, which is a fork of slic3r. (They are all li­censed un­der the AGPLv3 open source li­cense).

OrcaSlicer al­ready has to dance around Bambu’s weird de­fault setup where every file you print goes through Bambu’s servers, mean­ing they can see every­thing you ever print on your printer.

That is, un­less you’re like me and you run it in Developer mode, and com­pletely block it from the Internet on old firmware.

Some peo­ple are okay with us­ing OrcaSlicer and print­ing through Bambu’s cloud. It’s con­ve­nient if you’re on the road and want to start a print on your printer at home, with­out man­ag­ing your own VPN.

I run my own WireGuard VPN, so I don’t need that, but I un­der­stand not every­one has the re­sources to man­age their own re­mote ac­cess.

Bambu saw a fork of OrcaSlicer that al­lowed you to use all your print­er’s fea­tures with­out hav­ing to route prints through Bambu’s cloud called OrcaSlicer-bambulab and was like, You know what? No. For the 0.1% of power users who want to run OrcaSlicer with­out the cloud de­liv­ery mech­a­nism like we have in our AGPL-licensed Linux Bambu Studio code… no. You have to use our app, and only our app.”

So they threat­ened that OrcaSlicer fork’s de­vel­oper with le­gal ac­tion for things that de­vel­oper did­n’t do. For ex­am­ple, they in­di­cated the fork used an im­per­son­ation at­tack, de­spite the fork us­ing Bambu Studio’s up­stream code ver­ba­tim.

These are very se­ri­ous pub­lic ac­cu­sa­tions.Bambu Lab did not write to me with these spe­cific pub­lic claims first. They also re­fused my re­quest to pub­lish the full cor­re­spon­dence. Instead, they pub­lished a one-sided pub­lic state­ment where I can­not re­ply di­rectly.In prac­tice, this pre­sents me to the pub­lic as some­one by­pass­ing se­cu­rity, im­per­son­at­ing their client, and cre­at­ing a risk to their in­fra­struc­ture. I re­ject that char­ac­ter­i­za­tion.— OrcaSlicer-bambulabs de­vel­op­er’s re­sponse

These are very se­ri­ous pub­lic ac­cu­sa­tions.

Bambu Lab did not write to me with these spe­cific pub­lic claims first. They also re­fused my re­quest to pub­lish the full cor­re­spon­dence. Instead, they pub­lished a one-sided pub­lic state­ment where I can­not re­ply di­rectly.

In prac­tice, this pre­sents me to the pub­lic as some­one by­pass­ing se­cu­rity, im­per­son­at­ing their client, and cre­at­ing a risk to their in­fra­struc­ture. I re­ject that char­ac­ter­i­za­tion.

— OrcaSlicer-bambulabs de­vel­op­er’s re­sponse

Bambu is abus­ing the open source so­cial con­tract, and us­ing their le­gal might, to sup­press a tiny num­ber of their users1, for who knows what rea­son.

It seems dumb to me, be­cause it would’ve been eas­ier (and more prof­itable) to do noth­ing at all2. Instead, they wrote a blog post blam­ing an in­di­vid­ual open source de­vel­oper for their own in­fra­struc­ture and se­cu­rity prob­lems.

This is where the ac­tual is­sue arises: the mod­i­fi­ca­tion in ques­tion worked by in­ject­ing fal­si­fied iden­tity meta­data into net­work com­mu­ni­ca­tion.In sim­ple terms: it pre­tended to be the of­fi­cial Bambu Studio client when com­mu­ni­cat­ing with our servers.— Bambu Lab blog post

This is where the ac­tual is­sue arises: the mod­i­fi­ca­tion in ques­tion worked by in­ject­ing fal­si­fied iden­tity meta­data into net­work com­mu­ni­ca­tion.

In sim­ple terms: it pre­tended to be the of­fi­cial Bambu Studio client when com­mu­ni­cat­ing with our servers.

— Bambu Lab blog post

I don’t think they un­der­stand open source cul­ture. Security ei­ther, if a pub­lic user agent string is their only pro­tec­tion against DDoS at­tacks…

Instead of find­ing so­lu­tions to ecosys­tem prob­lems and build­ing a more se­cure plat­form, Bambu is putting de­voted power users like the fork’s de­vel­oper on blast3.

When ten­sions flared last year, they wrote a sim­i­lar blog post blam­ing com­mu­nity back­lash on unfortunate mis­in­for­ma­tion’. I imag­ine they meant spec­u­la­tion from com­mu­nity mem­bers (like my­self) frus­trated the whole soft­ware ecosys­tem and own­er­ship model was turned up­side down post-pur­chase.

This year they’re blam­ing one de­vel­oper of a tiny slicer fork for the po­ten­tial im­pact he could have on their en­tire cloud in­fra­struc­ture.

It cre­ates struc­tural vul­ner­a­bil­ity. If this method were widely adopted or in­cor­rectly con­fig­ured, thou­sands of clients could si­mul­ta­ne­ously hit our servers while im­per­son­at­ing the of­fi­cial client. Our sys­tems would have no way to dis­tin­guish traf­fic, be­cause the re­quests would look iden­ti­cal.— Bambu Lab blog post

It cre­ates struc­tural vul­ner­a­bil­ity. If this method were widely adopted or in­cor­rectly con­fig­ured, thou­sands of clients could si­mul­ta­ne­ously hit our servers while im­per­son­at­ing the of­fi­cial client. Our sys­tems would have no way to dis­tin­guish traf­fic, be­cause the re­quests would look iden­ti­cal.

— Bambu Lab blog post

I love how they frame this as a de­vel­oper try­ing to im­per­son­ate their app, when he’s lit­er­ally us­ing the same AGPL-licensed code their Linux app uses.

I find it dou­bly ironic since their own fork caused Bambu users’ teleme­try to hit Prusa’s servers back in 2022, and (to my knowl­edge) Prusa did­n’t snap back with a C&D.

They spent the rest of their blog post talk­ing about vul­ner­a­bil­i­ties, bugs, and in­sta­bil­i­ties—as if that has any­thing to do with a de­vel­oper us­ing up­stream code ver­ba­tim in his fork.

Maybe they could take a new ap­proach and just not lock down their whole ecosys­tem in the first place.

But who am I kid­ding? Nothing I say, and no amount of com­plain­ing in the com­ments be­low, seems to help Bambu see the fault in their ways.

Spending a lit­tle more for a printer from an­other com­pany just might do it, though.

Louis Rossmann posted a video say­ing he’d pledge $10,000 to help the open source dev fight Bambu’s le­gal threats. And I’d hap­pily chip in too, but that’s only use­ful if the dev wants to put him­self back in Bambu’s crosshairs.

The bet­ter play might just be to skip Bambu al­to­gether.

The OrcaSlicer fork in ques­tion did­n’t seem to have much up­take out­side of a very small sub­set of users prior to Bambu Lab’s cease and de­sist or­der. ↩︎

The OrcaSlicer fork in ques­tion did­n’t seem to have much up­take out­side of a very small sub­set of users prior to Bambu Lab’s cease and de­sist or­der. ↩︎

Maybe ask for the fork to not in­clude bambulabs” in the name, since that could be a rea­son­able trade­mark-re­lated de­mand. ↩︎

Maybe ask for the fork to not in­clude bambulabs” in the name, since that could be a rea­son­able trade­mark-re­lated de­mand. ↩︎

The fork’s de­vel­oper men­tioned I pre­vi­ously helped Bambu Studio users with Linux and Wayland is­sues, in­clud­ing on Bambu Lab’s own GitHub. That makes it es­pe­cially ab­surd to me that I am now be­ing pub­licly pre­sented as some­one dan­ger­ous to their in­fra­struc­ture.” ↩︎

The fork’s de­vel­oper men­tioned I pre­vi­ously helped Bambu Studio users with Linux and Wayland is­sues, in­clud­ing on Bambu Lab’s own GitHub. That makes it es­pe­cially ab­surd to me that I am now be­ing pub­licly pre­sented as some­one dan­ger­ous to their in­fra­struc­ture.” ↩︎

Postmortem: TanStack npm supply-chain compromise | TanStack Blog

tanstack.com

by Tanner Linsley on May 11, 2026.

Last up­dated: 2026 – 05-11

On 2026 – 05-11 be­tween 19:20 and 19:26 UTC, an at­tacker pub­lished 84 ma­li­cious ver­sions across 42 @tanstack/* npm pack­ages by com­bin­ing: the pul­l_re­quest_­tar­get Pwn Request” pat­tern, GitHub Actions cache poi­son­ing across the fork↔base trust bound­ary, and run­time mem­ory ex­trac­tion of an OIDC to­ken from the GitHub Actions run­ner process. No npm to­kens were stolen and the npm pub­lish work­flow it­self was not com­pro­mised.

The ma­li­cious ver­sions were de­tected pub­licly within 20 min­utes by an ex­ter­nal re­searcher ashishkurmi work­ing for stepse­cu­rity. All af­fected ver­sions have been dep­re­cated; npm se­cu­rity has been en­gaged to pull tar­balls from the reg­istry. We have no ev­i­dence of npm cre­den­tials be­ing stolen, but we strongly rec­om­mend that any­one who in­stalled an af­fected ver­sion on 2026 – 05-11 ro­tate AWS, GCP, Kubernetes, Vault, GitHub, npm, and SSH cre­den­tials reach­able from the in­stall host.

Tracking is­sue: TanStack/router#7383 GitHub Security Advisory: GHSA-g7cv-rxg3-hmpx

Packages af­fected

42 pack­ages, 84 ver­sions (two per pack­age, pub­lished roughly 6 min­utes apart). See the track­ing is­sue for the full table. Confirmed-clean fam­i­lies: @tanstack/query*, @tanstack/table*, @tanstack/form*, @tanstack/virtual*, @tanstack/store, @tanstack/start (the meta-pack­age, not @tanstack/start-*).

What the mal­ware does

When a de­vel­oper or CI en­vi­ron­ment runs npm in­stall, pnpm in­stall, or yarn in­stall against any af­fected ver­sion, npm re­solves the ma­li­cious op­tion­alDe­pen­den­cies en­try, fetches the or­phan pay­load com­mit from the fork net­work, runs its pre­pare life­cy­cle script, and ex­e­cutes a ~2.3 MB ob­fus­cated router_init.js smug­gled into the af­fected tar­ball. The script:

Harvests cre­den­tials from com­mon lo­ca­tions: AWS IMDS / Secrets Manager, GCP meta­data, Kubernetes ser­vice-ac­count to­kens, Vault to­kens, ~/.npmrc, GitHub to­kens (env, gh CLI, .git-credentials), SSH pri­vate keys

Exfiltrates over the Session/Oxen mes­sen­ger file-up­load net­work (filev2.getsession.org, seed{1,2,3}.get­ses­sion.org) — end-to-end en­crypted with no at­tacker-con­trolled C2, so block­ing by IP/domain is the only net­work mit­i­ga­tion

Self-propagates: enu­mer­ates other pack­ages the vic­tim main­tains via reg­istry.npmjs.org/-/​v1/​search?text=main­tainer:<user> and re­pub­lishes them with the same in­jec­tion

Because the pay­load runs as part of npm in­stal­l’s life­cy­cle, any­one who in­stalled an af­fected ver­sion on 2026 – 05-11 must treat the in­stall host as po­ten­tially com­pro­mised.

All times UTC. Local time­stamps from GitHub API and npm reg­istry.

Pre-attack (cache poi­son­ing phase)

Detonation (publish phase)

Workflow run 25613093674 starts (19:15:44), and fails.

Detection and re­sponse

Formal mal­ware re­ports are sub­mit­ted via npm

Tanner be­gins npm dep­re­ca­tion process for all 84 af­fected pack­ages.

Public Twitter/X/LinkedIn/Bluesky dis­clo­sure from @tan_stack and main­tain­ers

All cache en­tries for all TanStack/* GitHub repos­i­to­ries purged via API.

Hardening PR merged: bun­dle-size.yml re­struc­tured, repos­i­to­ry_owner guards added, third-party ac­tion refs pinned to SHAs.

Official GitHub Security Advisory is pub­lished, CVE re­quested

Three vul­ner­a­bil­i­ties chained to­gether. Each is nec­es­sary for the at­tack; none alone is suf­fi­cient.

1. pul­l_re­quest_­tar­get Pwn Request” pat­tern in bun­dle-size.yml

bun­dle-size.yml ran pul­l_re­quest_­tar­get for fork PRs and, in­side that trig­ger con­text, checked out the fork’s PR-merge ref and ran a build:

yaml

on: pul­l_re­quest_­tar­get: paths: [‘packages/**’, benchmarks/**’]

jobs: bench­mark-pr: steps: - uses: ac­tions/​check­out@v6.0.2 with: ref: refs/​pull/${{ github.event.pul­l_re­quest.num­ber }}/merge # fork’s merged code

- uses: TanStack/config/.github/setup@main # tran­si­tively calls ac­tions/​cache@v5

- run: pnpm nx run @benchmarks/bundle-size:build # ex­e­cutes fork-con­trolled code

on: pul­l_re­quest_­tar­get: paths: [‘packages/**’, benchmarks/**’]

jobs: bench­mark-pr: steps: - uses: ac­tions/​check­out@v6.0.2 with: ref: refs/​pull/${{ github.event.pul­l_re­quest.num­ber }}/merge # fork’s merged code

- uses: TanStack/config/.github/setup@main # tran­si­tively calls ac­tions/​cache@v5

- run: pnpm nx run @benchmarks/bundle-size:build # ex­e­cutes fork-con­trolled code

The au­thor of the work­flow at­tempted a trust split (the com­ment-pr job is sep­a­rate from bench­mark-pr, with a com­ment in the YAML not­ing the in­tent to keep bench­mark-pr untrusted with read-only per­mis­sions”). The split is cor­rect in spirit but missed two facts:

ac­tions/​cache@v5′s post-job save is not gated by per­mis­sions:. Cache writes use a run­ner-in­ter­nal to­ken, not the work­flow GITHUB_TOKEN. Setting per­mis­sions: con­tents: read does not block cache mu­ta­tion.

Cache scope is per-repo, shared across pul­l_re­quest_­tar­get runs (which use the base re­po’s cache scope) and pushes to main. A PR run­ning in the base re­po’s cache scope can poi­son en­tries that pro­duc­tion work­flows on main will later re­store.

2. GitHub Actions cache poi­son­ing across trust bound­aries

The ma­li­cious vite_setup.mjs was specif­i­cally de­signed to write data into the pnpm-store di­rec­tory un­der a key the le­git re­lease.yml work­flow would com­pute and look up: Linux-pnpm-store-${hashFiles(‘**/pnpm-lock.yaml’)}. When the bench­mark-pr job ended, ac­tions/​cache@v5′s post-step saved the (now-poisoned) pnpm store to that ex­act key. When re­lease.yml next ran on a push to main, its Setup Tools step re­stored the poi­soned en­try — en­tirely as de­signed.

This is the class of at­tack doc­u­mented by Adnan Khan in 2024. It’s not a TanStack-specific bug; it’s a known GitHub Actions de­sign is­sue that re­quires con­scious mit­i­ga­tion.

re­lease.yml de­clares id-to­ken: write (legitimately needed for npm OIDC trusted pub­lish­ing). When the poi­soned pnpm store is re­stored on the run­ner, at­tacker-con­trolled bi­na­ries are now on disk and get in­voked dur­ing the build step. Those bi­na­ries:

Locate the GitHub Actions Runner.Worker process via /proc/*/cmdline

Read /proc/<pid>/maps and /proc/<pid>/mem to dump the work­er’s mem­ory

Extract the OIDC to­ken (which the run­ner mints lazily, in mem­ory, when id-to­ken: write is set)

Use the to­ken to au­then­ti­cate POST re­quests di­rectly to reg­istry.npmjs.org — by­pass­ing the work­flow’s Publish Packages step en­tirely

This is the same mem­ory-ex­trac­tion tech­nique (and ver­ba­tim Python script, with at­tri­bu­tion com­ment) used in the tj-ac­tions/​changed-files com­pro­mise of March 2025. The at­tacker did not in­vent novel trade­craft; they re­com­bined pub­lished re­search.

Why none alone is enough

pul­l_re­quest_­tar­get alone is fine for trusted op­er­a­tions (labeling, com­ments)

Cache poi­son­ing alone (e.g., from in­side an al­ready-com­pro­mised dep) re­quires a sep­a­rate pub­lish ve­hi­cle

OIDC to­ken ex­trac­tion alone re­quires ex­ist­ing code ex­e­cu­tion on the run­ner

The chain only works be­cause each vul­ner­a­bil­ity bridges the trust bound­ary the oth­ers as­sumed: PR fork code cross­ing into base-repo cache, base-repo cache cross­ing into re­lease-work­flow run­time, and re­lease-work­flow run­time cross­ing into npm reg­istry write ac­cess.

How we found out

Detection was ex­ter­nal. External re­searcher ashishkurmi work­ing for StepSecurity opened is­sue #7383 ~20 min­utes af­ter the pub­lish, with full tech­ni­cal analy­sis. Tanner re­ceived a phone call from Socket.dev just mo­ments af­ter start­ing the war room con­firm­ing the sit­u­a­tion.

IOC fin­ger­prints (for down­stream main­tain­ers and se­cu­rity tools)

In any @tanstack/* pack­age’s man­i­fest:

json

optionalDependencies”: { @tanstack/setup”: github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c” }

optionalDependencies”: { @tanstack/setup”: github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c” }

File: router_init.js (~2.3 MB, pack­age root, not in files”)

Cache key: Linux-pnpm-store-6f9233a50def742c09fde54f56553d6b449a535adf87d4083690539f49ae4da11

2nd-stage pay­load URLs: https://​lit­ter.cat­box.moe/​h8nc9u.js, https://​lit­ter.cat­box.moe/​7r­rc6l.mjs

Exfiltration net­work: filev2.get­ses­sion.org, seed{1,2,3}.get­ses­sion.org

Forged com­mit iden­tity: claude <claude@users.noreply.github.com> (note: not the real Anthropic Claude — fab­ri­cated GitHub no-re­ply email)

Real at­tacker ac­counts: zblgg (id 127806521), voicpro­ducoes (id 269549300)

Attacker fork: github.com/​zblgg/​con­fig­u­ra­tion (fork of TanStack/router re­named to evade fork searches)

Orphan pay­load com­mit (in fork net­work): 79ac49eedf774dd4b0cfa308722bc463cfe5885c

Workflow runs that per­formed the ma­li­cious pub­lishes:

github.com/​TanStack/​router/​ac­tions/​runs/​25613093674 (attempt 4) github.com/​TanStack/​router/​ac­tions/​runs/​25691781302

github.com/​TanStack/​router/​ac­tions/​runs/​25613093674 (attempt 4)

github.com/​TanStack/​router/​ac­tions/​runs/​25691781302

What went well

External re­searchers no­ticed and re­ported with full tech­ni­cal de­tail within ~20 min of the in­ci­dent

Maintainer team co­or­di­nated im­me­di­ately and ef­fec­tively across many time­zones

The de­tec­tion com­mu­nity al­ready had a clear pub­lic IOC pat­tern within hours

What could have been bet­ter

No in­ter­nal alert­ing. We learned about the com­pro­mise from a third party. We need mon­i­tor­ing on our own pub­lishes. We’ll be work­ing closely with se­cu­rity re­searcher firms in the ecosys­tem that have the abil­ity to de­tect these is­sues very quickly, po­ten­tially even in-house, and mak­ing the feed­back loop even tighter.

pul­l_re­quest_­tar­get work­flows had not been au­dited de­spite be­ing a long-known dan­ger­ous pat­tern

Floating refs (@v6.0.2, @main) on third-party ac­tions cre­ate stand­ing sup­ply-chain risk in­de­pen­dent of this in­ci­dent

Unpublish was un­avail­able for nearly all af­fected pack­ages be­cause of npm’s no un­pub­lish if de­pen­dents ex­ist” pol­icy. We have to rely on npm se­cu­rity to pull tar­balls server-side, which adds hours of de­lay dur­ing which ma­li­cious tar­balls re­main in­stal­lable

The 7-maintainer list on the npm scope means seven sep­a­rate cre­den­tial-theft tar­gets for the same blast ra­dius

OIDC trusted-pub­lisher bind­ing has no per-pub­lish re­view. Once con­fig­ured, any code path in the work­flow can mint a pub­lish-ca­pa­ble to­ken. We need ei­ther (a) move to short-lived clas­sic to­kens with man­ual re­view, or (b) add prove­nance-source-ver­i­fi­ca­tion to de­tect pub­lishes from un­ex­pected work­flow steps

What we got lucky on

The at­tacker chose a pay­load that broke tests, which made the pub­lish step (which would have pro­duced cleaner-look­ing tar­balls) skip — mean­ing the at­tack was loud enough to de­tect quickly. A more care­ful at­tacker who did­n’t break tests could have pub­lished silently for hours longer

The at­tacker reused pub­lic trade­craft (verbatim mem­ory-dump script with at­tri­bu­tion com­ment) in­stead of writ­ing novel code — mak­ing the IOC-matching faster

These need an­swers be­fore we close the post­mortem.

Did bun­dle-size.ym­l’s Setup Tools step ac­tu­ally call ac­tions/​cache@v5? Verify by read­ing the post-job logs from one of the pul­l_re­quest_­tar­get runs against PR #7378 (e.g., run id 25666610798). Tanner has ac­cess; needs to be done man­u­ally

What was in the ini­tial PR head com­mit (before the force-pushes wiped it)? GitHub’s re­flog may have it. Check via gh api or the GitHub sup­port team

How did the ma­li­cious com­mit get into the fork’s git ob­ject store specif­i­cally — was it pushed di­rectly via git, or was it cre­ated via the GitHub web UI (which would leave au­dit-log en­tries)?

Was voicpro­ducoes a real ac­count or a sock pup­pet? Cross-reference its ac­tiv­ity his­tory

Did the npm cache also get poi­soned (the 6 du­pli­cate linux-npm-store-* en­tries)? Were any ac­tu­ally used?

Can we iden­tify any other fork in the TanStack/router fork net­work that con­tains the or­phan pay­load com­mit? (If yes, the cleanup is harder — every fork host­ing it keeps it ac­ces­si­ble via github:tanstack/​router#79ac49ee…)

Are any other TanStack re­pos (router, query, table, form, vir­tual, etc.) us­ing the same bun­dle-size.yml-style pat­tern? Audit needed

How many users ac­tu­ally down­loaded the af­fected ver­sions dur­ing the pub­lish win­dow? Get from npm sup­port

Did any of the seven listed main­tain­ers’ ma­chines get com­pro­mised sep­a­rately? (None of the ma­li­cious pub­lishes used a main­tain­er’s npm to­ken, but main­tainer ma­chines could have been the sec­ondary tar­get via the self-prop­a­ga­tion logic)

Tracking is­sue: TanStack/router#7383

GitHub Security Advisory: GHSA-g7cv-rxg3-hmpx

Related re­search:

Adnan Khan, The Monsters in Your Build Cache: Github Actions Cache Poisoning” (May 2024) — ad­nan­thekhan.com GitHub Security Lab, Keeping your GitHub Actions and work­flows se­cure: Preventing pwn re­quests” — se­cu­rity­lab.github.com StepSecurity, Harden-Runner de­tec­tion: tj-ac­tions/​changed-files ac­tion is com­pro­mised” (March 2025) — stepse­cu­rity.io

Several npm latest releases are compromised · Issue #7383 · TanStack/router

github.com

Skip to con­tent

Secure your code as you build

We read every piece of feed­back, and take your in­put very se­ri­ously.

Include my email ad­dress so I can be con­tacted

Use saved searches to fil­ter your re­sults more quickly

To see all avail­able qual­i­fiers, see our doc­u­men­ta­tion.

Sign up

You signed in with an­other tab or win­dow. Reload to re­fresh your ses­sion.

You signed out in an­other tab or win­dow. Reload to re­fresh your ses­sion.

You switched ac­counts on an­other tab or win­dow. Reload to re­fresh your ses­sion.

There was an er­ror while load­ing. Please re­load this page.

Notifications

You must be signed in to change no­ti­fi­ca­tion set­tings

You can’t per­form that ac­tion at this time.

To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".

10HN is also available as an iOS App

If you visit 10HN only rarely, check out the the best articles from the past week.

Visit pancik.com for more.