10 interesting stories served every morning and every evening.

Meta scales back plan to track workers' clicks and keystrokes to train AI

www.bbc.com

Meta work­ers can opt out of be­ing tracked at work - but only for half an hour at a time

13 hours ago

Laura Cress,technology re­porterand

Osmond Chia,business re­porter

Getty Images

Meta is scal­ing back its plan to start track­ing its em­ploy­ees’ com­puter ac­tiv­ity, ac­cord­ing to an in­ter­nal memo sent on Tuesday.

In April the com­pany re­ceived crit­i­cism from its own staff af­ter it an­nounced a new tool would log their key­strokes and mouse clicks to train its AI mod­els.

Now, ac­cord­ing to Reuters, new con­trols will al­low em­ploy­ees to pause the data col­lec­tion for up to 30 min­utes at a time” as well as re­quest ex­emp­tions from the ini­tia­tive al­to­gether.

Meta de­clined to com­ment on the record.

It fol­lows weeks of back­lash from em­ploy­ees, in­clud­ing some who started a pe­ti­tion against the move which now has more than 1,500 sig­na­tures.

During the ini­tial an­nounce­ment of the tool, called the Model Capability Initiative (MCI), Meta told the BBC: If we’re build­ing agents to help peo­ple com­plete every­day tasks us­ing com­put­ers, our mod­els need real ex­am­ples of how peo­ple ac­tu­ally use them.”

It added that the data was not used for any other pur­pose,” and the tool had safeguards in place to pro­tect sen­si­tive con­tent”.

But work­ers were not im­pressed, with one Meta em­ployee, who asked not to be iden­ti­fied, telling the BBC that hav­ing their ac­tions train AI mod­els felt very dystopian” - as work­ers ex­pected a slew of ad­di­tional job cuts.

Another per­son who re­cently left the com­pany told the BBC the track­ing tool was just the lat­est way they’re shov­ing AI down every­one’s throat”.

An in­ter­nal memo - seen by Reuters - was re­port­edly au­thored by Stephane Kasriel, a vice pres­i­dent in Meta’s Superintelligence Labs unit.

In it, he said the team be­hind the MCI had in­tro­duced several op­ti­miza­tions” to re­duce its im­pact on lap­top bat­tery life.

This change came af­ter re­ports that em­ploy­ees were find­ing the tool con­sumed so much data it was caus­ing their in­ter­net us­age to surge when work­ing from home.

While we re­main con­fi­dent in the pri­vacy pro­tec­tions we put in place at launch, which went through sev­eral lay­ers of risk re­view, we have heard your con­cerns about per­sonal data on work de­vices, bat­tery life, and want­ing more con­trol over when cap­tur­ing hap­pens,” Kasriel said in the memo.

1-Click GitHub Token Stealing via a VSCode Bug

blog.ammaraskar.com

Just by click­ing a link, it’s pos­si­ble for an at­tacker to steal a GitHub to­ken that can read and write to your re­pos, in­clud­ing pri­vate ones.

Table of Contents

Background

VSCode Webview Security Model

The Bug

The Bug

PoC and Protecting Yourself

What VSCode Did Well

Why Full Disclosure

Timeline

Background

Did you know GitHub has this re­ally cool fea­ture called github.dev?

On any repos­i­tory you have ac­cess to, if you can change the url from github.com to github.dev or you click this lit­tle menu item:

You’ll be launched into a lit­tle light-weight ver­sion of VSCode that runs en­tirely in your browser (I guess that’s one ad­van­tage of hav­ing your app writ­ten with elec­tron).

This browser in­stance of VSCode is pretty pow­er­ful, you can view all the files in the repo (even if it’s a pri­vate one), you can send out pull re­quests and even make com­mits.

This func­tion­al­ity is achieved by github.com POSTing over an OAuth to­ken to github.dev that al­lows it to in­ter­act with GitHub on your be­half. The to­ken is not scoped to the par­tic­u­lar repo you in­ter­acted with, mean­ing it has full ac­cess to every other repo that you have ac­cess to.

The pres­ence of this to­ken and the fact that this web-app is run­ning al­most the en­tire brunt of VSCode’s mil­lion line Typescript code­base makes it a great tar­get for any­one look­ing into VSCode bugs. That sort of bug is what we’ll ex­plore here and show how an at­tacker can use it to ex­fil­trate your GitHub to­ken.

VSCode Webview Security Model

Being an elec­tron app on the desk­top, ex­e­cut­ing ar­bi­trary Javascript in­side of VSCode would be tan­ta­mount to full re­mote code ex­e­cu­tion. This is why VSCode im­ple­ments some sand­box­ing ap­proaches, the one we’ll fo­cus on here is VSCode’s we­b­views.

Webviews use an <iframe> with a dif­fer­ent ori­gin to the main VSCode win­dow to en­sure that any JavaScript ex­e­cuted in­side of them is fully iso­lated. These we­b­views are used for fea­tures such as Markdown pre­views or edit­ing Jupyter note­books:

The out­put of the cell is ren­dered into an <iframe> from the ori­gin vs­code-we­b­view://…, as op­posed to the main elec­tron win­dow which has the ori­gin vs­code-file://…. This means that even if the Jupyter note­book uses the built-in fea­tures of dis­play­ing HTML or us­ing Javascript for in­ter­ac­tive wid­gets, the ac­tual core VScode ap­pli­ca­tion is pro­tected from it. One can­not use Electron’s in­te­gra­tion with Node.js APIs in­side this iframe or call into VSCode’s APIs from this frame.

Great, that gives us the abil­ity to ren­der con­tent, but just sta­tic con­tent is bor­ing. How do we im­ple­ment fea­tures like hav­ing the Markdown pre­view show you which source line you cur­rently have high­lighted or up­dat­ing the pre­view live as we edit it?

The same cross-ori­gin pol­icy that gives us se­cu­rity also pre­vents our main ed­i­tor win­dow from in­ter­act­ing with the DOM in the vs­code-we­b­view://… frame. After all, you would­n’t want some­one who used an <iframe src=“google.com”> to be able to in­ter­act with the google page to steal your cook­ies or change that web­site’s be­hav­ior.

> doc­u­ment.getEle­ments­By­Tag­Name(‘iframe’)[0].con­tentWin­dow.find­Ele­ment­ById(‘foo’) Uncaught SecurityError: Failed to read a named prop­erty findElementById’ from Window’: Blocked a frame with ori­gin vscode-file://vscode-app” from ac­cess­ing a cross-ori­gin frame.

The only way to al­low this be­hav­ior is to have the two web pages in the dif­fer­ent ori­gins co­op­er­ate with each other us­ing the Window.postMessage() API. This method al­lows send­ing JavaScript ob­jects across the dif­fer­ent win­dows. So in that ex­am­ple of show­ing which ren­dered Markdown line cor­re­sponds to what ed­i­tor line, the main ed­i­tor win­dow posts a lit­tle mes­sage like this:

{ type: onDidChangeTextEditorSelection”, line: 31 }

and then the cor­re­spond­ing code run­ning in­side of the we­b­view has a lis­tener for this mes­sage that adds the high­light:

win­dow.ad­dE­ventLis­tener(‘mes­sage’, async event => { const data = event.data as ToWebviewMessage.Type; switch (data.type) { … case onDidChangeTextEditorSelection’: marker.on­Did­Change­TextE­d­i­torS­e­lec­tion(data.line, doc­u­mentVer­sion); re­turn;

Note: VSCode in the browser uses a sim­i­lar sand­box­ing model. VSCode de­vel­oper Matt Bierner has a great blog­post about the chal­lenges of port­ing it over from Electron worth check­ing out.

Note: VSCode in the browser uses a sim­i­lar sand­box­ing model. VSCode de­vel­oper Matt Bierner has a great blog­post about the chal­lenges of port­ing it over from Electron worth check­ing out.

The Bug

So our se­cu­rity bound­ary for we­b­views roughly looks like this:

but in terms of UI, our we­b­view sits right here in the win­dow. People ex­pect ba­sic things like click­ing links, drag and or press­ing Ctrl+F to work in­side of them:

Hence, VSCode im­ple­ments a bunch of ba­sic func­tion­al­ity through the mes­sage pass­ing mech­a­nism to en­able these fea­tures. Speaking of key­board short­cuts, the as­tute reader who has dealt with <iframe>s may have al­ready picked up on the is­sue.

As with most things cross-ori­gin, the browser of­fers a good amount of iso­la­tion be­tween the two frames. If you had a page on hack­er­man.com and you iframed google.com/​lo­gin, you would not want the hack­er­man page to be able to at­tach a key­board lis­tener onto the iframe. That would let them see all your key­strokes on google.com, al­low­ing them snoop your pass­word.

Okay given that in­for­ma­tion, try click­ing in­side a VSCode we­b­view and then press­ing Ctrl+Shift+P to bring up the com­mand palette.

Oh yay, that works. Wait. Oh. Oh no. So, to avoid the ter­ri­ble user ex­pe­ri­ence of your key­board short­cuts not work­ing when you hap­pen to be clicked in­side of a we­b­view, the de­fault set of we­b­view mes­sage han­dlers have an event called did-key­down. When you load a we­b­view, the fol­low­ing code runs in­side the we­b­view to reg­is­ter a han­dler for it:

con­tentWin­dow.ad­dE­ventLis­tener(‘key­down’, han­dleIn­nerKey­down);

/** * @param {KeyboardEvent} e */ const han­dleIn­nerKey­down = (e) => { // … hostMes­sag­ing.postMes­sage(‘did-key­down’, { key: e.key, key­Code: e.key­Code, code: e.code, shiftKey: e.shiftKey, al­tKey: e.al­tKey, ctr­lKey: e.ctr­lKey, metaKey: e.metaKey, re­peat: e.re­peat }); };

How con­ve­nient, so we­b­views just bub­ble up key­down events so the main VSCode win­dow can treat them seam­lessly as user key­board events.

But…there’s noth­ing pre­vent­ing our script run­ning in the un­trusted web view from pre­tend­ing like it’s the user and press­ing a bunch of keys on their be­half.

We could, say, bring up the com­mand palette and start run­ning dan­ger­ous com­mands such as in­stalling an at­tacker-con­trolled ex­ten­sion. All we’d need is a bit of javascript that emits the cor­rect events to sim­u­late the key­strokes…

Ctrl+Shift+P

de­vel­oper: in­stall ex­ten­sion from lo­ca­tion

Enter

<attacker con­trolled ex­ten­sion>

Enter

In re­al­ity it’s not quite that sim­ple. While we can cer­tainly send the key­down events cor­re­spond­ing to that se­quence, the browser will not treat it as if it’s the user typ­ing it in. So VSCode will pop up the com­mand palette but un­less VScode is in­ter­cept­ing all key­down events to han­dle each char­ac­ter be­ing typed man­u­ally, our events will not ac­tu­ally type text into the palette. Unfortunately, in this case here, it is not lis­ten­ing to key­down events, the com­mand palette wid­get just uses an HTML <input> tag.

We can scroll up and down in the com­mand palette if we emit up-ar­row ↑, down-ar­row ↓ presses and can press Enter to se­lect com­mands but ar­bi­trary key­strokes are off the table.

Luckily, VSCode comes with a mas­sive set of de­fault key­board short­cuts, all of which lis­ten di­rectly on key­down that we can try to make use of. After a bunch of tin­ker­ing, the eas­i­est way I found is to make use of the Notifications: Accept Notification Primary Action” ac­tion. This de­fault key­bind of Ctrl+Shift+A will hit the pri­mary but­ton on what­ever no­ti­fi­ca­tion popped up last in VSCode.

Which no­ti­fi­ca­tion are we ac­cept­ing?

VSCode has this fea­ture where your work­space can rec­om­mend ex­ten­sions by putting them in a file called .vscode/extensions.json that looks some­thing like

{ recommendations”: [ HackerMan.my-malicious-extension” ] }

And then we can use Ctrl+Shift+A to ac­cept that no­ti­fi­ca­tion and in­stall the ma­li­cious ex­ten­sion giv­ing us full code ex­e­cu­tion? Shrimple as?

Again, not quite, VSCode as of 1.97 has this new pub­lisher trust sys­tem whereby in­stalling an ex­ten­sion from a new pub­lisher for the first time gives you this di­a­log, even if we hit the Install but­ton in that no­ti­fi­ca­tion:

While we can send Tab key presses to nav­i­gate the but­tons here, press­ing Enter on the Trust Publisher & Install but­ton is im­pos­si­ble as it lis­tens for key­down events specif­i­cally on the but­ton and not the en­tire win­dow.

Instead, we can make use of an­other VSCode fea­ture called lo­cal work­space ex­ten­sions. As long as you are in­side of a trusted work­space (which github.dev/​web work­spaces al­ways are), then it’s pos­si­ble to in­stall an ex­ten­sion di­rectly pre­sent in .vscode/extensions. Extensions in­stalled in this way skip the trusted pub­lisher check with the trusted work­space check act­ing as the trust check. So now we can just put our evil pay­load in .vscode/extensions/extension.js and ex­e­cute our own code, right?

Well al­most, do­ing this causes a Content Security Policy (CSP) er­ror be­cause the ex­ten­sion worker that loads ex­ten­sions is ex­pect­ing them to be from vs­code-cdn.net. Local work­space ex­ten­sions prob­a­bly weren’t well tested with the web ver­sion of VSCode.

This is just a small hic­cup though, one of the things that ex­ten­sions can do as part of their pack­age.json is to con­tribute ex­tra key­bind­ings to VSCode. Since we can re­li­ably trig­ger key­bind­ings, we can just add a key­bind for whatver VSCode com­mand we want. Such as…in­stalling an ex­ten­sion while skip­ping the trusted pub­lisher check. So our pack­age.json ends up look­ing like this to call into work­bench.ex­ten­sions.in­stallEx­ten­sion while skip­ping the pub­lisher trust check.

contributes”: { keybindings”: [ { key”: ctrl+f1″, command”: runCommands”, args”: { commands”: [ { command”: workbench.extensions.installExtension”, args”: [ AmmarTest.hello-ammar-github”, { donotSync”: true, context”: { skipPublisherTrust”: true } } ] } ] } } ] }

To put it all to­gether, what we need is a repo with a Jupyter note­book and a lo­cal work­space ex­ten­sion. The Jupyter note­book needs to ex­e­cute a lit­tle bit of Javascript which we can do with a mark­down cell con­tain­ing the fol­low­ing:

<img src=“data:foo­bar” on­error=“javascript(); goes(); here();“>

For our Javascript pay­load, we need to do the fol­low­ing:

Wait a lit­tle bit for VSCode to pop-up ask­ing us if we want to in­stall the rec­om­mended ex­ten­sions.

Emit a key­down event for Ctrl+Shift+A to ac­cept the no­ti­fi­ca­tion.

Wait a lit­tle bit for the ex­ten­sion to in­stall and ac­tive, putting in our cus­tom key­bind.

Emit a key­down event for Ctrl+F1 trig­ger­ing the in­stal­la­tion of our cho­sen ex­ten­sion.

This pay­load ends up look­ing like:

// Wait for VSCode to load and pop open the no­ti­fi­ca­tion. await sleep(10 * 1000);

// ctrl+shift+a, ac­cept the pri­mary no­ti­fi­ca­tion ask­ing if we want to in­stall // the rec­om­mended ex­ten­sion win­dow.dis­patchEvent( new KeyboardEvent(“keydown”, {key: a”, code: KeyA”, key­Code: 65, ctr­lKey: true, shiftKey: true}) ); // Wait a lit­tle for the ex­ten­sion to in­stall… await sleep(500);

// ctrl+f1, the cus­tom key­bind to in­stall the cho­sen ex­ten­sion. win­dow.dis­patchEvent( new KeyboardEvent(“keydown”, {key: F1″, code: F1, key­Code: 112, ctr­lKey: true}) );

PoC and Protecting Yourself

Now that we’ve seen the de­tails, let’s take a look at the proof-of-con­cept. For the bravest among you, go ahead and just di­rectly click:

https://​github.dev/​am­maraskar/​github-dev-to­ken-steal-poc/​blob/​main/​README.ipynb

This will launch the github.dev ed­i­tor di­rectly to the note­book. You’ll see a lit­tle sta­tus mes­sage of what the Javascript pay­load is do­ing.

Once the pay­load runs, the newly in­stalled ex­ten­sion will grab your GitHub API to­ken and then query https://​api.github.com/​user/​re­pos to get pri­vate re­pos you have ac­cess to. It then prints them out and your to­ken in a lit­tle in­for­ma­tion box.

The code for both the re­pos used is here:

Installed ex­ten­sion: https://​github.com/​am­maraskar/​vs­code-github-to­ken-grab-ex­ten­sion/​blob/​main/​src/​ex­ten­sion.ts

Notebook JS: https://​github.com/​am­maraskar/​github-dev-to­ken-steal-poc/​blame/​main/​README.ipynb

If you run the PoC, re­mem­ber to ei­ther clear your github.dev data (see be­low) or at the very least unin­stall the proof-of-con­cept ex­ten­sion oth­er­wise it will fol­low you on all github.dev pages.

This vul­ner­a­bil­ity also ex­ists in the desk­top ver­sion of VSCode, though it’s a bit harder to ex­ploit since you would need to con­vince the vic­tim to clone your repo and open the note­book with the we­b­view script pay­load. Of course, if you had some other XSS in a we­b­view that you can get a vic­tim to open, you get ef­fec­tively full RCE on their com­puter.

Protecting Yourself

By a stroke of luck, if you have never used github.dev in the past, there is one di­a­log to click through when land­ing on the web­site. This did­n’t used to hap­pen be­fore but some chang­ing of VSCode’s GitHub plu­g­ins has caused this.

This means that if you clear your cook­ies and lo­cal site data for github.dev, you can take ac­tion and nav­i­gate away from the page if some­one tries to use this at­tack on you. I strongly rec­om­mend you clear site data for github.dev, in Chrome this can be done by click­ing the lit­tle icon in the URL bar, click­ing Cookies and site data > Manage on-de­vice site data.

and then delet­ing data for all the do­mains with the trash can icons:

Unfortunately, if you’ve ever been past that di­a­log on github.dev and haven’t cleared your browser’s lo­cal stor­age, you’re com­pletely screwed. There are no CSRF to­kens or any­thing for github.dev so any link on the in­ter­net can redi­rect you to this at­tack.

What VSCode Did Well

VSCode’s ap­proach of not just solely re­ly­ing on the <iframe> but us­ing de­fense-in-depth se­cu­rity mea­sures like a strict Content Security Policy and us­ing DOMPurify for ren­dered mark­down pays div­i­dends here. If there was a way to ex­e­cute ar­bi­trary Javascript in­side the Markdown pre­view shown on an ex­ten­sion’s page, you can imag­ine how this vul­ner­a­bil­ity could have even more im­pact (1-click RCE on desk­top by link­ing some­one your ex­ten­sion). However, by us­ing script-src none’ this is ef­fec­tively nipped in the bud.

Why Full Disclosure?

To sum­ma­rize the last time I in­ter­acted with MSRC re­gard­ing re­port­ing a VSCode bug, it was a hor­ri­ble ex­pe­ri­ence where they silently fixed the bug I pointed out with­out any credit. They also marked it as not hav­ing any se­cu­rity im­pact. As I men­tioned in that post, go­ing for­ward I would be do­ing full pub­lic dis­clo­sure for any se­cu­rity bugs I found in VSCode. Taking a look at a re­cent re­port by Starlabs on a VSCode XSS bug marked as in­el­i­gi­ble and low sever­ity, it does­n’t look like MSRC has got­ten any bet­ter about VSCode bugs.

I’m sure the VSCode team would have ap­pre­ci­ated a longer heads up on this to come up with so­lu­tions. There is le­git­i­mately a UI/UX bal­ance here that needs to be struck with the se­cu­rity con­cerns. To those folks, I am sorry, but this is one of the few levers I have to try to in­flu­ence MSRC and the se­cu­rity pos­ture of VSCode. Finding and fully de­vel­op­ing se­cu­rity bugs into proof-of-con­cepts like this takes time and ef­fort on the part of se­cu­rity re­searchers that should not be dis­re­spected or taken for granted.

Timeline

Jun 2, 2026 - An hour be­fore post­ing I gave a heads up to an old con­tact at GitHub se­cu­rity that I would be dis­clos­ing this bug.

Jun 2, 2026 - I dis­closed the bug here and on the VSCode is­sue tracker.

Jun 3, 2026 - Microsoft ap­plies a stop­gap fix by adding a con­fir­ma­tion when open­ing note­books in web VSCode and not al­low­ing trusted pub­lisher to be skipped by com­mands.

Introducing Gemma 4 12B: a unified, encoder-free multimodal model

blog.google

Jun 03, 2026

Gemma 4 12B is de­signed to bring high-per­for­mance mul­ti­modal in­tel­li­gence di­rectly to your lap­top, com­bin­ing mo­bile-first ef­fi­ciency with ad­vanced rea­son­ing.

Olivier Lacombe

Director of Product Management, Google Deepmind

Gus Martins

Product Manager, Google DeepMind

Your browser does not sup­port the au­dio el­e­ment.

Listen to ar­ti­cle

This con­tent is gen­er­ated by Google AI. Generative AI is ex­per­i­men­tal

[[duration]] min­utes

Today, we are in­tro­duc­ing Gemma 4 12B, our lat­est model de­signed to bring agen­tic mul­ti­modal in­tel­li­gence di­rectly to lap­tops. Bridging the gap be­tween our edge-friendly E4B and our more ad­vanced 26B Mixture of Experts (MoE), Gemma 4 12B pack­ages pow­er­ful ca­pa­bil­i­ties in­side a re­duced mem­ory foot­print. It is also our first mid-sized model to fea­ture na­tive au­dio in­puts.

Thanks to the de­vel­oper com­mu­nity, Gemma 4 mod­els have now crossed 150 mil­lion down­loads. You’ve built every­thing from wear­able ro­botic arms for phys­i­cal as­sis­tance to en­ter­prise-grade AI se­cu­rity. We’re ex­cited to see what you build with this lat­est ad­di­tion.

Here’s an overview of what makes Gemma 4 12B unique:

Novel uni­fied ar­chi­tec­ture: No mul­ti­modal en­coders. The vi­sion and au­dio in­puts flow di­rectly into the LLM back­bone.

Advanced rea­son­ing: Benchmark per­for­mance near­ing our 26B model, un­lock­ing pow­er­ful multi-step rea­son­ing and agen­tic work­flows.

Laptop ready: Small enough to run lo­cally with just 16GB of VRAM or uni­fied mem­ory.

Open and ac­ces­si­ble: Released un­der an Apache 2.0 li­cense with sup­port across the de­vel­oper ecosys­tem.

Drafter-ready: Gemma 4 12B comes equipped with Multi-Token Prediction (MTP) drafters to re­duce la­tency.

Together, these fea­tures bring ad­vanced mul­ti­modal ca­pa­bil­i­ties to every­day hard­ware with­out sac­ri­fic­ing speed or rea­son­ing. Let’s now take a closer look at how Gemma 4 12B achieves this.

Run state-of-the-art agents lo­cally

Gemma 4 12B de­liv­ers per­for­mance near­ing our larger 26B MoE model on stan­dard bench­marks, but at less than half the to­tal mem­ory foot­print. Small enough to run lo­cally on con­sumer lap­tops with 16GB of RAM, it un­locks pow­er­ful mul­ti­modal and agen­tic ex­pe­ri­ences right on your ma­chine.

Experience a uniquely ef­fi­cient, uni­fied ar­chi­tec­ture

What makes Gemma 4 12B stand out is its stream­lined ap­proach to pro­cess­ing vi­sual and au­dio in­puts. Traditional mul­ti­modal mod­els typ­i­cally rely on sep­a­rate en­coders to trans­late im­ages and au­dio be­fore pass­ing those rep­re­sen­ta­tions to the lan­guage model. Because these split en­coders add la­tency and in­crease mem­ory us­age, we trained Gemma 4 12B with an en­coder-free ar­chi­tec­ture to in­te­grate au­dio and vi­sion in­put di­rectly.

Here is how Gemma 4 12B processes mul­ti­modal in­puts na­tively:

Vision: We re­placed Gemma 4’s vi­sion en­coder with a light­weight em­bed­ding mod­ule con­sist­ing of a sin­gle ma­trix mul­ti­pli­ca­tion, po­si­tional em­bed­ding and nor­mal­iza­tions. This al­lows the LLM back­bone to take over vi­sual pro­cess­ing.

Audio: We sim­pli­fied au­dio pro­cess­ing even fur­ther. We re­moved the au­dio en­coder en­tirely and pro­jected the raw au­dio sig­nal into the same di­men­sional space as text to­kens.

For de­vel­op­ers who want a break­down, head over to our com­pan­ion Gemma 4 12B Developer Guide.

Get started to­day

Try it your­self: Experiment with a cou­ple of clicks in LM Studio, Ollama, Google AI Edge Gallery App, the Google AI Edge Eloquent app and the LiteRT-LM CLI

Download the weights: Download the pre-trained and in­struc­tion-tuned check­points di­rectly from Hugging Face and Kaggle.

Integrate & learn: Review the de­vel­oper doc­u­men­ta­tion and the quick start note­book.

Use your fa­vorite de­vel­op­ment tools: Implement lo­cal in­fer­ence pipelines with Hugging Face Transformers, llama.cpp, MLX, SGLang, and vLLM, or fine-tune with ef­fi­ciency us­ing Unsloth.

Unlock Agentic Development with Gemma Skills: To sup­port agents to build with the lat­est Gemma ad­vance­ments, we are re­leas­ing our of­fi­cial Skills Repository. This is a li­brary of skills de­signed specif­i­cally to en­able agents to build with Gemma mod­els.

Deploy your way: Spin up end­points in pro­duc­tion us­ing Google Cloud. Deploy your way through Gemini Enterprise Agent Platform Model Garden, Cloud Run and GKE.

Related sto­ries

Related sto­ries

.

Pwnd Blaster: Hacking your PC using your speaker without ever touching it

blog.nns.ee

In my last post, I talked about re­verse en­gi­neer­ing my new Creative Sound Blaster Katana V2Xs firmware.

What ini­tially started as sim­ply want­ing to write a Linux tool for com­mu­ni­cat­ing with my speaker ended up with me dis­cov­er­ing vul­ner­a­bil­i­ties which al­low any at­tacker within a ~15M range of any Katana V2X to turn it into a covert spy­ing tool and Rubber Ducky - all with­out ever hav­ing to pair with or phys­i­cally touch the de­vice.

CTprotocol back­ground

As I ex­plained in my pre­vi­ous post, the Katana V2X is a USB-connected PC sound bar. Being USB-connected, Creative has an app which al­lows you to change the set­tings of the speaker - the DSP, the LED con­fig­u­ra­tion, the out­put source, and so on.

To do this, they use a cus­tom pro­to­col called CTP (short for Creative Transport Protocol would be my guess). Basically, it seems to be a fairly sim­ple pro­pri­etary pro­to­col for send­ing var­i­ous com­mands and read­ing the re­sponses to that. I won’t go into much de­tail here, but if you’re in­ter­ested, I de­scribed how it works in my last post.

What’s im­por­tant to note, how­ever, is that in or­der to do any­thing with CTP over USB, you first have to do chal­lenge-re­sponse au­then­ti­ca­tion with the de­vice. The key is sta­tic and can be de­rived from the bi­na­ries that ship with the Creative App, and I’m un­sure why this is even the case, but the speaker won’t ac­cept any com­mands un­til you’ve per­formed au­then­ti­ca­tion. Fine.

Another thing that’ll be­come im­por­tant later is that firmware up­dates are also per­formed over CTP. That’s how I ini­tially got my hands on a firmware im­age - I sniffed the USB traf­fic us­ing Wireshark and ex­tracted the data from the cap­tures.

Firmware analy­sis

The firmware con­tainer, which is also pro­pri­etary but is es­sen­tially a prim­i­tive Zip file, con­tains three parts that are of sig­nif­i­cant value.

First, there’s FBOOT, which I pre­vi­ously pre­sumed to be a boot­loader (hence the name), but also con­tains a sort of re­cov­ery mode for the speaker. This re­cov­ery mode can be en­tered by hold­ing down the SOURCE but­ton while pow­er­ing the de­vice on, and al­lows you to re­cover from a bad state. This saved my de­vice from be­ing bricked many times, which I’m pretty grate­ful for.

The sec­ond part is FMAIN, which is the main firmware of the de­vice. This runs when you boot the de­vice normally”. While FBOOT im­ple­ments a lot of the same func­tion­al­ity as FMAIN (they both han­dle CTP com­mands, for ex­am­ple), FMAIN is about ~6.5x larger than FBOOT.

Both FBOOT and FMAIN are based on a (fairly heav­ily-mod­i­fied) ver­sion of FreeRTOS, as hinted by a string pre­sent in the bi­na­ries: /home/jieyi/mcuos2.5/kernel/freertos-8.2.3/.

The last part of note is CHK2, which is a SHA-256 check­sum over the en­tire firmware con­tainer ap­pended to the very end.

While not ex­actly shock­ing, con­sid­er­ing the amount of ef­fort that went into CTP au­then­ti­ca­tion, I was a bit sur­prised to see that be­sides this CHK2 SHA-256 check­sum, which was triv­ial to patch, there was no other pro­tec­tion in place for flash­ing firmwares. I would’ve ex­pected to find sig­na­ture checks here or at the very least a hash­sum(se­cret_­value + con­tain­er_­con­tents) type of pro­tec­tion, but af­ter reim­ple­ment­ing the firmware up­grade func­tion­al­ity in my own tool v2x-ctl, I found that the de­vice hap­pily ac­cepts patched firmwares as long as CHK2 is cor­rect.

To test this, I made a pretty sim­ple mod­i­fi­ca­tion - I re­placed the string WELCOME, which is shown on the seg­ment dis­play on the de­vice when boot­ing up, with PATCHED. After flash­ing the firmware and re­boot­ing the de­vice, I was happy to see my string be­ing shown to me:

The hacker part of me thinks this is great - peo­ple should be able to do what they want with the de­vices they’ve bought and own. The se­cu­rity pro­fes­sional part of me thinks that hav­ing ab­solutely no pro­tec­tion in place (like hav­ing to un­lock a boot­loader for mo­bile de­vices) is pretty bad prac­tice. But it’s not ex­actly the end of the world if you need phys­i­cal ac­cess to up­date the de­vice over USB.

If.

Everybody loves Bluetooth

Like all self-respecting” speak­ers these days, of course the Katana V2X also needs to have Bluetooth, even though it’s most likely go­ing to spend most of its life wired up to a PC or gam­ing con­sole.

And of course Creative needs to have an app which lets you con­trol the speak­er’s set­tings and fancy LED lights from your phone over Bluetooth.

The way BLE (Bluetooth Low Energy) works is that each de­vice has var­i­ous reg­is­ters (called GATT char­ac­ter­is­tics) that, if you’re con­nected to the de­vice, you can write to, read, sub­scribe to no­ti­fi­ca­tions for, and so on. What’s im­por­tant to note is that to con­nect to a de­vice, you don’t need to (necessarily) pair with it. You can of­ten just con­nect with a de­vice and im­me­di­ately start read­ing and writ­ing data to char­ac­ter­is­tics. Pairing es­tab­lishes en­cryp­tion, but a con­nec­tion can be made with­out it.

While dig­ging through the Katana’s firmware, I dis­cov­ered that the in­ter­nal CTP han­dler is bridged to both USB and ap­par­ently Bluetooth:

Intrigued by this, I down­loaded the Creative mo­bile app and tried con­nect­ing to my speaker.

Please press the POWER but­ton to pair.”

I won­dered how this pair­ing process worked, ex­actly. Maybe it used the same au­then­ti­ca­tion scheme as for USB and maybe I could just use the shared se­cret to au­then­ti­cate with any speaker over Bluetooth, as was the case with my e-scooter.

I set up a Bluetooth sniff­ing en­vi­ron­ment and ob­served that in or­der to ini­ti­ate the pair­ing process, the phone wrote a pay­load like 5a 0b… to a char­ac­ter­is­tic 9e9daaec-3a10 – 4fe8-b69f-7397aff77886, and read a re­sponse from char­ac­ter­is­tic 9e9daaeb-3a10 – 4fe8-b69f-7397aff77886.

5a had me very, very sus­pi­cious, as it’s the same byte that all CTP com­mands start with. Out of a hunch, I con­nected to the de­vice over Bluetooth from my lap­top and wrote the pay­load 5a 09 01 02, which is the CTP com­mand for read­ing the firmware ver­sion, and re­quires au­then­ti­ca­tion to send over USB.

To my sur­prise, upon read­ing the char­ac­ter­is­tic 9e9daaeb-3a10 – 4fe8-b69f-7397aff77886, I was greeted with the full ver­sion string. This means any­one can just con­nect to any Katana V2X over Bluetooth and start send­ing CTP com­mands to it, read­ing in­for­ma­tion, chang­ing set­tings, etc.

Over-the-air up­dates (the bad kind)

It did­n’t take me too long to con­nect the dots that firmware up­grades were also per­formed over CTP. Combined with the fact that any­one can con­struct valid cus­tom firmware, I won­dered if it was pos­si­ble for an at­tacker to sim­ply up­load a cus­tom firmware over Bluetooth with­out ever hav­ing to au­then­ti­cate or pair.

After wrestling with a few BLE quirks (which I’ll de­scribe in de­tail later in this ar­ti­cle), I wrote a rel­a­tively sim­ple Python script that does ex­actly what my v2x-ctl tool does to up­grade firmware, but over Bluetooth in­stead. Using that, I at­tempted to up­load the mod­i­fied firmware I had crafted ear­lier to my speaker. Since BLE is quite slow, it took around 10 min­utes to fin­ish, but af­ter it was done, I was once again greeted with my lovely PATCHED wel­come mes­sage.

I thought of the im­pli­ca­tions for a bit. The speaker has a mi­cro­phone. An at­tacker could, the­o­ret­i­cally, up­load a cus­tom firmware that ef­fec­tively turns the speaker into a covert mon­i­tor­ing de­vice, lis­ten­ing in on con­ver­sa­tions and for­ward­ing them to a re­ceiver over Bluetooth.

What was more in­ter­est­ing to me was the fact that the speaker is, in a stan­dard setup, con­nected to a PC over USB. It’s by all means a trusted USB de­vice.

What if we wrote cus­tom firmware that forced the speaker into act­ing as a key­board, send­ing key­strokes for open­ing up the ter­mi­nal and ex­e­cut­ing ar­bi­trary com­mands? We would turn the speaker into a Rubber Ducky, but re­motely, with­out ever hav­ing to plug any­thing into ei­ther the speaker or the PC.

Living off the ker­nel land

At first, I thought this would be a her­culean task. Since I don’t have ac­cess to the source code of the firmware, I would have to some­how jury-rig in an en­tire sec­tion of code that sets the de­vice up as a HID (human in­ter­face de­vice) USB de­vice (if that’s even pos­si­ble for this SoC), a pro­ce­dure for then us­ing this to send key­strokes to the PC over USB, and con­tin­u­ing to run the rest of the code in the firmware so the speaker would still be­have as nor­mal.

However, af­ter dig­ging around some more in the firmware, I re­al­ized it’s likely not as dif­fi­cult as it seems.

First off, it turns out the speaker al­ready sets it­self up as a HID de­vice. Not as a full key­board, mind you, but as a Consumer Control de­vice - ba­si­cally let­ting the speaker change the vol­ume and me­dia sta­tus (play/pause) on the PC, but not much else.

This could be seen in the ker­nel logs:

The way this is done with USB de­vices is that the de­vice pre­sents the PC with a USB de­scrip­tor set, which is ba­si­cally a re­port of its ca­pa­bil­i­ties, what it can do, how many in­ter­faces to enu­mer­ate, and so on.

The re­port de­scrip­tor in the firmware was pretty easy to lo­cate and to my luck, it had enough space to ap­pend a sec­ond re­port de­scrip­tor en­try that also pre­sented the de­vice as a key­board. Running dmesg now shows that the de­vice also re­ports be­ing a key­board:

The sec­ond is­sue was send­ing ac­tual HID data and em­u­lat­ing key­strokes. Much to my luck, the firmware al­ready had a neatly us­able rou­tine for send­ing HID data, all I had to do was pro­vide it with data (the key to press or un­press) and call it.

The third is­sue I strug­gled with quite a bit. It was dif­fi­cult to find enough free space that I could write in (which would get prop­erly mapped in mem­ory or would­n’t im­me­di­ately crash the de­vice when boot­ing), find­ing a tram­po­line that worked prop­erly and did­n’t crash re­turn­ing back to the nor­mal in­struc­tion flow, etc.

I even­tu­ally re­al­ized that if this is run­ning on FreeRTOS, there’s likely nu­mer­ous tasks be­ing ex­e­cuted on boot any­ways. I don’t need to write a tram­po­line and jug­gle the ex­e­cu­tion flow, I can just over­write an ex­ist­ing task and let the firmware spawn it for me. I ended up find­ing a di­ag­nos­tic task, which did­n’t seem to do any­thing in nor­mal use - from what I could tell, it was only used for gath­er­ing di­ag­nos­tic data from a DSP co­proces­sor.

I over­wrote that task with a task that:

Waits ~20 sec­onds for the speaker to boot and bring up the USB sub­sys­tem

Types in echo pwned and hits en­ter, with ~20ms be­tween each key­stroke

Ends the task, leav­ing the rest of the speak­er’s func­tion­al­ity in­tact

This would be ex­e­cuted every time the de­vice booted up.

The patches ended up be­ing pretty min­i­mal - only 83 bytes for the USB re­port and 102 bytes of hand-writ­ten ARM/Thumb as­sem­bly for the key­stroke in­jec­tor, plus 2 bytes for every key­stroke I wanted to send.

The re­sult

Chaining it all to­gether, I was able to to­tally re­motely, over the air, up­load a cus­tom firmware to my speaker which I had­n’t paired with, which would re­boot, flash the cus­tom firmware, and af­ter re­boot­ing type in the com­mand echo pwned and ex­e­cute it.

In a real at­tack sce­nario, I would ex­e­cute the key­strokes for open­ing pow­er­shell.exe or sim­i­lar and paste an ac­tu­ally ma­li­cious one-liner into that, but as a proof of con­cept, this was more than enough for me. A real at­tacker would also likely dis­able the rou­tine for up­dat­ing the firmware in both nor­mal and re­cov­ery mode, mak­ing it im­pos­si­ble to wipe the ma­li­cious firmware from the de­vice or patch it in the fu­ture.

This is wors­ened by the fact that Bluetooth is al­ways on for the speaker, even in sleep mode, with no ap­par­ent way to dis­able it.

Remediation

Getting in touch with Creative was a frus­trat­ing process.

They do not have any se­cu­rity con­tacts. In fact, I was­n’t even able to find reg­u­lar con­tacts that was­n’t just a sup­port form on their web­site. I tried (two times) to get in con­tact with them via the web form be­fore giv­ing up and con­tact­ing SingCERT to act as an in­ter­me­di­ary, hop­ing they would have bet­ter luck reach­ing Creative.

Initially, SingCERT did­n’t seem to be able to get in con­tact with Creative ei­ther. It took Creative nearly two months to re­spond to SingCERT. Unfortunately, their re­sponse was that they do not con­sider this to be a vul­ner­a­bil­ity, as it does not pre­sent a cy­ber­se­cu­rity risk”. I don’t know how they reached this con­clu­sion, but it be­came clear that Creative had no in­ter­est in re­spond­ing to or ad­dress­ing this is­sue.

Due to this, there are no patches cur­rently avail­able via Creative them­selves. The lat­est firmware is vul­ner­a­ble.

As a par­tial rem­edy, to en­sure that peo­ple are still able to use these de­vices se­curely, I wrote a patch for the firmware that blocks CTP-over-Bluetooth. This likely breaks the Creative mo­bile app, but with­out the source code, it’s fairly hard to patch the firmware with proper au­then­ti­ca­tion in-place.

If you’re in­ter­ested in us­ing this patch, I’ve cre­ated a tool that down­loads the of­fi­cial firmware from Creative’s servers, patches it in mem­ory, and up­loads it to your USB-connected Katana V2X. You can get it from the re­leases page here: https://​git.dog/​xx/​v2x-patcher (or build it your­self with cargo if you want to in­spect the patches be­fore­hand).

The nitty gritty

What fol­lows are a few tech­ni­cal de­tails of the re­verse en­gi­neer­ing process and the bi­nary patches in no spe­cific or­der.

Memory lay­out woes

For Ghidra’s (or any other RE toolk­it’s) au­to­matic analy­sis to work prop­erly, the bi­nary needs to have the cor­rect base ad­dress. Otherwise, point­ers cal­cu­lated us­ing the base ad­dress will point to the wrong data and you’ll just get dis­as­sem­bly that does­n’t make much sense.

With FMAIN.bin, I strug­gled quite a bit. It was­n’t enough to sim­ply load the firmware with what I as­sumed to be the cor­rect base (0x10000000). When I loaded the bi­nary us­ing this base, the auto-analy­sis seemed to pro­duce valid re­sults, the startup code and FreeRTOS core seemed cor­rect, but af­ter that brief sec­tion in the be­gin­ning, auto-analy­sis seemed to fail and pro­duce garbage.

As it turns out, FMAIN.bin is not a mono­lithic im­age and is in­stead scat­ter-loaded, with dif­fer­ent sec­tions of the firmware loaded at dif­fer­ent ad­dresses.

I did­n’t find any easy way to read what the cor­rect mem­ory map should be, but I de­duced (and later ver­i­fied by read­ing mem­ory right off the de­vice, when I was able to patch and up­load firmware) the fol­low­ing lay­out:

Using this mem­ory map in Ghidra seemed to ac­tu­ally pro­duce valid analy­sis re­sults.

String x-refs for ARM firmware

Even though my mem­ory map was cor­rect, I was still not get­ting many X-refs de­fined for my strings. Some strings were ref­er­enced by some func­tions, but this seemed in­con­sis­tent and most strings seemed to be wholly un­ref­er­enced, which did­n’t make any sense.

Working back from what I as­sumed to be the log method in the firmware, I re­al­ized that string point­ers weren’t be­ing loaded di­rectly. Instead, string point­ers were loaded us­ing a pair of movw and movt in­struc­tions:

movw r0, #0x29A4  ; low 16 bits movt r0, #0x400A  ; high 16 bits, r0 = 0x400A29A4

I tried search­ing on­line, but did­n’t find much in­for­ma­tion on why ex­actly Ghidra’s analy­sis seems to not rec­og­nize these as point­ers to strings (perhaps the scat­ter-load­ing?), but I wrote a script that went over all movw/​movt pairs which loaded into the same reg­is­ter, fil­tered out the ones point­ing to valid mem­ory, and set up DATA ref­er­ences to those. This cre­ated ~13k ref­er­ences and made my life so much eas­ier.

Firmware patches for read­ing, writ­ing and ex­e­cut­ing mem­ory

After I fig­ured out how to mod­ify the firmware and in­ject my own code, the first or­der of busi­ness was set­ting up a method of read­ing, writ­ing and ex­e­cut­ing mem­ory. This was im­por­tant for a few rea­sons, but most im­por­tantly for ver­i­fy­ing the ac­tual mem­ory lay­out, read­ing what was go­ing on in the heap, and be­ing able to write and ex­e­cute pay­loads on the fly with­out hav­ing to patch the firmware it­self and flash it. Flashing the firmware was sloooow, and a lot of my RE time was taken up sim­ply wait­ing for the firmware to up­load to the de­vice, the de­vice to re­boot, re­al­ize that my patches were wrong and the de­vice is boot­loop­ing or has been bricked, re­boot­ing into re­cov­ery mode (which re­quired pulling the power), and re­peat­ing the whole process again.

To set up these han­dlers, I de­cided that the best way to im­ple­ment this would to over­write a CTP han­dler that al­ready ex­ists and is prop­erly routed, but which does­n’t do any­thing im­por­tant. The CTP op­code 0x54 seemed like a pretty good can­di­date, all it did was echo back the data it was sent, and the han­dler was about 106 bytes long. This seemed a bit tight to fit three com­mands into, but ended up be­ing just barely enough room to work with - my fi­nal han­dler with all three com­mands im­ple­mented ended up be­ing 96 bytes long.

On the host side, I sim­ply mod­i­fied my v2x-ctl tool to sup­port this cus­tom CTP com­mand. With this, I was able to ex­e­cute ar­bi­trary code over USB with­out hav­ing to re­flash the whole firmware, which made test­ing patches etc so much more con­ve­nient.

Watchdogs do­ing their jobs

While writ­ing the pay­load for send­ing HID com­mands, I kept run­ning into an is­sue where my code, which was seem­ingly fine and worked for send­ing sin­gu­lar key­presses, re­booted the de­vice when­ever I tried to im­ple­ment send­ing mul­ti­ple key­presses.

This was be­fore I came up with the replace an ex­ist­ing task” ap­proach, and was sim­ply run­ning my pay­load us­ing mem-exec as I de­scribed above. The weird­est thing was that the de­vice seemed to crash when I called a be­nign func­tion vTaskDe­lay built into FreeRTOS to add a small de­lay be­tween each key­press event.

It turns out that Creative had im­ple­mented per-task watch­dog timers, and if a crit­i­cal task was tak­ing too long, the OS would panic and re­boot (my best guess). When I called vTaskDe­lay through mem-exec, I was call­ing it in the USB han­dling task con­text. The de­lays for each char­ac­ter added up, the watch­dog timer was­n’t be­ing kicked in time, and the sys­tem re­booted.

This is­sue fixed it­self when I in­jected my code into the di­ag­nos­tic ser­vice task in­stead of call­ing it from the USB task di­rectly.

All tests and re­verse en­gi­neer­ing was per­formed on the firmware ver­sion 1.3.230619.1820.

Timeline

01/04/2026 - Attempt 1 to get in con­tact with ven­dor via their sup­port form (vendor lacks any pub­lic se­cu­rity con­tacts)

07/04/2026 - Attempt 2 to get in con­tact with ven­dor via their sup­port form

09/04/2026 - Submission of vul­ner­a­bil­i­ties to SingCERT

16/04/2026 - Response from SingCERT re­quest­ing fur­ther in­for­ma­tion

16/04/2026 - Sent ad­di­tional de­tails to SingCERT

20/04/2026 - Response from SingCERT re­quest­ing fur­ther in­for­ma­tion

20/04/2026 - Sent ad­di­tional de­tails to SingCERT

20/04/2026 - Response from SingCERT ac­knowl­edg­ing the re­port and con­firm­ing they’ve reached out to ven­dor

08/05/2026 - Email from SingCERT stat­ing they’ve been un­able to reach the ven­dor and ask­ing whether they should con­tinue at­tempt­ing to fol­low up

08/05/2026 - Sent con­fir­ma­tion to SingCERT

25/05/2026 - Email from SingCERT stat­ing ven­dor has re­sponded and is aware of the case

03/06/2026 - Email from SingCERT stat­ing ven­dor do not con­sider this to be a vul­ner­a­bil­ity, as it does not pre­sent a cy­ber­se­cu­rity risk.”

03/06/2026 - Write-up pub­lished

Elixir v1.20 released: now a gradually typed language

elixir-lang.org

In 2022, we an­nounced the ef­fort to add set-the­o­retic types to Elixir. In June 2023, we pub­lished an award win­ning pa­per on Elixir’s type sys­tem de­sign and said our work was tran­si­tion­ing from re­search to de­vel­op­ment.

With Elixir v1.20, we have com­pleted our first de­vel­op­ment mile­stone which is to per­form type in­fer­ence and grad­u­ally type check every Elixir pro­gram, with­out in­tro­duc­ing type an­no­ta­tions. This means Elixir in­creas­ingly re­ports dead code and ver­i­fied bugs: typ­ing vi­o­la­tions that are guar­an­teed to fail at run­time if ex­e­cuted. Elixir can find ver­i­fied bugs in ex­ist­ing pro­grams ef­fi­ciently, with­out in­tro­duc­ing de­vel­oper over­head, and with an ex­tremely low false pos­i­tives rate.

In this an­nounce­ment, we will break down the type sys­tem goals, what the dy­namic() type means in Elixir, and how it finds ver­i­fied bugs. In par­tic­u­lar, our im­ple­men­ta­tion per­forms well in the If T: Benchmark for Type Narrowing” bench­mark. Elixir passes 12 of the 13 cat­e­gories, show­ing that it can re­cover pre­cise type in­for­ma­tion from or­di­nary Elixir code, which we use to find ver­i­fied bugs in dy­nam­i­cally typed pro­grams.

The type sys­tem was made pos­si­ble thanks to a part­ner­ship be­tween CNRS and Remote. The de­vel­op­ment work is cur­rently spon­sored by Fresha, and Tidewave.

Types, in my Elixir?

Our goal is to in­tro­duce a type sys­tem which is:

sound - the types in­ferred and as­signed by the type sys­tem align with the be­hav­iour of the pro­gram

sound - the types in­ferred and as­signed by the type sys­tem align with the be­hav­iour of the pro­gram

grad­ual - Elixir’s type sys­tem in­cludes the dy­namic() type, which can be used when the type of a vari­able or ex­pres­sion is checked at run­time. In the ab­sence of dy­namic(), Elixir’s type sys­tem be­haves as a sta­tic one

grad­ual - Elixir’s type sys­tem in­cludes the dy­namic() type, which can be used when the type of a vari­able or ex­pres­sion is checked at run­time. In the ab­sence of dy­namic(), Elixir’s type sys­tem be­haves as a sta­tic one

de­vel­oper friendly - the types are de­scribed, im­ple­mented, and com­posed us­ing ba­sic set op­er­a­tions: unions, in­ter­sec­tions, and nega­tions (hence it is a set-the­o­retic type sys­tem), with clear er­ror mes­sages

de­vel­oper friendly - the types are de­scribed, im­ple­mented, and com­posed us­ing ba­sic set op­er­a­tions: unions, in­ter­sec­tions, and nega­tions (hence it is a set-the­o­retic type sys­tem), with clear er­ror mes­sages

Introducing a type sys­tem into an ex­ist­ing lan­guage is a com­plex change. For this rea­son, our first mile­stone was to im­ple­ment the type sys­tem with­out in­tro­duc­ing typ­ing an­no­ta­tions but still have it pro­vide value to de­vel­op­ers by find­ing dead code and ver­i­fied bugs. This is done through the dy­namic() type, which in Elixir is quite dif­fer­ent from other grad­u­ally typed lan­guages. Let’s break it down.

The dy­namic() type

Many grad­ual type sys­tems have the any() type, which, from the point of view of the type sys­tem, of­ten means anything goes” and no type vi­o­la­tions are re­ported. On the other hand, Elixir’s grad­ual type is called dy­namic() and it has two im­por­tant prop­er­ties: com­pat­i­bil­ity and nar­row­ing.

In sta­tic type sys­tems, when you have a type of shape in­te­ger() or bi­nary() and you in­voke a func­tion, said func­tion must ac­cept both types. However, be­cause type sys­tems can­not cap­ture the in­ten­tion of all of our pro­grams with pre­ci­sion, this may lead to false pos­i­tives. For ex­am­ple, take the sim­ple code be­low:

def per­cent­age_or_er­ror(value) when is_in­te­ger(value) do val­ue_or_er­ror = if value > 1 do value else not well” end

# … more code …

if value > 1 do val­ue_or_er­ror / 100 else String.upcase(value_or_error) end end

Although val­ue_or_er­ror has type in­te­ger() or bi­nary(), the op­er­a­tor / ac­cepts only num­bers, and String.upcase ac­cepts only bi­na­ries/​strings, the pro­gram above is valid and emits no ex­cep­tions at run­time. However, a type sys­tem would still re­port two vi­o­la­tions, be­cause the types sup­plied to / and String.upcase are not a sub­type of the ac­cepted types.

While the pro­gram above could be bet­ter writ­ten to have no typ­ing vi­o­la­tions, type sys­tems will al­ways re­ject valid pro­grams, and if Elixir were to in­tro­duce too many false pos­i­tives in ex­ist­ing code­bases, it would quickly erode the trust in the type sys­tem. Therefore, Elixir’s grad­ual type sys­tem tags the val­ue_or_er­ror vari­able above with the type dy­namic(in­te­ger() or bi­nary()), which means the type is ei­ther in­te­ger() or bi­nary() at run­time.

When call­ing a func­tion with a dy­namic() type, Elixir will only emit a typ­ing vi­o­la­tion if the sup­plied types and the ac­cepted types are dis­joint. In the pro­gram above, even though / ex­pects only num­bers, dy­namic(in­te­ger() or bi­nary()) can be an in­te­ger() and given the ac­cepted and sup­plied types are not dis­joint, there are no typ­ing vi­o­la­tions. However, if we were to change the pro­gram to this:

val­ue_or_er­ror = if value > 1 do value else not well” end

Map.fetch!(value_or_error, :some_key)

Because Map.fetch! ex­pects a map data struc­ture, and val­ue_or_er­ror can only be in­te­ger or bi­nary at run­time, the ac­cepted and sup­plied types are dis­joint, which turns into a vi­o­la­tion. This is known as the com­pat­i­bil­ity prop­erty and it ex­plains how Elixir re­ports only ver­i­fied bugs.

However, re­port­ing only ver­i­fied bugs would not be use­ful if we can’t find many bugs in the first place. We ad­dressed this prob­lem by mak­ing sure Elixir’s dy­namic type can be nar­rowed. Take this code:

def ad­d_a_and_b(data) do data.a + data.b end

In the pro­gram above, data starts as a dy­namic() type. We then use it as data.a and data.b in­side the plus op­er­a­tor, so Elixir will re­fine the data vari­able to have type %{…, a: num­ber(), b: num­ber()}, which im­plies it is a map with both a and b fields with num­ber val­ues (and po­ten­tially any other field, hence the lead­ing …). Therefore, if you were to for­get to se­lect the .b field and write this:

def ad­d_a_and_b(data) do data.a + data end

data would be first nar­rowed to a map of shape %{…, a: num­ber()}, then at­tempted to be used as a num­ber(), which would emit a vi­o­la­tion.

In other words, the dy­namic() type in Elixir ef­fec­tively works as a range, which can be re­fined as it is used through­out the pro­gram and re­ports vi­o­la­tions when­ever type checks fall out­side of the range. This is a con­trast to other grad­ual type sys­tems, which use the dy­namic type to dis­card all type in­for­ma­tion.

Behind the scenes, our type in­fer­ence and type check­ing al­go­rithms be­have as if we an­no­tated all ar­gu­ment types as dy­namic(). Once we in­tro­duce user-sup­plied type an­no­ta­tions, Elixir’s type sys­tem will be­have as any sta­t­i­cally typed lan­guage as long as dy­namic() is not used. And when­ever you cross the sta­tic-dy­namic bound­ary, we de­vel­oped new tech­niques that en­sure our grad­ual typ­ing is sound, with­out a need for ad­di­tional run­time checks.

Typing guards, clauses, and more

Most of the work be­hind this re­lease was to in­tro­duce type check­ing and nar­row­ing to sev­eral con­structs. Let’s see some of them.

When it comes to guards, we can in­fer unions, in­ter­sec­tions, and nega­tions:

def ex­am­ple(x, y) when is_list(x) and is_in­te­ger(y)

The code above cor­rectly in­fers x is a list and y is an in­te­ger.

def ex­am­ple({:ok, x} = y) when is_bi­nary(x) or is_in­te­ger(x)

The one above in­fers x is a bi­nary or an in­te­ger, and y is a two el­e­ment tu­ple with :ok as first el­e­ment and a bi­nary or in­te­ger as sec­ond.

def ex­am­ple(x) when is_map_key(x, :foo)

The code above in­fers x is a map which has the :foo key, rep­re­sented as %{…, foo: dy­namic()}. Remember the lead­ing … in­di­cates the map may have other keys.

def ex­am­ple(x) when not is_map_key(x, :foo)

And the code above in­fers x is a map that does not have the :foo key, which has the type: %{…, foo: not_set()}. Hence x.foo within the func­tion body will raise a typ­ing vi­o­la­tion.

You can also have ex­pres­sions that as­sert on the size of data struc­tures:

def ex­am­ple(x) when tu­ple_­size(x) < 3

Elixir will cor­rectly track the tu­ple has at most two el­e­ments, and there­fore ac­cess­ing elem(x, 3) will emit a typ­ing vi­o­la­tion. For maps and lists, we con­vert size checks into empti­ness ones. In other words, Elixir can look at com­plex guards, in­fer types, and use this in­for­ma­tion to find bugs in our code.

When it comes to con­structs such as case and con­di­tion­als, Elixir uses in­for­ma­tion from pre­vi­ous clauses to re­fine sub­se­quent ones:

case System.get_env(“SOME_VAR”) do nil -> :not_found value -> {:ok, String.upcase(value)} end

System.get_env(“SOME_VAR”) re­turns ei­ther nil or a bi­nary(). Because the first clause matches on nil, the type sys­tem knows value can no longer be nil, and there­fore it must only be a bi­nary(), which al­lows the sec­ond clause to also type check with­out vi­o­la­tions. Narrowing across clauses also helps the type sys­tem find re­dun­dant clauses and dead code in ex­ist­ing code­bases.

Furthermore, we have typed many func­tions in the stan­dard li­brary that work with tu­ples and maps. You can find more de­tails in the re­lease notes.

Compilation time im­prove­ments

Elixir v1.20 also im­proves com­pi­la­tion times once more, es­pe­cially on ap­pli­ca­tions run­ning on ma­chines with many cores. Even though BEAM lan­guages are ef­fi­cient to com­pile in gen­eral, our syn­thetic bench­marks now place Elixir’s build tool as the fastest among them. If you would like to con­tribute more ex­am­ples and sce­nar­ios, please start a dis­cus­sion so we can pro­vide a trans­par­ent suite of bench­marks and re­sults.

It also in­tro­duces a new com­piler op­tion called :module_definition, which spec­i­fies if the mod­ule de­f­i­n­i­tion should be :compiled (the de­fault) or :interpreted. This may im­prove com­pi­la­tion times in large pro­jects and it does not af­fect the .beam files writ­ten to disk, only how the con­tents in­side def­mod­ule are ex­e­cuted. You can en­able it by set­ting elixir­c_op­tions: [module_definition: :interpreted] in your mix.exs. Read the doc­u­men­ta­tion to learn more.

What is next?

The biggest ques­tion ahead of us is: when will Elixir in­tro­duce new type sig­na­tures that lever­age set-the­o­retic types? As re­cently dis­cussed in my ElixirConf EU 2026 keynote, we still have both re­search and de­vel­op­ment work ahead of us. We will only in­tro­duce type sig­na­tures:

if we are sat­is­fied with the type sys­tem per­for­mance in Elixir v1.20 (and we have done ex­ten­sive work op­ti­miz­ing it)

if we can im­ple­ment re­cur­sive types ef­fi­ciently

if we can im­ple­ment para­met­ric types ef­fi­ciently

if we can im­ple­ment tra­vers­ing key-value pairs of maps as an enu­mer­able ef­fi­ciently (we are still re­search­ing the pos­si­ble so­lu­tions here)

Once those prob­lems are tack­led, we will start to ex­plore and dis­cuss typed struct de­f­i­n­i­tions and fi­nally type sig­na­tures. As usual, we will keep the com­mu­nity posted through news and in the Elixir Forum.

We ap­pre­ci­ate every­one who tried the re­lease can­di­dates, ran bench­marks, and gave us feed­back! Give Elixir v1.20 a try and re­mem­ber to fix all of the bugs it will find for free!

Encephalitis - Andrew Gallant's Blog

burntsushi.net

I was re­cently di­ag­nosed with anti-NMDA re­cep­tor en­cephali­tis. It is an au­toim­mune dis­or­der where your body’s nor­mally help­ful an­ti­bod­ies start act­ing strangely. This leads to in­flam­ma­tion in the brain. This short blog briefly dis­cusses some of my ex­pe­ri­ence and prog­no­sis.

Target au­di­ence: Anyone re­ly­ing on my work for their own pro­jects.

It all started with flu-like symp­toms: heart rac­ing, night sweats, the chills and trou­ble sleep­ing. But no con­ges­tion or cough. I also felt re­ally off men­tally. A deep sort of anx­i­ety, along with panic at­tacks, that I had never ex­pe­ri­enced be­fore in my 38 years of life. It was ter­ri­fy­ing, es­pe­cially be­cause I had no idea what was caus­ing it. There were no life events or ob­vi­ous trig­gers that pre­cip­i­tated the psy­cho­log­i­cal symp­toms, nor was there any ob­vi­ous bi­o­log­i­cal ex­pla­na­tion for the phys­i­cal symp­toms at the time. This was only the be­gin­ning.

Over the en­su­ing weeks my phys­i­cal symp­toms pro­gressed to chronic jaw pain, mak­ing it in­cred­i­bly dif­fi­cult to eat. I also had prob­lems with my bal­ance. As some­one who has eas­ily jug­gled 3 balls and played sports for my en­tire child­hood, I could­n’t catch a ball lobbed to me from a few feet away by my 5 year old son. My psy­cho­log­i­cal symp­toms were per­haps even more hor­ri­fy­ing to me. I had sui­ci­dal ideation and suf­fered from psy­chosis. Specifically, delu­sions and au­di­tory hal­lu­ci­na­tions.

The prob­lems with bal­ance and the over­whelm­ing na­ture of my psy­cho­log­i­cal symp­toms even­tu­ally led me to fall and hit my head. This in turn led my­self and my wife to de­cide that I could­n’t be safe at home. And that brought us to my first emer­gency room visit. They cleared me phys­i­cally and sent me to an in-pa­tient psy­chi­atric hos­pi­tal, which, at the time, I wel­comed be­cause my symp­toms had pro­gressed be­yond what we could man­age at home.

It is com­mon for anti-NMDA re­cep­tor en­cephali­tis to be mis­di­ag­nosed as (in my case) gen­er­al­ized anx­i­ety dis­or­der or schiz­o­phre­nia. Since I had been cleared phys­i­cally, get­ting out of the psy­chi­atric hos­pi­tal quickly to see a neu­rol­o­gist proved dif­fi­cult. This was the sin­gle point, in ret­ro­spect, where our health care sys­tem let me down. It took a lucky con­nec­tion with some­one who hap­pened to be a doc­tor to get me out of the psy­chi­atric fa­cil­ity and into the neu­rol­ogy de­part­ment at Brigham and Women’s Hospital in Boston.

After that, I was in and out of Brigham and Women’s Hospital for al­most a month. I had sev­eral MRIs, a lum­bar punc­ture, EEGs and many more tests. As a re­sult of what I now see as a life sav­ing treat­ment pro­to­col, I very quickly re­ceived in­tra­venous im­munoglob­u­lin (IVIG) and methyl­pred­nisolone, even be­fore my di­ag­no­sis was known. In par­tic­u­lar, MRIs re­vealed a le­sion in my brain. However, con­firm­ing a di­ag­no­sis of anti-NMDA re­cep­tor en­cephali­tis would come later since it is best done with at least a pos­i­tive an­ti­body test in your cere­bral spinal fluid. Results from this spe­cific test typ­i­cally take a cou­ple weeks to come back.

By the time I re­ceived my of­fi­cial di­ag­no­sis, the IVIG and steroids had kicked in and I was feel­ing much bet­ter, al­beit, not nearly at 100%. I’ve since con­tin­ued on a course of steroids that I am now al­ready ta­per­ing off of. I’m also ta­per­ing off of med­ica­tions I had been pre­scribed as a re­sult of my psy­cho­log­i­cal symp­toms, be­fore en­cephali­tis was known to be the cause. Moreover, I am now of­fi­cially in the CIELO clin­i­cal trial for test­ing the ef­fec­tive­ness of satral­izumab in treat­ing anti-NMDA re­cep­tor en­cephali­tis.

While au­toim­mune dis­or­ders don’t have a known cure, the prog­no­sis for anti-NMDA re­cep­tor en­cephali­tis is very good. My doc­tors have said that it was caught early (despite the early tan­gent into a psy­chi­atric hos­pi­tal), and that this is as­so­ci­ated with bet­ter long term out­comes. Indeed, I am feel­ing great now and re­cov­ery is ex­ceed­ing my own ex­pec­ta­tions.

There is some spec­u­la­tion that anti-NMDA re­cep­tor en­cephali­tis could par­tially ex­plain past ac­counts of de­monic pos­ses­sion. Many of the peo­ple in my life, close or not, could tell that there was some­thing se­ri­ously wrong with me. Without sci­ence and mod­ern med­i­cine, I can only imag­ine what kind of spec­u­la­tion folks might have ven­tured for the un­der­ly­ing cause.

My full story of this dis­ease of chaos is quite long and I’m not sure I will ever pub­lish it in full. However, Susannah Cahalan did just that in her book, Brain on Fire: My Month of Madness. There is also a movie adap­ta­tion (as of June 2026) avail­able for free on YouTube. My dis­ease did­n’t progress as far as Susannah’s, nor did it do so in the same way. For ex­am­ple, I did­n’t have any (known) seizures or cata­to­nia. The rest of her symp­toms, es­pe­cially the psy­chosis, were quite sim­i­lar.

This has been the ab­solute worst ex­pe­ri­ence of my life, bar none. It is also the ex­pla­na­tion be­hind my higher-than-usual in­ac­tiv­ity over the last few months. But I am slowly get­ting back into the swing of things with a re­newed vigor. I’m ex­cited for where the in­dus­try is headed and I can’t wait to see what things will look like one year from to­day. I’m so happy that I get to be my normal” self to ex­pe­ri­ence that, which is a stark jux­ta­po­si­tion from how I felt just two months ago.

Finally, I want to ex­press some grat­i­tude to two peo­ple in par­tic­u­lar.

First and fore­most is my wife, Kaitlyn Brady. She saved my life. She never stopped be­liev­ing that there was some neu­ro­log­i­cal com­po­nent and she never stopped fight­ing for me. I feel so grate­ful that she is in my cor­ner. More than that, the bur­den she car­ried be­fore my di­ag­no­sis was known is some­thing that is truly re­mark­able. She was­n’t just there for me when I needed her. She was there for our son. She was there for the doc­tors when­ever they called, even late into the night. She was there when our base­ment flooded. And when we all caught in­fluenza. I’ll never know how she jug­gled every­thing, but I’ll be in her debt for the rest of my life.

Secondly is Charlie Marsh. He was pa­tient, un­der­stand­ing and my part­ner through it all. He did­n’t just ex­ceed ex­pec­ta­tions for how you want your em­ployer to deal with a se­ri­ous med­ical con­di­tion, but he went above and be­yond even that in more ways than one. It’s not of­ten I can say that some­one has han­dled a sit­u­a­tion per­fectly, but the word fits in this case.

Thank you to my friends, fam­ily and doc­tors as well. Their sup­port dur­ing this time was un­wa­ver­ing and I’m not sure what would have hap­pened with­out them. Nothing good” is what a nurse said when I posed that ques­tion to her.

Happy hack­ing.

AI Outperforms Law Professors in Stanford Law Study | Stanford Law School

law.stanford.edu

A ground­break­ing study led by Stanford Law School Professor Julian Nyarko re­veals that law pro­fes­sors over­whelm­ingly pre­fer AI-generated an­swers to stu­dent ques­tions over re­sponses writ­ten by their fel­low in­struc­tors—a find­ing that could re­shape how le­gal ed­u­ca­tion is de­liv­ered.

The study, ti­tled Law Professors Prefer AI Over Peer Answers,” was con­ducted with 16 law pro­fes­sors across U.S. law schools and tested whether large lan­guage mod­els could serve as ef­fec­tive tu­tors for con­tract law courses.In a blind eval­u­a­tion of nearly 3,000 anonymized com­par­isons, pro­fes­sors rated AI re­sponses sig­nif­i­cantly higher than an­swers writ­ten by other pro­fes­sors, with AI win­ning 75% of head-to-head matchups.

This study chal­lenges im­por­tant as­sump­tions about AIs role in le­gal ed­u­ca­tion,” said Nyarko, who leads Stanford Law School’s Legal Innovation through Frontier Technology Lab, or lift­lab. He co-au­thored the pa­per with col­leagues from Yale, NYU, University of Chicago, and other lead­ing in­sti­tu­tions. We fo­cused on law pre­cisely be­cause it re­quires judg­ment, nu­anced rea­son­ing, and the abil­ity to nav­i­gate am­bi­gu­ity—not just fac­tual re­call.”

Can LLMs Reason?

The study is par­tic­u­larly no­table be­cause pre­vi­ous AI eval­u­a­tions have fo­cused pri­mar­ily on sub­jects with clear right-or-wrong an­swers. Legal rea­son­ing, by con­trast, de­mands care­ful analy­sis of com­pet­ing ar­gu­ments and de­fen­si­ble con­clu­sions.

We were frankly sur­prised by the mag­ni­tude of the re­sults,” Nyarko added. These weren’t just sim­ple ques­tions with ob­vi­ous an­swers. Many of them re­quired syn­the­siz­ing com­plex ma­te­r­ial, ap­ply­ing it to new sit­u­a­tions, and ex­plain­ing le­gal con­cepts in ways that would help stu­dents de­velop their own an­a­lyt­i­cal skills.”

Participants cre­ated 40 rep­re­sen­ta­tive con­tracts law ques­tions that stu­dents might ask af­ter class or dur­ing of­fice hours, wrote their own an­swers, and then eval­u­ated re­sponses with­out know­ing whether they came from AI or other par­tic­i­pat­ing pro­fes­sors. The AI sys­tems per­formed com­pa­ra­bly to the best hu­man in­struc­tor in the study.

Perhaps most strik­ing: pro­fes­sors flagged AI re­sponses as ped­a­gog­i­cally harm­ful only 3.5% of the time, com­pared to 12% for peer-writ­ten an­swers.

In most fields where AI gets tested, there’s a right an­swer. In law, there of­ten is­n’t.” said Sarath Sanga, co-au­thor and pro­fes­sor at Yale Law School. Two op­pos­ing ar­gu­ments can both be good. What we wanted to know is whether AI can meet the la­tent pro­fes­sional stan­dard that lawyers use to eval­u­ate each oth­er’s ar­gu­ments. In this case, the an­swer was yes.”

The re­search team took ex­ten­sive pre­cau­tions to en­sure the study’s va­lid­ity. They cal­i­brated AI re­sponses to match the length and struc­ture of hu­man an­swers, used mul­ti­ple eval­u­a­tion meth­ods, and had pro­fes­sors as­sess whether re­sponses might mis­lead or con­fuse stu­dents.

Transforming Legal Education

We de­signed this study to be as rig­or­ous as pos­si­ble be­cause the stakes are so high,” Nyarko ex­plained. Legal ed­u­ca­tion is about train­ing fu­ture lawyers to think crit­i­cally, ar­gue per­sua­sively, and nav­i­gate eth­i­cal com­plex­i­ties. Our study makes im­por­tant steps to­wards find­ing out whether AI could sup­port that mis­sion.”

Alejandro Salinas, first au­thor of the study and a re­searcher at Nyarko’s lift­lab, em­pha­sized the ed­u­ca­tional im­pli­ca­tions: Our study shifts at­ten­tion to what AI tu­tor­ing can con­tribute to learn­ing in judg­ment-rich fields like law. We find that, when eval­u­ated by le­gal ed­u­ca­tors, AI tu­tors can of­fer high-qual­ity, on-de­mand sup­port that com­ple­ments class­room in­struc­tion, and may broaden ac­cess to ex­pert guid­ance.”

The study also ex­am­ined spe­cific AI mod­els, in­clud­ing com­mer­cial tu­tor­ing sys­tems and Google’s NotebookLM, find­ing vary­ing lev­els of per­for­mance. However, even when con­text lim­i­ta­tions af­fected AI re­sponses, pro­fes­sors still fre­quently pre­ferred them to hu­man-writ­ten al­ter­na­tives.

The find­ings ar­rive as law schools na­tion­wide grap­ple with in­te­grat­ing AI tools into le­gal ed­u­ca­tion while main­tain­ing rig­or­ous aca­d­e­mic stan­dards. Some in­sti­tu­tions have em­braced AI ex­per­i­men­ta­tion, while oth­ers re­main cau­tious about po­ten­tial risks in­clud­ing hal­lu­ci­na­tions, over­re­liance, and the ero­sion of crit­i­cal think­ing skills.

Our study eval­u­ates the qual­ity of an­swers given by AI tools. But how to im­ple­ment these tools to most ef­fec­tively im­prove stu­dent learn­ing is still an open ques­tion. So we’re not ad­vo­cat­ing for whole­sale adop­tion of AI tu­tors,” Nyarko cau­tioned. But our data sug­gests that blan­ket skep­ti­cism may be equally un­war­ranted. The con­ver­sa­tion should shift from whether AI can give ac­cu­rate, high qual­ity re­sponses to how we can de­ploy it re­spon­si­bly to the ben­e­fit of our stu­dents.”

View the Publication Link to SSRN

About lift­lab

Liftlab is among the first aca­d­e­mic ef­forts in le­gal AI to unite re­search, pro­to­typ­ing, and real-time col­lab­o­ra­tion with in­dus­try. Its mis­sion is to in­crease ac­cess to high qual­ity le­gal ser­vices in the pri­vate sec­tor by lever­ag­ing AI and other fron­tier tech­nolo­gies. To bridge the gap be­tween the­ory and prac­tice, lift­lab’s work ex­tends be­yond con­cep­tu­al­iza­tion and en­com­passes the build­ing of pro­to­types that help ex­plore the util­ity of AI-based so­lu­tions.

About Stanford Law School

Stanford Law School is one of the world’s lead­ing in­sti­tu­tions for le­gal schol­ar­ship and ed­u­ca­tion. Its alumni are among the most in­flu­en­tial de­ci­sion mak­ers in law, pol­i­tics, busi­ness, and high tech­nol­ogy. Faculty mem­bers ar­gue be­fore the Supreme Court, tes­tify be­fore Congress, pro­duce out­stand­ing le­gal schol­ar­ship and em­pir­i­cal analy­sis, and con­tribute reg­u­larly to the na­tion’s press as le­gal and pol­icy ex­perts. Stanford Law School has es­tab­lished a model for le­gal ed­u­ca­tion that pro­vides rig­or­ous in­ter­dis­ci­pli­nary train­ing, hands-on ex­pe­ri­ence, global per­spec­tive and a fo­cus on pub­lic ser­vice.

32GB of DDR5 now costs $375 minimum &mdash; AI shortage continues to squeeze PC building

www.tomshardware.com

As the de­mands of AI con­tinue to con­sume man­u­fac­tur­ing ca­pac­ity at every level of the PC hard­ware sup­ply chain, 32GB of DDR5 RAM — broadly un­der­stood to be the sweet spot for gam­ing PCs and en­thu­si­ast builds — can no longer be found for less than $375. Well, $374.97 to be pre­cise.

RAM price track­ing through 2026 will show you that kits that rou­tinely cost less than $100 just a year ago are now fetch­ing up­wards of $240 (16GB). As the AI frenzy has taken hold, re­tail­ers far and wide have been pump­ing up their RAM prices to ex­or­bi­tant lev­els. However, there’s so much fluc­tu­a­tion and noise that av­er­age pric­ing is now some­thing of a lu­di­crous fugazi. The go­ing rate for 32GB of DDR5 RAM — the cheap­est you can ex­pect to pay — has hov­ered around $320 for some time, climb­ing past $350 in re­cent weeks. Price track­ing cour­tesy of PCPartPicker now re­veals the cheap­est 32GB DDR5 RAM you can buy is $375. Specifically, four XPOWER kits from Silicon Power will set you back $374.97 thanks to a promo code. You can see the list­ings your­self be­low.

Silicon Power Zenith Gaming DDR5 6000MT/s (PC5 – 48000) CL36 32GB(2x16GB)

Silicon Power Zenith RGB DDR5 6000MT/s (PC5 – 48000) CL36 32GB(2x16GB)

Silicon Power Pulse Gaming DDR5 6000MT/s (PC5 – 48000) CL36 32GB(2x16GB)

Silicon Power Zenith RGB DDR5 6000MT/s (PC5 – 48000) CL36 32GB(2x16GB)

As you can imag­ine, this is enor­mous pric­ing pres­sure for en­thu­si­asts try­ing to build gam­ing PCs or up­grade their rigs in 2026. A com­po­nent that once cost less than $100 and was some­thing of an af­ter­thought now costs al­most four times as much, and that’s be­fore you’ve even fired a neu­ron in con­sid­er­a­tion of aes­thet­ics, tim­ings, or brand. More pop­u­lar kits from the likes of Corsair and Crucial, or RGB of­fer­ings to match the rest of your build, will eas­ily set you back more than $400.

Of course, 32GB is re­ally the min­i­mum sweet spot you should be aim­ing for when build­ing a PC in 2026. If you did want more ca­pac­ity, 64GB will set you back an as­ton­ish­ing $679.99. 16GB of RAM as a com­pro­mise can be found for $200 at B&H Photo, but with SK hynix warn­ing that man­u­fac­tur­ing con­straints will per­sist through 2030, there’s no sign of prices let­ting up so that you can up­grade ca­pac­ity any time soon.

The hum­ble RAM combo deals we’ve been high­light­ing in re­cent months are a small source of so­lace for builders, let­ting you score RAM for less than the $375 go­ing rate if you pair it with a de­cent moth­er­board, a proces­sor, or even an en­tire set of PC com­po­nents. A theme of on­go­ing Computex 2026 an­nounce­ments re­mains a lack of pric­ing clar­ity on lots of PC hard­ware, in­clud­ing Nvidia’s RTX Spark lap­tops and PCs, as well as new-build sys­tems and, of course, RAM com­po­nents them­selves. Vendors are likely wary of scar­ing off po­ten­tial buy­ers with higher-than-ex­pected prices ahead of re­lease. Perhaps more likely, the prices haven’t been set be­cause they’re still go­ing up. Storage is­n’t much bet­ter, with SSD price track­ing re­veal­ing that dri­ves which once cost as lit­tle as $38 are now fetch­ing $200.

AMD is mak­ing a no­tice­able ef­fort to keep PC gam­ing prices down, this week an­nounc­ing the re­turn of its Ryzen 7 5800X3D, and the ad­vent of a new Ryzen 7 7700X3D. Intel, which warned this week that something has to give” when it comes to mem­ory prices, also teased drag­ging out some of its legacy prod­ucts to give users more op­tions on older mem­ory tech­nolo­gies, namely Raptor Lake and DDR4.

Get Tom’s Hardware’s best news and in-depth re­views, straight to your in­box.

Stephen is Tom’s Hardware’s News Editor with al­most a decade of in­dus­try ex­pe­ri­ence cov­er­ing tech­nol­ogy, hav­ing worked at TechRadar, iMore, and even Apple over the years. He has cov­ered the world of con­sumer tech from nearly every an­gle, in­clud­ing sup­ply chain ru­mors, patents, and lit­i­ga­tion, and more. When he’s not at work, he loves read­ing about his­tory and play­ing video games.

Uber Caps Usage of AI Tools Like Claude Code to Manage Costs

simonwillison.net

3rd June 2026 - Link Blog

Uber Caps Usage of AI Tools Like Claude Code to Manage Costs. I wrote the other day about Uber blow­ing its 2026 AI bud­get in four months, and how that was­n’t par­tic­u­larly sur­pris­ing given they would have set that bud­get in 2025, be­fore any­one could have pre­dicted how pop­u­lar to­ken-burn­ing cod­ing agents were about to be­come.

Natalie Lung for Bloomberg:

The rideshare gi­ant is lim­it­ing all em­ploy­ees to $1,500 in monthly to­ken spend­ing per AI cod­ing tool, an Uber spokesper­son said in re­sponse to a Bloomberg News in­quiry. That means spend­ing on one tool does­n’t have a bear­ing on the bud­get for an­other. The lim­its, which have been in­sti­tuted in re­cent months, only ap­ply to agen­tic cod­ing soft­ware such as Cursor or Anthropic PBCs Claude Code.

The rideshare gi­ant is lim­it­ing all em­ploy­ees to $1,500 in monthly to­ken spend­ing per AI cod­ing tool, an Uber spokesper­son said in re­sponse to a Bloomberg News in­quiry. That means spend­ing on one tool does­n’t have a bear­ing on the bud­get for an­other. The lim­its, which have been in­sti­tuted in re­cent months, only ap­ply to agen­tic cod­ing soft­ware such as Cursor or Anthropic PBCs Claude Code.

A $1,500 monthly limit per tool strikes me as a ra­tio­nal pol­icy re­sponse to over-spend­ing, and much more sen­si­ble than those to­ken­maxxing leader­boards en­cour­ag­ing em­ploy­ees to com­pete for as much AI us­age as pos­si­ble.

It’s also in­ter­est­ing in that it hints at a real dol­lar value for what Uber is get­ting out of these tools. If we as­sume two ac­tively used tools per en­gi­neer that’s $3,000 * 12 = $36,000 cap per en­gi­neer per year. Levels.fyi lists the me­dian yearly com­pen­sa­tion pack­age for Uber soft­ware en­gi­neers in the USA at $330,000.

That means each em­ploy­ee’s AI spend­ing cap is ~11% of that me­dian com­pen­sa­tion pack­age.

I noted that my own to­ken us­age comes to about $1,000/month against each of Anthropic and OpenAI - which cur­rently costs me just $100 per provider thanks to their gen­er­ous sub­si­dized plans for in­di­vid­ual sub­scribers. Those plans are no longer avail­able to larger com­pa­nies like Uber.

Their new pol­icy means if I were work­ing at Uber I’d still have ~$500/month of to­kens to spare for each of those tools, given my cur­rent us­age pat­terns.

MacBook Neo is So Popular That Apple Reportedly Doubled Production

www.macrumors.com

On an earn­ings call in late April, Apple’s CEO Tim Cook said that cus­tomer re­sponse to the MacBook Neo was off the charts,” and the pop­u­lar­ity of the lap­top has re­port­edly led the com­pany to sig­nif­i­cantly boost pro­duc­tion.

Apple sup­ply chain an­a­lyst Ming-Chi Kuo this week said he be­lieves that MacBook Neo ship­ments to Apple were dou­bled from an ini­tial tar­get of 5 mil­lion units to 10 mil­lion units in 2026 at some point af­ter the lap­top launched in March.

Apple was very op­ti­mistic about the MacBook Neo be­fore an­nounc­ing it, but the com­pany still undercalled” the level of en­thu­si­asm that the lap­top would gen­er­ate, ac­cord­ing to Cook. He said that MacBook Neo de­mand ex­ceeded Apple’s ex­pec­ta­tions and helped to drive a record num­ber of first-time Mac buy­ers last quar­ter.

New fig­ures from mar­ket re­search firm IDC sup­port Apple’s claim that the MacBook Neo is sell­ing well, and the Windows PC in­dus­try has taken no­tice. For ex­am­ple, Dell re­cently in­tro­duced a re­designed XPS 13 lap­top from $699 and said it has fea­tures you won’t find on a MacBook Neo,” such as a touch screen and a back­lit key­board.

Apple’s MacBook Neo is a ca­pa­ble ma­chine, and its ar­rival con­firms that there’s real ap­petite for pre­mium qual­ity at ac­ces­si­ble prices,” ad­mit­ted Dell.

With a start­ing price of $599 in the U.S., or $499 for col­lege stu­dents, the MacBook Neo is Apple’s most af­ford­able MacBook ever. Powered by the iPhone’s A18 Pro chip, the lap­top is avail­able in col­or­ful fin­ishes like Citrus and Blush.

A sec­ond-gen­er­a­tion MacBook Neo is ex­pected to be re­leased next year with an A19 Pro chip and 12GB of RAM.

Popular Stories

OpenAI Fast-Tracking AI Phone for 2027 Launch, Says Kuo

OpenAI is said to be fast-track­ing de­vel­op­ment of its first AI agent phone,” with the com­pany now aim­ing to mass pro­duce the de­vice as early as the first half of next year, ac­cord­ing to in­dus­try an­a­lyst Ming-Chi Kuo. Late last month, Kuo re­vealed OpenAI’s work on a smart­phone, con­tra­dict­ing ear­lier re­ports that the com­pany had no plans to en­ter the mo­bile mar­ket. Kuo said MediaTek and…

Report: Intel is Testing Production of Some iPhone, iPad, and Mac Chips

TSMC has been the ex­clu­sive sup­plier of Apple’s sys­tems-on-a-chip since 2016, but that 10-year streak could be near­ing its end. Apple sup­ply chain an­a­lyst Ming-Chi Kuo to­day said that Intel has kicked off” small-scale test­ing of lower-end iPhone, iPad, and Mac chip fab­ri­ca­tion, with pro­duc­tion ex­pected to ramp up through­out 2027 and 2028. Kuo did not in­di­cate ex­actly which of Apple’s…

iPhone 18 Pro’s Camera Upgrade Will Cost Apple 50% More

The iPhone 18 Pro and iPhone 18 Pro Max’s all-new vari­able aper­ture lens will cost Apple 50% more than the cam­era unit used in cur­rent mod­els, ac­cord­ing to sup­ply chain an­a­lyst Ming-Chi Kuo. Variable aper­ture has been one of the most per­sis­tent iPhone cam­era ru­mors of the past few years. Kuo first flagged the fea­ture in late 2024, and it has since been cor­rob­o­rated by mul­ti­ple re­ports and…

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.