10 interesting stories served every morning and every evening.

I told them forced consent was unlawful. Five years later it cost Elkjop €1.8 million

www.thatprivacyguy.com

Back in the sum­mer of 2021 I was a mem­ber of the Elgiganten Kundklubb, the cus­tomer club the Elkjop group runs across the Nordics, and like a lot of mem­bers I was buried un­der mar­ket­ing emails. So I did the ob­vi­ous thing and went look­ing for a way to switch them off. What I found in­stead was the prob­lem that has taken five years to put right - the only way to stop the mar­ket­ing was to can­cel my mem­ber­ship of the club al­to­gether.

I wrote to their Data Protection Officer on 30th July and set out, in plain terms, why that arrange­ment breaks the law. Under Article 21(2) of the GDPR every per­son has an ab­solute right to ob­ject to di­rect mar­ket­ing. Under the ePri­vacy Directive, mar­ket­ing by email is only law­ful where I have given my con­sent, or where there is an ex­ist­ing cus­tomer re­la­tion­ship and I am of­fered a sim­ple way to opt out both at the point my de­tails are col­lected and in every mes­sage af­ter that. And con­sent, to be worth any­thing at all, has to be freely given - which un­der Article 4(11) and Article 7 means it can­not be bun­dled into, or made a con­di­tion of, some­thing else. Forcing me to sur­ren­der my mem­ber­ship and the ben­e­fits that come with it, just to ex­er­cise a right I al­ready hold, is the text­book ex­am­ple of con­sent that is not freely given.

They put the vi­o­la­tion in writ­ing

The re­ply I re­ceived a few days later did me the favour of putting the vi­o­la­tion on the record. Their po­si­tion, in their own words, was that in or­der to re­ceive mar­ket­ing / of­fers, it is a con­di­tion to be a mem­ber of the cus­tomer club.” That one sen­tence is the whole case. They had taken a right I am en­ti­tled to ex­er­cise for free and turned it into the price of ad­mis­sion.

So I es­ca­lated. I served a for­mal re­stric­tion of pro­cess­ing un­der Article 18, I sent a full sub­ject ac­cess re­quest un­der Article 15 - the le­gal ba­sis they were re­ly­ing on, the le­git­i­mate in­ter­est bal­anc­ing test, the re­cip­i­ents, the sub-proces­sors, the in­ter­na­tional trans­fers, the pro­fil­ing, all of it - and I filed a com­plaint with the Swedish su­per­vi­sory au­thor­ity, Integritetsskyddsmyndigheten (IMY), which is­sued the ref­er­ence DI-2021 – 6660. The com­pa­ny’s an­swer to all of this was to point me at a vague pri­vacy pol­icy, and then, when that did not wash, to stretch the dead­line on my ac­cess re­quest out to ninety days while cit­ing complexity” and limited in­ter­nal re­sources”.

How a Swedish com­plaint be­came a Norwegian fine

This is where the ma­chin­ery of the GDPR comes in. The cus­tomer club is run by the Norwegian par­ent, Elkjop Nordic AS, and on the facts it is the par­ent that holds the real de­ci­sion mak­ing power over the pur­poses and the means of the pro­cess­ing. So in September 2022 IMY de­cided it was not the right au­thor­ity to deal with this at all. Under the one-stop-shop in Article 56(1), the com­pe­tent reg­u­la­tor is the one for the con­troller’s main es­tab­lish­ment, and that es­tab­lish­ment sits in Norway. IMY handed the in­ves­ti­ga­tion and my com­plaint to Datatilsynet, the Norwegian DPA, which ac­cepted the case. And then, as these things tend to, it went quiet for a very long time.

On 1 June 2026 it stopped be­ing quiet. Datatilsynet fined the Elkjop group NOK 20 mil­lion, a lit­tle over €1.8 mil­lion, and it found pre­cisely what I had told them in 2021. The con­sent the com­pany was re­ly­ing on for its cus­tomer club was not valid - it was forced, it was not spe­cific, and mem­bers were not prop­erly in­formed. On top of that, the com­pany had taken the per­sonal data it gath­ered through the club and put it to fur­ther use for ad­ver­tis­ing and con­ver­sion track­ing, with­out ever car­ry­ing out the com­pat­i­bil­ity as­sess­ment that Article 6(4) de­mands be­fore you re­pur­pose peo­ple’s data like that. The de­ci­sion runs through Articles 4(11), 5(1)(a), 5(2), 6(1)(a), 6(1)(f) and 6(4) - the law­ful­ness, the fair­ness, the trans­parency and the ac­count­abil­ity of the en­tire arrange­ment.

I want to be clear about why this mat­ters well be­yond one re­tailer and one fine. Forced con­sent, pay-or-con­sent, bun­dled con­sent, the whole agree to every­thing or you can­not use the ser­vice” model - it is every­where, and it is the de­fault way an enor­mous part of the dig­i­tal econ­omy op­er­ates. It is also un­law­ful, for the same sim­ple rea­son every sin­gle time - if you can­not say no with­out los­ing some­thing you are en­ti­tled to keep, you have not freely con­sented to any­thing. Five years and a seven fig­ure fine later, that point is now sit­ting in a pub­lished de­ci­sion for any­one to read.

I had to read about it on a wiki

And yet there is a part of this story I am not will­ing to let slide, be­cause it is its own small scan­dal.

I did not find out about this de­ci­sion from IMY. I did not find out from Datatilsynet. I found out from GDPRhub, a vol­un­teer-run wiki, on a ran­dom Thursday morn­ing, nearly five years af­ter I filed my com­plaint and well af­ter the de­ci­sion had al­ready been made.

Under Article 77(2) of the GDPR a su­per­vi­sory au­thor­ity is un­der a bind­ing le­gal oblig­a­tion to keep a com­plainant in­formed of the progress and the out­come of their com­plaint. It is not a cour­tesy and it is not dis­cre­tionary - it is writ­ten into the law. I filed my com­plaint with IMY, IMY passed it on, the case ended in a multi-mil­lion euro en­force­ment ac­tion, and not one of the au­thor­i­ties in­volved thought to tell the per­son who started it.

So this morn­ing I wrote to IMY and asked them, in writ­ing, to ex­plain them­selves. I have given them five work­ing days. If the an­swer is what I sus­pect it will be, I will be fil­ing un­der the European Union’s in­fringe­ment pro­ce­dure, be­cause a su­per­vi­sory au­thor­ity that can­not meet its most ba­sic oblig­a­tion to the peo­ple it ex­ists to pro­tect is ex­actly the sort of thing the Commission is sup­posed to look at. I have walked the Commission down this road be­fore, over Phorm and the United Kingdom’s fail­ure to prop­erly im­ple­ment the EU rules on the con­fi­den­tial­ity of com­mu­ni­ca­tions, and I am en­tirely will­ing to do it again.

I have been say­ing for years that pri­vacy is per­sonal, and I mean it in the most lit­eral way I can. This was my club mem­ber­ship, my in­box, my data and my com­plaint. The law was on my side in 2021 and it is on my side now. The com­pany that told me to leave or put up with it has paid for that choice.

The only things still out­stand­ing are an ex­pla­na­tion from the Regulator that was meant to have my back the whole way through and civil lit­i­ga­tion against Elkjop group now that the reg­u­la­tory process has run its course - a lit­i­ga­tion that is go­ing to be so much more ex­ten­sive now we have fur­ther de­tails of fur­ther il­le­gal pro­cess­ing of that per­sonal data.

If they had lis­tened to me in 2021, they would have avoided the fine, they would have made their pro­cess­ing law­ful, they would have avoided the brand dam­age and the re­sult­ing lit­i­ga­tion.

When I write to you as DPO with a com­plaint, it would be wise for you to take note. I am not a layper­son, I am an ex­pert on this law that I helped to cre­ate and I do not stop just be­cause these ac­tions are in­con­ve­nient, it is my life’s work. Pay at­ten­tion, when I write to you I am giv­ing you free ad­vice and you should treat as such in­stead of get­ting de­fen­sive and re­fus­ing to change.

IN THE WEIGHTS

www.intheweights.com

The founder of Craigslist has given away half a billion dollars. He fears for an America where generosity is trolled

www.independent.co.uk

Craig Newmark, mul­ti­mil­lion­aire founder of Craigslist, has long had trou­ble keep­ing his mouth shut — lead­ing to some influential mis­takes”, he read­ily ad­mits.

But he does­n’t con­sider it a lapse in judge­ment that he’s given away half a bil­lion dol­lars to char­ity since found­ing the clas­si­fied ads site 30 years ago — nor voic­ing his hope that oth­ers with vast for­tunes will take a sim­i­lar tack.

There has been a shift away from phil­an­thropy to­ward hard-edged in­di­vid­u­al­ism and os­ten­ta­tious dis­plays of wealth in America in re­cent years, even in the high­est of­fice. President Donald Trump has in­creased his net worth from $4.3 bil­lion to $7.3 bil­lion dur­ing his sec­ond term, plans to spend $600 mil­lion on the White House ball­room and is gild­ing the cap­i­tal at every turn.

Billionaire ven­ture cap­i­tal­ist Peter Thiel told The New York Times ear­lier this year that he had been en­cour­ag­ing wealthy peers to undo com­mit­ments to The Giving Pledge, a long­stand­ing phil­an­thropic cam­paign that en­cour­ages the ul­tra-rich to give away vast sums dur­ing their life­time to causes of their choos­ing. Thiel claimed con­tri­bu­tions would go to left-wing” non­prof­its, ac­cord­ing to an au­dio tran­script pro­vided to Reuters, and dubbed it an Epstein-adjacent fake Boomer club.”

Newmark signed The Giving Pledge last year and re­cently wrote aNew York Times op-ed on how he was dumb­founded by Thiel and some other bil­lion­aires’ po­si­tions.

When I started Craigslist in the mid-1990s, I never thought I’d be­come rich. But I did. A lot of peo­ple in tech around that time also got lucky. Millions — even bil­lions — were made sim­ply by be­ing in the right place at the right time,” he wrote. That’s too much money for any­one to have, so I’m giv­ing most of it away to peo­ple and causes that need it. It makes no sense to me that oth­ers with this kind of money would crit­i­cize any­one do­ing this.”

Newmark, 74, told The Independent that he does­n’t judge other wealthy peo­ple who don’t want to give their money away but nev­er­the­less finds their de­ci­sions hard to fathom.

Everyone has to make their own moral de­ci­sions,” he said. There are some highly vis­i­ble, su­per-rich peo­ple who’ve made their own de­ci­sions, and it’s their right to make those de­ci­sions. I just don’t re­ally un­der­stand.”

His own fi­nan­cial de­ci­sions are rooted in a class­room at the Jewish Community Center in Morristown, New Jersey. It’s there that six-year-old Newmark at­tended the Sunday school of Holocaust sur­vivors Rafael and Rachel Levin in the late Fifties, and never for­got the les­son that kind­ness was more im­por­tant than riches.

They told me that I should treat peo­ple like I want to be treated,” he said. I should know when enough is enough. And they told me I should be my broth­er’s keeper or my sis­ter’s keeper. And that made sense to me.”

The sen­ti­ment re­mained with him early in his ca­reer when he was a soft­ware en­gi­neer at Bank of America and Charles Schwab, and even af­ter he launched his un­ex­pect­edly pop­u­lar clas­si­fieds site Craigslist in 1996.

The site started as a weekly email de­tail­ing up­com­ing events that Newmark would send to friends while liv­ing in San Francisco. Over time, the email trans­formed into a clas­si­fieds site with list­ings for every­thing from cars and free couches to jobs and events.

In 1999, Craigslist took off and Newmark re­al­ized he had to turn it into a real” com­pany. But he did­n’t know how to man­age the grow­ing site with a team mainly made up of vol­un­teers, and he needed paid staff, such as pro­gram­mers and cus­tomer ser­vice. Newmark had a choice — get cozy with ven­ture cap­i­tal­ists and bankers for fund­ing, change the busi­ness model and get rich, or do it his way.

The VCs and bankers I met at in­dus­try events said they wanted to throw huge amounts of money at me — bil­lions,” he said. But I’d have to do the usual Silicon Valley thing and they would then mon­e­tize the site thor­oughly, and it would be­come a very dif­fer­ent place.”

Rather than suc­cumb to a model that likely would have charged peo­ple for list­ings, Newmark chose to charge busi­nesses for their posts but kept Craigslist free for the av­er­age user.

Craigslist grew into an amaz­ing suc­cess story, loved for its sim­ple de­sign and quirky list­ings, and re­mains among the top-500 most vis­ited web­sites in the world. More than 100 mil­lion users vis­ited Craiglist in April, ac­cord­ing to web an­a­lyt­ics firm Semrush.

While Newmark said he was never a bil­lion­aire, his wealth could have bought a fleet of lux­ury ve­hi­cles, su­pery­acht and homes on each con­ti­nent. But aside from an oc­ca­sional lav­ish meal, he says the trap­pings of ex­treme wealth held lit­tle pull for him. He does­n’t own a car and takes pub­lic trans­porta­tion in New York City.

His typ­i­cal day con­sists of work meet­ings, cof­fee with friends, feed­ing a pair of neigh­bor­hood pi­geons he’s named Mr. and Mrs. Hatbird, read­ing sci­ence fic­tion and watch­ing TV at night with his wife of 14 years, Eileen, who runs an apart­ment co-op in New York City.

I re­al­ized I [didn’t] have to make much money to be happy,” Newmark said. As a nerd, I was obliv­i­ous to the so­cial cues peo­ple use to in­di­cate what they’re about. had no in­ter­est in pres­tige or sta­tus items. Even now, I’m not com­fort­able with pres­tige or sta­tus.”

In 2015, he started Craig Newmark Philanthropies. Through the or­ga­ni­za­tion, Newmark do­nates to causes in ar­eas that in­ter­est him, such as cy­ber­se­cu­rity, jour­nal­ism, mil­i­tary fam­i­lies and vet­er­ans, and even pi­geon res­cue.

His work with or­ga­ni­za­tions like Blue Star Families, which helps mil­i­tary fam­i­lies find sup­port and build re­la­tion­ships wher­ever they’re sta­tioned, is a way to honor his dad, who served in the Army in Second World War and died from can­cer when Newmark was 13.

Newmark also joined The Giving Pledge, started by Microsoft bil­lion­aire Warren Buffet and Bill and Melinda Gates in 1996, to en­cour­age the world’s rich­est peo­ple to give away at least half of their for­tunes dur­ing their life­time. Members of the pledge in­clude OpenAI founder Sam Altman and hus­band Oliver Mulherin, phil­an­thropist MacKenzie Scott, and LinkedIn co-founder Reid Hoffman and wife Michelle Yee.

At first, Newmark could­n’t sign the pledge be­cause he was­n’t a bil­lion­aire, he said, but the ini­tia­tive sub­se­quently ex­panded its mem­ber­ship and he joined in 2025. He de­scribed it as a sur­real mo­ment, be­cause he never set out to be rich, but he was stand­ing up for regular peo­ple who want to share,” he said.

The Giving Pledge matched my val­ues,” Newmark said. I could make a state­ment putting my money where my mouth is, and that would be a good thing.“

Altruistic as the ini­tia­tive may seem, it’s re­ceived a fair amount of crit­i­cism over the past few years. Coinbase co­founder Brian Armstrong took the un­prece­dented step of with­draw­ing his pledge, ac­cord­ing to Forbes, while Oracle co­founder Larry Ellison had amended his pledge to in­clude for-profit work.

Thiel, in par­tic­u­lar, has openly dis­cour­aged peo­ple from sign­ing the pledge and has pushed par­tic­i­pants to leave it, ac­cord­ing to The New York Times. Thiel claimed in au­dio tran­scripts pro­vided to Reuters in March that he told Elon Musk, the world’s rich­est man, not to sign the pledge be­cause his con­tri­bu­tions would go to left-wing” non­prof­its of Bill Gates’ choos­ing.

Thiel’s claim con­tra­dicts the The Pledge’s poli­cies, which state that donors choose where the money goes and they pur­sue their phil­an­thropy in­de­pen­dently.”

Newmark ex­pressed con­cern about the back­lash in a opin­ion piece for The Times a few weeks later.

It’s bizarre to me that the pledge has now come un­der at­tack by some tech bil­lion­aires who say we’re giv­ing our money away fool­ishly, or com­plain that the money is go­ing to left-wing non­prof­its. I don’t un­der­stand the crit­ics’ logic, but pol­i­tics and that sort of crit­i­cism have never re­ally made much sense to me,” he wrote.

The piece touched a nerve with peo­ple across so­cial me­dia. Always grate­ful for your moral clar­ity, as ever,” LinkedIn user Anil Dash wrote in re­sponse to Newmark’s op-ed. There’s some­thing pro­found in just telling the truth and re­mind­ing peo­ple that do­ing the right thing, the good thing, can be *obvious*.”

Newmark says he was­n’t aware of the piece’s im­pact as he has no idea how much in­flu­ence I have.”

He added: Friends tell me that I have more than I know. But for me to see and re­al­ize the ef­fect re­quires the so­cial skills that I lack.”

His phil­an­thropy has been at the end of a long road, he said. Looking back on his pre-Craigslist days, he said he could be a real jerk” at work.

I would … cor­rect peo­ple in front of other peo­ple when there is no point in do­ing so,” he said. And that’s stu­pid. But I did­n’t know it un­til years af­ter.”

Newmark says he started to change dur­ing his years at Charles Schwab and when he han­dled cus­tomer ser­vice queries in his early days at Craigslist.

Doing cus­tomer ser­vice at Craigslist — re­ally in­tense, com­mit­ted cus­tomer ser­vice — helped me un­der­stand that, yeah, I needed to lis­ten to peo­ple bet­ter,” he said. I needed to treat them like I wanted to be treated.”

He ad­mit­ted that he still does­n’t care for small talk, com­par­ing him­self to Jackson Lamb, a fic­tional MI5 in­tel­li­gence of­fi­cer in the Slow Horses nov­els and TV se­ries known for his dis­taste of sur­face-level con­ver­sa­tion.

And he’s tak­ing his mes­sage of giv­ing back on the road. In May, he par­tic­i­pated in award-win­ning jour­nal­ist Tina Brown’s Truth Tellers Summit, an event that pushes back on the lies and mis­in­for­ma­tion that threaten pub­lic trust. He of­ten makes ap­pear­ances as a con­fer­ence speaker and guest on pod­casts.

All I know is to move for­ward,” Newmark said. I mean, I am an old guy. Limited time. I have to fig­ure out how to best use my re­sources, mean­ing time left plus money.”

Project Valhalla, Explained: How a Decade of Work Arrives in JDK 28 - JVM Weekly vol. 180

www.jvm-weekly.com

On June 15, Oracle en­gi­neer Lois Foltan con­firmed what a good chunk of the in­dus­try had stopped be­liev­ing: JEP 401: Value Classes and Objects will be in­te­grated into the main OpenJDK repos­i­tory and is tar­get­ing JDK 28.

The change is so large that the re­main­ing com­mit­ters were asked to hold off on big­ger com­mits dur­ing the in­te­gra­tion. The pull re­quest alone adds over 197 thou­sand lines of code across 1,816 files.

Before we pop the cham­pagne, though: this is pre­view, dis­abled by de­fault, and, as Brian Goetz was quick to cool every­one down, only the first part of Valhalla.” Goetz added a great ob­ser­va­tion that the they’ll never ship it” crowd will now smoothly switch over to but they did­n’t ship the most im­por­tant part” (and a joke has been go­ing around the com­mu­nity for years that we’ll sooner end up in Valhalla our­selves, the Norse-afterlife one, than the pro­ject ships).

You have to earn your own haters.

So this is a good mo­ment to tell the whole story. This is­sue is one big deep-dive, writ­ten on the as­sump­tion that you’ve never fol­lowed the work on Valhalla be­fore: from the 2014 prob­lem, through the evo­lu­tion of ideas (a fair num­ber of which ended up in the trash), all the way to what ex­actly we’ll be get­ting our hands on in JDK 28. Brew your­self a cof­fee. I’ve been sit­ting on this edi­tion for a long time, sav­ing it for ex­actly this oc­ca­sion.

The slo­gan Valhalla has car­ried from the start is: codes like a class, works like an int.” In a sin­gle sen­tence it cap­tures the whole point of the pro­ject: we want to write nor­mal, read­able classes with meth­ods, con­struc­tor val­i­da­tion, and sen­si­ble field names, but we want the JVM to be able to treat them as ef­fi­ciently as prim­i­tives.

To un­der­stand why this is a prob­lem, you have to go back to Java’s foun­da­tion. In this lan­guage, with the ex­cep­tion of the eight prim­i­tives (int, long, dou­ble, boolean, and the rest), every­thing is a ref­er­ence type. When you write Point p = new Point(1, 2), the vari­able p is­n’t a point. The vari­able p is a pointer, a coat-check num­ber: some­where on the heap sits an ob­ject, and you’re hold­ing a slip of pa­per with its ad­dress. Every time you want to read a field, the JVM has to go to the coat check,” per­form­ing a hop through the pointer (pointer in­di­rec­tion).

For a sin­gle ob­ject, that’s noth­ing. The prob­lem starts at scale. Every ob­ject on the heap has its own header (a dozen-or-so bytes of meta­data: among other things, so the JVM knows what type it is and whether any­one is syn­chro­niz­ing on it). Incidentally, this is ex­actly the prob­lem Project Lilliput has been tack­ling lately, help­ing to shrink ob­ject header sizes. But header size is­n’t every­thing. Every ob­ject has to be al­lo­cated, and later garbage col­lected. And since ob­jects are scat­tered across the heap, an ar­ray of a mil­lion Points is in prac­tice a mil­lion slips of pa­per point­ing at a mil­lion boxes strewn across the whole ware­house.

Brian Goetz, in his State of Valhalla” doc­u­ments, calls such a mem­ory lay­out fluffy”: puffed up, bloated. What we dream of is a dense lay­out, one where the data lies side by side.

Why does den­sity mat­ter? Because the hard­ware changed faster than Java did. In 1995, a mem­ory ac­cess cost roughly the same as a CPU op­er­a­tion. Today the CPU is two or­ders of mag­ni­tude faster than main mem­ory, and the whole gap is bridged by the cache. The proces­sor reads mem­ory in chunks called cache lines (usually 64 bytes). If the data lies densely and in or­der, one such chunk brings in a ton of use­ful val­ues at once. If we’re hop­ping across point­ers, every ac­cess risks a cache miss, and that can be a hun­dred times slower than a hit. This is lo­cal­ity of ref­er­ence, and it’s the real stake in this whole game.

But the JVM has es­cape analy­sis,” some­one sharp will say. True: the vir­tual ma­chine can rec­og­nize that some ob­ject never escapes” be­yond a lo­cal frag­ment of code, and then it does­n’t al­lo­cate it at all. From the pro­gram­mer’s point of view it looks as if the ob­ject ex­ists, but in re­al­ity its fields get spread out into or­di­nary vari­ables or CPU reg­is­ters. In the best case, the cost of al­lo­ca­tion and the later cleanup by the garbage col­lec­tor drops to prac­ti­cally zero.

The trou­ble is that this op­ti­miza­tion is un­pre­dictable and frag­ile. It works only when the JIT com­piler can trace the ob­jec­t’s en­tire flow with high con­fi­dence. But all it takes is for the ob­ject to land in a field of an­other class, get stored in an ar­ray, get passed into a more com­plex method, or ap­pear be­yond the bound­ary of code the JIT can an­a­lyze, and the whole trick stops work­ing. The source code stays iden­ti­cal, but the per­for­mance be­hav­ior can change dra­mat­i­cally.

This is pre­cisely why ex­pe­ri­enced JVM pro­gram­mers treat es­cape analy­sis as a nice bonus, not a pro­jec­t’s foun­da­tion. If an ap­pli­ca­tion’s per­for­mance de­pends on whether a par­tic­u­lar JIT ver­sion man­ages to ap­ply this op­ti­miza­tion, it’s very easy to fall into the trap of hard-to-pre­dict re­gres­sions. A mi­nor refac­tor, a JDK up­date, or a change in code struc­ture can send ob­jects back onto the heap, and the costs of al­lo­ca­tion and garbage-col­lec­tor work re­turn in full force.

That leaves the brute-force op­tion: give up on ob­jects and en­code the data by hand. Instead of a Color class, hold three bytes r, g, b. This is­n’t just an aca­d­e­mic ex­am­ple. The ap­proach has been used for years in game en­gines, graph­ics li­braries, im­age-pro­cess­ing sys­tems, data­bases, an­a­lyt­ics en­gines, and HPC code, where every byte of mem­ory and every al­lo­ca­tion mat­ters. The trou­ble is that the speed comes at the cost of safety and read­abil­ity. We lose names, pri­vate state, val­i­da­tion, and meth­ods. JEP 401 gives a sim­ple ex­am­ple: a de­vel­oper work­ing on raw” color bytes might mis­tak­enly in­ter­pret them as BGR in­stead of RGB, swap red with blue, and qui­etly cor­rupt the en­tire im­age. A class would­n’t have al­lowed it. A bare int? Sure it would.

And it’s ex­actly this di­chotomy, ei­ther con­ve­nient classes, or fast prim­i­tives, that Valhalla is try­ing to erase.

Officially, Project Valhalla started in 2014. James Gosling de­scribed it at the time as six PhDs tied into a sin­gle knot,” and that was no ex­ag­ger­a­tion. Interestingly, the idea is older than the pro­ject it­self: Java’s cre­ators wanted value types as early as the first ver­sion of the lan­guage, but in 1995 they gave up, be­cause the prob­lem was too hard.

The goal was set am­bi­tiously: to re­store align­ment be­tween the pro­gram­ming model and the per­for­mance char­ac­ter­is­tics of mod­ern hard­ware. In other words, to let pro­gram­mers de­clare their own types that are flat and dense in mem­ory like prim­i­tives, but look and be­have like nor­mal classes.

Easier said than done. Over the fol­low­ing years the team built five dif­fer­ent pro­to­types, each prob­ing a dif­fer­ent as­pect of the prob­lem. And this is where the most in­ter­est­ing part of the story be­gins, be­cause to ap­pre­ci­ate Valhalla’s cur­rent shape, you have to see how many ideas died along the way.

The early pro­to­types went in a di­rec­tion we now call Q World.” It as­sumed that the new value types were a fun­da­men­tally dif­fer­ent beast from ob­jects, with sep­a­rate type de­scrip­tors, sep­a­rate byte­codes, and sep­a­rate top types, ex­actly like prim­i­tives. Sounds log­i­cal: if they’re sup­posed to work like int, let them be rep­re­sented like int. The trou­ble is that such a sep­a­ra­tion flooded the en­tire JVM type sys­tem with ex­tra com­plex­ity: every­thing had to be done in two vari­ants.

The break­through came with a pro­to­type chris­tened L World” (roughly around 2019). The name comes from the fact that value types started shar­ing the same L car­rier” (the L de­scrip­tor, the same one the JVM uses for or­di­nary ref­er­ences) with ob­ject ref­er­ences. The team ex­pected such a uni­fi­ca­tion to be too hard, and yet, to their own sur­prise, it worked with­out ma­jor com­pro­mises and in­ci­den­tally solved a whole pile of prob­lems from the ear­lier rounds.

L World pro­duced one more fun­da­men­tal aha” that shaped every­thing that came af­ter: the lan­guage model and the JVM model don’t have to over­lap one hun­dred per­cent. L World is the right model for the vir­tual ma­chine, but you can treat it as a trans­la­tion tar­get and of­fer the pro­gram­mer some­thing more con­ve­nient in the lan­guage. This sep­a­ra­tion of lay­ers turned out to be the key to the rest of the pro­ject.

That’s also when the plan to split the work into two phases crys­tal­lized: first value classes (still called some­thing else at the time, more on that shortly), and only then, spe­cial­ized gener­ics. We’ll come back to gener­ics in sec­tion 6, be­cause that’s a sep­a­rate, longer trea­tise.

If you’ve ever tried to read about Valhalla and bounced off a wall of con­tra­dic­tory terms, it’s not your fault. The nam­ing changed sev­eral times here, and not cos­met­i­cally: be­hind each name change stood a change in the model. Let’s trace it, be­cause it’s the best il­lus­tra­tion of how this fea­ture was de­signed.

Stage 1: value types: The ear­li­est term. Vague, be­cause it was­n’t yet clear what ex­actly these things were sup­posed to be.

Stage 2: in­line classes: Around 2019 – 2020 a dis­tinc­tion set­tled in that has sur­vived to this day in its essence: classes split into iden­tity classes (the ones with iden­tity, that is, every­thing we’ve known un­til now) and the new in­line classes (without iden­tity). That’s when the slo­gan codes like a class, works like an int” was coined, and the ba­sic con­straints were set: in­line classes are fi­nal by de­fault, their fields are fi­nal, you can’t syn­chro­nize on them.

Stage 3: primitive classes” and the two-pro­jec­tion model. And here it gets in­ter­est­ing, be­cause this is ex­actly the idea that got sig­nif­i­cantly cut down. In the 2021 State of Valhalla” doc­u­ments, Valhalla promised three things: value ob­jects, prim­i­tive classes, and spe­cial­ized gener­ics. The idea for a primitive class” was that a sin­gle type would have two pro­jec­tions: a value vari­ant (flat, never null, be­hav­ing like a prim­i­tive) and a ref­er­ence vari­ant (a box that al­lows null). Across var­i­ous it­er­a­tions this was writ­ten as Point.val/Point.ref, and later they ex­per­i­mented with the Point! and Point? syn­tax.

The model was pow­er­ful, but also men­tally heavy. A pro­gram­mer would have to jug­gle two forms of the same type day to day and un­der­stand when a con­ver­sion be­tween them hap­pens. The team, faith­ful to the les­son simplify the model for the user, even at the cost of the per­for­mance ceil­ing,” ul­ti­mately dis­man­tled this du­al­ism.

Stage 4 (today): value classes” and value ob­jects.” The cur­rent JEP 401, au­thored by Dan Smith (reviewer: Brian Goetz), puts it sim­ply. There’s one new thing: a value class, de­clared with the value mod­i­fier. Its in­stances are value ob­jects: ob­jects with­out iden­tity. And (this is key) a value class is still a ref­er­ence type. The whole tricky busi­ness of non-nul­la­bil­ity has been split off into a sep­a­rate, op­tional JEP (Null-Restricted Value Class Types), which we’ll get to. So in­stead of one com­pli­cated con­cept we have two sim­ple, or­thog­o­nal ones: does it have iden­tity?” and, sep­a­rately, for later, does it al­low null?”

Worth re­mem­ber­ing, be­cause if you come across an older ar­ti­cle (or Baeldung de­scrib­ing primitive classes” as a sep­a­rate mech­a­nism), you’re read­ing about an out­dated model. In the OpenJDK canon, primitive classes” in that sense no longer ex­ist.

More things fell along the way. The orig­i­nal Value Objects” JEP draft was with­drawn and re­placed by JEP 401. The orig­i­nal Universal Generics” draft also went back for re­work. JEP 401 is ac­com­pa­nied by JEP 402: Enhanced Primitive Boxing (also pre­view), plus a whole se­ries of early-ac­cess builds (LW1, LW2, LW3…) and talks from the JVM Language Summit, among them Frédéric Parain on heap flat­ten­ing and Daniel Smith on the new ob­ject-ini­tial­iza­tion model.

The moral of this sec­tion is this: twelve years was­n’t twelve years of writing code.” It was twelve years of re­ject­ing ideas, un­til the one that can ac­tu­ally be main­tained was left.

Let’s get to specifics. Here’s ex­actly what we get.

Declaration. You cre­ate a value class by adding the value mod­i­fier:

value class USDCurrency im­ple­ments Comparable<USDCurrency> { pri­vate int cents; // im­plic­itly fi­nal pub­lic USDCurrency(int dol­lars, int cents) { this.cents = dol­lars * 100 + cents; }

pub­lic USDCurrency plus(US­D­Cur­rency that) { re­turn new USDCurrency(0, this.cents + that.cents); }

// dol­lars(), cents(), com­pareTo(), toString()… }

It can also be a value record. The rules: all in­stance fields are im­plic­itly fi­nal, meth­ods may not be syn­chro­nized, the class is fi­nal by de­fault (or it can form a hi­er­ar­chy com­posed of value classes and ab­stract value classes), it can’t in­herit from a class with iden­tity, but it hap­pily im­ple­ments in­ter­faces. Beyond these con­straints, it’s an or­di­nary class.

The defin­ing trait: no iden­tity. This is the crux. An or­di­nary ob­ject has iden­tity: two sep­a­rately cre­ated new Point(1,2) are two dif­fer­ent ob­jects, even if they have iden­ti­cal con­tents. A value ob­ject has no iden­tity, just as there aren’t two different” fours of type int. From this flow all the con­se­quences:

== changes mean­ing. Until now == com­pared iden­tity (whether it’s the same ad­dress). For value ob­jects, == checks sub­sti­tutabil­ity: whether both val­ues are the same class with the same fields, com­pared re­cur­sively (primitive fields bit by bit, ob­ject fields again via ==). That’s why new USDCurrency(3,95) == new USDCurrency(3,95) re­turns true. That’s good news: it ends the fa­mous con­fu­sion with == on Integers. But care­ful: == looks at in­ter­nal state, which is­n’t al­ways what the ob­ject rep­re­sents, so for is this the same data” com­par­isons keep us­ing equals.

== changes mean­ing. Until now == com­pared iden­tity (whether it’s the same ad­dress). For value ob­jects, == checks sub­sti­tutabil­ity: whether both val­ues are the same class with the same fields, com­pared re­cur­sively (primitive fields bit by bit, ob­ject fields again via ==). That’s why new USDCurrency(3,95) == new USDCurrency(3,95) re­turns true. That’s good news: it ends the fa­mous con­fu­sion with == on Integers. But care­ful: == looks at in­ter­nal state, which is­n’t al­ways what the ob­ject rep­re­sents, so for is this the same data” com­par­isons keep us­ing equals.

syn­chro­nized throws. There’s noth­ing to syn­chro­nize on. An at­tempt ends in IdentityException. When you need to force iden­tity, you have the new helpers Objects.requireIdentity and Objects.hasIdentity.

syn­chro­nized throws. There’s noth­ing to syn­chro­nize on. An at­tempt ends in IdentityException. When you need to force iden­tity, you have the new helpers Objects.requireIdentity and Objects.hasIdentity.

And now the most im­por­tant con­cep­tual trap: value ob­jects can STILL be null. This sur­prises every­one who thinks value = like a prim­i­tive = never null.” In the JDK 28 model, value class is a ref­er­ence type, so USDCurrency d = null; is per­fectly le­gal. Non-nullable types (with a null re­stric­tion) are a sep­a­rate, fu­ture JEP. They’re not in JDK 28. We’ll come back to this, be­cause it’s not a de­tail: it’s the key to full per­for­mance.

JEP 401 gives the JVM free­dom thanks to which value ob­jects can be op­ti­mized in two main ways.

Scalarization is a JIT com­piler tech­nique. A ref­er­ence to a value ob­ject gets broken down into its prime fac­tors,” re­duced to its essence, the set of fields, with no wrap­ping. Instead of pass­ing a pointer to Color, the JIT sim­ply passes three bytes r, g, b (plus one flag bit in­di­cat­ing whether the ref­er­ence is­n’t null). Such an ob­ject is in prac­tice free: no al­lo­ca­tion, no work for the GC. It’s a bit like es­cape analy­sis, but much more pre­dictable and far-reach­ing: it works even across the bound­aries of method calls the JIT did­n’t in­line. The lim­i­ta­tion: scalar­iza­tion usu­ally won’t work when a vari­able has a type that is a su­per­type of the value class (e.g. Object or, im­por­tantly, an erased generic pa­ra­me­ter). Then the ob­ject has to be ma­te­ri­al­ized on the heap.

Heap flat­ten­ing is the sec­ond mech­a­nism. The ob­jec­t’s essence gets en­coded as a com­pact bit vec­tor and writ­ten di­rectly into a field or an ar­ray cell, with­out a pointer to an­other place in mem­ory. This is ex­actly where den­sity and lo­cal­ity are born.

There’s a catch worth know­ing about here, though: flat­tened data has to be read­able and writable atom­i­cally (otherwise it risks tearing” un­der con­cur­rent ac­cess). On typ­i­cal plat­forms, small enough” to­day means as lit­tle as 64 bits, in­clud­ing the null flag. That’s why many small value classes will flat­ten beau­ti­fully, but a class with, say, two int fields or one dou­ble may not fit in an atomic write and end up as an or­di­nary ob­ject on the heap any­way. In the fu­ture, 128-bit en­cod­ings will ar­rive, and the afore­men­tioned JEP about null-re­stricted types will al­low flat­ten­ing larger classes in ex­change for giv­ing up the atom­ic­ity guar­an­tee. This is ex­actly the mo­ment where non-nul­la­bil­ity stops be­ing cos­met­ics and be­comes a lever for per­for­mance.

Remember the age-old cost of box­ing, wrap­ping int in Integer? In the new model, the wrap­per classes them­selves be­come value classes (when pre­view is on, Integer, Long, Double, and com­pany lose their iden­tity). Since the box no longer has iden­tity, the JVM can scalar­ize and flat­ten it. The ef­fect: Integer[] starts ap­proach­ing the ef­fi­ciency of int[], and the box­ing over­head, to quote JEP 401, shrinks dra­mat­i­cally. The ac­com­pa­ny­ing JEP 402 (Enhanced Primitive Boxing) goes fur­ther and smooths out con­ver­sions be­tween prim­i­tives and their boxes, open­ing the road to writ­ing things like List<int>. But that’s a sep­a­rate, still-ma­tur­ing piece, so don’t as­sume it’ll roll in com­plete along­side 401.

This is where the ef­fect shows best. Instead of hold­ing a mil­lion point­ers to a mil­lion scat­tered ob­jects, a Color[] ar­ray can store di­rectly flat­tened, 32-bit en­cod­ings of suc­ces­sive col­ors (again: plus a null flag). From a mem­ory stand­point, such an ar­ray starts to look and act like a plain int[]: a con­tigu­ous block of data the proces­sor sweeps through se­quen­tially, cache line by cache line.

For all of this to work, some re­ally deep foun­da­tions were moved: the new value mod­i­fier; strict con­struc­tion rules (all fields must be set be­fore any­thing gets to see the new ob­ject, in prac­tice be­fore the su­per() call, so that a mutation” of fi­nal fields can never be ob­served); the re­de­f­i­n­i­tion of == as a sub­sti­tutabil­ity test; adding a value-ob­ject check to the ref­er­ence-com­par­i­son byte­code (acmp); the scalar­iza­tion and flat­ten­ing ma­chin­ery; IdentityException; and the mi­gra­tion of ex­ist­ing value-based” classes. In short, this is­n’t syn­tac­tic sugar. It’s a re­build of an as­sump­tion that had been true in Java since 1995: that every ob­ject has iden­tity.

Let’s take the sim­plest pos­si­ble case and trace it so it’s clear even with­out know­ing the JVMs in­ter­nals.

Before Valhalla:

fi­nal class Point { // an or­di­nary class with iden­tity fi­nal int x; fi­nal int y; Point(int x, int y) { this.x = x; this.y = y; } }

Point[] points = new Point[1_000_000];

What’s hap­pen­ing here in mem­ory? The points ar­ray is a mil­lion point­ers. Each pointer leads to a sep­a­rate Point ob­ject ly­ing some­where on the heap. And each such ob­ject is not just its two ints (8 bytes), but also a header (another dozen-or-so bytes of meta­data). These ob­jects are scat­tered: the al­lo­ca­tor cre­ated them at dif­fer­ent mo­ments, in dif­fer­ent places. When you it­er­ate over the ar­ray and sum the co­or­di­nates, for each point the proces­sor has to: read the pointer from the ar­ray, jump to the in­di­cated ad­dress (risk of a cache miss), read the fields. A mil­lion times. This is ex­actly that fluffy” lay­out from sec­tion 1.

After Valhalla:

value class Point { // a value class with­out iden­tity fi­nal int x; fi­nal int y; Point(int x, int y) { this.x = x; this.y = y; } }

Point[] points = new Point[1_000_000];

The dif­fer­ence in the code is ex­actly one word: value. But the dif­fer­ence in mem­ory is fun­da­men­tal. The JVM can now store the val­ues them­selves in the ar­ray, laid out densely one af­ter an­other: 8 bytes per point (plus a pos­si­ble null flag), in a con­tigu­ous block. No head­ers per el­e­ment. No point­ers. No jump­ing around the heap.

[IMAGE: the same Point[] ar­ray in two vari­ants: before” (an ar­ray of ar­rows → scat­tered boxes with head­ers) and after” (a uni­form strip of num­ber pairs)]

When you now it­er­ate over the ar­ray, the proces­sor reads the data se­quen­tially. Each 64-byte cache line im­me­di­ately brings in sev­eral com­plete points. Summing a mil­lion co­or­di­nates run­ning at mem­ory-band­width speed, in­stead of chok­ing on misses. On data-in­ten­sive code that can be a dif­fer­ence of mul­ti­ples, not per­cent­ages.

And, most im­por­tant for main­tain­abil­ity, you did­n’t pay for it with ab­strac­tion. Point is still a class: it has a name, it has a con­struc­tor, it could have val­i­da­tion (if (x < 0) throw …), it could have meth­ods. You don’t have to, like be­fore, split points into two raw int[] xs and int[] ys ar­rays and pray you never mix up the in­dices. You got the den­sity of a prim­i­tive and the read­abil­ity of a class. That’s the whole of Project Valhalla in a sin­gle ex­am­ple.

This is the sec­ond half of Valhalla, and hon­estly the harder one. Let’s start with the source of the prob­lem.

Java im­ple­ments gener­ics through type era­sure. In prac­tice: List<String> and List<Integer> are, at run­time, the same or­di­nary List, and the type pa­ra­me­ter T is erased to Object. This of­ten gets mocked, but it’s worth know­ing it was a de­lib­er­ate, de­fen­si­ble de­ci­sion, not lazi­ness. Erasure gave Java grad­ual mi­gra­tion com­pat­i­bil­ity: you could take an ex­ist­ing, non-generic class and make it generic with­out break­ing a sin­gle ex­ist­ing source file or com­piled class, and clients could mi­grate right away, later, or never. In 2004, when Java al­ready had a huge code­base, the al­ter­na­tive (”here are gener­ics, but throw out all your li­braries”) would have been a ter­ri­ble deal. Today it would be even worse.

The trou­ble is that era­sure clashes with Valhalla ex­actly where we’d care most about per­for­mance. Since T erases to Object, a value ob­ject put into a List<Point> has to be ma­te­ri­al­ized as an or­di­nary ob­ject on the heap. In other words: your beau­ti­ful, flat­ten­able Point in a generic col­lec­tion loses its flat­ten­ing: the con­tainer holds ref­er­ences, not flat val­ues. All the den­sity you gained in Point[] evap­o­rates in ArrayList<Point>.

The re­pair plan is, like all of Valhalla, two-phase:

Phase 1: Universal Generics. This is a change at the lan­guage level: it lets type vari­ables also cover value types, that is, so you can even ex­press some­thing like ArrayList<Point> or List<int>. For now still through era­sure. The pro­gram­mer will feel it mainly as new com­piler warn­ings about null pol­lu­tion,” be­cause a field of type T starts out as null by de­fault, even if T is a value type. Addressing these warn­ings makes APIs specialization-ready.”

Phase 2: Specialized Generics. These are the fu­ture JVM ex­ten­sions that will gen­er­ate het­ero­ge­neous, spe­cial­ized class lay­outs for con­crete type ar­gu­ments (in the pro­jec­t’s jar­gon: species and type re­stric­tions). Only then will ArrayList<Point> re­ally be backed by flat mem­ory. This is the piece that’s still largely re­search work.

The con­se­quences for li­braries and frame­works are enor­mous, and that’s ex­actly why it’s hap­pen­ing grad­u­ally. Ultimately, col­lec­tions, streams, and en­tire APIs can be­come flat and al­lo­ca­tion-free over value types. But li­brary au­thors will have to ad­dress the new warn­ings and de­sign with spe­cial­iza­tion in mind. Let’s be hon­est: the orig­i­nal Universal Generics draft went through re­work, and the full re­ward from spe­cial­iza­tion is a mat­ter of fu­ture re­leases. JDK 28 does­n’t bring it.

Let’s gather this in one place, be­cause it’s easy to get lost be­tween it’s here al­ready!” and it’s not here yet.”

What got ac­cepted: JEP 401 (Value Classes and Objects) as a pre­view fea­ture, tar­get­ing JDK 28 (release in March 2027), with in­te­gra­tion into main­line planned for roughly July 2026. 197 thou­sand lines, 1,816 files, co­or­di­na­tion on Lois Foltan’s side, the re­quest to other com­mit­ters to hold off on large changes. Disabled by de­fault: to play with the syn­tax, you have to flip on –enable-preview.

What ac­tu­ally reaches users: the abil­ity to de­clare value class and value record; mi­gra­tion of ex­ist­ing value-based” classes in the JDK (among them the prim­i­tive wrap­pers like Integer) to value classes un­der pre­view; scalar­iza­tion and flat­ten­ing for qual­i­fy­ing classes; cheaper box­ing.

What can still evolve, and what’s NOT in 28: null-re­stricted types (non-nullable); full spe­cial­ized gener­ics; 128-bit en­cod­ings; a fully ma­ture JEP 402. And the syn­tax it­self, be­cause this is pre­view, and that’s ex­actly what’s ex­pected of it: that it can change from re­lease to re­lease in re­sponse to feed­back. Hence Goetz’s quote about only the first part.”

How it might af­fect the ecosys­tem: for high-per­for­mance Java (data, vec­tor com­pu­ta­tion, ML, gamedev, fi­nance, codecs) this is the path to dense data with­out giv­ing up ab­strac­tion, which is ex­actly what some of these do­mains have been wait­ing years for. Frameworks and li­braries will start mi­grat­ing their value-based classes. You’ll also have to watch out for a long tail of be­hav­ioral sur­prises around == and syn­chro­nized in code that (knowingly or not) re­lied on iden­tity. And one more thing worth keep­ing in mind when plan­ning: JDK 28 is not an LTS re­lease: the next LTS will prob­a­bly be JDK 29 in September 2027. So most com­pa­nies will meet a sta­bi­lized Valhalla only at the LTS, but it’s pre­cisely the pre­view in 28 that kicks off the real feed­back loop with ac­tual code. If you’re work­ing on some­thing that could ben­e­fit from this, now is the mo­ment to start ex­per­i­ment­ing and sub­mit­ting feed­back.

Why do I call this one of the biggest changes in the plat­for­m’s his­tory? Because Valhalla does­n’t bolt yet an­other fea­ture onto the lan­guage; it moves its deep­est as­sump­tion. Every ob­ject has iden­tity” had been true in Java since 1995; it’s the foun­da­tion every­thing else stood on. Letting the pro­gram­mer opt out of that as­sump­tion (choose which ob­jects need iden­tity and which don’t) is­n’t a refac­tor, it’s a shift of the foun­da­tion. And that’s ex­actly why it un­locks a whole decade of fur­ther work: uni­fy­ing prim­i­tives and ob­jects, spe­cial­iz­ing gener­ics, denser col­lec­tions, faster nu­mer­ics.

At the same time, and this is the hon­est ver­sion of the head­line, Valhalla rolls into JDK 28“ is a half-truth. It’s the first, pre­view step of a multi-stage roll­out. But it’s pre­cisely this team’s dis­ci­pline (simplify the model for the hu­man, do the hard per­for­mance things as op­tional) that’s the rea­son it took twelve years, and the rea­son it can be shipped at all now.

For us, as pro­gram­mers, one thing to take away mat­ters more than the syn­tax: in­ter­nal­ize the dis­tinc­tion iden­tity ver­sus value. The rest (==, flat­ten­ing, gener­ics) are con­se­quences of that one dis­tinc­tion. And the early-ac­cess builds are al­ready here: you can touch this on your own code be­fore your com­peti­tor does.

1. Is value class just a record? No, they’re two or­thog­o­nal de­ci­sions. record means I give up sep­a­rate in­ter­nal state” (content = com­po­nents). value means I give up iden­tity.” You can have any com­bi­na­tion: an or­di­nary class, a record, a value class, and a value record.

2. Can I com­pare value ob­jects with ==? Yes, but == now means some­thing dif­fer­ent: sub­sti­tutabil­ity, i.e. a com­par­i­son of all fields (recursively), not the ad­dress in mem­ory. For the ques­tion do they rep­re­sent the same data,” it’s still usu­ally bet­ter to use equals, be­cause == looks at in­ter­nal state, which is­n’t al­ways equal to the rep­re­sented state.

3. Can a value class be null? In the JDK 28 model, yes. value class is still a ref­er­ence type. Non-nullable types (with a null re­stric­tion) are a sep­a­rate, fu­ture JEP, and they’re the ones that will un­lock flat­ten­ing of larger value classes. They’re not in JDK 28.

4. Integer be­comes a value class, won’t that break my code? In most cases, no. Binaries still link, and the only new com­pi­la­tion er­rors are at­tempts to syn­chro­nize on such a type. The changes you might no­tice con­cern code that de­pends on iden­tity: == on Integers will start com­par­ing by value, and syn­chro­nized (someInteger) will stop work­ing. If you re­lied on ei­ther of those, it was frag­ile code any­way.

5. Will I get a fast, flat ArrayList<Point>? Not yet. Because of type era­sure, ob­jects in a generic col­lec­tion are ma­te­ri­al­ized on the heap. Flat generic col­lec­tions re­quire uni­ver­sal and spe­cial­ized gener­ics: that’s the fu­ture. In JDK 28, flat­ten­ing works di­rectly for fields and ar­rays of a value type, e.g. for Point[].

6. How is this dif­fer­ent from struct in C#? A struct in C# has iden­tity and mu­ta­tion, so the se­man­tics of copy­ing on as­sign­ment or pass­ing have to be pre­cisely de­fined, which gives a heav­ier model for the pro­gram­mer and less free­dom for the run­time. Value ob­jects in Valhalla have no iden­tity, and the way they’re laid out in mem­ory is left to the JVMs dis­cre­tion. A sim­pler model for the hu­man, more free­dom for the ma­chine.

7. Wasn’t es­cape analy­sis do­ing all of this al­ready? As I al­ready men­tioned, partly. Escape analy­sis can avoid al­lo­cat­ing an ob­ject when it proves the ob­ject does­n’t de­pend on iden­tity, but it’s un­pre­dictable and does­n’t help when the ob­ject lands in a field, in an ar­ray, or escapes” be­yond the op­ti­miza­tion’s reach. Scalarization of value ob­jects is pre­dictable and reaches much fur­ther, in­clud­ing across method-call bound­aries.

8. Do I have to rewrite code to ben­e­fit? For your own classes, it’s usu­ally enough to add the value mod­i­fier to those that rep­re­sent simple do­main val­ues” and don’t rely on iden­tity; the mi­gra­tion is mostly com­pat­i­ble. Some of the gains you’ll even get for free, be­cause it’s the JDK mi­grat­ing its own classes (like the prim­i­tive wrap­pers).

10. When will I see full Valhalla, with gener­ics, non-null types, and the whole rest? In fu­ture re­leases. The team ships it in­cre­men­tally: JDK 28 is the first pre­view of value classes. The full story (specialized gener­ics, null-re­stricted types, 128-bit en­cod­ings) will spread across many re­leases and will most likely sta­bi­lize only around the next LTS.

PS: You’ll find the early-ac­cess builds at jdk.java.net/​val­halla, and that’s prob­a­bly the best way to form your own opin­ion faster than I can write an­other is­sue on the sub­ject.

No posts

DuckDB Internals: Why is DuckDB Fast? (Part 1)

www.greybeam.ai

DuckDB has gone from a re­search pro­ject at CWI Amsterdam in 2019 to one of the most widely adopted data­bases of the past decade. The list of places it shows up is long: note­books, ETL pipelines, dash­boards, CI test run­ners, em­bed­ded an­a­lyt­ics in­side SaaS prod­ucts, even an iPhone run­ning TPC-H at scale fac­tor 100.

Companies have started build­ing real prod­ucts around it. MotherDuck is wrap­ping DuckDB into a cloud data ware­house. BI and data app plat­forms like Hex, Omni, and Evidence use it as an in-app ex­e­cu­tion en­gine and cache. Fivetran’s Managed Data Lake Service uses DuckDB in­side its data-lake writer for merg­ing and com­paction. Rill builds an open-source BI tool on top of it. We use it at Greybeam too, pow­er­ing mil­lions of queries for BI and an­a­lyt­ics work­loads.

What is DuckDB?

DuckDB is an in-process an­a­lyt­i­cal SQL data­base. Analytical means it’s op­ti­mized for the kind of queries that scan mil­lions of rows to fil­ter, ag­gre­gate, and join — not the kind that look up a sin­gle record by pri­mary key. In-process means there’s no server. You don’t con­nect to DuckDB; you load it as a li­brary in­side your pro­gram, the same way you’d load NumPy or Polars.

DuckDB has re­ceived wide­spread adop­tion be­cause it’s just so damn easy to use. It ships as a sin­gle bi­nary un­der 20 MB with no ex­ter­nal de­pen­den­cies. You in­stall it with pip in­stall duckdb, brew in­stall duckdb, or by link­ing lib­duckdb into a C++ pro­ject. It opens any di­rec­tory of Parquet, CSV, or JSON files like they were al­ready a SQL data­base.

DuckDB also hap­pens to be one of the fastest sin­gle-node an­a­lyt­i­cal en­gines avail­able, reg­u­larly hold­ing its own against en­tire clus­ters that cost mil­lions of dol­lars per year.

This is the first post in a three-part deep dive into DuckDB in­ter­nals. We’ll fol­low a query from the mo­ment it en­ters the en­gine to the mo­ment the re­sult is re­turned, and at each stage we’ll look at the de­sign choice that makes it fast.

DuckDB’s speed comes from a hand­ful de­sign choices:

In-process ex­e­cu­tion

Columnar, com­pressed stor­age with zonemaps

Vectorized ex­e­cu­tion

Morsel-driven par­al­lelism

Snapshot iso­la­tion with op­ti­mistic MVCC

And much more!

This post cov­ers the path from your SQL to the mo­ment the en­gine is ready to run the query, plus the stor­age layer the query will read from. By the end you’ll have a clear men­tal model of DuckDB’s setup work and stor­age lay­out. Query ex­e­cu­tion is cov­ered in Part 2 so make sure to sub­scribe!

Queries Run In-Process

You point DuckDB at a 6 GB Parquet file on your lap­top. The re­sults come back in un­der a sec­ond. No clus­ter, no setup, no mi­gra­tion, no CREATE TABLE. How does that work?

SELECT * FROM orders.parquet’;

Most an­a­lyt­i­cal data­bases are servers. Snowflake, Postgres, BigQuery, Redshift. You open a con­nec­tion, send SQL over TCP (a pro­to­col to send data over a net­work), and wait for re­sults to come back. Along the way, every record in the re­sult is se­ri­al­ized into a wire pro­to­col, trans­mit­ted across the net­work, and de­se­ri­al­ized on the other end.

Serializing and Deserializing

Inside a data­base, a query re­sult lives as typed val­ues at spe­cific mem­ory ad­dresses. A 64-bit in­te­ger here, a pointer to a string there. Those ad­dresses only ex­ist in that process. To send the re­sult to a client on an­other ma­chine, the data­base has to rewrite every value into an agreed byte for­mat (Postgres has its own, MySQL has an­other, with ODBC and JDBC as client-side APIs that dri­vers ex­pose on top) so it can be pushed through a TCP socket. The client then parses those bytes back into its own na­tive types. Every value may be touched mul­ti­ple times, once to en­code and once to de­code, and on a large re­sult set, that work of­ten takes longer than the query it­self.

DuckDB is not a server. It’s a li­brary. There is no DuckDB dae­mon, no port, no clus­ter. You load lib­duckdb into your pro­gram and call func­tions di­rectly against it.

In 2017, Mark Raasveldt and Hannes Mühleisen pub­lished Don’t Hold My Data Hostage, a pa­per mea­sur­ing what ac­tu­ally hap­pens when you pull a re­sult set out of a ware­house. They found that the client pro­to­col it­self — ODBC, JDBC, and sim­i­lar row-by-row value APIs — was of­ten the slow­est sin­gle step in the en­tire query, some­times dwarf­ing the time the data­base spent com­put­ing the an­swer.

Two costs drive this. The first is raw band­width: a typ­i­cal gi­ga­bit Ethernet link caps you at around 125 MB/s, and a large re­sult set can take longer to trans­mit than it took to com­pute. The sec­ond is per-value over­head. ODBC and JDBC hand back re­sults one row and one value at a time, which means the client makes a sep­a­rate func­tion call for every field in every row. On a 100-million-row re­sult, that’s hun­dreds of mil­lions of func­tion calls, each one do­ing its own lit­tle mem­ory copy, type check, and string al­lo­ca­tion.

ADBC trans­fers data be­tween sys­tems in colum­nar Arrow for­mat, which avoids the row-by-row se­ri­al­iza­tion/​de­se­ri­al­iza­tion that ODBC and JDBC re­quire. Our friends at Columnar are mak­ing this com­mon­place.

DuckDB side­steps both bot­tle­necks by liv­ing in the same process as the client.

When a Python script runs con.sql(“SE­LECT … FROM my_df”) against a pan­das dataframe, DuckDB can use a fea­ture called a re­place­ment scan. Instead of copy­ing the dataframe into an in­ter­nal table first, DuckDB re­places the table ref­er­ence with a func­tion that reads from the dataframe when the query runs.

In the best case, DuckDB can read the same un­der­ly­ing buffers the Python process al­ready owns, so it avoids ma­te­ri­al­iz­ing a sec­ond full copy of the data. This is zero-copy! If NumPy says here’s a buffer (contiguous chunk of mem­ory) of 1 mil­lion in­t64 val­ues,” DuckDB can of­ten read that same buffer di­rectly be­cause it un­der­stands the same phys­i­cal lay­out.

In prac­tice, whether the path is truly zero-copy de­pends on the dataframe’s phys­i­cal lay­out, col­umn types, null rep­re­sen­ta­tion, and string stor­age. If the types or lay­outs do not line up, DuckDB may al­lo­cate con­verted buffers for some columns.

Arrow is the clean­est ver­sion of this story be­cause Arrow is al­ready a colum­nar, typed mem­ory for­mat de­signed for shar­ing data be­tween sys­tems. That is why re­turn­ing DuckDB re­sults as Arrow, or query­ing Arrow-backed data, can avoid much of the row-by-row con­ver­sion over­head that tra­di­tional APIs im­pose.

From SQL to Logical Plan

Once your SQL reaches DuckDB, it goes through the usual stages: parse, bind, plan, op­ti­mize.

Parsing

The first step is to parse SQL into an ab­stract syn­tax tree (AST). DuckDB uses a fork of the Postgres parser, which is part of why DuckDB’s di­alect feels so fa­mil­iar.

An AST is a tree rep­re­sen­ta­tion of your query where each node is a syn­tac­tic con­struct: a SELECT state­ment, a col­umn ref­er­ence, a func­tion call, a join, a lit­eral. Parsing turns the flat string SELECT sum(l_quan­tity) FROM lineitem WHERE l_­ship­date > 2024 – 01-01’ into a struc­tured ob­ject the en­gine can ac­tu­ally rea­son about.

Select( ex­pres­sions=[ Sum( this=Col­umn( this=Iden­ti­fier(this=l_quan­tity, quoted=False)))], from_=From( this=Table( this=Iden­ti­fier(this=lineitem, quoted=False))), where=Where( this=GT( this=Col­umn( this=Iden­ti­fier(this=l_­ship­date, quoted=False)), ex­pres­sion=Lit­eral(this=‘2024 – 01-01’, is_string=True))))

AST from the SQLGlot li­brary.

A tree struc­ture is what lets the rest of the en­gine do its job. The binder walks the nodes to re­solve l_quan­tity to a spe­cific col­umn in a spe­cific table. The op­ti­mizer pat­tern-matches sub­trees to rec­og­nize that the WHERE pred­i­cate can be pushed down into the scan. The phys­i­cal plan­ner maps func­tion call nodes to ex­e­cutable op­er­a­tors. None of these passes can op­er­ate on raw SQL. They need to tra­verse, pat­tern-match, and rewrite a typed struc­ture.

Binding

The next step is bind­ing, which re­solves every name in the AST against the cat­a­log. lineitem be­comes a spe­cific table with a known schema. l_quan­tity be­comes a spe­cific col­umn with a known type. sum be­comes a spe­cific ag­gre­gate func­tion whose in­put type matches that col­umn. Type check­ing hap­pens here too: com­par­ing l_­ship­date to the string 2024 – 01-01’ works be­cause the binder co­erces the lit­eral to a date.

The out­put is a bound tree where every node knows what it refers to and what type it pro­duces. Errors like un­re­solved columns, am­bigu­ous ref­er­ences, and type mis­matches sur­face at this stage.

At this point, DuckDB has turned raw SQL text into a typed tree. The en­gine no longer sees l_quan­tity as just a string in a query; it sees a spe­cific col­umn with a spe­cific type from a spe­cific table.

The Optimizer

In DuckDB, the op­ti­mizer con­sists of a se­quence of small, fo­cused trans­for­ma­tions that you can, in fact, in­spect and dis­able in­di­vid­u­ally.

D SELECT * FROM duck­d­b_op­ti­miz­ers(); ┌────────────────────────────┐ │ name │ │ var­char │ ├────────────────────────────┤ │ ex­pres­sion_rewriter │ │ fil­ter_pullup │ │ fil­ter_­push­down │ │ emp­ty_re­sult_pullup │ │ cte_­fil­ter_­pusher │ │ regex_range │ │ in­_­clause │ │ join_or­der │ │ de­lim­i­na­tor │ │ unnest_rewriter │ │ un­used_­columns │ │ sta­tis­tic­s_prop­a­ga­tion │ │ com­mon_­subex­pres­sions │ │ com­mon_ag­gre­gate │ │ col­um­n_life­time │ │ lim­it_­push­down │ │ row_­group_pruner │ │ top_n │ │ top_n_win­dow_e­lim­i­na­tion │ │ build_­side_probe_­side │ │ com­pressed_­ma­te­ri­al­iza­tion │ │ du­pli­cate_­groups │ │ re­order_­fil­ter │ │ sam­pling_­push­down │ │ join_­fil­ter_­push­down │ │ ex­ten­sion │ │ ma­te­ri­al­ized_cte │ │ sum_rewriter │ │ late_­ma­te­ri­al­iza­tion │ │ cte_in­lin­ing │ │ com­mon_­sub­plan │ │ join_e­lim­i­na­tion │ │ win­dow_­self­_join │ └────────────────────────────┘ 33 rows

Running SET dis­abled_op­ti­miz­ers = filter_pullup, join_or­der’ turns spe­cific passes off so you can see what they were do­ing.

Here are a few in­ter­est­ing op­ti­miz­ers:

Filter push­down

This is a clas­sic data­base op­ti­miza­tion: move WHERE pred­i­cates as close to the scan as pos­si­ble so you prune data as early as pos­si­ble. DuckDB first pulls fil­ters up to the top of the plan so they can be com­bined and re­or­ga­nized, then pushes them back down as far as pos­si­ble.

Subquery unnest­ing

Correlated sub­queries tra­di­tion­ally force a data­base to run the in­ner query once per outer row, which is slow. DuckDB im­ple­ments tech­niques from the Unnesting Arbitrary Queries pa­per to rewrite these as joins, which are dra­mat­i­cally faster.

Dynamic join-fil­ter push­down

During a hash join (more on hash joins here), the build side has to be fully read be­fore the probe side starts. DuckDB takes ad­van­tage of that or­der­ing: once the build side is in mem­ory, it com­putes the min and max of the join key val­ues it ac­tu­ally con­tains, then pushes those bounds back into the probe-side scan as a run­time fil­ter. If the build side turned out to con­tain val­ues only be­tween 100 and 200, the probe scan can use the table’s zonemaps to skip any row groups out­side that range be­fore read­ing them.

When the build side has fewer than 50 dis­tinct join key val­ues, the fil­ter be­comes an IN list in­stead of a min-max range, which is more pre­cise and skips even more rows.

Join or­der op­ti­miza­tion

Join or­der is the most con­se­quen­tial de­ci­sion the op­ti­mizer makes. The or­der in which joins run de­ter­mines how big each in­ter­me­di­ate re­sult is. A query join­ing six ta­bles has 30,240 pos­si­ble tree shapes, and the dif­fer­ence be­tween best and worst can be or­ders of mag­ni­tude in run­time. Picking well re­quires es­ti­mat­ing how many rows each can­di­date join will pro­duce, which de­pends on table sizes, pred­i­cate se­lec­tiv­ity, and the or­der of joins that came be­fore.

DuckDB mod­els the query as a graph. Each table is a node, and each join pred­i­cate is an edge con­nect­ing the ta­bles it ref­er­ences. The op­ti­miz­er’s job is to pick an or­der to com­bine the nodes into a sin­gle tree, where each com­bi­na­tion is a join. For ex­am­ple, if we have a query join­ing a to b , b to c, and c to d, the graph might look like this:

a ── b ── c ── d

To find the best tree, DuckDB uses dy­namic pro­gram­ming, such as DPhyp or DPccp. Dynamic pro­gram­ming is a fancy name for a sim­ple idea: if you’ve al­ready fig­ured out the best way to join {a, b, c}, you can reuse that an­swer when fig­ur­ing out the best way to join {a, b, c, d}. You don’t need to re-ex­plore all the or­der­ings in­side {a, b, c} . It does this for every con­nected pair, then triplet, then quadru­plet, etc.

There are dozens more op­ti­miza­tions to ex­plore and the en­tire op­ti­miza­tion phase usu­ally fin­ishes in about a mil­lisec­ond. After op­ti­miza­tion, DuckDB has a log­i­cal plan. The next step is to trans­late that plan into some­thing the en­gine can ac­tu­ally ex­e­cute.

If you’ve en­joyed read­ing this so far, con­sider sub­scrib­ing. We’ll con­tinue shar­ing more about the in­tri­ca­cies of DuckDB and many other query en­gines.

The Physical Plan

Imagine the op­ti­mizer hands the en­gine this plan, writ­ten in plain English:

Read events from disk. Drop the rows where even­t_­date is on or be­fore 2026 – 01-01. Group what’s left by cus­tomer_id and add up amount. Sort the re­sult by to­tal de­scend­ing. Return the top 10.

Read events from disk. Drop the rows where even­t_­date is on or be­fore 2026 – 01-01. Group what’s left by cus­tomer_id and add up amount. Sort the re­sult by to­tal de­scend­ing. Return the top 10.

The en­gine now has to de­cide how to ac­tu­ally run those steps in a way that uses the CPU well and par­al­lelizes across cores.

Mapping Logical Steps to Physical Operators

The op­ti­miz­er’s out­put is still a log­i­cal plan. It says what each step needs to com­pute but not which al­go­rithm should do the com­put­ing. Most log­i­cal steps have sev­eral phys­i­cal im­ple­men­ta­tions.

Take a join. The same log­i­cal join can be turned into any of: hash join, in­dex join, piece­wise merge join, carte­sian join.

DuckDB walks the log­i­cal plan and picks a phys­i­cal op­er­a­tor for each node based on the shape of its in­puts and pred­i­cates. The out­put is a phys­i­cal plan — a tree of phys­i­cal op­er­a­tors the ex­ecu­tor knows how to run.

We will save the de­tails of vec­tor­ized ex­e­cu­tion for Part 2, but one ex­e­cu­tion con­cept is use­ful now: the phys­i­cal plan is not run as one gi­ant tree walk. DuckDB breaks it into pipelines.

Pipelines

Think of a pipeline as an as­sem­bly line. Data en­ters at one end and passes through a chain of sta­tions. Each sta­tion does one thing (drop a row, trans­form a col­umn, look up a value in a hash table) and hands the re­sult to the next sta­tion. As long as each sta­tion can de­cide what to do with a row us­ing only that row, the line keeps mov­ing. Examples of pipelines:

WHERE: it ei­ther passes the row through or drops it. No state needed.

A Projection: it com­putes new col­umn val­ues and emits them.

Probe side of hash join: once the hash table has been built, it looks up the row’s key in the hash table and emits the joined row or noth­ing if no match.

In DuckDB, a con­nected chain of stream­ing sta­tions like this is called a pipeline. Pipelines par­al­lelize cleanly since every CPU core can run its own copy of the as­sem­bly line on its own slice of the in­put.

Pipeline break­ers

Some op­er­a­tors can’t work this way. They need to see the en­tire in­put be­fore they can pro­duce an out­put.

ORDER BY can’t emit a sin­gle sorted row un­til its seen every row be­cause it does­n’t know which row be­longs first.

GROUP BY can’t emit the fi­nal sum un­til it has ac­counted for every row in a group­ing.

Build side of a hash join has to build the hash table be­fore it can start look­ing any­thing up.

These op­er­a­tors are called pipeline break­ers or sinks. They mark the end of one pipeline and the be­gin­ning of the next. The phys­i­cal plan is ef­fec­tively a se­quence of pipelines stitched to­gether by sinks.

Going back to our orig­i­nal query, the phys­i­cal plan may look some­thing like this:

Pipeline 1: ends at the GROUP BY sink:scan events → fil­ter even­t_­date > 2026 – 01-01’ → write into GROUP BYs hash table

Pipeline 2: ends at the ORDER BY sink:read groups out of the hash table → write them into the sorted run

Pipeline 3: the fi­nal as­sem­bly line:read sorted runs → take the first 10 rows → re­turn re­sults

Each pipeline runs in par­al­lel in­ter­nally. Multiple threads run the en­tire as­sem­bly line at once, each on its own morsel of in­put. Pipelines that de­pend on each other run in se­quence, be­cause pipeline 2 can’t start read­ing un­til pipeline 1′s GROUP BY is done writ­ing.

What Happens in a Sink

A sink runs in three phases: sink, com­bine, and fi­nal­ize.

Sink

Every thread ac­cepts chunks (DuckDB’s 2048 row batches) and writes them into its own lo­cal state, for ex­am­ple, its own hash table for a HASH_GROUP_BY, its own sorted run for ORDER_BY, its own par­tial ag­gre­gate for UNGROUPED_AGGREGATE, its own hash table for the build side of HASH_JOIN. Threads do not share state. If every thread wrote into one shared hash table, they’d be fight­ing for a lock on every in­sert. Local state lets each thread sink at full speed with no co­or­di­na­tion.

Combine

Once every thread fin­ishes writ­ing to its lo­cal space, the re­sults have to merge into a sin­gle global state. For a GROUP BY, that means com­bin­ing the par­tial sums and counts for each group across all the thread-lo­cal hash ta­bles. DuckDB de­signs the sink so the com­bine step it­self runs across all cores, rather than as a sin­gle-threaded merge at the end (covered in Part 3).

Finalize

The merged global state is read out as the in­put to the next pipeline. For our GROUP BY, that’ll be a stream of cus­tomer_id, to­tal) rows.

Parallelism is Local

A pipeline runs across all cores by giv­ing each thread its own morsel of in­put. A sink runs across all cores by giv­ing each thread its own lo­cal state and merg­ing in par­al­lel. DuckDB does not try to plan global par­al­lelism for the whole query, it par­al­lelizes one pipeline at a time. This is a part of what makes morsel-dri­ven par­al­lelism (covered in Part 3) and vec­tor­ized ex­e­cu­tion (covered in Part 2) work.

The Storage Layer

The amaz­ing thing about DuckDB is that it can turn most files into a SQL data­base, and in fact is of­ten used to di­rectly query file for­mats like Parquet, CSV, JSON, XLSX, etc.

To study how chips really work, MIT researchers built their own operating system

news.mit.edu

A new ker­nel, or core pro­gram within an op­er­at­ing sys­tem, gives re­searchers a cleaner view of what’s hap­pen­ing in­side a proces­sor. Called Fractal and de­vel­oped at MIT, the ker­nel has al­ready sur­faced pre­vi­ously un­known be­hav­ior in Apple’s M1.

When se­cu­rity re­searchers want to un­der­stand what a mod­ern proces­sor is re­ally do­ing with the kind of de­tail that de­ter­mines whether at­tacks like Spectre and Meltdown are pos­si­ble, they usu­ally run their ex­per­i­ments on top of an op­er­at­ing sys­tem that was never built for the job. They open up ma­cOS or Linux, patch the ker­nel by hand, and hope the mod­i­fi­ca­tions hold. The ap­proach is un­sta­ble, hard to re­pro­duce, and on Apple’s plat­forms, slated for dep­re­ca­tion.

A team at MITs Computer Science and Artificial Intelligence Laboratory (CSAIL) de­cided to build some­thing dif­fer­ent. Fractal, an op­er­at­ing sys­tem ker­nel writ­ten from the ground up, treats the hard­ware it­self as the ob­ject of study. Its first ma­jor use, a deep look at branch pre­dic­tors — a CPUs way of guess­ing what code to run next, be­fore it knows for cer­tain, so it does­n’t have to waste time wait­ing to find out — in­side Apple’s M1 proces­sor, has al­ready turned up find­ings that prior work missed, in­clud­ing the first ev­i­dence that a class of spec­u­la­tive at­tack known as Phantom” af­fects Apple Silicon.

We’re us­ing hard­ware in ways it was­n’t de­signed for,” says Joseph Ravichandran, the MIT PhD stu­dent in elec­tri­cal en­gi­neer­ing and com­puter sci­ence (EECS) who led the pro­ject. It’s not even ob­vi­ous that this is a pos­si­ble thing you could do with the hard­ware. But we found a way to pull all these dif­fer­ent prim­i­tives off. It’s like a mi­cro­scope. If you’ve got a hand mag­ni­fy­ing glass, you can see a lit­tle bit. But if you had an elec­tron mi­cro­scope, now we’re re­ally talk­ing. That’s what Fractal is. The elec­tron mi­cro­scope of op­er­at­ing sys­tems.”

A clean room for chip re­search

The core prob­lem Fractal solves is one that re­searchers have worked around for years. Modern proces­sors keep state in many in­ter­nal struc­tures: branch pre­dic­tors, caches, trans­la­tion looka­side buffers, and more. To study how those struc­tures be­have across the bound­ary be­tween user code and ker­nel code, two do­mains the chip is sup­posed to keep iso­lated, re­searchers need to run nearly iden­ti­cal ex­per­i­ments on each side of that bound­ary. On a gen­eral-pur­pose op­er­at­ing sys­tem, that is very dif­fi­cult. The sys­tem it­self man­ages priv­i­lege lev­els, ad­dress spaces, and sched­ul­ing, and it in­jects its own ac­tiv­ity into every mea­sure­ment.

Fractal in­verts the model. It boots di­rectly on bare metal, with no other soft­ware run­ning, and ex­poses prim­i­tives that let a sin­gle ex­per­i­ment switch priv­i­lege lev­els at run­time while ex­e­cut­ing the same in­struc­tions in the same ad­dress space. The team calls the un­der­ly­ing tech­nique multi-priv­i­lege con­cur­rency, and it re­lies on a new con­struct they in­tro­duced: the outer ker­nel thread, which sits in­side a user process’s mem­ory but ex­e­cutes with ker­nel priv­i­leges.

The re­sult is an ex­per­i­men­tal setup with al­most no back­ground noise. Where mea­sure­ments taken un­der ma­cOS or Linux are blurred by in­ter­rupts, sched­uler ac­tiv­ity, and ad­dress-space man­age­ment, Fractal pro­duces flat base­lines and clean sig­nals.

What Fractal found on the M1

Apple’s M1 im­ple­ments an ARM spec­i­fi­ca­tion called CSV2, which is sup­posed to pre­vent code run­ning in one priv­i­lege level from steer­ing spec­u­la­tion in an­other. Using Fractal, the MIT team con­firmed that the pro­tec­tion works for the ex­e­cute stage of in­di­rect branch pre­dic­tion: a user-mode pro­gram can­not make the ker­nel spec­u­la­tively ex­e­cute a cho­sen tar­get through the in­di­rect branch pre­dic­tor.

But the team also found some­thing the chip’s de­sign­ers may not have in­tended. The CPU still fetches the tar­get into the in­struc­tion cache be­fore the pro­tec­tion kicks in. That fetch is ob­serv­able through a side chan­nel, which means user code can still in­flu­ence what the ker­nel pulls into its caches across the priv­i­lege bound­ary. The same pat­tern ap­peared be­tween processes as­signed dif­fer­ent ad­dress space iden­ti­fiers.

The team also pro­duced the first ev­i­dence that Apple Silicon ex­hibits Phantom spec­u­la­tion, a class of mis­pre­dic­tion pre­vi­ously demon­strated only on AMD and Intel proces­sors. In Phantom, or­di­nary in­struc­tions, in­clud­ing a no-op, can be mis­in­ter­preted by the CPU as branches, trig­ger­ing spec­u­la­tive be­hav­ior the pro­gram never asked for. On the M1, Fractal showed that Phantom fetches suc­ceed across both priv­i­lege lev­els and ad­dress spaces, though the ex­e­cute phase re­mains blocked.

A sep­a­rate Fractal ex­per­i­ment over­turned a find­ing from ear­lier work on the M1s con­di­tional branch pre­dic­tor, which had re­ported that cross-priv­i­lege train­ing worked on Apple’s per­for­mance cores, but not its ef­fi­ciency cores. The Fractal team showed that the con­di­tional branch pre­dic­tor has no priv­i­lege iso­la­tion at all, on ei­ther core type, and that the ear­lier re­sult was likely an ar­ti­fact of ma­cOS qui­etly mi­grat­ing threads be­tween cores dur­ing sys­tem calls.

For us, it is a true in­de­pen­dent vari­able,” Ravichandran says. You change the priv­i­lege level, noth­ing else changes. The only thing that could ex­plain whether the at­tack suc­ceeds or not is the priv­i­lege level.”

A tool, not a one-off

Fractal sup­ports x86_64, ARM64, and RISC-V, and con­sists of more than 31,000 lines of code. The team de­signed it as in­fra­struc­ture rather than as a sin­gle ex­per­i­ment, with fa­mil­iar POSIX sys­tem calls, a C li­brary, and ports of stan­dard tools like vim, GCC, and the dash shell, so that re­searchers can move ex­ist­ing ex­per­i­ment code over with min­i­mal fric­tion.

The MIT team dis­closed its M1 find­ings to Apple’s prod­uct se­cu­rity team. In an un­usual re­ver­sal, Apple’s en­gi­neers also ex­am­ined Fractal.

The longer-term am­bi­tion is big­ger than any sin­gle re­sult. Ravichandran wants Fractal to be­come to mi­croar­chi­tec­ture re­search what tools like QEMU and FFmpeg are to their fields: shared in­fra­struc­ture that the whole com­mu­nity builds on.

My hope is that our re­sults as a com­mu­nity get sig­nif­i­cantly more re­li­able, sig­nif­i­cantly more ac­cu­rate,” says Ravichadran. With this re­duced noise, this clar­ity, and this guar­an­tee that you’re run­ning on the right core, on the right sys­tem.”

Fractal is a strong ar­chi­tec­ture con­tri­bu­tion be­cause it turns an of­ten ad hoc mi­croar­chi­tec­tural re­verse-en­gi­neer­ing work­flow into reusable re­search in­fra­struc­ture,” says Uni­ver­sity of Southern California as­sis­tant pro­fes­sor Mengyuan Li, who was­n’t in­volved in the pa­per. By re­duc­ing soft­ware noise and giv­ing re­searchers tighter con­trol across priv­i­lege bound­aries, it makes dif­fi­cult hard­ware ex­per­i­ments much eas­ier to in­ter­pret.”

Ravichandran worked with Mengjia Yan, an MIT as­so­ci­ate pro­fes­sor of EECS and CSAIL prin­ci­pal in­ves­ti­ga­tor, on the pa­per. Their work was sup­ported, in part, by the National Science Foundation, the U.S. Air Force Office of Scientific Research, and ACE, which is part of a pro­gram spon­sored by the U.S. Defense Advanced Research Projects Agency. They pre­sented their work at the IEEE Symposium on Security and Privacy in San Francisco, California.

Press Mentions

IEEE Spectrum

Writing for IEEE Spectrum, re­porter Matthew S. Smith high­lights Fractal, a new op­er­at­ing sys­tem hand-coded by CSAIL re­searchers to pro­vide a clear view of se­cu­rity vul­ner­a­bil­i­ties. We paved the way with tech­niques such as cus­tom ker­nel patches and ker­nel ex­ten­sions,” says grad­u­ate stu­dent Joseph Ravichandran. The dream was al­ways to have a com­pletely cus­tom op­er­at­ing sys­tem which would make these hacks un­nec­es­sary.”

Related Links

Fractal pro­ject web­site

Joseph Ravichandran

Mengjia Yan

Computer Science and Artificial Intelligence Laboratory (CSAIL)

Department of Electrical Engineering and Computer Science (EECS)

School of Engineering

MIT Schwarzman College of Computing

The AirPods Effect

www.theescapenewsletter.com

A LITTLE TIME away can be clar­i­fy­ing. When you’ve had a break from a place, you’re able to see it with fresh eyes. You no­tice things that rou­tine and fa­mil­iar­ity had ren­dered in­vis­i­ble.

During my last trip home to the U.S., one of the things that jumped out at me was the num­ber of peo­ple with AirPods in their ears.

Where I live, in south­west Germany, AirPods are far less com­mon. It was jar­ring to see so many lit­tle white glob­ules drip­ping out of the ears of those around me in cof­fee shops, in gro­cery stores, and pretty much every­where else I went dur­ing my trip to sub­ur­ban Detroit. Whether young or old, chic or grungy, ath­leisured or den­imed, every­one seemed to be sport­ing some type of ear­phone.

Americans are speak­ing less and less to one an­other. The num­ber of spo­ken words ut­tered by the av­er­age per­son fell by 28% be­tween 2005 and 2019.

Americans are speak­ing less and less to one an­other. The num­ber of spo­ken words ut­tered by the av­er­age per­son fell by 28% be­tween 2005 and 2019.

The pop­u­lar­ity of AirPods is noth­ing new. But as the func­tion­al­ity of our tech-con­nected ear gear has im­proved — and as pod­casts have ex­ploded into one of the most con­sumed forms of me­dia in America — earphones have as­sumed a big­ger role in our daily lives.

By some mar­ket es­ti­mates, 44% of Americans use Bluetooth or wire­less ear­phones, and an ad­di­tional 24% use some­thing wired. I could­n’t find good data on the per­cent­age of peo­ple who reg­u­larly wear ear­phones as they go about their daily lives. But dur­ing my re­cent trips to Michigan and Florida, I felt like half the peo­ple around me in pub­lic had some kind of de­vice-con­nected ear­wear on their head.

There is dis­ap­point­ingly lit­tle peer-re­viewed re­search on the ef­fects ear­phones have on our daily lives and in­ter­ac­tions. But the ev­i­dence we do have sug­gests that while AirPods and sim­i­lar tech­nolo­gies do some won­der­ful things for us, they also sub­tly in­flu­ence our be­liefs, re­in­force our in­se­cu­ri­ties, and push us far­ther apart.

During the pre-smart­phone era of iPods and other portable mu­sic de­vices, a small study of col­lege stu­dents found that those who were heavy users of head­phones ex­pe­ri­enced higher lev­els of so­cial iso­la­tion and lone­li­ness.

More than 15 years later, in 2021, a sur­vey con­ducted by the au­dio tech­nol­ogy com­pany Jabra came to sim­i­lar con­clu­sions. Heavy head­phone use makes peo­ple feel lone­lier, the sur­vey found. It also makes peo­ple less likely to have a mean­ing­ful con­ver­sa­tion with some­one new. Many of those in­ter­viewed for the sur­vey said they wore head­phones in part to avoid hav­ing to talk to other peo­ple.

This habit of us­ing head­phones to dodge un­com­fort­able in­ter­ac­tions may be es­pe­cially com­mon among younger adults, for whom so­cial un­ease and feel­ings of iso­la­tion are well-doc­u­mented prob­lems that have be­come more com­mon in re­cent decades.

I be­lieve hu­man in­ter­ac­tion is fad­ing, largely in part to the con­stant us­age of AirPods or other forms of head­phones,” wrote Eva Long, a stu­dent at Liberty University in Virginia, in a 2025 opin­ion piece for her school’s news­pa­per, The Liberty Champion.

No one talks on the bus. No one greets the barista. Even in class, stu­dents are choos­ing to lis­ten to mu­sic in­stead of their pro­fes­sors,” Long wrote. When pass­ing some­one I know who has AirPods in their ears, it’s dif­fi­cult to catch their at­ten­tion un­less we make di­rect eye con­tact. This lack of en­gage­ment is dis­cour­ag­ing, and it makes spon­ta­neous so­cial con­nec­tions less likely.”

Headphones are a so­cial crutch, grant­ing us the abil­ity to tune in or out of the world as we please,” wrote sopho­more Katelyn Halverson in The Cornell Daily Sun. Interpersonal in­ter­ac­tion in pub­lic spaces has be­come more or less op­tional with the use of head­phones — and it ap­pears that the ma­jor­ity (myself in­cluded) have a sneaky ten­dency to opt out.”

Both of these col­lege-pa­per think pieces were writ­ten in 2025, but I found a half-dozen oth­ers — some dat­ing back to 2019. All of them be­moaned the fact that, thanks largely to head­phones, the col­le­giate ex­pe­ri­ence has be­come less so­cial, less im­mer­sive, and less in­ter­ac­tive. Basically, less col­le­gial.

All these lit­tle con­ver­sa­tions add up to us feel­ing like peo­ple are gen­er­ally good, I can talk to any­body, and I have a place in this world. That’s some­thing we all need.’

All these lit­tle con­ver­sa­tions add up to us feel­ing like peo­ple are gen­er­ally good, I can talk to any­body, and I have a place in this world. That’s some­thing we all need.’

While ear­phone-as­sisted com­fort bub­bles are noth­ing new on cam­pus — or for that mat­ter, in cof­fee shops or on pub­lic tran­sit — I see them bleed­ing into sit­u­a­tions where, just a few years ago, they would never have oc­curred.

People now wear their AirPods all day at the of­fice. They keep them in while or­der­ing and pay­ing for things in stores and su­per­mar­kets.

I played golf last sum­mer at a pub­lic course in Michigan, and the guy I was paired with wore AirPods through­out our nine holes to­gether. After shak­ing my hand and of­fer­ing me a terse play well,” the guy did­n’t say five words to me for the rest of our round. I would have felt less iso­lated play­ing alone.

I know that a lot of peo­ple wear AirPods to fa­cil­i­tate com­mu­ni­ca­tion, not to de­ter it. AirPods can func­tion as hear­ing aids — block­ing out back­ground noise while help­fully am­pli­fy­ing the words of a con­ver­sa­tion part­ner.

The prob­lem is that un­less you al­ready know the AirPod wearer and you’re con­fi­dent they won’t be both­ered if you start chat­ting with them, ear­phones are the equiv­a­lent of a Do Not Disturb” sign. We see them and as­sume the per­son wear­ing them is ei­ther lis­ten­ing to some­thing or try­ing to block out dis­trac­tion. To strike up a con­ver­sa­tion with some­one wear­ing ear­buds feels in­tru­sive — like you’re bulling your way into their per­sonal space with­out per­mis­sion.

I’m sure some peo­ple read­ing this will say, Well, so what? Small talk is a drag any­way, es­pe­cially with strangers or loose ac­quain­tances. As long as a per­son has close con­nec­tions in their lives — peo­ple for whom they ei­ther take out their AirPods or use them to con­nect and com­mu­ni­cate — then what’s the harm?

I used to feel this way my­self, but I’ve learned some things that have changed my mind.

For a piece I wrote re­cently for Time mag­a­zine, I de­tailed the find­ings of a new study that found Americans are speak­ing to one an­other far less than they used to. According to that study, the num­ber of spo­ken words ut­tered by the av­er­age per­son fell by 28% be­tween 2005 and 2019. Each year dur­ing that time pe­riod, the num­ber of words peo­ple spoke in an av­er­age day de­clined.

One of the au­thors of that study, the University of Arizona so­cial psy­chol­o­gist Matthias Mehl, told me it’s highly likely that spo­ken com­mu­ni­ca­tion has fallen fur­ther since 2019. He pointed to the loss of idle chitchat and other pub­lic-space in­ter­ac­tions as sig­nif­i­cant con­trib­u­tors to the trend. We can shop for gro­ceries now with­out talk­ing with a check­out per­son, and in restau­rants we can some­times or­der and pay with­out ever talk­ing with a server,” he said. All these ways in which we have ren­dered our daily lives more ef­fi­cient may have also re­sulted in ren­der­ing our so­cial lives more rudi­men­tary.”

When peo­ple lis­tened to pod­cast-style au­dio con­tent through head­phones, they per­ceived the pod­caster to be warmer and friend­lier, more per­sua­sive, and more em­pa­thetic than if they lis­tened to the same piece of con­tent on speak­ers.

When peo­ple lis­tened to pod­cast-style au­dio con­tent through head­phones, they per­ceived the pod­caster to be warmer and friend­lier, more per­sua­sive, and more em­pa­thetic than if they lis­tened to the same piece of con­tent on speak­ers.

For that Time piece, I also spoke with Gillian Sandstrom, a psy­chol­o­gist at the University of Sussex and au­thor of the new book Once Upon a Stranger.

Sandstrom told me that ca­sual con­ver­sa­tions with peo­ple we don’t know well can make us feel more con­nected to one an­other. These con­ver­sa­tions also ex­er­cise and en­hance our so­cial skills. They may even bol­ster our faith in hu­man­ity. When we have these in­ter­ac­tions, they tend to go much bet­ter than we thought they would, and we come away from them with a sense that peo­ple are gen­er­ally good,” she told me.

The more I’ve thought about what she told me, the more im­por­tant her mes­sage feels.

For those of us who wear ear­buds all the time, the peo­ple drift­ing by on the out­side of our ar­ti­fi­cially qui­eted, per­son­ally cu­rated sound si­los can be­gin to re­sem­ble other ve­hi­cles on a traf­fic-choked in­ter­state — that is, like lit­tle more than nui­sances crowd­ing our space and im­ped­ing our progress.

I think we need reg­u­lar doses of real hu­man con­tact — not just with close friends, but with ac­quain­tances, and even with strangers — to coun­ter­bal­ance all the neg­a­tiv­ity we en­counter in the news and on­line, and to re­mind us that, on the whole, peo­ple are kind and well-mean­ing.

Apart from throw­ing up road­blocks that pre­vent these sorts of ca­sual in­ter­ac­tions, ear­buds may change our re­la­tion­ship to the con­tent we con­sume.

For a study creep­ily (but aptly) ti­tled A Voice Inside My Head,” re­searchers at sev­eral University of California schools found that when peo­ple lis­tened to pod­cast-style au­dio con­tent through head­phones, as op­posed to via speak­ers, they tended to form a more pos­i­tive im­pres­sion of the per­son de­liv­er­ing the pod­cast. They per­ceived the pod­caster to be warmer and friend­lier, more per­sua­sive, and more em­pa­thetic than if they lis­tened to the same piece of con­tent on speak­ers.

The ex­pla­na­tion for this, ac­cord­ing to the study’s au­thors, is that head­phones may re­duce the psy­cho­log­i­cal dis­tance be­tween lis­tener and speaker; head­phones give lis­ten­ers the sense that the speak­er’s voice is com­ing from in­side their head — al­most as though the voice they’re hear­ing and their own in­ter­nal thoughts are one and the same. It is im­por­tant to un­der­stand how the medium through which peo­ple lis­ten can af­fect their per­cep­tions, at­ti­tudes, and be­hav­iors,” the study’s au­thors wrote. We find con­sis­tent ev­i­dence that lis­ten­ing to a mes­sage via head­phones (vs. speak­ers) leads lis­ten­ers to feel closer to com­mu­ni­ca­tors, lead­ing to dif­fer­ent psy­cho­log­i­cal and be­hav­ioral re­sponses to mes­sages.”

It’s pos­si­ble that many of us are so taken with pod­casts — and so amenable to the the­o­ries and opin­ions we en­counter in them — in part be­cause of these sub­tle per­cep­tual and psy­cho­log­i­cal ef­fects. (As Marshall McLuhan fa­mously put it, the medium is the mes­sage.”)

While all these con­se­quences are con­cern­ing, I think the great­est prob­lem our ear­phones pose to us — and the one that led me, sev­eral years ago, to cut back my own use — is the way au­dio con­tent can crowd out time we should prop­erly spend with our own thoughts.

Back in 2019, I wrote a piece ti­tled Why Your Brain Needs Idle Time.” I de­tailed all the rea­sons we need to give our minds reg­u­lar breaks from new in­for­ma­tion so that we have time to con­sider and make sense of our ex­pe­ri­ences.

The deeper re­flec­tive states, where you make mean­ing of what’s go­ing on and con­nect it to self and iden­tity and in­te­grate knowl­edge to­gether into co­her­ent nar­ra­tives — these kinds of processes only hap­pen when you’re not fo­cused on some in-the-mo­ment ac­tiv­ity,” Mary Helen Immordino-Yang, a pro­fes­sor at the University of Southern California, told me for that piece.

These vi­tal pe­ri­ods of con­tem­pla­tion and mean­ing mak­ing re­quire us to step away from our var­i­ous con­tent streams and al­low our thoughts to wan­der freely. But thanks to ear­buds, such op­por­tu­ni­ties to rest and re­flect are in­creas­ingly op­tional — and ef­fort­ful.

During my last trip home to Detroit, I was fill­ing a con­tainer at a gro­cery store salad bar when an older man, un­prompted, pointed at the jalapeno slaw I was spoon­ing up and said, You’re go­ing to eat that?”

He looked at me side­ways, shak­ing his head and smil­ing. Oh man, that looks too spicy for me. You’re go­ing to have to tell me how it is. I don’t know about that!”

Living abroad, one of the many things I miss about the U.S. is the warmth and friend­li­ness of its peo­ple. (In my ex­pe­ri­ence, a German would never in­ter­act with a stranger the way this older man had in­ter­acted with me.) I told the man I’d be sure to let him know about the slaw, and he wished me a good day. The in­ter­ac­tion lasted 15 sec­onds, but it bright­ened my whole af­ter­noon.

The great­est ben­e­fit we get from chat­ting with other peo­ple — and the one we may ul­ti­mately miss the most if we spend less time talk­ing with one an­other — is also the hard­est to quan­tify, Sandstrom told me for that Time ar­ti­cle.

All these lit­tle con­ver­sa­tions add up to us feel­ing like peo­ple are gen­er­ally good, I can talk to any­body, and I have a place in this world,” she said. That’s very hard to mea­sure, but that’s some­thing we all need.”

The more time we all spend with AirPods in our ears, the more that need is likely to go un­met.

No posts

NS Flex webshop | NS

www.ns.nl

Train tick­ets for in­ter­na­tional jour­neys can be pur­chased from NS International

Direct trains to

All train des­ti­na­tions

Log in My NSI

Visit NS International

Enterprise-Managed Authorization: Zero-touch OAuth for MCP

blog.modelcontextprotocol.io

The Enterprise-Managed Authorization ex­ten­sion is now sta­ble. Organizations can cen­trally man­age au­tho­riza­tion for MCP servers and end-users can ac­cess all con­nected MCP servers through a sin­gle log in. The ex­ten­sion is be­ing adopted by Anthropic, Microsoft, Okta and a grow­ing num­ber of MCP servers.

The Enterprise-Managed Authorization (EMA) ex­ten­sion is now sta­ble. We’ve heard from the com­mu­nity that au­tho­riza­tion and re­peated con­sent prompts from con­nected MCP servers is one of the biggest pain points when it comes to man­ag­ing con­nec­tiv­ity in en­ter­prise en­vi­ron­ments. This ex­ten­sion helps ad­dress this.

EMA al­lows or­ga­ni­za­tions to con­trol MCP server ac­cess cen­trally through their trusted iden­tity provider. For end-users, this means a zero-touch setup: the MCP servers they need are con­nected on first lo­gin, with no per-app OAuth and noth­ing to con­fig­ure as a one-off.

Per-user auth is high fric­tion

The stan­dard MCP au­tho­riza­tion model was de­signed to be user-scoped and bound to the tra­di­tional in­ter­ac­tive auth con­ven­tions. While this might work well for more gen­eral con­sumer sce­nar­ios where in­di­vid­u­als de­cide what touches their data, this does­n’t quite scale for en­ter­prise de­ploy­ments:

Every em­ployee has to au­tho­rize every server in­di­vid­u­ally: on­board­ing means man­u­ally con­nect­ing ser­vice af­ter ser­vice.

Security teams can­not en­force con­sis­tent pol­icy: ac­cess is what­ever each user au­tho­rized, with no cen­tral con­trol or au­dit trail.

Work and per­sonal ac­counts blur to­gether: there’s no way to re­quire a cor­po­rate iden­tity, so a user can con­nect a per­sonal ac­count to a work tool.

This com­bi­na­tion of fac­tors slows MCP adop­tion and pushes peo­ple to­ward brit­tle workarounds. With no uni­ver­sal stan­dard for pre­serv­ing shared auth state, every­one in­vents their own be­spoke so­lu­tion. The data and tools are avail­able, but the per-user au­tho­riza­tion tax keeps most of them switched off.

Authorize once, in­herit every­where

Enterprise-Managed Authorization makes the or­ga­ni­za­tion’s IdP the au­thor­i­ta­tive de­ci­sion-maker for MCP server ac­cess. Administrators de­fine the pol­icy once and users can au­then­ti­cate with their ex­ist­ing iden­tity into the MCP host. The IdP can grant or deny ac­cess based on group mem­ber­ship, role, and con­di­tional ac­cess rules.

Under the hood, the client ob­tains an Identity Assertion JWT Authorization Grant (ID-JAG) from the IdP dur­ing sin­gle sign-on and ex­changes it for an ac­cess to­ken from the MCP server’s au­tho­riza­tion server. The user is never redi­rected through a per-server con­sent screen. Three prop­er­ties fall out of that flow:

Authorize once, in­herit every­where: ad­mins en­able a server for the org. Users get it au­to­mat­i­cally, scoped to the groups and roles they al­ready have.

Centralized pol­icy and au­dit: ac­cess de­ci­sions live in the IdP ad­min con­sole, with one au­ditable trail across every con­nec­tor.

Removing per­sonal/​en­ter­prise mix­ups: by re­mov­ing the in­ter­ac­tive ac­count se­lec­tion step, it’s much eas­ier to pre­vent data flow­ing be­tween per­sonal and en­ter­prise ac­counts by mis­take or com­pro­mise.

We see this as a brand new base­line for MCP in the en­ter­prise. When users log in, their client should be con­nected to the tools and data they’re au­tho­rized to use with no ex­tra steps in be­tween.

Early adopters

This launch brought to­gether three groups that col­lab­o­rated closely on mak­ing the im­ple­men­ta­tion real:

Identity providers: Okta is the first sup­ported iden­tity provider. Organizations us­ing Okta can pro­vi­sion MCP ac­cess to sup­ported servers through any sup­ported client, us­ing Okta’s Cross App Access (XAA).

Clients: Anthropic has im­ple­mented the ex­ten­sion in its shared MCP layer for Claude. Admins can au­tho­rize MCP servers for users across Claude, Claude Code, and Cowork. Additionally, Visual Studio Code has also added sup­port for EMA right in the IDE.

Servers: Asana, Atlassian, Canva, Figma, Granola, Linear and Supabase now sup­port EMA, with Slack and more ac­tively adding sup­port.

We’re ex­cited for more iden­tity providers, clients, and servers to adopt Enterprise-Managed Auth to help re­duce the au­tho­riza­tion-re­lated fa­tigue and sig­nif­i­cantly im­prove the se­cu­rity and ob­serv­abil­ity pos­ture for its im­ple­menters.

The mo­men­tum around MCP is in­cred­i­ble, but as we move to­ward an in­ter­con­nected AI work­force, se­cu­rity can’t be an af­ter­thought. By em­bed­ding the Cross App Access pro­to­col into MCP as the Enterprise-Managed Authorization ex­ten­sion, we turn iden­tity into a cen­tral­ized gov­er­nance plane and give se­cu­rity teams strict com­pli­ance con­trol and users a seam­less, se­cure ex­pe­ri­ence.”— Aaron Parecki, Director of Identity Standards, Okta

The mo­men­tum around MCP is in­cred­i­ble, but as we move to­ward an in­ter­con­nected AI work­force, se­cu­rity can’t be an af­ter­thought. By em­bed­ding the Cross App Access pro­to­col into MCP as the Enterprise-Managed Authorization ex­ten­sion, we turn iden­tity into a cen­tral­ized gov­er­nance plane and give se­cu­rity teams strict com­pli­ance con­trol and users a seam­less, se­cure ex­pe­ri­ence.”

— Aaron Parecki, Director of Identity Standards, Okta

The Figma MCP brings the power of code and can­vas to­gether so teams can move faster, ex­plore more and ship prod­ucts that stand out. As MCP adop­tion grows, XAA makes it eas­ier for en­ter­prises to scale their MCP de­ploy­ments se­curely with­out slow­ing teams down.”— Devdatta Akhawe, VP of Engineering, Figma

The Figma MCP brings the power of code and can­vas to­gether so teams can move faster, ex­plore more and ship prod­ucts that stand out. As MCP adop­tion grows, XAA makes it eas­ier for en­ter­prises to scale their MCP de­ploy­ments se­curely with­out slow­ing teams down.”

— Devdatta Akhawe, VP of Engineering, Figma

Logging in once and au­to­mat­i­cally hav­ing all your MCP con­nec­tors au­to­mat­i­cally setup is pretty mag­i­cal.”— Tom Moor, Head of Engineering, Linear

Logging in once and au­to­mat­i­cally hav­ing all your MCP con­nec­tors au­to­mat­i­cally setup is pretty mag­i­cal.”

— Tom Moor, Head of Engineering, Linear

Get in­volved

As with all other MCP ex­ten­sions, fea­tures, and en­hance­ments, we wel­come your in­put. We’re en­cour­ag­ing clients, servers, and iden­tity plat­forms to re­view the ex­ten­sion spec­i­fi­ca­tion and add sup­port for the new stan­dard into their prod­ucts:

Read the re­quire­ments: the Enterprise-Managed Authorization page doc­u­ments the flow for clients, servers, and au­tho­riza­tion servers.

Source and draft spec: see the ext-auth repos­i­tory and the draft spec­i­fi­ca­tion for the lat­est in EMA evo­lu­tion as well as any sup­port ma­te­ri­als that will help you get started.

If you’re in­ter­ested in dis­cussing the ex­ten­sion, shar­ing com­pat­i­bil­ity re­ports, or it­er­at­ing on the ex­ten­sion, join the EMA Interest Group.

Acknowledgements

Enterprise-Managed Authorization is the work of the MCP com­mu­nity: the au­thors of SEP-990, the main­tain­ers of the ext-auth repos­i­tory, and the iden­tity and MCP providers who tested early im­ple­men­ta­tions and pushed the spec for­ward. Thank you to every­one who con­tributed.

Notes from a Tired Egyptian Guy Whose Job Is Explaining That Humans Built the Pyramids

www.mcsweeneys.net

Internet Tendency

The Store

Books Division

Quarterly Concern

The Believer

Donate

Day 4,382 of peo­ple ask­ing whether normal work­ers” could re­ally move large stones with­out as­sis­tance from mys­ti­cal sky be­ings.

Yes. That is gen­er­ally how con­struc­tion func­tions.

- - -

A man ap­proached me near the Nile to­day and whis­pered, But have you con­sid­ered… vis­i­tors from the stars?”

Brother. We do not even have re­li­able san­dals yet. Why would in­ter­galac­tic civ­i­liza­tions travel unimag­in­able dis­tances only to help stack tri­an­gles?

- - -

People dra­mat­i­cally un­der­es­ti­mate what thou­sands of or­ga­nized hu­mans can ac­com­plish when they are ad­e­quately fed, ag­gres­sively su­per­vised, and de­nied al­ter­na­tive ca­reer paths.

- - -

Another trav­eler asked: How could an­cient peo­ple pos­si­bly un­der­stand math­e­mat­ics?”

Excellent ques­tion. We ac­ci­den­tally in­vented geom­e­try while try­ing to avoid car­ry­ing rocks in­cor­rectly.

- - -

There is a strange ten­dency among fu­ture civ­i­liza­tions to imag­ine an­cient Egyptians spent all day wor­ship­ping cats, speak­ing in rid­dles, and wait­ing for aliens to ex­plain ba­sic en­gi­neer­ing.

- - -

I showed one vis­i­tor the ramps.

The pul­leys. The la­bor records. The ar­chi­tec­tural plan­ning.

He nod­ded thought­fully and replied, Interesting. But what if ex­trater­res­tri­als?”

At this point, I be­lieve some peo­ple sim­ply find aliens emo­tion­ally com­fort­ing.

- - -

Do you know what sounds more be­liev­able than A so­phis­ti­cated civ­i­liza­tion de­vel­oped im­pres­sive con­struc­tion tech­niques over cen­turies”?

Apparently: Space peo­ple.”

- - -

Yesterday, some­one pointed at the pyra­mids and said, There’s no way hu­mans did this.”

This feels deeply in­sult­ing con­sid­er­ing hu­mans also cre­ated tax­a­tion, or­ga­nized war­fare, and raisins.

Clearly, we are ca­pa­ble of ter­ri­ble per­se­ver­ance.

- - -

The work­ers them­selves would be fu­ri­ous hear­ing these the­o­ries. Imagine ded­i­cat­ing twenty years to haul­ing lime­stone un­der desert heat only for some­body in the fu­ture to con­clude, Honestly, this feels Martian.”

Also, if aliens truly pos­sessed ad­vanced cos­mic tech­nol­ogy, why would they choose pyra­mids? Why not in­vent in­door cool­ing? Or chairs that sup­port the lower back?

- - -

Tomorrow, I must re­turn to su­per­vis­ing en­tirely hu­man work­ers us­ing en­tirely hu­man tools to build an­other en­tirely hu­man mon­u­ment that fu­ture peo­ple will some­how at­tribute to lizards from space.

Please help sup­port our writ­ers and keep our site ad-free by be­com­ing a pa­tron.

January 13, 2014

Sorry Guys, But I’ll Be Cryosleeping Throughout the Rest of this Space Expedition

by Jason Edward Harrington

January 13, 2014

Sorry Guys, But I’ll Be Cryosleeping Throughout the Rest of this Space Expedition

by Jason Edward Harrington

November 15, 2024

I’m Willing to Give Our Alien Overlords a Second Chance

by Rachel Klein

November 15, 2024

I’m Willing to Give Our Alien Overlords a Second Chance

by Rachel Klein

May 28, 2003

Unused Audio Commentary By Dinesh D’Souza and Ann Coulter, Recorded Spring 2003, for Aliens Special Red-State Edition DVD, Part Two

by Jeff Alexander and Tom Bissell

May 28, 2003

Unused Audio Commentary By Dinesh D’Souza and Ann Coulter, Recorded Spring 2003, for Aliens Special Red-State Edition DVD, Part Two

by Jeff Alexander and Tom Bissell

August 14, 2025

I Am the Overtourist and I’m Here to Marry Your Town

by Jenny Shank

August 14, 2025

I Am the Overtourist and I’m Here to Marry Your Town

by Jenny Shank

June 11, 2026

AI Economics for Dummies

by Andrew Singleton

June 11, 2026

AI Economics for Dummies

by Andrew Singleton

June 5, 2026

I Wished on a Monkey’s Paw for Susan Collins to Lose Her Senate Seat, and Now We’re Stuck with Graham Platner

by Justin P. Drew

June 5, 2026

I Wished on a Monkey’s Paw for Susan Collins to Lose Her Senate Seat, and Now We’re Stuck with Graham Platner

by Justin P. Drew

May 15, 2026

At Long Last, I Have Maxximized My Looks

by Josh Gondelman

May 15, 2026

At Long Last, I Have Maxximized My Looks

by Josh Gondelman

June 5, 2026

The New Odyssey Movie Is Historically Inaccurate; Matt Damon Isn’t the Least Bit Greek

by Carlos Greaves

June 5, 2026

The New Odyssey Movie Is Historically Inaccurate; Matt Damon Isn’t the Least Bit Greek

by Carlos Greaves

June 18, 2026

Dad Band Dad Jokes

by Julia McCloy, Kevin Lutz, Patrick Barb, and Sam Allemang

June 18, 2026

Dad Band Dad Jokes

by Julia McCloy, Kevin Lutz, Patrick Barb, and Sam Allemang

June 18, 2026

Sir Ernest Shackleton’s Journal Entries from the Week He Stayed Home with His Sick Kids

by Iwan Davies

June 18, 2026

Sir Ernest Shackleton’s Journal Entries from the Week He Stayed Home with His Sick Kids

by Iwan Davies

June 18, 2026

Lest We Forget the Horrors: An Unending Catalog of Trump’s Cruelties, Collusions, Corruptions, and Crimes: May 2026: Atrocities 931 – 1013

by Emily Greenberg and Chase Bush-McLaughlin

June 18, 2026

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.