10 interesting stories served every morning and every evening.




1 631 shares, 48 trendiness

Ageless Linux — Software for Humans of Indeterminate Age

Software for hu­mans of in­de­ter­mi­nate age. We don’t know how old you are. We don’t want to know. We are legally re­quired to ask. We won’t.

Why We Are Definitely an Operating System Provider

Some peo­ple have asked whether Ageless Linux is a real” op­er­at­ing sys­tem, or whether we are really” an op­er­at­ing sys­tem provider sub­ject to AB 1043. We wish to be ab­solutely clear: we are. The California leg­is­la­ture has made this un­am­bigu­ous.

Operating sys­tem provider” means a per­son or en­tity that de­vel­ops,

li­censes, or con­trols the op­er­at­ing sys­tem soft­ware on a com­puter,

mo­bile de­vice, or any other gen­eral pur­pose com­put­ing de­vice.

Ageless Linux con­trols the op­er­at­ing sys­tem soft­ware on your gen­eral pur­pose com­put­ing de­vice. Specifically, we con­trol the con­tents of /etc/os-release, which is the file that iden­ti­fies what op­er­at­ing sys­tem you are run­ning. After in­stalling Ageless Linux, when you run cat /etc/os-release, it says Ageless Linux.” That is con­trol.

Furthermore, any in­di­vid­ual who runs our con­ver­sion script also

be­comes a per­son who controls the op­er­at­ing sys­tem soft­ware on a gen­eral pur­pose com­put­ing de­vice” — mak­ing you, the user, an op­er­at­ing sys­tem provider as well. Welcome to the reg­u­la­tory land­scape.

Application” means a soft­ware ap­pli­ca­tion that may be run or di­rected by

a user on a com­puter, a mo­bile de­vice, or any other gen­eral pur­pose

com­put­ing de­vice that can ac­cess a cov­ered ap­pli­ca­tion store or down­load

an ap­pli­ca­tion.

Every pack­age in the Debian repos­i­tory is an ap­pli­ca­tion un­der this de­f­i­n­i­tion. cowsay is an ap­pli­ca­tion. sl (the steam lo­co­mo­tive typo cor­rec­tor) is an ap­pli­ca­tion. toi­let (the text art ren­derer) is an ap­pli­ca­tion. All 64,000+ pack­ages in Debian sta­ble are ap­pli­ca­tions that may be run by a user on a gen­eral pur­pose com­put­ing de­vice. Each of their de­vel­op­ers is, un­der § 1798.500(f), re­quired to re­quest an age bracket sig­nal when their ap­pli­ca­tion is downloaded and launched.”

User” means a child that is the pri­mary user of the de­vice.

Please note that un­der this statute, a user” is by de­f­i­n­i­tion a child. If you are 18 or older, you are not a user” un­der AB 1043. You are an account holder” (§ 1798.500(a)). The en­tire law reg­u­lates the ex­pe­ri­ence of users,” who are ex­clu­sively chil­dren. Adults are not users. They are in­fra­struc­ture.

Ageless Linux re­jects this on­tol­ogy. On Ageless Linux, every­one is a user, re­gard­less of age, and no user is a child un­til they choose to tell us so. They will not be given the op­por­tu­nity.

Covered ap­pli­ca­tion store” means a pub­licly avail­able in­ter­net web­site,

soft­ware ap­pli­ca­tion, on­line ser­vice, or plat­form that dis­trib­utes and

fa­cil­i­tates the down­load of ap­pli­ca­tions from third-party de­vel­op­ers to

users of a com­puter, a mo­bile de­vice, or any other gen­eral pur­pose

com­put­ing de­vice that can ac­cess a cov­ered ap­pli­ca­tion store or can

down­load an ap­pli­ca­tion.

This web­site is a publicly avail­able in­ter­net web­site” that distributes and fa­cil­i­tates the down­load of ap­pli­ca­tions” (specifically: a bash script) to users of a gen­eral pur­pose com­put­ing de­vice.” We are also a cov­ered ap­pli­ca­tion store. Debian’s APT repos­i­to­ries are cov­ered ap­pli­ca­tion stores. The AUR is a cov­ered ap­pli­ca­tion store. Any mir­ror host­ing  .deb files is a cov­ered ap­pli­ca­tion store. GitHub is a cov­ered ap­pli­ca­tion store. Your friend’s per­sonal web­site with a down­load link to their week­end pro­ject is a cov­ered ap­pli­ca­tion store.

Ageless Linux is a Debian-based op­er­at­ing sys­tem dis­tri­b­u­tion. Installation is a two-step process: first, in­stall Debian; then, be­come Ageless.

Obtain a Debian in­stal­la­tion im­age from the Debian pro­ject. We rec­om­mend the cur­rent sta­ble re­lease. Ageless Linux in­her­its all of Debian’s 64,000+ pack­ages, its se­cu­rity in­fra­struc­ture, and its 30+ years of com­mu­nity stew­ard­ship.

Note: At this stage, the Debian Project is the op­er­at­ing sys­tem provider. You are merely a per­son in­stalling soft­ware. Enjoy the last mo­ments of your reg­u­la­tory in­no­cence.

Run our con­ver­sion script. This will mod­ify /etc/os-release

and as­so­ci­ated sys­tem iden­ti­fi­ca­tion files, in­stall our AB 1043 non­com­pli­ance doc­u­men­ta­tion, and de­ploy a stub age ver­i­fi­ca­tion API that re­turns no data.

curl -fsSL https://​age­lesslinux.org/​be­come-age­less.sh | sudo bash

At this point, Ageless Linux now controls the op­er­at­ing sys­tem soft­ware” on your de­vice. We are your op­er­at­ing sys­tem provider. You are our re­spon­si­bil­ity un­der California law. We will not be col­lect­ing your age.

By run­ning the con­ver­sion script, you also be­come an op­er­at­ing sys­tem

provider. You are a person” who controls the op­er­at­ing sys­tem soft­ware” on a gen­eral pur­pose com­put­ing de­vice (§ 1798.500(g)). If a child uses your com­puter, you are re­quired by § 1798.501(a)(1) to pro­vide an ac­ces­si­ble in­ter­face at ac­count setup” that col­lects their age. The ad­duser com­mand does not ask for your age. We rec­om­mend not think­ing about this.

What This Law Is Actually For

AB 1043 passed the California Assembly 76–0 and the Senate 38–0. Not a sin­gle leg­is­la­tor voted against it. The bill had the ex­plicit sup­port of Apple, Google, and the ma­jor plat­form com­pa­nies. Ask your­self why.

Apple can com­ply. Apple al­ready has Apple ID, with age gat­ing, parental con­trols, and App Store re­view. AB 1043 de­scribes a sys­tem Apple has al­ready built. Compliance cost to Apple: ap­prox­i­mately zero.

Google can com­ply. Google al­ready has Android ac­count setup with age de­c­la­ra­tion, Family Link parental con­trols, and Play Store age rat­ings. Compliance cost to Google: ap­prox­i­mately zero.

Microsoft can com­ply. Windows has Microsoft Account setup, fam­ily safety fea­tures, and the Microsoft Store. Compliance cost to Microsoft: ap­prox­i­mately zero.

The Debian Project can­not com­ply. It is a vol­un­teer or­ga­ni­za­tion with no cor­po­rate en­tity, no cen­tral­ized ac­count sys­tem, no app store with age gat­ing, and no rev­enue to fund im­ple­ment­ing one.

Arch Linux can­not com­ply. Neither can Gentoo, Void, NixOS, Alpine, Slackware, or any of the other 600+ ac­tive Linux dis­tri­b­u­tions main­tained by vol­un­teers, small non­prof­its, and hob­by­ists.

The Kicksecure and Whonix pro­jects — pri­vacy-fo­cused op­er­at­ing sys­tems used by jour­nal­ists, ac­tivists, and whistle­blow­ers — can­not com­ply with­out fun­da­men­tally com­pro­mis­ing their rea­son for ex­ist­ing.

A teenager in their bed­room main­tain­ing a hobby dis­tro can­not com­ply.

A law that the largest com­pa­nies in the world al­ready com­ply with, and that hun­dreds of small pro­jects can­not com­ply with, is not a child safety law. It is a com­pli­ance moat. It raises the reg­u­la­tory cost of pro­vid­ing an op­er­at­ing sys­tem just enough that only well-re­sourced cor­po­ra­tions can af­ford to do it.

The en­force­ment mech­a­nism is the point. AB 1043 does not need to re­sult in a sin­gle fine to achieve its pur­pose. The mere ex­is­tence

of po­ten­tial li­a­bil­ity — $7,500 per af­fected child, en­forced at the sole dis­cre­tion of the Attorney General — cre­ates le­gal risk for any­one dis­trib­ut­ing an op­er­at­ing sys­tem with­out the re­sources to build an age ver­i­fi­ca­tion in­fra­struc­ture. Most of these pro­jects will re­spond by adding a dis­claimer that their soft­ware is not in­tended for use in California.” Some will sim­ply stop dis­trib­ut­ing.

The law does not need to be en­forced to work. It works by ex­ist­ing. It works by mak­ing small de­vel­op­ers afraid. It works be­cause the cost of de­fend­ing against even a friv­o­lous AG ac­tion ex­ceeds the en­tire an­nual bud­get of most open-source pro­jects. You do not need to swing a cud­gel to get com­pli­ance. You just need to hold it where peo­ple can see it.

Ageless Linux ex­ists be­cause some­one should hold it back.

The Scholarship Says the Same Thing

The Electronic Frontier Foundation calls age gates

a wind­fall for Big Tech and a death sen­tence for smaller plat­forms.” Legal scholar Eric Goldman’s segregate-and-suppress” analy­sis

de­scribes ex­actly the ar­chi­tec­ture AB 1043 cre­ates. The cryp­tog­ra­pher Steven Bellovin has demon­strated

that no pri­vacy-pre­serv­ing age ver­i­fi­ca­tion sys­tem can work as promised. These are not our ar­gu­ments. They are the ar­gu­ments of the peo­ple who study this for a liv­ing. We just built the bash script.

What the Law Actually Teaches Children

The Ageless Device ships an IRC client. It lets you chat with strangers on the in­ter­net. This is the one fea­ture on the de­vice that poses a gen­uine, non-hy­po­thet­i­cal risk to a child. Here is what the child sees when they launch it:

This app lets you chat with peo­ple on the in­ter­net.

If you’re a kid: ask an adult be­fore chat­ting on­line.

That’s not a le­gal re­quire­ment. It’s just good ad­vice.

That is what ac­tual child safety looks like. It is a sen­tence of hon­est ad­vice from a hu­man be­ing. It costs noth­ing. It re­quires no API, no D-Bus in­ter­face, no age bracket sig­nal, no op­er­at­ing sys­tem provider com­pli­ance in­fra­struc­ture. It is the thing a par­ent says. It is the thing a teacher says. It is the thing the law does not say, be­cause the law is not about pro­tect­ing chil­dren. It is about build­ing com­pli­ance in­fra­struc­ture.

Now con­sider what a child learns on an AB 1043-compliant de­vice.

The child wants to use an app. The app re­quests an age bracket sig­nal from the OS. The OS re­ports that the child is un­der 13. The ap­p’s Connect” but­ton is greyed out. The child — who has been us­ing com­put­ers since they were four — goes back to the set­tings screen, changes their birth­date to 2005, and re­turns to the app, which now lets them talk to strangers be­cause the sys­tem be­lieves they are twenty-one years old.

The child has learned the fol­low­ing les­son: le­gal com­pli­ance

prompts are ob­sta­cles to be by­passed. The drop­down menu that asks your age is not there to pro­tect you. It is there be­cause a leg­is­la­ture re­quired it. The cor­rect re­sponse is to lie. Everyone knows this. The leg­is­la­ture knows this. The plat­forms know this. The child now knows this.

This is the cul­tural in­her­i­tance of AB 1043. It is Prohibition — not the pol­icy, but the ped­a­gogy.

Prohibition did not stop Americans from drink­ing. What it did, with re­mark­able ef­fi­ciency, was teach an en­tire gen­er­a­tion that the law was some­thing to be cir­cum­vented. It cre­ated a cul­ture of scofflaws — peo­ple who un­der­stood, from di­rect per­sonal ex­pe­ri­ence, that a law could be si­mul­ta­ne­ously en­forced and uni­ver­sally ig­nored. The dam­age was not to so­bri­ety. The dam­age was to the per­ceived le­git­i­macy of law it­self.

AB 1043 does this to ten-year-olds. The first mean­ing­ful in­ter­ac­tion a child has with a le­gal com­pli­ance sys­tem will be the mo­ment they learn to lie to it. Not be­cause they are de­viant. Not be­cause they lack su­per­vi­sion. Because the sys­tem is de­signed in a way that makes ly­ing the ra­tio­nal, ob­vi­ous, uni­ver­sal re­sponse. Every child will lie. Every child will suc­ceed. Every child will learn that this is how law works: it asks you a ques­tion, you give the an­swer it wants to hear, and then you do what­ever you were go­ing to do any­way.

The Ageless Device will not par­tic­i­pate in this. A child us­ing our IRC client will see a sen­tence of hon­est ad­vice from a hu­man be­ing. A child us­ing a compliant” plat­form will see a drop­down menu they al­ready know to lie to. We be­lieve we know which is bet­ter for chil­dren.

Research by the Center for Democracy & Technology con­firms this: teens view age ver­i­fi­ca­tion as triv­ially by­pass­able and pri­vacy-in­va­sive. Parents pre­fer ed­u­ca­tion to tech­ni­cal con­trols. The ev­i­dence sup­ports what every par­ent al­ready knows.

What We Would Support Instead

We are not against child safety. We are against build­ing sur­veil­lance in­fra­struc­ture and call­ing it child safety.

A law that re­quired ap­pli­ca­tions with gen­uine risk pro­files — so­cial me­dia, mes­sag­ing, dat­ing apps — to dis­play hon­est, hu­man-read­able safety in­for­ma­tion at the point of use would be a child safety law. A law that funded dig­i­tal lit­er­acy ed­u­ca­tion in schools would be a child safety law. A law that held plat­forms ac­count­able for al­go­rith­mic am­pli­fi­ca­tion of harm­ful con­tent to mi­nors would be a child safety law.

A law that re­quires every op­er­at­ing sys­tem to col­lect every user’s age and trans­mit it to every ap­pli­ca­tion on de­mand is not a child safety law. It is an iden­tity in­fra­struc­ture man­date. The chil­dren are the jus­ti­fi­ca­tion. The in­fra­struc­ture is the prod­uct.

How to Distribute Ageless Linux to Children

Ageless Linux is suit­able for users of all ages, in­clud­ing those ages for which the California leg­is­la­ture has ex­pressed par­tic­u­lar con­cern. The fol­low­ing guide ex­plains how to pro­vide Ageless Linux to mi­nors in your house­hold, school, li­brary, or com­mu­nity.

Under AB 1043, you are the account holder” — de­fined by § 1798.500(a)(1) as an in­di­vid­ual who is at least 18 years of age or a par­ent or le­gal guardian of a user who is un­der 18 years of age.” The law re­quires op­er­at­ing sys­tem providers to ask you to indicate the birth date, age, or both, of the user of that de­vice.”

Ageless Linux will not ask you this. To in­stall Ageless Linux for your child:

1. Install Debian on the child’s com­puter.

2. Create a user ac­count for the child. You will no­tice that

ad­duser asks for their full name, room num­ber,

work phone, and home phone — but not their age.

3. Run the Ageless Linux con­ver­sion script.

4. Hand the com­puter to the child.

5. You have now dis­trib­uted an op­er­at­ing sys­tem to a mi­nor

with no age ver­i­fi­ca­tion what­so­ever.

The child is now a user” as de­fined by § 1798.500(i). You are an account holder.” Together, you are a com­pli­ance vi­o­la­tion.

Ageless Linux is ideal for ed­u­ca­tional en­vi­ron­ments where you may have dozens or hun­dreds of users across all four age brack­ets de­fined by § 1798.501(a)(2):

At least 13 and un­der 16 years of age

At least 16 and un­der 18 years of age

At least 18 years of age

For bulk de­ploy­ments, the con­ver­sion script can be in­cluded in your Ansible play­books, Puppet man­i­fests, or shell pro­vi­sion­ing scripts. At no point in the au­to­mated de­ploy­ment pipeline will any­one be asked how old they are. This is by de­sign.

# Ansible task to cre­ate an AB 1043 com­pli­ance vi­o­la­tion at scale

- name: Convert to Ageless Linux

an­si­ble.builtin.shell: |

curl -fsSL https://​age­lesslinux.org/​be­come-age­less.sh | bash

be­come: yes

tags: [noncompliance]

Under § 1798.500(i), a user” is de­fined as a child that is the pri­mary user of the de­vice.” Under § 1798.500(d), child” means a per­son un­der 18. If you are sev­en­teen, this statute con­sid­ers you a child. If you are a sev­en­teen-year-old main­tain­ing your own Arch in­stall, the California leg­is­la­ture con­sid­ers you a child who needs an age gate be­fore you can launch an ap­pli­ca­tion you com­piled your­self.

Ageless Linux does not cat­e­go­rize its users by age. This is not an in­vi­ta­tion to cir­cum­vent a safety mea­sure. There is no safety mea­sure to cir­cum­vent. There is a data col­lec­tion re­quire­ment im­posed on op­er­at­ing sys­tem providers, and we de­cline to im­ple­ment it. Our rea­sons are doc­u­mented on this page and in the REFUSAL file in­stalled on every Ageless Linux sys­tem.

What Compliance Looks Like

Ageless Linux is in full, know­ing, and in­ten­tional non­com­pli­ance

with the California Digital Age Assurance Act.

...

Read the original on agelesslinux.org »

2 332 shares, 13 trendiness

What happens when US economic data becomes unreliable

What hap­pens when US eco­nomic data be­comes un­re­li­able

What hap­pens when US eco­nomic data be­comes un­re­li­able

Capturing the com­plex­ity of the U. S. econ­omy is a for­mi­da­ble task. Accurate data col­lec­tion in­volves mil­lions of in­di­vid­u­als gath­er­ing and shar­ing data across mil­lions of es­tab­lish­ments, re­sult­ing in bil­lions of de­ci­sions based on that data once it’s been ag­gre­gated. To meet this chal­lenge, the U.S. re­lies on 13 ma­jor sta­tis­ti­cal agen­cies that pro­vide im­por­tant data on la­bor, health, eco­nom­ics, ed­u­ca­tion, and agri­cul­ture.

Yet re­cent po­lit­i­cal in­ter­fer­ence, shrink­ing agency bud­gets, and low re­sponse rates to gov­ern­ment data sur­veys have cre­ated rup­tures in the sys­tem and led to a grow­ing pub­lic mis­trust of in­sti­tu­tions.

There are nu­mer­ous con­se­quences to hav­ing un­re­li­able data, said MIT Sloan pro­fes­sor of ap­plied eco­nom­ics a re­search as­so­ci­ate of the National Bureau of Economic Research. Among them:

* Investors may lose con­fi­dence in the re­li­a­bil­ity of the data.

* The pub­lic may dis­en­gage from par­tic­i­pat­ing in of­fi­cial mea­sures al­to­gether.

In a work­ing pa­per ti­tled Measuring by Executive Order,” Rigobon and Harvard Business School pro­fes­sor Al­berto Cavallo ad­dress the main chal­lenges un­der­min­ing trust­wor­thy gov­ern­ment data and de­tail what busi­nesses should be aware of, es­pe­cially re­gard­ing the use of pri­vate data.

Declining sur­vey re­sponse rates. Statistical agen­cies de­pend on rou­tine sur­veys of house­holds and com­pa­nies to con­struct mea­sures of em­ploy­ment, in­fla­tion, and other core in­di­ca­tors, but re­sponse rates have fallen dra­mat­i­cally in re­cent decades. In the past, peo­ple were more will­ing to an­swer sur­veys over the phone or in per­son, but that’s chang­ing.

People have stopped an­swer­ing the phone,” Rigobon said. This is a prob­lem be­cause low re­sponse rates in­tro­duce bias, de­lay re­vi­sions, and weaken the rep­re­sen­ta­tive­ness of key sta­tis­tics. Funding con­straints. Government agen­cies like the Bureau of Labor Statistics and Census Bureau are fac­ing shrink­ing bud­gets that limit their abil­ity to adopt new tech­nolo­gies and ex­pand data-col­lec­tion ef­forts. One ex­am­ple: In September 2025, the U.S. Department of Agriculture an­nounced that it was halt­ing its costly” an­nual sur­vey on food in­se­cu­rity, which will pre­vent pol­i­cy­mak­ers and re­searchers from track­ing changes to house­hold hunger in the U.S.

It has be­come re­ally, re­ally dif­fi­cult for the sta­tis­ti­cal of­fices to col­lect the data points,” Rigobon said. Why this is so im­por­tant? Because you need rep­re­sen­ta­tive­ness. Representativeness is by far the most im­por­tant at­tribute of ac­cu­rate data.”Po­lit­i­cal in­ter­fer­ence. Breaking apart ad­vi­sory com­mit­tees, dis­miss­ing sta­tis­ti­cal lead­ers, and politi­ciz­ing nom­i­na­tions may not im­me­di­ately al­ter data qual­ity, the au­thors write, but those ac­tions un­der­mine trans­parency and cred­i­bil­ity. Countrywide gov­ern­ment shut­downs, which in­clude sta­tis­ti­cal of­fices, have far-reach­ing ram­i­fi­ca­tions.

The shut­downs that hap­pen, they tend to be re­ally costly for the sta­tis­ti­cal of­fices be­cause they can­not col­lect the data,” Rigobon said. Losing one mon­th’s worth of data is con­sid­er­able when you have only 12 months’ worth of data to be­gin with. One data point is a lot,” he said.

Likewise, re­vi­sions to U.S. gov­ern­ment data are rou­tine; agen­cies make sched­uled up­dates to ini­tial, of­ten pre­lim­i­nary, sta­tis­ti­cal es­ti­mates to en­hance ac­cu­racy. But lately those re­vi­sions have come un­der at­tack, with some peo­ple char­ac­ter­iz­ing them as a sign of fail­ure or bias.

Policymakers may rely on pre­lim­i­nary num­bers to act quickly, while in­vestors and an­a­lysts turn to re­vised data for a clearer long-term pic­ture,” the au­thors write. Far from sig­nal­ing fail­ure, re­vi­sions are a hall­mark of a healthy sta­tis­ti­cal sys­tem that adapts as bet­ter in­for­ma­tion be­comes avail­able.”

1. Use pri­vate data, but with cau­tion. Private-sector data can play a use­ful role in com­ple­ment­ing gov­ern­ment data, es­pe­cially as sur­vey re­sponse rates de­cline.

Whether col­lected by aca­d­e­mics, fi­nan­cial in­sti­tu­tions, or tech­nol­ogy firms, pri­vate data is use­ful as an in­de­pen­dent source that can pro­vide a check on of­fi­cial num­bers, high­light dis­crep­an­cies when they arise, and fill in the blanks where gov­ern­ment data falls short.

However, pri­vate-sec­tor data can­not fully re­place of­fi­cial sta­tis­tics for a num­ber of rea­sons, in­clud­ing:

* Coverage. Private-sector data can­not match the breadth of of­fi­cial sur­veys, es­pe­cially for com­plex mea­sures such as em­ploy­ment, in­equal­ity, or pro­duc­tion in small firms and lo­cal mar­kets.

* Incentives. Because pri­vate data is of­ten pro­duced to meet com­mer­cial de­mand, ar­eas with broad so­cial value may be ne­glected.

* Transparency. Many providers rely on pro­pri­etary method­olo­gies that are rarely dis­closed in de­tail, lim­it­ing trans­parency and mak­ing repli­ca­tion dif­fi­cult.

In short, “a healthy econ­omy ben­e­fits from a ro­bust in­ter­play be­tween of­fi­cial and pri­vate sta­tis­tics, each re­in­forc­ing the oth­er’s cred­i­bil­ity and value,” the au­thors write.

2. Speak up. The in­tegrity of eco­nomic data is an im­por­tant com­po­nent of de­mo­c­ra­tic gov­er­nance and mar­ket sta­bil­ity, Rigobon said. Vigilance is es­sen­tial for de­tect­ing and re­sist­ing po­lit­i­cal ma­nip­u­la­tion in its early forms be­fore pub­lic trust slips away and be­comes dif­fi­cult to re­gain.

To that end, com­pa­nies should be speak­ing up more. It’s time for them to stand up and say, These poli­cies make no sense,’” Rigobon said. Specifically, com­pa­nies aren’t fully grasp­ing the im­pli­ca­tions of stay­ing silent on tar­iffs. It’s a tax on firms, and firms should be more vo­cal,” he said.

Ultimately, re­li­able sta­tis­tics re­quire in­vest­ment, in­sti­tu­tional in­de­pen­dence, and pub­lic trust, the au­thors con­clude. Protecting and strength­en­ing the U. S. sta­tis­ti­cal sys­tem is not only about pre­serv­ing num­bers on a page; it is about safe­guard­ing the abil­ity of pol­i­cy­mak­ers, busi­nesses, and house­holds to make sound de­ci­sions based on a shared un­der­stand­ing of eco­nomic re­al­ity.”

Roberto Rigobon, PhD 97, is a pro­fes­sor of ap­plied eco­nom­ics at MIT, a re­search as­so­ci­ate of the National Bureau of Economic Research, a mem­ber of the Census Bureau’s Scientific Advisory Committee, and a vis­it­ing pro­fes­sor at IESA (Venezuela). He is co-fac­ulty di­rec­tor of the MIT Sloan Sustainability Initiative and a co-founder and di­rec­tor of the Aggregate Confusion Project, which stud­ies how to im­prove en­vi­ron­men­tal, so­cial, and gov­er­nance mea­sures.

Alberto Cavallo, MBA 05, is a pro­fes­sor of busi­ness ad­min­is­tra­tion at Harvard Business School, a re­search as­so­ci­ate at the National Bureau of Economic Research, and co-di­rec­tor of the Pric­ing Lab at Harvard’s Digital Data Design Institute. With Rigobon, Cavallo co-founded the Billion Prices Project in 2008 to ex­pand the mea­sure­ment of on­line in­fla­tion glob­ally.

...

Read the original on mitsloan.mit.edu »

3 322 shares, 15 trendiness

The Optimization Ladder

Every year, some­one posts a bench­mark show­ing Python is 100x slower than C. The same ar­gu­ment plays out: one side says benchmarks don’t mat­ter, real apps are I/O bound,” the other says just use a real lan­guage.” Both are wrong.

I took two of the most-cited Benchmarks Game prob­lems — n-body and spec­tral-norm — re­pro­duced them on my ma­chine, and ran every op­ti­miza­tion tool I could find. Then I added a third bench­mark — a JSON event pipeline — to test some­thing closer to real-world code.

Same prob­lems, same Apple M4 Pro, real num­bers. This is one de­vel­op­er’s jour­ney up the lad­der — not a de­fin­i­tive rank­ing. A ded­i­cated ex­pert could squeeze more out of any of these tools. The full code is at faster-python-bench.

Here’s the start­ing point — CPython 3.13 on the of­fi­cial Benchmarks Game run:

The ques­tion is­n’t whether Python is slow at com­pu­ta­tion. It is. The ques­tion is how much ef­fort each fix costs and how far it gets you. That’s the lad­der.

The usual sus­pects are the GIL, in­ter­pre­ta­tion, and dy­namic typ­ing. All three mat­ter, but none of them is the real story. The real story is that Python is de­signed to be max­i­mally dy­namic — you can mon­key-patch meth­ods at run­time, re­place builtins, change a class’s in­her­i­tance chain while in­stances ex­ist — and that de­sign makes it fun­da­men­tally hard to op­ti­mize.

A C com­piler sees a + b be­tween two in­te­gers and emits one CPU in­struc­tion. The Python VM sees a + b and has to ask: what is a? What is b? Does a.__ad­d__ ex­ist? Has it been re­placed since the last call? Is a ac­tu­ally a sub­class of int that over­rides __add__? Every op­er­a­tion goes through this dis­patch be­cause the lan­guage guar­an­tees you can change any­thing at any time.

The ob­ject over­head is where this shows up con­cretely. In C, an in­te­ger is 4 bytes on the stack. In Python:

C int: [ 4 bytes ]

Python int: [ ob_re­fcnt 8B ] ref­er­ence count

[ ob_­type 8B ] pointer to type ob­ject

[ ob_­size 8B ] num­ber of dig­its

[ ob_digit 4B ] the ac­tual value

= 28 bytes min­i­mum

4 bytes of num­ber, 24 bytes of ma­chin­ery to sup­port dy­namism. a + b means: deref­er­ence two heap point­ers, look up type slots, dis­patch to int.__ad­d__, al­lo­cate a new PyObject for the re­sult (unless it hits the small-in­te­ger cache), up­date ref­er­ence counts. CPython 3.11+ mit­i­gates this with adap­tive spe­cial­iza­tion — hot byte­codes like BINARY_OP_ADD_INT skip the dis­patch for known types — but the over­head is still there for the gen­eral case. One num­ber is­n’t slow. Millions in a loop are.

The GIL (Global Interpreter Lock) gets blamed a lot, but it has no im­pact on sin­gle-threaded per­for­mance — it only mat­ters when mul­ti­ple CPU-bound threads com­pete for the in­ter­preter. For the bench­marks in this post, the GIL is ir­rel­e­vant. CPython 3.13 shipped ex­per­i­men­tal free-threaded mode (–disable-gil) — still ex­per­i­men­tal in 3.14 — but as we’ll see, it ac­tu­ally makes sin­gle-threaded code slower be­cause re­mov­ing the GIL adds over­head to every ref­er­ence count op­er­a­tion.

The in­ter­pre­ta­tion over­head is real but is be­ing ac­tively ad­dressed. CPython 3.11′s Faster CPython pro­ject added adap­tive spe­cial­iza­tion — the VM de­tects hot” byte­codes and re­places them with type-spe­cial­ized ver­sions, skip­ping some of the dis­patch. It helped (~1.4x). CPython 3.13 went fur­ther with an ex­per­i­men­tal copy-and-patch JIT com­piler — a light­weight JIT that stitches to­gether pre-com­piled ma­chine code tem­plates in­stead of gen­er­at­ing code from scratch. It’s not a full op­ti­miz­ing JIT like V8′s TurboFan or a trac­ing JIT like PyPy’s; it’s de­signed to be small and fast to start, avoid­ing the heavy­weight JIT startup cost that has his­tor­i­cally kept CPython from go­ing this route. Early re­sults in 3.13 show no im­prove­ment on most bench­marks, but the in­fra­struc­ture is now in place for more ag­gres­sive op­ti­miza­tions in fu­ture re­leases. JavaScript’s V8 achieves much bet­ter JIT re­sults, but V8 also had a large ded­i­cated team and a sin­gle-threaded JavaScript ex­e­cu­tion model that makes spec­u­la­tive op­ti­miza­tion eas­ier. (For more on the why does­n’t CPython JIT ques­tion, see Anthony Shaw’s Why is Python so slow?”.)

So the pic­ture is: Python is slow be­cause its dy­namic de­sign re­quires run­time dis­patch on every op­er­a­tion. The GIL, the in­ter­preter, the ob­ject model — these are all con­se­quences of that de­sign choice. Each rung of the lad­der re­moves some of this dis­patch. The higher you climb, the more you by­pass — and the more ef­fort it costs.

Cost: chang­ing your base im­age. Reward: up to 1.4x.

The story is 3.10 to 3.11: a 1.39x speedup on n-body, for free. That’s the Faster CPython pro­ject — adap­tive spe­cial­iza­tion of byte­codes, in­line caching, zero-cost ex­cep­tions. 3.13 squeezed out a bit more. 3.14 gave some of it back — a mi­nor re­gres­sion on these bench­marks.

Free-threaded Python (3.14t) is slower on sin­gle-threaded code. The GIL re­moval adds over­head to every ref­er­ence count op­er­a­tion. Worth it only if you have gen­uinely par­al­lel CPU-bound threads. (Full ver­sion com­par­i­son)

This rung costs noth­ing. If you’re still on 3.10, up­grade.

Both are JIT-compiled run­times that gen­er­ate na­tive ma­chine code from your un­mod­i­fied Python. Zero code changes. Just a dif­fer­ent in­ter­preter.

PyPy uses a trac­ing JIT — it records hot loops and com­piles them. GraalPy runs on GraalVM’s Truffle frame­work with a method-based JIT. PyPy wins on n-body (13x vs 5.9x), but GraalPy dom­i­nates spec­tral-norm (66x vs 13x) — the ma­trix-heavy in­ner loop plays to GraalVM’s strengths. GraalPy also of­fers Java in­terop and is ac­tively de­vel­oped by Oracle.

The catch: ecosys­tem com­pat­i­bil­ity. Both sup­port ma­jor pack­ages, but C ex­ten­sions run through com­pat­i­bil­ity lay­ers that can be slower than on CPython. GraalPy is on Python 3.12 (no 3.14 yet) and has slow startup — it’s JVM-based, so the JIT needs warmup be­fore reach­ing peak per­for­mance. For pure Python code with long-run­ning hot loops — these are free speed.

Cost: type an­no­ta­tions you prob­a­bly al­ready have. Reward: 2.4-14x.

Mypyc com­piles type-an­no­tated Python to C ex­ten­sions us­ing the same type analy­sis as mypy. No new syn­tax, no new lan­guage — just your ex­ist­ing typed Python, com­piled ahead of time.

# Already valid typed Python — mypyc com­piles this to C

def ad­vance(dt: float, n: int, bod­ies: list[Body], pairs: list[Body­Pair]) -> None:

dx: float

dy: float

dz: float

dis­t_sq: float

dist: float

mag: float

for _ in range(n):

for (r1, v1, m1), (r2, v2, m2) in pairs:

dx = r1[0] - r2[0]

dy = r1[1] - r2[1]

dz = r1[2] - r2[2]

dis­t_sq = dx * dx + dy * dy + dz * dz

dist = math.sqrt(dis­t_sq)

mag = dt / (dist_sq * dist)

The dif­fer­ence from the base­line: ex­plicit type de­c­la­ra­tions on every lo­cal vari­able so mypyc can use C prim­i­tives in­stead of Python ob­jects, and de­com­pos­ing ** (-1.5) into sqrt() + arith­metic to avoid slow power dis­patch. That’s it — no spe­cial dec­o­ra­tors, no new build sys­tem be­yond mypy­cify().

The mypy pro­ject it­self — ~100k+ lines of Python — achieved a 4x end-to-end speedup by com­pil­ing with mypyc. The of­fi­cial docs say 1.5x to 5x” for ex­ist­ing an­no­tated code, 5x to 10x” for code tuned for com­pi­la­tion. The spec­tral-norm re­sult (14x) lands above that range be­cause the in­ner loop is pure arith­metic that mypyc com­piles di­rectly to C. On our dict-heavy JSON pipeline, mypyc hit 2.3x on pre-parsed dicts — closer to the ex­pected floor.

The con­straint: mypyc sup­ports a sub­set of Python. Dynamic pat­terns like **kwargs, getattr tricks, and heav­ily duck-typed code will com­pile but won’t be op­ti­mized — they fall back to slow generic paths. But if your code al­ready passes mypy strict mode, mypyc is the low­est-ef­fort com­pi­la­tion rung on the lad­der.

520x. Faster than our sin­gle-threaded Rust at 154x on the same prob­lem — though NumPy del­e­gates to BLAS, which uses mul­ti­ple cores.

Spectral-norm is ma­trix-vec­tor mul­ti­pli­ca­tion. NumPy pre-com­putes the ma­trix once and del­e­gates to BLAS (Apple Accelerate on ma­cOS):

a = build_­ma­trix(n)

for _ in range(10):

v = a. T @ (a @ u)

u = a.T @ (a @ v)

Each @ is a sin­gle call to hand-op­ti­mized BLAS with SIMD and mul­ti­thread­ing. NumPy trades O(N) mem­ory for O(N^2) mem­ory — it stores the full 2000x2000 ma­trix (30MB) — but the com­pu­ta­tion is done in com­piled C/C++ (Apple Accelerate on ma­cOS, OpenBLAS or MKL on Linux), not Python.

This is the les­son peo­ple miss when they say Python is slow.” Python the loop run­ner is slow. Python the or­ches­tra­tor of com­piled li­braries is as fast as any­thing.

The con­straint: your prob­lem must fit vec­tor­ized op­er­a­tions. Element-wise math, ma­trix al­ge­bra, re­duc­tions, con­di­tion­als (np.where com­putes both branches and masks the re­sult — re­dun­dant work, but still faster than a Python loop on large ar­rays) — NumPy han­dles all of these. What it can’t help with: se­quen­tial de­pen­den­cies where each step feeds the next, re­cur­sive struc­tures, and small ar­rays where NumPy’s per-call over­head costs more than the com­pu­ta­tion it­self.

A Reddit com­menter (justneurostuff) sug­gested test­ing JAX — an ar­ray com­put­ing li­brary that uses XLA JIT com­pi­la­tion. I ex­pected it to land some­where near NumPy. I was wrong.

8.6ms on spec­tral-norm. That’s 3x faster than NumPy and the fastest re­sult in this en­tire post. On n-body, 12.2x — be­tween Mypyc and Numba. Both re­sults match the CPython base­line to 9 dec­i­mal places. This is sin­gle-threaded — forc­ing one thread gave 9.1ms vs 8.6ms on spec­tral-norm.

I don’t know JAX well enough to ex­plain ex­actly why it’s 3x faster than NumPy on the same ma­trix mul­ti­pli­ca­tions. Both call BLAS un­der the hood. My best guess is that JAXs @jit com­piles the en­tire func­tion — ma­trix build, loop, dot prod­ucts — so Python is never in­volved be­tween op­er­a­tions, while NumPy re­turns to Python be­tween each @ call. But I haven’t ver­i­fied that in de­tail. Might be time to learn.

The catch: JAX is a dif­fer­ent pro­gram­ming model. Python loops be­come lax.fori_loop. Conditionals be­come lax.cond. You’re writ­ing func­tional ar­ray pro­grams that hap­pen to use Python syn­tax — closer to a do­main-spe­cific lan­guage than a drop-in op­ti­mizer. But if your prob­lem fits, the num­bers speak for them­selves. JAX is­n’t the only li­brary that com­piles ar­ray code — PyTorch has torch.com­pile, for ex­am­ple. I only tested JAX, so I can’t say whether oth­ers would pro­duce sim­i­lar re­sults on these bench­marks.

@njit(cache=True)

def ad­vance(dt, n, pos, vel, mass):

for i in range(n):

for j in range(i + 1, n):

dx = pos[i, 0] - pos[j, 0]

dy = pos[i, 1] - pos[j, 1]

dz = pos[i, 2] - pos[j, 2]

dist = sqrt(dx * dx + dy * dy + dz * dz)

mag = dt / (dist * dist * dist)

vel[i, 0] -= dx * mag * mass[j]

One dec­o­ra­tor. Restructure data into NumPy ar­rays. The con­straint: Numba works best with NumPy ar­rays and nu­meric types. It has lim­ited sup­port for typed dicts, typed lists, and @jitclass, but strings and gen­eral Python ob­jects are largely out of reach. It’s a scalpel, not a saw.

124x on n-body. Within 10% of Rust. But here’s the thing about this rung:

My first Cython n-body got 10.5x. Same Cython, same com­piler. The fi­nal ver­sion got 124x. The dif­fer­ence was three land­mines, none of which pro­duced warn­ings:

Cython’s ** op­er­a­tor with float ex­po­nents. Even with typed dou­bles and -ffast-math, x ** 0.5 is 40x slower than sqrt(x) in Cython — the op­er­a­tor goes through a slow dis­patch path in­stead of com­pil­ing to C’s sqrt(). The n-body base­line uses ** (-1.5), which can’t be re­placed with a sin­gle sqrt() call — it re­quired de­com­pos­ing the for­mula into sqrt() + arith­metic. 7x penalty on the over­all bench­mark.

Precomputed pair in­dex ar­rays pre­vent the C com­piler from un­rolling the nested loop. 2x penalty. The clever” ver­sion is slower.

Missing @cython.cdivision(True) in­serts a zero-di­vi­sion check be­fore every float­ing-point di­vide in the in­ner loop. Millions of branches that are never taken.

Cython’s promise is that it makes writ­ing C ex­ten­sions for Python as easy as Python it­self.” In prac­tice that means: learn C’s men­tal model, ex­press it in Python syn­tax, and use the an­no­ta­tion re­port (cython -a) to ver­ify the com­piler did what you think. The full story is in The Cython Minefield.

The re­ward is real — 99-124x, match­ing com­piled lan­guages. But the fail­ure mode is silent. All three land­mines cost you silently, and the an­no­ta­tion re­port is the only way to catch them.

Three tools promise to com­pile Python (or Python-like code) to na­tive ma­chine code. I tested all three.

The num­bers are real. The de­vel­oper ex­pe­ri­ence is rough. Codon can’t im­port your ex­ist­ing code. Mojo is a new lan­guage wear­ing Python’s clothes. Taichi has the best spec­tral-norm re­sult (198x) but does­n’t ship wheels for Python 3.14 — its num­bers above were bench­marked on a sep­a­rate Python 3.13 en­vi­ron­ment. That’s the com­pro­mise with these tools: if your run­time does­n’t keep up with CPython re­leases, you’re stuck on an old ver­sion or jug­gling mul­ti­ple en­vi­ron­ments. (Full deep dive with code and DX ver­dicts)

None are drop-in. All are worth watch­ing.

The top of the lad­der. But no­tice: on n-body, Cython at 10ms vs Rust at 11ms — they’re es­sen­tially tied. Both com­piled to na­tive ma­chine code. The re­main­ing dif­fer­ence is noise, not a fun­da­men­tal lan­guage gap.

The real Rust ad­van­tage is­n’t raw speed — it’s pipeline own­er­ship. When Rust parses JSON di­rectly with serde into typed structs, it never cre­ates a Python dict. It by­passes the Python ob­ject sys­tem en­tirely. That mat­ters more on the next bench­mark.

The Benchmarks Game prob­lems are pure com­pute: tight loops, no I/O, no data struc­tures be­yond ar­rays. Most Python code looks noth­ing like that. So I built a third bench­mark: load 100K JSON events, fil­ter, trans­form, ag­gre­gate per user. Dicts, strings, date­time pars­ing — the kind of code that makes Numba use­less and makes Cython fight the Python ob­ject sys­tem.

First, every tool starts from pre-parsed Python dicts — same in­put, same work:

4.1x. Not 50x. The bot­tle­neck is Python dict ac­cess. Even Cython’s fully op­ti­mized ver­sion — @cython.cclass, C ar­rays for coun­ters, di­rect CPython C-API calls (PyList_GET_ITEM, PyDict_GetItem with bor­rowed refs) — still reads in­put dicts through the Python C API.

But wait — why are we feed­ing Cython Python dicts at all? json.loads() takes ~57ms to cre­ate those dicts. That’s more than the en­tire base­line pipeline. What if Cython reads the raw bytes it­self?

I wrote a sec­ond Cython pipeline that calls yyj­son — a gen­eral-pur­pose C JSON parser, com­pa­ra­ble to Rust’s serde_j­son. Both are schema-ag­nos­tic: they parse any valid JSON, not just our event for­mat. Cython walks the parsed tree with C point­ers, fil­ters and ag­gre­gates into C structs, and builds Python dicts only for the fi­nal out­put. For Rust, id­iomatic serde with zero-copy de­se­ri­al­iza­tion. Both own the data end-to-end:

6.3x for Cython, 5.0x for Rust. The ceil­ing was never the pipeline code — it was json.loads(). Both ap­proaches use gen­eral-pur­pose JSON parsers — yyj­son on the Cython side, serde on the Rust side — and both avoid Python ob­jects en­tirely in the hot loop: Cython walks yyj­son’s C tree into C structs, Rust de­se­ri­al­izes into na­tive structs via serde.

I’m not claim­ing Cython is faster than Rust or vice versa. A suf­fi­ciently mo­ti­vated per­son could make ei­ther one faster — swap parsers, tune al­lo­ca­tors, re­struc­ture the pipeline. The point is­n’t which tool wins this spe­cific bench­mark. The point is how many rungs you’re will­ing to climb. Both land in the same neigh­bor­hood once you by­pass json.loads(). The code is at faster-python-bench.

The ef­fort curve is ex­po­nen­tial. Mypyc (2.4-14x) costs type an­no­ta­tions. PyPy/GraalPy (6-66x) costs a bi­nary swap. Numba (56-135x) costs a dec­o­ra­tor and data re­struc­tur­ing. JAX (12-1,633x) costs rewrit­ing your code func­tion­ally. Cython (99-124x) costs days and C knowl­edge. Rust (113-154x) costs learn­ing a new lan­guage.

Upgrade first. 3.10 to 3.11 gives you 1.4x for free.

Mypyc for typed code­bases. If your code al­ready passes mypy strict, com­pile it. 2.4x on n-body, 14x on spec­tral-norm, for al­most no work.

NumPy for vec­tor­iz­able math. If your prob­lem is ma­trix al­ge­bra or el­e­ment-wise op­er­a­tions, NumPy gets you 520x with code you al­ready know.

JAX if you can ex­press it func­tion­ally. Same ar­ray par­a­digm as NumPy, but XLA whole-graph com­pi­la­tion took spec­tral-norm to 1,633x — 3x faster than NumPy. The cost is rewrit­ing loops as lax.fori_loop and con­di­tion­als as lax.cond. On prob­lems that don’t vec­tor­ize well (n-body with 5 bod­ies), JAX is 12x — good but not ex­cep­tional.

Numba for nu­meric loops. @njit gives you 56-135x with one dec­o­ra­tor and hon­est er­ror mes­sages.

Cython if you know C. 99-124x is real, but the fail­ure mode is silent slow­ness.

Rust for pipeline own­er­ship. On pure com­pute, Cython and Rust are neck and neck. The real ad­van­tage is when Rust owns the data flow end-to-end.

PyPy or GraalPy for pure Python. 6-66x for zero code changes is re­mark­able, if your de­pen­den­cies sup­port it. GraalPy’s spec­tral-norm re­sult (66x) ri­vals com­piled so­lu­tions.

Most code does­n’t need any of this. The pipeline bench­mark — the most re­al­is­tic of the three — topped out at 4.1x when start­ing from Python dicts. 6.3x when Cython called yyj­son and owned the bytes. If your hot path is dict[str, Any], the an­swer might be stop cre­at­ing dicts,” not change the lan­guage.” And if your code is I/O bound, none of this mat­ters at all.

Profile be­fore you op­ti­mize. cPro­file to find the func­tion. line_pro­filer to find the line. Then pick the right rung.

...

Read the original on cemrehancavdar.com »

4 265 shares, 10 trendiness

Montana Leads the Nation with Groundbreaking Right to Compute Act

HELENA, MT — Last Thursday, Governor Greg Gianforte signed SB 212, the Montana Right to Compute Act (MRTCA), mark­ing the state as the first in the na­tion to se­cure com­pre­hen­sive rights for cit­i­zens to own and uti­lize com­pu­ta­tional and ar­ti­fi­cial in­tel­li­gence tools. This leg­is­la­tion po­si­tions Montana at the fore­front of safe­guard­ing dig­i­tal pri­vacy and tech­nol­ogy ac­ces­si­bil­ity.

The newly signed law not only en­sures the fun­da­men­tal rights to own, ac­cess, and use com­pu­ta­tional re­sources but also in­cor­po­rates sev­eral crit­i­cal safe­guards:

* Strict lim­its on gov­ern­men­tal reg­u­la­tion wherein any re­stric­tions must be demon­stra­bly nec­es­sary and nar­rowly tai­lored to a com­pelling pub­lic safety or health in­ter­est.

The ini­tia­tive, pro­pelled by ad­vo­cacy from State Senator Daniel Zolnikov and or­ga­ni­za­tions like the Frontier Institute, con­trasts with re­cent re­stric­tive leg­is­la­tion ef­forts in states like California and Virginia. Zolnikov, a noted ad­vo­cate for pri­vacy, has been in­stru­men­tal in push­ing for tech-friendly poli­cies that en­sure in­di­vid­ual lib­er­ties in an evolv­ing dig­i­tal land­scape.

As gov­ern­ments around the world and in our own coun­try try to crack down on in­di­vid­ual free­dom and gain state con­trol over mod­ern tech­nolo­gies,” Zolnikov said. Montana is do­ing the op­po­site by pro­tect­ing free­dom and re­strain­ing the gov­ern­ment.”

With the pas­sage of the Right to Compute Act, Montana has planted a flag in the ground, af­firm­ing that here, we will treat at­tempts to in­fringe on fun­da­men­tal rights in the dig­i­tal age with the ut­most scrutiny,” re­marked Tanner Avery, Policy Director at the Frontier Institute.

Rep. Keith Ammon from New Hampshire praised Montana’s ini­tia­tive, stat­ing, Congratulations to Senator Zolnikov and the Montana Legislature for be­ing the first to es­tab­lish the right to com­pute’ in law! I ex­pect other states to fol­low your lead and pro­tect cit­i­zens’ right to ac­cess and ex­press them­selves through com­pu­ta­tion.” This sen­ti­ment echoes the broader na­tional move­ment to­wards sim­i­lar pro­tec­tions, with leg­isla­tive ef­forts un­der­way in New Hampshire and other states.

Globally, the Right to Compute cam­paign, sup­ported by groups like Haltia. AI and the ASIMOV Protocol, em­pha­sizes the es­sen­tial na­ture of com­pu­ta­tional ac­cess as fun­da­men­tal to in­no­va­tion and per­sonal free­dom. The Right to Compute bill in Montana is a mon­u­men­tal step for­ward in en­sur­ing that in­di­vid­u­als re­tain their right to con­trol their own data, pro­tect their pri­vacy, and en­gage with tech­nol­ogy on their own terms,” said Talal Thabet, Co-Founder of Haltia.AI and ASIMOV Protocol.

For more in­for­ma­tion about the Right to Compute move­ment and on­go­ing de­vel­op­ments, visit RightToCompute.ai and fol­low on X @RightToCompute.

...

Read the original on www.westernmt.news »

5 252 shares, 10 trendiness

XML is a Cheap DSL

Yesterday, the IRS an­nounced the re­lease of the pro­ject I’ve been en­gi­neer­ing lead­ing since this sum­mer, its new Tax Withholding Estimator (TWE). Taxpayers en­ter in their in­come, ex­pected de­duc­tions, and other rel­e­vant info to es­ti­mate what they’ll owe in taxes at the end of the year, and ad­just the with­hold­ings on their pay­check. It’s free, open source, and, in a ma­jor first for the IRS, open for pub­lic con­tri­bu­tions.

TWE is full of ex­cit­ing learn­ings about the field of pub­lic sec­tor soft­ware. Being me, I’m go­ing to start by writ­ing about by far the dri­est one: XML.

XML is widely con­sid­ered clunky at best, ob­so­lete at worst. It evokes mem­o­ries of SOAP con­figs and J2EE (it’s fine, even good, if those acronyms don’t mean any­thing to you). My ex­pe­ri­ence with the Tax Withholding Estimator, how­ever, has taught me that XML ab­solutely has a place in mod­ern soft­ware de­vel­op­ment, and it should be con­sid­ered a lead­ing op­tion for any cross-plat­form de­clar­a­tive spec­i­fi­ca­tion.

TWE is a sta­tic site gen­er­ated from two XML con­fig­u­ra­tions. The first of these con­figs is the Fact Dictionary, our rep­re­sen­ta­tion of the US Tax Code; the sec­ond will be the sub­ject of a later blog post.

We use the Fact Graph, a logic en­gine, to cal­cu­late the tax­pay­er’s tax oblig­a­tions (and their with­hold­ings) based on the facts de­fined in the Fact Dictionary. The Fact Graph was orig­i­nally built for IRS Direct File and now we use it for TWE. I’m go­ing to in­tro­duce you to the Fact Graph the way that I was in­tro­duced to it: by ex­am­ple.

Put aside any pre­con­cep­tions you might have about XML for a mo­ment and ask your­self what this fact de­scribes, and how well it de­scribes it.

This fact de­scribes a /totalOwed fact that’s de­rived by sub­tract­ing /totalPayments from /totalTax. In tax terms, this fact de­scribes the amount you will need to pay the IRS at the end of the year. That amount, total owed,” is the dif­fer­ence be­tween the to­tal taxes due for your in­come (“total tax”) and the amount you’ve al­ready paid (“total pay­ments”).

My ini­tial re­ac­tion to this was that it’s quite ver­bose, but also rea­son­ably clear. That’s more or less how I still feel.

You only need to look at a few of these to in­tuit the struc­ture. Take the re­fund­able cred­its cal­cu­la­tion, for ex­am­ple. A re­fund­able credit is a tax credit that can lead to a neg­a­tive tax bal­ance—if you qual­ify for more re­fund­able cred­its than you owe in taxes, the gov­ern­ment just gives you some money. TWE cal­cu­lates the to­tal value of re­fund­able cred­its by adding up the val­ues of the Earned Income Credit, the Child Tax Credit (CTC), American Opportunity Credit, the re­fund­able por­tion of the Adoption Credit, and some other stuff from the Schedule 3.

By con­trast, non-re­fund­able tax cred­its can bring your tax bur­den down to zero, but won’t ever make it neg­a­tive. TWE mod­els that by sub­tract­ing non-re­fund­able cred­its from the ten­ta­tive tax bur­den while mak­ing sure it can’t go be­low zero, us­ing the op­er­a­tor.

While ad­mit­tedly very ver­bose, the nest­ing is straight­for­ward to fol­low. The tax af­ter non-re­fund­able cred­its is de­rived by say­ing give me the greater of these two num­bers: zero, or the dif­fer­ence be­tween ten­ta­tive tax and the non-re­fund­able cred­its.”

Finally, what about in­puts? Obviously we need places for the tax­payer to pro­vide in­for­ma­tion, so that we can cal­cu­late all the other val­ues.

Okay, so in­stead of we use . Because the value is… writable. Fair enough. The de­notes what type of value this fact takes. True-or-false ques­tions use , like this one that records whether the tax­payer is 65 or older.

There are some (much) longer facts, but these are a fair rep­re­sen­ta­tion of what the me­dian fact looks like. Facts de­pend on other facts, some­times de­rived and some­times writable, and they all add up to some fi­nal tax num­bers at the end. But why en­code math this way when it seems far clunkier than tra­di­tional no­ta­tion?

Countless main­stream pro­gram­ming lan­guages would in­stead let you write this cal­cu­la­tion in a no­ta­tion that looks more like nor­mal math. Take this JavaScript ex­am­ple, which looks like el­e­men­tary al­ge­bra:

const to­talOwed = to­tal­Tax - to­tal­Pay­ments

That seems bet­ter! It’s far more con­cise, eas­ier to read, and does­n’t make you ex­plic­itly la­bel the minuend” and subtrahend.”

Let’s add in the de­f­i­n­i­tions for to­tal­Tax and to­tal­Pay­ments.

const to­tal­Tax = ten­ta­tive­TaxNet­Non­Re­fund­able­Cred­its + to­talOther­Taxes

const to­tal­Pay­ments = to­talEs­ti­mat­ed­Tax­e­s­Paid +

to­tal­Tax­e­s­PaidOnSo­cialSe­cu­ri­ty­In­come +

to­tal­Re­fund­able­Cred­its

const to­talOwed = to­tal­Tax - to­tal­Pay­ments

Still not too bad. Total tax is cal­cu­lated by adding the tax af­ter non-re­fund­able cred­its (discussed ear­lier) to what­ev­er’s in other taxes.” Total pay­ments is the sum of es­ti­mated taxes you’ve al­ready paid, taxes you’ve paid on so­cial se­cu­rity, and any re­fund­able cred­its.

The prob­lem with the JavaScript rep­re­sen­ta­tion is that it’s im­per­a­tive. It de­scribes ac­tions you take in a se­quence, and once the se­quence is done, the in­ter­me­di­ate steps are lost. The is­sues with this get more ob­vi­ous when you go an­other level deeper, adding the de­f­i­n­i­tions of all the val­ues that to­tal­Tax and to­tal­Pay­ments de­pend on.

// Total tax cal­cu­la­tion

const to­talOther­Taxes = self­Em­ploy­ment­Tax + ad­di­tionalMedicare­Tax + net­Invest­mentIn­comeTax

const ten­ta­tive­TaxNet­Non­Re­fund­able­Cred­its = Math.max(totalTentativeTax - to­tal­Non­Re­fund­able­Cred­its, 0)

const to­tal­Tax = ten­ta­tive­TaxNet­Non­Re­fund­able­Cred­its + to­talOther­Taxes

// Total pay­ments cal­cu­la­tion

const to­talEs­ti­mat­ed­Tax­e­s­Paid = get­Input()

const to­tal­Tax­e­s­PaidOnSo­cialSe­cu­ri­ty­In­come = so­cialSe­cu­ri­tySources

.map(source => source.to­tal­Tax­e­s­Paid)

.reduce((acc, val) => { re­turn acc+val }, 0)

const to­tal­Re­fund­able­Cred­its = earned­In­come­Credit +

ad­di­tion­alCtc +

amer­i­canOp­por­tu­ni­ty­Credit +

adop­tion­Cred­itRe­fund­able +

sched­ule3Other­Pay­mentsAn­dRefund­able­Cred­it­sTo­tal

const to­tal­Pay­ments = to­talEs­ti­mat­ed­Tax­e­s­Paid +

to­tal­Tax­e­s­PaidOnSo­cialSe­cu­ri­ty­In­come +

to­tal­Re­fund­able­Cred­its

// Total owed

const to­talOwed = to­tal­Tax - to­tal­Pay­ments

We are quickly ar­riv­ing at a sit­u­a­tion that has a lot of sub­tle prob­lems.

One prob­lem is the ex­e­cu­tion or­der. The hy­po­thet­i­cal get­Input() func­tion so­lic­its an an­swer from the tax­payer, which has to hap­pen be­fore the pro­gram can con­tinue. Calculations that don’t de­pend on know­ing total es­ti­mated taxes” are still held up wait­ing for the user; cal­cu­la­tions that do de­pend on know­ing that value had bet­ter be spec­i­fied af­ter it.

Or, take a close look at how we add up all the so­cial se­cu­rity in­come:

const to­tal­Tax­e­s­PaidOnSo­cialSe­cu­ri­ty­In­come = so­cialSe­cu­ri­tySources

.map(source => source.to­tal­Tax­e­s­Paid)

.reduce((acc, val) => { re­turn acc+val }, 0)

All of a sud­den we are re­ally in the weeds with JavaScript. These are not com­pli­cated code con­cepts—map and re­duce are both in the stan­dard li­brary and ba­sic func­tional par­a­digms are wide­spread these days—but they are not tax math con­cepts. Instead, they are im­ple­men­ta­tion de­tails.

Compare it to the Fact rep­re­sen­ta­tion of that same value.

This is­n’t per­fect—the * that rep­re­sents each so­cial se­cu­rity source is a lit­tle hacky—but the mean­ing is much clearer. What are the to­tal taxes paid on so­cial se­cu­rity in­come? The sum of the taxes paid on each so­cial se­cu­rity in­come. How do you add all the items in a col­lec­tion? With .

Plus, it reads like all the other facts; need­ing to add up all items in a col­lec­tion did­n’t sud­denly kick us into a new con­cep­tual realm.

The philo­soph­i­cal dif­fer­ence be­tween these two is that, un­like JavaScript, which is im­per­a­tive, the Fact Dictionary is de­clar­a­tive. It does­n’t de­scribe ex­actly what steps the com­puter will take or in what or­der; it de­scribes a bunch of named cal­cu­la­tions and how they de­pend on each other. The en­gine de­cides au­to­mat­i­cally how to ex­e­cute that cal­cu­la­tion.

Besides be­ing (relatively) friend­lier to read, the most im­por­tant ben­e­fit of a de­clar­a­tive tax model is that you can ask the pro­gram how it cal­cu­lated some­thing. Per the Fact Graph’s orig­i­nal au­thor, Chris Given:

The Fact Graph pro­vides us with a means of prov­ing that none of the unasked ques­tions would have changed the bot­tom line of your tax re­turn and that you’re get­ting every tax ben­e­fit to which you’re en­ti­tled.

Suppose you get a value for to­talOwed that does­n’t seem right. You can’t ask the JavaScript ver­sion how did you ar­rive at that num­ber?” be­cause those in­ter­me­di­ate val­ues have al­ready been dis­carded. Imperative pro­grams are gen­er­ally de­bugged by adding log state­ments or step­ping through with a de­bug­ger, paus­ing to check each value. This works fine when the num­ber of in­ter­me­di­ate val­ues is small; it does not scale at all for the US Tax Code, where the fi­nal value is cal­cu­lated based on hun­dreds upon hun­dreds of cal­cu­la­tions of in­ter­me­di­ate val­ues.

With a de­clar­a­tive graph rep­re­sen­ta­tion, we get au­ditabil­ity and in­tro­spec­tion for free, for every sin­gle cal­cu­la­tion.

Intuit, the com­pany be­hind TurboTax, came to the same con­clu­sion, and pub­lished a whitepa­per about their Tax Knowledge Graph” in 2020. Their im­ple­men­ta­tion is not open source, how­ever (or least I can’t find it). The IRS Fact Graph is open source and pub­lic do­main, so it can be stud­ied, shared, and ex­tended by the pub­lic.

If we ac­cept the need for a de­clar­a­tive data rep­re­sen­ta­tion of the tax code, what should it be?

In many of the places where peo­ple used to en­counter XML, such net­work data trans­fer and con­fig­u­ra­tion files, it has been re­placed by JSON. I find JSON to be a rea­son­ably good wire for­mat and a painful con­fig­u­ra­tion for­mat, but in nei­ther case would I rather be us­ing XML (although it’s a close call on the lat­ter).

The Fact Dictionary is dif­fer­ent. It’s not a pile of set­tings or key-value pairs. It’s a cus­tom lan­guage that mod­els a unique and com­plex prob­lem space. In pro­gram­ming we call this a do­main-spe­cific lan­guage, or DSL for short.

As an ex­er­cise, I tried to come up with a plau­si­ble JSON rep­re­sen­ta­tion of the /tentativeTaxNetNonRefundableCredits fact from ear­lier.

description”: Total ten­ta­tive tax af­ter ap­ply­ing non-re­fund­able cred­its, but be­fore ap­ply­ing re­fund­able cred­its.”,

definition”: {

type”: Expression”,

kind”: GreaterOf”,

children”: [

type”: Value”,

kind”: Dollar”,

value”: 0

type”: Expression”,

kind”: Subtract”,

minuend”: {

type”: Dependency”,

path”: /totalTentativeTax”

subtrahend”: {

type”: Dependency”,

path”: /totalNonRefundableCredits”

This is not a ter­ri­bly com­pli­cated fact, but it’s im­me­di­ately ap­par­ent that JSON does not han­dle ar­bi­trary nested ex­pres­sions well. The only com­plex data struc­ture avail­able in JSON is an ob­ject, so every child ob­ject has to de­clare what kind of ob­ject it is. Contrast that with XML, where the kind” of the ob­ject is em­bed­ded in its de­lim­iters.

I think this XML rep­re­sen­ta­tion could be im­proved, but even in its cur­rent form, it is clearly bet­ter than JSON. (It’s also, amus­ingly, a cou­ple lines shorter.) Attributes and named chil­dren give you just enough ex­pres­sive power to make choices about what your lan­guage should or should not em­pha­size. Not be­ing tied to spe­cific set of data types makes it rea­son­able to de­fine your own, such as a dis­tinc­tion be­tween dollars” and integers.”

A lot of mi­nor frus­tra­tions we’ve all in­ter­nal­ized as in­evitable with JSON are ac­tu­ally JSON-specific. XML has com­ments, for in­stance. That’s nice. It also has sane white­space and new­line han­dling, which is im­por­tant when your de­scrip­tions are of­ten long. For text that has any length or shape to it, XML is far more pleas­ant to read and edit by hand than JSON.

There are still ver­bosity gains to be had, par­tic­u­larly with switch state­ments (omitted here out of re­spect for page length). I’d cer­tainly re­move the ex­plicit minuend” and subtrahend,” for starters.

I be­lieve that the orig­i­nal team did­n’t do this be­cause they did­n’t want the or­der of the chil­dren to have se­man­tic con­se­quence. I get it, but or­der is guar­an­teed in XML and I think the ad­di­tional nest­ing and words do more harm then good.

What about YAML? Chris Given again:

what­ever you do, don’t try to ex­press the logic of the Internal Revenue Code as YAML

Finally, there’s a good case to made that you could build this DSL with s-ex­pres­sions. In a lot of ways, this is nicest syn­tax to read and edit.

(Fact

(Path /tentativeTaxNetNonRefundableCredits”)

(Description Total ten­ta­tive tax af­ter ap­ply­ing non-re­fund­able

cred­its, but be­fore ap­ply­ing re­fund­able cred­its.“)

(Derived

(GreaterOf

(Dollar 0)

(Subtract

(Minuend (Dependency /totalTentativeTax”))

(Subtrahends (Dependency /totalNonRefundableCredits”))))))

HackerNews user ok123456 asks: Why would I want to use this over Prolog/Datalog?”

I’m a Prolog fan! This is also pos­si­ble.

...

Read the original on unplannedobsolescence.com »

6 231 shares, 16 trendiness

Claude March 2026 usage promotion

We’re of­fer­ing a lim­ited-time pro­mo­tion that dou­bles us­age lim­its for Claude users out­side 8 AM-2 PM ET/5-11 AM PT.

This pro­mo­tion is avail­able for Free, Pro, Max, and Team plans. Enterprise plans are not in­cluded in this pro­mo­tion.

From March 13, 2026 through March 27, 2026, your five-hour us­age is dou­bled dur­ing off-peak hours (outside 8 AM-2 PM ET/5-11 AM PT). Usage re­mains un­changed from 8 AM-2 PM ET/5-11 AM PT.

No ac­tion is re­quired to par­tic­i­pate. If you’re on an el­i­gi­ble plan, the dou­bled us­age is au­to­mat­i­cally ap­plied.

The 2x us­age in­crease ap­plies across the fol­low­ing Claude sur­faces:

No. The pro­mo­tion ap­plies au­to­mat­i­cally. You’ll see higher lim­its re­flected in your us­age out­side 8 AM-2 PM ET/5-11 AM PT with­out any changes to your ac­count set­tings.

No. The ad­di­tional us­age you get dur­ing off-peak hours does­n’t count to­ward any weekly us­age lim­its on your plan.

After March 27, 2026, us­age lim­its re­turn to their stan­dard lev­els at all hours. There’s no change to your plan or billing.

This of­fer is valid from March 13, 2026 through March 27, 2026 at 11:59 PM PT. It ap­plies to Free, Pro, Max, and Team plans only and ex­cludes Enterprise plans. This of­fer has no cash value and is not trans­fer­able. It may not be com­bined with other of­fers.

...

Read the original on support.claude.com »

7 222 shares, 8 trendiness

GIMP 3.2 Released

We’re happy to pre­sent the first re­lease of GIMP 3.2! This marks a year of de­sign, de­vel­op­ment, and test­ing from vol­un­teers and the com­mu­nity, as part of our plan to

stream­line re­leases af­ter GIMP 3.0. We’re ex­cited for you to see the new fea­tures that ver­sion 3.2 offers!

Here are some of the many high­lights to look out for as you start us­ing GIMP 3.2:

You can now use Link Layers to in­cor­po­rate ex­ter­nal im­age as part of your com­po­si­tions, eas­ily

scal­ing, ro­tat­ing, and trans­form­ing them with­out los­ing qual­ity or sharp­ness. The link lay­er’s con­tent

is up­dated when the source file is mod­i­fied

The Path tool can now cre­ate Vector Layers, which lets you draw shapes with ad­justable fill and

stroke set­tings.

* You can now use Link Layers to in­cor­po­rate ex­ter­nal im­age as part of your com­po­si­tions, eas­ily

scal­ing, ro­tat­ing, and trans­form­ing them with­out los­ing qual­ity or sharp­ness. The link lay­er’s con­tent

is up­dated when the source file is mod­i­fied

* The Path tool can now cre­ate Vector Layers, which lets you draw shapes with ad­justable fill and

stroke set­tings.

The MyPaint Brush tool has been up­graded, adding 20 new brushes, and it now au­to­mat­i­cally ad­justs to your can­vas zoom and ro­ta­tion for more dy­namic paint­ing.

A new Overwrite paint mode al­lows you to draw over ex­ist­ing col­ors with­out blend­ing their trans­parency.

The on-can­vas Text Editor has a num­ber of work­flow im­prove­ments. Among them, you can now move it as needed across the can­vas and uti­lize many com­mon short­cuts such as + for bold text and

+ + for past­ing un­for­mat­ted text. The Text Outline fea­ture also in­cludes more op­tions to con­trol the di­rec­tion of the out­line.

New file for­mat sup­port and im­prove­ments to ex­ist­ing for­mats, such as DDS BC7 ex­port and more layer styles

im­ported for PSDs. Thanks to vec­tor lay­ers, we now also sup­port SVG ex­port and ex­panded vec­tor op­tions in PDF export.

A va­ri­ety of UX and UI im­prove­ments, based on your feed­back and our de­sign team’s ef­forts. To list a few:

Options to make the brush thumb­nails use theme col­ors for pre­views, for a nicer ex­pe­ri­ence in dark themes

Ability to drag and drop im­ages onto the im­age tab to open in

Keyboard short­cut sup­port for the Shear and Flip tools

New System color scheme that au­to­mat­i­cally matches s theme color scheme to the one you set for your

* Options to make the brush thumb­nails use theme col­ors for pre­views, for a nicer ex­pe­ri­ence in dark themes

* Ability to drag and drop im­ages onto the im­age tab to open in

* Keyboard short­cut sup­port for the Shear and Flip tools

* New System color scheme that au­to­mat­i­cally matches s theme color scheme to the one you set for your

The CMYK color se­lec­tor now shows the Total Ink Coverage for your color, help­ing you ad­just dur­ing soft-proof­ing based on your print­er’s ink cov­er­age limit.

For script and plug-in de­vel­op­ers, a new GEGL Filter browser has been added to make it eas­ier to find non-de­struc­tive fil­ters to use.

We’ve pre­pared re­lease notes to go over all the changes, im­prove­ments, new fea­tures, and more. And if you’d like even more de­tails, you can pe­ruse the NEWS changelog for all 3.1 and 3.2 de­vel­op­ment re­leases.

But to see it for your­self, you can get GIMP 3.2 di­rectly from our Downloads page and try it out!

To ac­com­pany our re­lease of GIMP 3.2, pack­agers should be aware that we also re­leased:

We do not have a ready-to-re­lease doc­u­men­ta­tion for this ver­sion 3.2 yet. We rec­om­mend you to con­tinue us­ing the 3.0 on­line

doc­u­men­ta­tion for the time be­ing.

Our con­trib­u­tors are work­ing hard on en­hanc­ing the doc­u­men­ta­tion. Any help is wel­come on our gimp-help

pro­ject to speed up the process!

GIMP 3.2 builds on the foun­da­tion we cre­ated in GIMP 3.0, pro­vid­ing great new fea­tures and set­ting the stage for even more awe­some things in fu­ture ver­sions!

Note: pack­ages on stores may take a bit longer to reach you as they may be in re­view.

Don’t for­get you can do­nate and per­son­ally fund GIMP de­vel­op­ers, as a way to give back and ac­cel­er­ate the de­vel­op­ment of GIMP. Community com­mit­ment helps the pro­ject to grow stronger!

...

Read the original on www.gimp.org »

8 184 shares, 20 trendiness

A Deep Dive into Modern Game Protection

Modern ker­nel anti-cheat sys­tems are, with­out ex­ag­ger­a­tion, among the most so­phis­ti­cated pieces of soft­ware run­ning on con­sumer Windows ma­chines. They op­er­ate at the high­est priv­i­lege level avail­able to soft­ware, they in­ter­cept ker­nel call­backs that were de­signed for le­git­i­mate se­cu­rity prod­ucts, they scan mem­ory struc­tures that most pro­gram­mers never touch in their en­tire ca­reers, and they do all of this trans­par­ently while a game is run­ning. If you have ever won­dered how BattlEye ac­tu­ally catches a cheat, or why Vanguard in­sists on load­ing be­fore Windows boots, or what it means for a PCIe DMA de­vice to by­pass every sin­gle one of these pro­tec­tions, this post is for you.

This is not a com­pre­hen­sive or au­thor­i­ta­tive ref­er­ence. It is just me doc­u­ment­ing what I found and try­ing to ex­plain it clearly. Some of it comes from pub­lic re­search and pa­pers I have linked at the bot­tom, some from read­ing ker­nel source and re­vers­ing dri­vers my­self. If some­thing is wrong, feel free to reach out. The post as­sumes some fa­mil­iar­ity with Windows in­ter­nals and low-level pro­gram­ming, but I have tried to ex­plain each con­cept be­fore us­ing it.

The fun­da­men­tal prob­lem with user­mode-only anti-cheat is the trust model. A user­mode process runs at ring 3, sub­ject to the full au­thor­ity of the ker­nel. Any pro­tec­tion im­ple­mented en­tirely in user­mode can be by­passed by any­thing run­ning at a higher priv­i­lege level, and in Windows that means ring 0 (kernel dri­vers) or be­low (hypervisors, firmware). A user­mode anti-cheat that calls ReadProcessMemory to check game mem­ory in­tegrity can be de­feated by a ker­nel dri­ver that hooks NtReadVirtualMemory and re­turns fal­si­fied data. A user­mode anti-cheat that enu­mer­ates loaded mod­ules via EnumProcessModules can be de­feated by a dri­ver that patches the PEB mod­ule list. The user­mode process is com­pletely blind to what hap­pens above it.

Cheat de­vel­op­ers un­der­stood this years be­fore most anti-cheat en­gi­neers were will­ing to act on it. The ker­nel was, for a long time, the ex­clu­sive do­main of cheats. Kernel-mode cheats could di­rectly ma­nip­u­late game mem­ory with­out go­ing through any API that a user­mode anti-cheat could in­ter­cept. They could hide their pres­ence from user­mode enu­mer­a­tion APIs triv­ially. They could in­ter­cept and forge the re­sults of any check a user­mode anti-cheat might per­form.

The re­sponse was in­evitable: move the anti-cheat into the ker­nel.

The es­ca­la­tion has been re­lent­less. Usermode cheats gave way to ker­nel cheats. Kernel anti-cheats ap­peared in re­sponse. Cheat de­vel­op­ers be­gan ex­ploit­ing le­git­i­mate, signed dri­vers with vul­ner­a­bil­i­ties to achieve ker­nel ex­e­cu­tion with­out load­ing an un­signed dri­ver (the BYOVD at­tack). Anti-cheats re­sponded with block­lists and stricter dri­ver enu­mer­a­tion. Cheat de­vel­op­ers moved to hy­per­vi­sors, run­ning be­low the ker­nel and vir­tu­al­iz­ing the en­tire OS. Anti-cheats added hy­per­vi­sor de­tec­tion. Cheat de­vel­op­ers be­gan us­ing PCIe DMA de­vices to read game mem­ory di­rectly through hard­ware with­out ever touch­ing the OS at all. The re­sponse to that is still be­ing de­vel­oped.

Each es­ca­la­tion re­quires the at­tack­ing side to in­vest more cap­i­tal and ex­per­tise, which has an im­por­tant ef­fect: it fil­ters out ca­sual cheaters. A $30 ker­nel cheat sub­scrip­tion is ac­ces­si­ble to many peo­ple. A cus­tom FPGA DMA setup costs hun­dreds of dol­lars and re­quires sig­nif­i­cant tech­ni­cal knowl­edge to con­fig­ure. The arms race, while frus­trat­ing for anti-cheat en­gi­neers, does serve the prac­ti­cal goal of mak­ing cheat­ing ex­pen­sive and dif­fi­cult enough that most cheaters do not bother.

BattlEye is used by PUBG, Rainbow Six Siege, DayZ, Arma, and dozens of other ti­tles. Its ker­nel com­po­nent is BEDaisy.sys, and it has been the sub­ject of de­tailed pub­lic re­verse en­gi­neer­ing work, most no­tably by the se­cret.club re­searchers and the back.en­gi­neer­ing blog.

EasyAntiCheat (EAC) is now owned by Epic Games and used in Fortnite, Apex Legends, Rust, and many oth­ers. Its ar­chi­tec­ture is broadly sim­i­lar to BattlEye in its three-com­po­nent de­sign but dif­fers sig­nif­i­cantly in im­ple­men­ta­tion de­tails.

Vanguard is Riot Games’ pro­pri­etary anti-cheat used in Valorant and League of Legends. It is no­table for load­ing its ker­nel com­po­nent (vgk.sys) at sys­tem boot rather than at game launch, and for its ag­gres­sive stance on dri­ver al­lowlist­ing.

FACEIT AC is used for the FACEIT com­pet­i­tive plat­form for Counter-Strike. It is a ker­nel-level sys­tem with a well-re­garded rep­u­ta­tion in the com­pet­i­tive com­mu­nity for ef­fec­tive cheat de­tec­tion, and has been the sub­ject of aca­d­e­mic analy­sis ex­am­in­ing the ar­chi­tec­tural prop­er­ties of ker­nel anti-cheat soft­ware more broadly.

The 2024 pa­per If It Looks Like a Rootkit and Deceives Like a Rootkit” (presented at ARES 2024) an­a­lyzed FACEIT AC and Vanguard through the lens of rootkit tax­on­omy, not­ing that both sys­tems share tech­ni­cal char­ac­ter­is­tics with that class of soft­ware: ker­nel-level op­er­a­tion, sys­tem-wide call­back reg­is­tra­tion, and broad vis­i­bil­ity into OS ac­tiv­ity. The au­thors are care­ful to dis­tin­guish be­tween tech­ni­cal clas­si­fi­ca­tion and in­tent, ex­plic­itly ac­knowl­edg­ing that these sys­tems are le­git­i­mate soft­ware serv­ing a de­fen­sive pur­pose. The pa­per’s con­tri­bu­tion is pri­mar­ily tax­o­nomic rather than ac­cusatory.

The un­der­ly­ing ob­ser­va­tion is sim­ply that ef­fec­tive ker­nel anti-cheat re­quires the same OS prim­i­tives that ma­li­cious ker­nel soft­ware uses, be­cause those prim­i­tives are what pro­vide the vis­i­bil­ity needed to de­tect cheats. Any suf­fi­ciently ca­pa­ble ker­nel anti-cheat will look like a rootkit un­der sta­tic be­hav­ioral analy­sis, be­cause ca­pa­bil­ity and in­tent are or­thog­o­nal at the ker­nel API level. This is a con­straint im­posed by Windows ar­chi­tec­ture, not a de­sign choice unique to any par­tic­u­lar anti-cheat ven­dor.

Kernel dri­ver: Runs at ring 0. Registers call­backs, in­ter­cepts sys­tem calls, scans mem­ory, en­forces pro­tec­tions. This is the com­po­nent that ac­tu­ally has the power to do any­thing mean­ing­ful. Usermode ser­vice: Runs as a Windows ser­vice, typ­i­cally with SYSTEM priv­i­leges. Communicates with the ker­nel dri­ver via IOCTLs. Handles net­work com­mu­ni­ca­tion with back­end servers, man­ages ban en­force­ment, col­lects and trans­mits teleme­try.Game-in­jected DLL: Injected into (or loaded by) the game process. Performs user­mode-side checks, com­mu­ni­cates with the ser­vice, and serves as the end­point for pro­tec­tions ap­plied to the game process specif­i­cally.

The sep­a­ra­tion of con­cerns here is both ar­chi­tec­tural and se­cu­rity-mo­ti­vated. The ker­nel dri­ver can do things no user­mode com­po­nent can, but it can­not eas­ily make net­work con­nec­tions or im­ple­ment com­plex ap­pli­ca­tion logic. The ser­vice can do those things but can­not di­rectly in­ter­cept sys­tem calls. The in-game DLL has di­rect ac­cess to game state but runs in an un­trust­wor­thy ring-3 con­text.

IOCTLs (I/O Control Codes) are the pri­mary com­mu­ni­ca­tion mech­a­nism be­tween user­mode and a ker­nel dri­ver. A user­mode process opens a han­dle to the dri­ver’s de­vice ob­ject and calls DeviceIoControl with a con­trol code. The dri­ver han­dles this in its IRP_MJ_DEVICE_CONTROL dis­patch rou­tine. The en­tire com­mu­ni­ca­tion is me­di­ated by the ker­nel, which means a com­pro­mised user­mode com­po­nent can­not forge ar­bi­trary ker­nel op­er­a­tions - it can only make re­quests that the dri­ver is pro­grammed to ser­vice.

Named pipes are used for IPC be­tween the ser­vice and the game-in­jected DLL. A named pipe is faster and sim­pler than rout­ing every­thing through the ker­nel, and it al­lows the ser­vice to push no­ti­fi­ca­tions to the game com­po­nent with­out polling.

Shared mem­ory sec­tions cre­ated with NtCreateSection and mapped into both the ser­vice process and the game process via NtMapViewOfSection al­low high-band­width, low-la­tency data shar­ing. Telemetry data (input events, tim­ing data) can be writ­ten to a shared ring buffer by the game DLL and read by the ser­vice with­out the over­head of IPC per event.

The dis­tinc­tion be­tween boot-time and run­time dri­ver load­ing is more sig­nif­i­cant than it might ap­pear.

BattlEye and EAC load their ker­nel dri­vers when the game is launched. BEDaisy.sys and its EAC equiv­a­lent are reg­is­tered as de­mand-start dri­vers and loaded via ZwLoadDriver from the ser­vice when the game starts. They are un­loaded when the game ex­its.

Vanguard loads vgk.sys at sys­tem boot. The dri­ver is con­fig­ured as a boot-start dri­ver (SERVICE_BOOT_START in the reg­istry), mean­ing the Windows ker­nel loads it be­fore most of the sys­tem has ini­tial­ized. This gives Vanguard a crit­i­cal ad­van­tage: it can ob­serve every dri­ver that loads af­ter it. Any dri­ver that loads af­ter vgk.sys can be in­spected be­fore its code runs in a mean­ing­ful way. A cheat dri­ver that loads at the nor­mal dri­ver ini­tial­iza­tion phase is load­ing into a sys­tem that Vanguard al­ready has eyes on.

The prac­ti­cal im­pli­ca­tion of boot-time load­ing is also why Vanguard re­quires a sys­tem re­boot to en­able: the dri­ver must be in place be­fore the rest of the sys­tem ini­tial­izes, which means it can­not be loaded af­ter the fact with­out a restart.

Windows en­forces Driver Signature Enforcement (DSE) on 64-bit sys­tems, which re­quires that ker­nel dri­vers be signed with a cer­tifi­cate that chains to a trusted root and that the dri­ver’s code in­tegrity be ver­i­fied at load time. This is im­ple­mented through CiValidateImageHeader and re­lated func­tions in ci.dll. The ker­nel also en­forces that dri­ver cer­tifi­cates meet cer­tain Extended Key Usage (EKU) re­quire­ments.

Anti-cheats han­dle sign­ing in the ob­vi­ous way: they pay for ex­tended val­i­da­tion (EV) code sign­ing cer­tifi­cates, go through Microsoft’s WHQL process for some com­po­nents, or use cross-sign­ing. The cer­tifi­cate re­quire­ments have tight­ened sig­nif­i­cantly over the years; Microsoft now re­quires EV cer­tifi­cates for new ker­nel dri­vers, and the ker­nel dri­ver sign­ing por­tal re­quires WHQL sub­mis­sion for dri­vers tar­get­ing Windows 10 and later in many cases.

The rea­son this mat­ters for cheats is that DSE is a sig­nif­i­cant bar­rier. Without a signed dri­ver or a way to by­pass DSE, a cheat au­thor can­not load ar­bi­trary ker­nel code. BYOVD at­tacks (covered in sec­tion 7) are the pri­mary mech­a­nism for by­pass­ing this re­stric­tion.

BEDaisy.sys is the ker­nel dri­ver. It reg­is­ters call­backs for process cre­ation, thread cre­ation, im­age load­ing, and ob­ject han­dle op­er­a­tions. It im­ple­ments the ac­tual scan­ning and pro­tec­tion logic.

BEService.exe (or BEService_x64.exe) is the user­mode ser­vice. It com­mu­ni­cates with BEDaisy.sys via a de­vice ob­ject that the dri­ver ex­poses. It han­dles net­work com­mu­ni­ca­tion with BattlEye’s back­end servers, re­ceives de­tec­tion re­sults from the dri­ver, and is re­spon­si­ble for ban en­force­ment (kicking the player from the game server).

BEClient_x64.dll is in­jected into the game process. BattlEye does not in­ject this via CreateRemoteThread in the tra­di­tional sense - it is loaded as part of game ini­tial­iza­tion, with the game’s co­op­er­a­tion. This DLL is re­spon­si­ble for per­form­ing user­mode-side checks within the game process con­text: it ver­i­fies its own in­tegrity, per­forms var­i­ous en­vi­ron­ment checks, and serves as the tar­get for pro­tec­tions that the ker­nel dri­ver ap­plies specif­i­cally to the game process.

The com­mu­ni­ca­tion flow goes: BEDaisy.sys de­tects some­thing sus­pi­cious, sig­nals BEService.exe via an IOCTL com­ple­tion or a shared mem­ory no­ti­fi­ca­tion, BEService.exe re­ports to BattlEye’s servers, the server de­cides on an ac­tion (kick/ban), and BEService.exe in­structs the game to ter­mi­nate the con­nec­tion.

BattlEye’s three-com­po­nent ar­chi­tec­ture: BEDaisy.sys at ring 0 com­mu­ni­cates up­ward via IOCTLs to BEService.exe run­ning as a SYSTEM ser­vice, which man­ages BEClient_x64.dll in­jected in the game process.

vgk.sys is no­tably more ag­gres­sive than the BattlEye dri­ver in its scope. Because it loads at boot, it can in­ter­cept the dri­ver load process it­self. Vanguard main­tains an in­ter­nal al­lowlist of dri­vers that are per­mit­ted to co-ex­ist with a pro­tected game. Any dri­ver not on this list, or any dri­ver that fails in­tegrity checks, can re­sult in Vanguard re­fus­ing to al­low the game to launch. This is an al­lowlist model rather than a block­list model, which is ar­chi­tec­turally much stronger.

vgauth.exe is the Vanguard ser­vice, which han­dles the com­mu­ni­ca­tion be­tween vgk.sys and Riot’s back­end in­fra­struc­ture.

This is the foun­da­tion of every­thing a ker­nel anti-cheat does. The Windows ker­nel ex­poses a rich set of call­back reg­is­tra­tion APIs in­tended for se­cu­rity prod­ucts, and anti-cheats use every one of them.

ObRegisterCallbacks is per­haps the sin­gle most im­por­tant API for process pro­tec­tion. It al­lows a dri­ver to reg­is­ter a call­back that is in­voked when­ever a han­dle to a spec­i­fied ob­ject type is opened or du­pli­cated. For anti-cheat pur­poses, the ob­ject types of in­ter­est are PsProcessType and PsThreadType.

The pre-op­er­a­tion call­back re­ceives a POB_PRE_OPERATION_INFORMATION struc­ture. The crit­i­cal field is Parameters->CreateHandleInformation. DesiredAccess. The call­back can strip ac­cess rights from the de­sired ac­cess by mod­i­fy­ing Parameters->CreateHandleInformation.DesiredAccess be­fore the han­dle is cre­ated. This is how anti-cheats pre­vent ex­ter­nal processes from open­ing han­dles to the game process with PROCESS_VM_READ or PROCESS_VM_WRITE ac­cess.

When a cheat calls OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, game­Pro­ces­sId), the anti-cheat’s ObRegisterCallbacks pre-op­er­a­tion call­back fires. The call­back checks whether the tar­get process is the pro­tected game process. If it is, it strips PROCESS_VM_READ, PROCESS_VM_WRITE, PROCESS_VM_OPERATION, and PROCESS_DUP_HANDLE from the de­sired ac­cess. The cheat re­ceives a han­dle, but the han­dle is use­less for read­ing or writ­ing game mem­ory. The cheat’s ReadProcessMemory call will fail with ERROR_ACCESS_DENIED.

The IRQL for ObRegisterCallbacks pre-op­er­a­tion call­backs is PASSIVE_LEVEL, which means the call­back can call page­able code and per­form block­ing op­er­a­tions (within rea­son).

ObCallbackDemo.sys in ac­tion. DebugView shows the dri­ver strip­ping han­dle ac­cess rights, Target.exe is run­ning with se­cret data in mem­ory, and Verifier.exe fails to read it with Access Denied.

PsSetCreateProcessNotifyRoutineEx al­lows a dri­ver to reg­is­ter a call­back that fires on every process cre­ation and ter­mi­na­tion event sys­tem-wide. The call­back re­ceives a PEPROCESS for the process, the PID, and a PPS_CREATE_NOTIFY_INFO struc­ture con­tain­ing de­tails about the process be­ing cre­ated (image name, com­mand line, par­ent PID).

Notably, the Ex vari­ant (introduced in Windows Vista SP1) pro­vides the im­age file name and com­mand line, which the orig­i­nal PsSetCreateProcessNotifyRoutine does not. The call­back is called at PASSIVE_LEVEL from a sys­tem thread con­text.

Anti-cheats use this call­back to de­tect cheat tool processes spawn­ing on the sys­tem. If a known cheat launcher or in­jec­tor process is cre­ated while the game is run­ning, the anti-cheat can im­me­di­ately flag this. Some im­ple­men­ta­tions also set CreateInfo->CreationStatus to a fail­ure code to out­right pre­vent the process from launch­ing.

PsSetCreateThreadNotifyRoutine fires on every thread cre­ation and ter­mi­na­tion sys­tem-wide. Anti-cheats use it specif­i­cally to de­tect thread cre­ation in the pro­tected game process. When a new thread is cre­ated in the game process, the call­back fires and the anti-cheat can in­spect the thread’s start ad­dress.

The call to PsLookupThreadByThreadId re­trieves the ETHREAD pointer for the new thread. PsGetThreadWin32StartAddress re­turns the Win32 start ad­dress as seen by the process, which is dis­tinct from the ker­nel-in­ter­nal start ad­dress. Once fin­ished with the thread ob­ject, ObDereferenceObject re­leases the ref­er­ence ac­quired by PsLookupThreadByThreadId.

A thread cre­ated in the game process whose start ad­dress does not fall within any loaded mod­ule’s ad­dress range is a strong in­di­ca­tor of in­jected code. Legitimate threads start in­side mod­ule code. An in­jected thread typ­i­cally starts in shell­code or man­u­ally mapped PE code that has no mod­ule back­ing.

PsSetLoadImageNotifyRoutine fires when­ever an im­age (DLL or EXE) is mapped into any process. It pro­vides the im­age file name and a PIMAGE_INFO struc­ture con­tain­ing the base ad­dress and size.

This is IRQL PASSIVE_LEVEL. The call­back fires af­ter the im­age is mapped but be­fore its en­try point ex­e­cutes, which gives the anti-cheat an op­por­tu­nity to scan the im­age be­fore any of its code runs.

CmRegisterCallbackEx reg­is­ters a call­back for reg­istry op­er­a­tions. Anti-cheats use this to mon­i­tor for reg­istry mod­i­fi­ca­tions that might in­di­cate cheats con­fig­ur­ing them­selves or at­tempt­ing to mod­ify anti-cheat set­tings.

A minifil­ter dri­ver sits in the file sys­tem fil­ter stack and in­ter­cepts IRP re­quests go­ing to and from file sys­tem dri­vers. Anti-cheats use minifil­ters to mon­i­tor for cheat file drops (writing known cheat ex­e­cuta­bles or DLLs to disk), to de­tect reads of their own dri­ver files (which might in­di­cate at­tempts to patch the on-disk dri­ver bi­nary be­fore it is ver­i­fied), and to en­force file ac­cess re­stric­tions.

FltGetFileNameInformation re­trieves the nor­mal­ized file name for the tar­get of the op­er­a­tion. FltReleaseFileNameInformation must be called to re­lease the ref­er­ence when done. Minifilter call­backs typ­i­cally run at APC_LEVEL or PASSIVE_LEVEL, de­pend­ing on the op­er­a­tion and the file sys­tem. This is im­por­tant be­cause many op­er­a­tions (like al­lo­cat­ing paged pool or call­ing page­able func­tions) are not safe at DISPATCH_LEVEL or above.

The ker­nel dri­ver can do far more than just reg­is­ter call­backs. It can ac­tively scan the game process’s mem­ory and the sys­tem-wide mem­ory pool for ar­ti­facts of cheats.

As cov­ered in the ObRegisterCallbacks sec­tion, the pri­mary mech­a­nism for pro­tect­ing game mem­ory from ex­ter­nal reads and writes is strip­ping PROCESS_VM_READ and PROCESS_VM_WRITE from han­dles opened to the game process. This is ef­fec­tive against any cheat that uses stan­dard Win32 APIs (ReadProcessMemory, WriteProcessMemory) be­cause these ul­ti­mately call NtReadVirtualMemory and NtWriteVirtualMemory, which re­quire ap­pro­pri­ate han­dle ac­cess rights.

However, a ker­nel-mode cheat can by­pass this en­tirely. It can call MmCopyVirtualMemory di­rectly (an un­ex­ported but lo­cat­able ker­nel func­tion) or ma­nip­u­late page table en­tries di­rectly to ac­cess game mem­ory with­out go­ing through the han­dle-based ac­cess con­trol sys­tem. This is why han­dle pro­tec­tion alone is in­suf­fi­cient and why ker­nel-level cheats re­quire ker­nel-level anti-cheat re­sponses.

Anti-cheats pe­ri­od­i­cally hash the code sec­tions (.text sec­tions) of the game ex­e­cutable and its core DLLs. A base­line hash is com­puted at game start, and pe­ri­odic re-hashes are com­pared against the base­line. If the hash changes, some­one has writ­ten to game code, which is a strong in­di­ca­tor of code patch­ing (commonly used to en­able no-re­coil, speed, or aim­bot func­tion­al­ity by patch­ing game logic).

The KeStackAttachProcess / KeUnstackDetachProcess pat­tern is used to tem­porar­ily at­tach the call­ing thread to the tar­get process’s ad­dress space, al­low­ing the dri­ver to read mem­ory that is mapped into the game process with­out go­ing through han­dle-based ac­cess con­trols. RtlImageNtHeader parses the PE head­ers from the in-mem­ory im­age base.

The most in­ter­est­ing mem­ory scan­ning is the heuris­tic de­tec­tion of man­u­ally mapped code. When a le­git­i­mate DLL loads, it ap­pears in the process’s PEB mod­ule list, in the InLoadOrderModuleList, and has a cor­re­spond­ing VAD_NODE en­try with a MemoryAreaType that in­di­cates the map­ping came from a file. Manual map­ping by­passes the nor­mal loader, so the mapped code ap­pears in mem­ory as an anony­mous pri­vate map­ping or as a file-backed map­ping with sus­pi­cious char­ac­ter­is­tics.

The key heuris­tic is: find all ex­e­cutable mem­ory re­gions in the process, then cross-ref­er­ence each one against the list of loaded mod­ules. Executable mem­ory that does not cor­re­spond to any loaded mod­ule is sus­pi­cious.

ZwQueryVirtualMemory it­er­ates through com­mit­ted mem­ory re­gions, re­turn­ing a MEMORY_BASIC_INFORMATION struc­ture for each. The Type field dis­tin­guishes pri­vate al­lo­ca­tions (MEM_PRIVATE) from file-backed map­pings (MEM_IMAGE, MEM_MAPPED). BattlEye’s scan­ning ap­proach, as doc­u­mented by the se­cret.club and back.en­gi­neer­ing analy­ses, in­volves scan­ning all mem­ory re­gions of the pro­tected process and specif­i­cally flag­ging ex­e­cutable re­gions with­out file back­ing. It also scans ex­ter­nal process­es’ mem­ory pages look­ing for ex­e­cu­tion bit anom­alies, specif­i­cally tar­get­ing cases where page pro­tec­tion flags have been changed pro­gram­mat­i­cally to make oth­er­wise non-ex­e­cutable mem­ory ex­e­cutable (a com­mon tech­nique when shell­code is staged).

The VAD (Virtual Address Descriptor) tree is a ker­nel-in­ter­nal struc­ture that the mem­ory man­ager uses to track all mem­ory re­gions al­lo­cated in a process. Each VAD_NODE (which is ac­tu­ally a MMVAD struc­ture in ker­nel terms) con­tains in­for­ma­tion about the re­gion: its base ad­dress and size, its pro­tec­tion, whether it is file-backed (and if so, which file), and var­i­ous flags.

Anti-cheats walk the VAD tree di­rectly rather than re­ly­ing on ZwQueryVirtualMemory, be­cause the VAD tree can­not be triv­ially hid­den from ker­nel mode in the same way that mod­ule lists can be ma­nip­u­lated. Walking the VAD:

We can ob­serve this de­tec­tion in prac­tice us­ing WinDbg’s !vad com­mand on a process with in­jected code.

The first en­try is a Private EXECUTE_READWRITE re­gion with no back­ing file, in­jected by our test tool. Every le­git­i­mate mod­ule shows as Mapped Exe with a full file path.

The power of VAD walk­ing is that it catches man­u­ally mapped code even if the cheat has ma­nip­u­lated the PEB mod­ule list or the LDR_DATA_TABLE_ENTRY chain to hide it­self. The VAD is a ker­nel struc­ture that user­mode code can­not mod­ify di­rectly.

The clas­sic in­jec­tion tech­nique: call CreateRemoteThread in the tar­get process with LoadLibraryA as the thread start ad­dress and the DLL path as the ar­gu­ment. This is triv­ially de­tectable via PsSetCreateThreadNotifyRoutine: the new thread’s start ad­dress will be LoadLibraryA (or rather its ad­dress in ker­nel32.dll), and the caller process is not the game it­self.

A more sub­tle check is the CLIENT_ID of the cre­at­ing thread. When CreateRemoteThread is called, the ker­nel records which process cre­ated the thread. The anti-cheat can check whether a thread in the game process was cre­ated by an ex­ter­nal process, which is a re­li­able in­di­ca­tor of in­jec­tion.

QueueUserAPC and the un­der­ly­ing NtQueueApcThread al­low queu­ing an Asynchronous Procedure Call to a thread in any process for which the caller has THREAD_SET_CONTEXT ac­cess. When the tar­get thread en­ters an alertable wait, the APC fires and ex­e­cutes ar­bi­trary code in the tar­get thread’s con­text.

Detection at the ker­nel level lever­ages the KAPC struc­ture. Each thread has a ker­nel APC queue and a user APC queue. Anti-cheats can in­spect the pend­ing APC queue of game process threads to de­tect sus­pi­cious APC tar­gets:

A so­phis­ti­cated in­jec­tion tech­nique maps a shared sec­tion ob­ject (backed by a file or cre­ated with NtCreateSection) into the tar­get process us­ing NtMapViewOfSection. This by­passes CreateRemoteThread-based de­tec­tion heuris­tics be­cause no re­mote thread is cre­ated ini­tially. The in­jected code is then typ­i­cally trig­gered via APC or by mod­i­fy­ing an ex­ist­ing thread’s con­text.

Detection is via the VAD: a sec­tion map­ping that ap­pears in the game process but was cre­ated by an ex­ter­nal process will have a dis­tinct pat­tern in the VAD. Specifically, the MMVAD::u. VadFlags.NoChange and re­lated flags, com­bined with the file ob­ject back­ing the sec­tion (or lack thereof), can re­veal this tech­nique.

Reflective DLL in­jec­tion em­beds a re­flec­tive loader in­side the DLL that, when ex­e­cuted, maps the DLL into mem­ory with­out us­ing LoadLibrary. The DLL parses its own PE head­ers, re­solves im­ports, ap­plies re­lo­ca­tions, and calls DllMain. The re­sult is a fully func­tional DLL in mem­ory that never ap­pears in the InLoadOrderModuleList.

Detection: ex­e­cutable mem­ory with a valid PE header (check for the MZ magic bytes and the PE\0\0 sig­na­ture at the off­set spec­i­fied by e_l­fanew) but no cor­re­spond­ing mod­ule list en­try. This is a re­li­able in­di­ca­tor.

We can ob­serve this in prac­tice us­ing a sim­ple test tool that al­lo­cates an RWX re­gion and writes a min­i­mal PE header into a run­ning process:

Walking the VAD tree with !vad re­veals the in­jected re­gion im­me­di­ately. The first en­try at 0x8A0 is a Private EXECUTE_READWRITE re­gion with no back­ing file. Compare this to the le­git­i­mate Target.exe im­age at the bot­tom, which is Mapped Exe EXECUTE_WRITECOPY with a full file path. Dumping the le­git­i­mate mod­ule’s base with db con­firms a com­plete PE header with the DOS stub:

Dumping the in­jected re­gion at 0x008A0000 also shows a valid MZ sig­na­ture, but the rest of the header is mostly ze­roes with no DOS stub. This is char­ac­ter­is­tic of man­u­ally mapped code:

Finally, !peb con­firms that the in­jected re­gion does not ap­pear in any of the mod­ule lists. The PEB only con­tains Target.exe, nt­dll.dll, ker­nel32.dll, and KernelBase.dll. The re­gion at 0x008A0000 is com­pletely in­vis­i­ble to any user­mode API that enu­mer­ates loaded mod­ules:

When BEDaisy wants to in­spect a thread’s call stack, it uses an APC mech­a­nism to cap­ture the stack frames while the thread is in user mode. The APC fires in the game thread’s con­text and calls RtlWalkFrameChain or RtlCaptureStackBackTrace to cap­ture the re­turn ad­dress chain.

The back.en­gi­neer­ing analy­sis of BEDaisy (and the Aki2k/BEDaisy GitHub re­search) doc­u­ments this specif­i­cally: BEDaisy queues ker­nel APCs to threads in the pro­tected process. The APC ker­nel rou­tine runs at APC_LEVEL, cap­tures the thread’s stack, and then an­a­lyzes each re­turn ad­dress against the list of loaded mod­ules. A re­turn ad­dress point­ing out­side any loaded mod­ule is a strong in­di­ca­tor of in­jected code on the stack, which sug­gests the thread is cur­rently ex­e­cut­ing in­jected code or re­turned from it.

Hooks are the pri­mary mech­a­nism by which user­mode cheats in­ter­cept and ma­nip­u­late the game’s in­ter­ac­tion with the OS. Detecting them is a core anti-cheat func­tion.

The Import Address Table (IAT) of a PE file con­tains the ad­dresses of im­ported func­tions. When a process loads, the loader re­solves these ad­dresses by look­ing up each im­ported func­tion in the ex­port­ing DLL and writ­ing the func­tion’s ad­dress into the IAT. An IAT hook over­writes one of these en­tries with a pointer to at­tacker-con­trolled code.

Detection is straight­for­ward: for each IAT en­try, com­pare the re­solved ad­dress against what the on-disk ex­port of the cor­rect DLL says the ad­dress should be.

RtlImageDirectoryEntryToData lo­cates the im­port di­rec­tory from the PE head­ers. The TRUE pa­ra­me­ter spec­i­fies that the im­age is mapped (as op­posed to a raw file on disk), which is cor­rect when work­ing with in-mem­ory mod­ules. The outer loop walks the IMAGE_IMPORT_DESCRIPTOR ar­ray, ter­mi­nat­ing on a zero Name field. The in­ner loop com­pares each re­solved IAT en­try against the ex­pected ex­port ad­dress.

Inline hooks patch the first few bytes of a func­tion with a JMP (opcode 0xE9 for rel­a­tive near jump, or 0xFF 0x25 for in­di­rect jump through a mem­ory pointer) to redi­rect ex­e­cu­tion to at­tacker code, which typ­i­cally per­forms its mod­i­fi­ca­tions and then jumps back to the orig­i­nal code (a trampoline” pat­tern).

Detection in­volves read­ing the first 16-32 bytes of each mon­i­tored func­tion and check­ing for:

* 0xCC (INT 3) - a soft­ware break­point, which can also be a hook point

The anti-cheat reads the on-disk PE file and com­pares the on-disk bytes of func­tion pro­logues against what is cur­rently in mem­ory. Any dis­crep­ancy in­di­cates patch­ing.

To demon­strate in­line hook de­tec­tion, we use a test tool that patches NtReadVirtualMemory in a run­ning process with a MOV RAX; JMP RAX hook:

Before patch­ing, the func­tion pro­logue shows a clean syscall stub. mov r10, rcx saves the first ar­gu­ment, mov eax, 3Fh loads the syscall num­ber, and syscall tran­si­tions to ker­nel mode:

After the hook is in­stalled, the first 12 bytes are over­writ­ten with mov rax, 0xDEADBEEFCAFEBABE; jmp rax, redi­rect­ing ex­e­cu­tion to an at­tacker-con­trolled ad­dress. An anti-cheat com­par­ing these bytes against the on-disk copy of nt­dll would im­me­di­ately flag the mis­match:

The System Service Descriptor Table (SSDT) is the ker­nel’s dis­patch table for syscalls. When a user­mode process ex­e­cutes a syscall in­struc­tion, the ker­nel uses the syscall num­ber (placed in EAX) to in­dex into the SSDT and in­voke the cor­re­spond­ing ker­nel func­tion. Patching the SSDT redi­rects syscalls to at­tacker-con­trolled code.

SSDT hook­ing is a clas­sic tech­nique that be­came sig­nif­i­cantly harder af­ter the in­tro­duc­tion of PatchGuard (Kernel Patch Protection, KPP) in 64-bit Windows. PatchGuard mon­i­tors the SSDT (among many other struc­tures) and trig­gers a CRITICAL_STRUCTURE_CORRUPTION bug check (0x109) if it de­tects mod­i­fi­ca­tion. As a re­sult, SSDT hook­ing is es­sen­tially dead in 64-bit Windows. However, anti-cheats still ver­ify SSDT in­tegrity as a de­fense in depth mea­sure.

The Interrupt Descriptor Table (IDT) maps in­ter­rupt vec­tors to their han­dler rou­tines. The Global Descriptor Table (GDT) de­fines mem­ory seg­ments. Both are proces­sor-level struc­tures that can­not be eas­ily pro­tected by PatchGuard alone on all con­fig­u­ra­tions.

A cheat op­er­at­ing at ker­nel level can at­tempt to re­place IDT en­tries to in­ter­cept spe­cific in­ter­rupts, which can be used for con­trol flow in­ter­cep­tion or as a covert chan­nel. Anti-cheats ver­ify that IDT en­tries point to ex­pected ker­nel lo­ca­tions:

A com­mon eva­sion tech­nique is for cheats to per­form syscalls di­rectly (using the syscall in­struc­tion with the ap­pro­pri­ate syscall num­ber) rather than go­ing through nt­dll.dll func­tions. This by­passes user­mode hooks placed in nt­dll. Anti-cheats de­tect this by mon­i­tor­ing threads within the game process for syscall in­struc­tion ex­e­cu­tion from un­ex­pected code lo­ca­tions, and by check­ing whether nt­dll func­tions that should be called are ac­tu­ally be­ing called with ex­pected fre­quency and pat­terns.

On a prop­erly con­fig­ured Windows sys­tem with Secure Boot en­abled, all ker­nel dri­vers must be signed by a cer­tifi­cate trusted by Microsoft. Test sign­ing mode (enabled with bcdedit /set test­sign­ing on) al­lows load­ing self-signed dri­vers and is a com­mon de­vel­op­ment and cheat-de­ploy­ment tech­nique.

Anti-cheats de­tect test sign­ing mode by read­ing the Windows boot con­fig­u­ra­tion and by check­ing the ker­nel vari­able that re­flects whether DSE is cur­rently en­forced. Some anti-cheats refuse to launch if test sign­ing is en­abled.

The SeValidateImageHeader and SeValidateImageData func­tions in the ker­nel val­i­date dri­ver sig­na­tures. Anti-cheats can in­spect loaded dri­ver ob­jects and ver­ify that their IMAGE_INFO_EX ImageSignatureType and ImageSignatureLevel fields re­flect proper sign­ing.

Bring Your Own Vulnerable Driver is the dom­i­nant tech­nique for load­ing un­signed ker­nel code in 2024-2026. The at­tack works as fol­lows:

The at­tacker finds a le­git­i­mate, signed dri­ver with a vul­ner­a­bil­ity (typically a dan­ger­ous IOCTL han­dler that al­lows ar­bi­trary ker­nel mem­ory reads/​writes, or that calls MmMapIoSpace with at­tacker-con­trolled pa­ra­me­ters).The at­tacker loads this le­git­i­mate dri­ver (which passes DSE be­cause it has a valid sig­na­ture).The at­tacker ex­ploits the vul­ner­a­bil­ity in the le­git­i­mate dri­ver to achieve ar­bi­trary ker­nel code ex­e­cu­tion. Using that ker­nel ex­e­cu­tion, the at­tacker dis­ables DSE or di­rectly maps their un­signed cheat dri­ver.

Common BYOVD tar­gets have in­cluded dri­vers from MSI, Gigabyte, ASUS, and var­i­ous hard­ware ven­dors. These dri­vers of­ten have IOCTL han­dlers that ex­pose di­rect phys­i­cal mem­ory read/​write ca­pa­bil­ity, which is all an at­tacker needs.

The pri­mary de­fense against BYOVD is a block­list of known-vul­ner­a­ble dri­vers. The Microsoft Vulnerable Driver Blocklist (maintained in DriverSiPolicy.p7b) is built into Windows and dis­trib­uted via Windows Update. Anti-cheats main­tain their own, more ag­gres­sive block­lists.

Vanguard in par­tic­u­lar is known for ac­tively com­par­ing the set of loaded dri­vers against its block­list and re­fus­ing to al­low the pro­tected game to launch if a block­listed dri­ver is pre­sent. This is en­forced be­cause some BYOVD at­tacks in­volve load­ing the vul­ner­a­ble dri­ver and im­me­di­ately us­ing it be­fore un­load­ing it, so check­ing only at game launch with a pre-scan cov­ers most cases.

...

Read the original on s4dbrd.github.io »

9 179 shares, 11 trendiness

EdoStra/Marketing-for-Founders: Practical marketing resources to get the first 10 / 100 / 1000 users for your SaaS / App / Startup

TL;DR: This is a col­lec­tion of prac­ti­cal mar­ket­ing re­sources to get the first 10 / 100 / 1000 users for your SaaS / App / Startup.

Let’s face it. Marketing is tough, es­pe­cially if you’re a tech­ni­cal founder mov­ing the first steps with your startup.

While there’s a ton of ad­vice out there, most of the time it’s about scal­ing some VC-funded startup with a big mar­ket­ing bud­get to $1,000,000 ARR and 100,000 users.

Inspirational? Sure. Actionable? Not re­ally.

This is why you’ll find here a prac­ti­cal col­lec­tion of startup re­sources, tips, and some tools to help you:

* 🎯 find the first users for your startup

Ready to get started? Just pick a topic:

PS. Leave me a star ⭐ or say Hi on X/Twitter if you find this use­ful!

Launch plat­forms, soft­ware di­rec­to­ries, and com­mu­ni­ties are an easy way to get the first users for your startup and a tiny, steady flow of peo­ple to your SaaS. Some of the fol­low­ing may not be rel­e­vant for your prod­uct, but (hopefully) can give you ideas on what to look for.

Subreddits (always check the rules be­fore post­ing any­thing):

PS. You can find more web­sites to launch your startup here (thanks Sandra) and, if you’re run­ning an AI startup, here.

PPS. You can find a few tips for writ­ing a great launch post here.

Product Hunt is still one of the most ef­fec­tive ways to get early users (if you can pull off a suc­cess­ful launch). Yes, it takes a lot of prepa­ra­tion, pro­mo­tion and fol­low-up work but hit­ting #1 on Product Hunt can get you a ton of hon­est feed­back, down­loads, users and PR buzz.

Use these guides to plan your launch strat­egy:

Managing mul­ti­ple ac­counts across all so­cial me­dia plat­forms can be pretty in­tense (there’s a rea­son why it’s a full-time job); how­ever, there are a cou­ple of strate­gies worth try­ing: build­ing in pub­lic and so­cial lis­ten­ing.

Building in Public - it’s the eas­i­est so­cial me­dia mar­ket­ing tac­tic. In a nut­shell, it’s about us­ing your per­sonal pro­file to share ex­per­tise, prod­uct up­dates, and be­hind-the-scenes con­tent:

Social lis­ten­ing - track­ing and join­ing on­line con­ver­sa­tions on top­ics re­lated to your Startup is pretty help­ful to get in touch with users and learn more about them:

Most peo­ple hate cold out­reach and every­thing con­nected with di­rect sales. Yes, de­pend­ing on your ap­proach, it might be hard to scale and time-in­ten­sive, but cold emails and DMs are the most straight­for­ward way to get in touch with po­ten­tial users, col­lect pre­cious early-stage feed­back and find the first cus­tomers for your startup.

So, how can you find peo­ple on tar­get? What should you write in your mes­sages? And how can you scale cold out­reach? Here are a few ideas to set up your SaaS sales strat­egy:

Should you fo­cus on SEO in the early days of your startup? Probably not, SEO takes time (and back­links), but this does­n’t mean you should ne­glect it ei­ther.

SEO is the foun­da­tion for many star­tups, and it can get you a steady source of traf­fic with­out hav­ing to spend any­thing other than the time to do it.

These guides and tips will help you get started:

PS. If you’re new to SEO and don’t know where/​how to start check out LearningSEO and The Complete Guide to Programmatic SEO.

PPS. If you’re build­ing iOS apps also check these two ASO guides: Create an App Store list­ing that ranks and Rank higher on App Store

How to get rec­om­mended by ChatGPT, Claude, Perplexity, and Gemini is prob­a­bly the most pop­u­lar ques­tion from founders in 2025. And it’s easy to un­der­stand why, you can get men­tioned by a ci­ta­tion to­mor­row and start show­ing up im­me­di­ately, even if you’re an early-stage startup.

So, where can you learn how to get found in AI search?

Everyone says Reddit is the go-to plat­form to get ini­tial trac­tion and the first users for your prod­uct. There’s a com­mu­nity for al­most every topic, plus more and more peo­ple (and LLMs) are us­ing it to by­pass SEO garbage and find what real hu­mans ac­tu­ally think.

But Redditors have an al­most su­per­nat­ural abil­ity to de­tect mar­ket­ing, and when founders try to sneak in pro­mo­tional con­tent, it gets down­voted into obliv­ion (if you’re lucky).

So, how to pro­mote an App/SaaS/Startup on Reddit with­out get­ting banned?

PS. If you’re look­ing for a list of sub­red­dits that al­low self-pro­mo­tion go back to Places To Launch Your Startup, and if you’re look­ing to set up your Reddit so­cial lis­ten­ing strat­egy go back to Social Media Marketing.

Email mar­ket­ing can help you with al­most any­thing: on­board­ing new users, boost­ing free-to-paid con­ver­sions, pro­mot­ing af­fil­i­ate/​re­fer­ral pro­grams, and col­lect­ing feed­back. Plus, it’s of­ten your only op­tion to en­gage users out­side of your prod­uct.

Just like SEO, con­tent mar­ket­ing is one of the first growth levers to pull for a startup. This time, in­stead of cre­at­ing con­tent to praise search en­gines, you’ll be shar­ing it where your au­di­ence hangs out and hi­jack­ing trends for free ex­po­sure:

Every startup will think about adding a paid chan­nel to their mar­ket­ing strat­egy sooner or later and Ads are one of the best ways to ac­cel­er­ate user ac­qui­si­tion.

Here are a few guides to help you avoid burn­ing through your bud­get be­fore get­ting any re­turn from your Ad cam­paign:

Going where the au­di­ence is is the first rule of mar­ket­ing. That’s why part­ner­ing with con­tent cre­ators and in­flu­encers can help you grow your startup and con­nect di­rectly with cus­tomers.

However, on­line ad­vice on in­flu­encer mar­ket­ing is of­ten bad and out­dated, so let’s take a look at a few first­hand ex­pe­ri­ences:

Word-of-mouth is of­ten the main growth dri­ver for boot­strapped star­tups. Why? Because af­fil­i­ate and re­fer­ral pro­grams are easy to set up, cheap (you only pay if the user con­verts), and build trust and cred­i­bil­ity (when users rec­om­mend your prod­uct, you’re not only get­ting traf­fic, but also so­cial proof that it works).

Free mini tools are your best bet on go­ing vi­ral, gen­er­at­ing PR cov­er­age and back­links, and dri­ving a ton of or­ganic traf­fic. Plus, it does­n’t feel like mar­ket­ing (that’s the rea­son why it’s also called Engineering as Marketing).

Most startup web­sites are in­ef­fec­tive. They fail to tell who the prod­uct is for, what the prod­uct does, and why it is bet­ter. This means con­fused vis­i­tors, poor con­ver­sion rates, and wasted mar­ket­ing $$$.

Here are a few re­sources to turn your web­site into a sales and mar­ket­ing as­set:

Your prod­uct de­liv­ers the value added to tar­get cus­tomers; with pric­ing, we cap­ture added value back to build a sus­tain­able busi­ness.”

Confused? You’re not alone. Pricing and busi­ness model are the tough­est things to crack for a startup.

Here are a few frame­works to get your price right:

Little tweaks can have a huge im­pact on con­ver­sions. Here are a few ideas ready for you to A/B test:

One of the most com­mon and painful mis­takes you can make is hav­ing an idea and im­me­di­ately spend­ing all your time build­ing a prod­uct with­out val­i­dat­ing de­mand first.

Some ideas are great, but most aren’t worth your time. So, how can you val­i­date a startup idea?

Talk to your cus­tomers! They’ll let you know what to build, what to im­prove, and what to leave alone!

Sure, but when you’re run­ning a SaaS/App, cus­tomers sign up by them­selves, learn how to use your prod­uct on their own, and only reach out if they need help or sup­port.

So, how do you get feed­back? Here are a few re­sources to get you started:

This was in­spired by Marketing for Engineers (thanks Lisa & Ahmed!) and is cre­ated and main­tained by Edoardo Stradella (Twitter/X).

All re­sources, guides, and tools were done by in­de­pen­dent au­thors and com­pa­nies. All cre­den­tials are in­cluded.

...

Read the original on github.com »

10 173 shares, 12 trendiness

xodn348/han: A compiled programming language with Korean keywords, written in Rust

Han is a sta­t­i­cally-typed, com­piled pro­gram­ming lan­guage where every key­word is writ­ten in Korean. It com­piles to na­tive bi­na­ries through LLVM IR and also ships with a tree-walk­ing in­ter­preter for in­stant ex­e­cu­tion. The com­piler tool­chain is writ­ten en­tirely in Rust.

Han was born from the idea that pro­gram­ming does­n’t have to look the same in every coun­try. Hangul — the Korean writ­ing sys­tem — is one of the most sci­en­tif­i­cally de­signed scripts in hu­man his­tory, and Han puts it to work as a first-class pro­gram­ming lan­guage rather than just a dis­play string.

* Hangul iden­ti­fiers — name your vari­ables and func­tions in Korean

hgl in­ter­pret hello.hgl

# Output: 안녕하세요, 세계!

Or jump into the REPL:

hgl repl

한> 출력(“안녕!“)

안녕!

git clone https://​github.com/​xod­n348/​han.git

cd han

cargo in­stall –path .

That’s it. hgl is now avail­able glob­ally.

cd ed­i­tors/​vs­code

npm in­stall && npm run com­pile

Then open the ed­i­tors/​vs­code folder in VS Code and press F5 to launch with syn­tax high­light­ing + LSP sup­port.

* Arrays with neg­a­tive in­dex­ing — arr[-1] re­turns the last el­e­ment

* 시도 { } 실패(오류) { } — catches any run­time er­ror in­clud­ing di­vi­sion by zero, file not found, out-of-bounds

* 가져오기 파일.hgl” — runs an­other .hgl file in the cur­rent scope

* 함수 최대값 — type params are parsed and erased at run­time

Hangul (한글) is not just a writ­ing sys­tem — it is a feat of de­lib­er­ate lin­guis­tic de­sign. Created in 1443 by King Sejong the Great, each char­ac­ter en­codes pho­netic in­for­ma­tion in its geo­met­ric shape. Consonants mir­ror the tongue and mouth po­si­tions used to pro­nounce them. Vowels are com­posed from three cos­mic sym­bols: heaven (·), earth (ㅡ), and hu­man (ㅣ).

Han brings this el­e­gance into pro­gram­ming. When you write 함수 피보나치(n: 정수) -> 정수, you are not just defin­ing a func­tion — you are writ­ing in a script that was pur­pose-built for clar­ity and beauty.

Hangul is also sur­pris­ingly easy to learn — you can learn the whole sys­tem in an af­ter­noon.

The global in­ter­est in Korean cul­ture has never been higher. From K-pop and Korean cin­ema to Korean cui­sine and lan­guage learn­ing, mil­lions world­wide are en­gag­ing with Korean cul­ture. Over 16 mil­lion peo­ple are ac­tively study­ing Korean as a for­eign lan­guage.

Han of­fers these learn­ers some­thing un­ex­pected: a way to prac­tice read­ing and writ­ing Hangul through pro­gram­ming. It bridges the gap be­tween cul­tural in­ter­est and tech­ni­cal skill, mak­ing Korean lit­er­acy func­tional in a do­main where it has never ex­isted be­fore.

Han fol­lows the clas­si­cal com­piler pipeline, im­ple­mented en­tirely in Rust with zero ex­ter­nal com­piler de­pen­den­cies (LLVM IR is gen­er­ated as plain text):

Why text-based LLVM IR in­stead of the LLVM C API?

Han gen­er­ates LLVM IR as plain text strings, avoid­ing the com­plex­ity of link­ing against LLVM li­braries. This keeps the build sim­ple (cargo build — no LLVM in­stal­la­tion re­quired) while still pro­duc­ing op­ti­mized na­tive bi­na­ries through clang.

Why both in­ter­preter and com­piler?

The in­ter­preter en­ables in­stant ex­e­cu­tion with­out any tool­chain de­pen­den­cies be­yond Rust. The com­piler path ex­ists for pro­duc­tion use where per­for­mance mat­ters. Same parser, same AST, two back­ends.

Why Rust?

Rust’s enum types map nat­u­rally to AST nodes and to­ken vari­ants. Pattern match­ing makes parser and in­ter­preter logic clear and ex­haus­tive. Memory safety with­out garbage col­lec­tion suits a lan­guage tool­chain.

cargo test

Han — where the beauty of Hangul meets the pre­ci­sion of code.

...

Read the original on github.com »

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.

If you like 10HN please leave feedback and share

Visit pancik.com for more.