10 interesting stories served every morning and every evening.

GUIs should be fully keyboard-driven

ckardaris.com

This post has made it to the Hacker News front page. See the dis­cus­sion.

Last week I came across a post on Hacker News that en­cour­aged ap­pli­ca­tion de­vel­op­ers to stop mak­ing ter­mi­nal user in­ter­faces1 (a.k.a. TUIs) and in­stead fo­cus on graph­i­cal user in­ter­faces (a.k.a. GUIs). The post reached the HN front page and sparked a lively de­bate in the com­ments sec­tion.

I think there is merit in both sides of the de­bate. On one hand, I un­der­stand the GUI-positive ar­gu­ments of the post au­thor. In the­ory, the ca­pa­bil­i­ties of GUI ap­pli­ca­tion frame­works are a su­per­set of the ca­pa­bil­i­ties of their TUI coun­ter­parts, so they should be pre­ferred. On the other hand, as a heavy ter­mi­nal user, I also greatly ap­pre­ci­ate all TUIs that al­low me to stay” in the ter­mi­nal and ful­fill all my needs.

But I want to op­pose a re­cur­ring ar­gu­ment in fa­vor of TUIs that in my opin­ion does not have a solid foun­da­tion2. To para­phrase var­i­ous com­menters:

TUIs should be pre­ferred be­cause they are key­board-dri­ven.

TUIs should be pre­ferred be­cause they are key­board-dri­ven.

While it’s true that if you ran­domly pick a GUI and a TUI ap­pli­ca­tion, the lat­ter is more prob­a­ble to be fully key­board-dri­ven, this does not tip the scale in fa­vor of de­vel­op­ing TUIs over GUIs3. What it does is high­light the in­ad­e­qua­cies of key­board nav­i­ga­tion in many GUI ap­pli­ca­tions.

There is noth­ing pre­vent­ing a GUI from be­ing fully key­board-dri­ven4 just like — or even bet­ter than — a TUI. In fact, many GUI frame­work ap­pli­ca­tion guide­lines ex­plic­itly en­cour­age GUI ap­pli­ca­tion de­vel­op­ers to pro­vide sup­port for key­board-dri­ven nav­i­ga­tion that cov­ers the whole func­tion­al­ity of the ap­pli­ca­tion.

For ex­am­ple, the GNOME Human Interface Guidelines state that just as it should be pos­si­ble to per­form every ac­tion with a point­ing de­vice, every ac­tion should also be pos­si­ble with the key­board and that it should be pos­si­ble to move around and in­ter­act with every part of your user in­ter­face us­ing the key­board.

This res­onates with me as a user. Being able to in­tu­itively — and pre­dictably — nav­i­gate around a GUI ap­pli­ca­tion with only my key­board gives me more in­cen­tive to choose it com­pared to its al­ter­na­tives.

Knowing that, and when wear­ing my de­vel­oper hat, I have to make sure that my ap­pli­ca­tions are key­board-friendly. For my first ever GUI ap­pli­ca­tion, Klisi, I in­vested some time to im­ple­ment key­board short­cuts tar­get­ing the whole range of avail­able ac­tions.

Keyboard nav­i­ga­tion is not that hard to achieve in most cases and re­sults in an over­all bet­ter user ex­pe­ri­ence. It is not a mat­ter of fea­si­bil­ity, but a mat­ter of will on the ap­pli­ca­tion de­vel­op­er’s part.

The take­away is sim­ple. Do not com­pro­mise on the user ex­pe­ri­ence you pro­vide with your ap­pli­ca­tion. Strive to make it as in­tu­itive as pos­si­ble. To that end, en­abling full key­board nav­i­ga­tion should not be ig­nored.

Or al­ter­na­tively text-based user in­ter­faces. ↩

Or al­ter­na­tively text-based user in­ter­faces. ↩

The orig­i­nal post au­thor men­tions this as well. ↩

The orig­i­nal post au­thor men­tions this as well. ↩

There are other more com­pelling ar­gu­ments to­wards tip­ping that scale, for ex­am­ple ease of porta­bil­ity. ↩

There are other more com­pelling ar­gu­ments to­wards tip­ping that scale, for ex­am­ple ease of porta­bil­ity. ↩

Of course, the dex­ter­ity achieved via mouse is still pre­ferred — or even re­quired — for some tasks. ↩

Of course, the dex­ter­ity achieved via mouse is still pre­ferred — or even re­quired — for some tasks. ↩

zai-org/GLM-5.3 · Hugging Face

huggingface.co

GLM-5.3 uses the same base model as GLM-5.2 — every gain comes from post-train­ing. Compared with GLM-5.2, it is much bet­ter at com­plex cod­ing and long-hori­zon tasks:

Stronger Coding: GLM-5.3 is the most ca­pa­ble open-weights model for cod­ing, with a 50% im­prove­ment over GLM-5.2 on our in-house Z.ai Code Bench. It also achieve open-source SOTA on pub­lic bench­marks in­clud­ing Terminal Bench 3.0 and Agents’ Last Exam.

Emergent Cyber Capability: As we scaled post-train­ing, cy­ber ca­pa­bil­ity de­vel­oped faster than we ex­pected. GLM-5.3 is state of the art on CyberGym for vul­ner­a­bil­ity dis­cov­ery, and its gains are largest fur­ther up the ex­ploita­tion chain, where it more than dou­bles GLM-5.2 on ex­ploita­tion bench­marks.

Benchmark

Serve GLM-5.3 Locally

GLM-5.3 sup­ports de­ploy­ment with the fol­low­ing frame­works. Feel free to try them out:

SGLang — see cook­book

vLLM — see recipes

TokenSpeed — see here

Transformers — see trans­form­ers docs

KTransformers — see tu­to­r­ial

Unsloth — see guide

For de­ploy­ment on the Ascend NPU plat­form, in­fer­ence frame­works such as vLLM-As­cend, xLLM and SGLang are sup­ported — see here.

Note

GLM-5.3 sup­ports con­trol­ling the think­ing bud­get through the rea­son­ing_­ef­fort pa­ra­me­ter, which ac­cepts three lev­els: low, high, and max. It de­faults to max if not passed (or if set to any other value). To use low or high, pass them ex­plic­itly. For bench­mark and leader­board re­pro­duc­tion, keep the de­fault max.

In the chat tem­plate for GLM-5.3, clear_­think­ing de­faults to false if not passed. For chat sce­nar­ios, ex­plic­itly pass clear_­think­ing=true.

Footnotes

HLE w/ tools: We use sam­pling pa­ra­me­ters of tem­per­a­ture=1.0 and top_p=0.95 for eval­u­a­tion, with a max­i­mum gen­er­a­tion length of 163,840 to­kens. The eval­u­a­tion is con­ducted with a max­i­mum con­text length of 300,000 to­kens, us­ing a con­text man­age­ment strat­egy. We use GPT-5.6-luna (medium) as the judge model.

NL2Repo: We eval­u­ated NL2Repo with tem­per­a­ture=1.0, top_p=1.0, and max_new_­to­kens=64k un­der 1M con­text. To pre­vent hack­ing, we use rule-based and a LLM-based judge­ment to pre­vent ma­li­cious be­hav­iors (e.g., unau­tho­rized pip or curl op­er­a­tions).

DeepSWE: We run DeepSWE us­ing the mini-swe-agent har­ness with tem­per­a­ture=0.95, top_p=1.0, time­out=6h and 400K con­text.

Terminal-Bench 2.1: We eval­u­ate in Claude Code 2.1.207 with tem­per­a­ture=1.0, top_p=1, max_new_­to­kens=65536 with 6h time­out.

Terminal-Bench 3.0: We eval­u­ate Terminal-Bench-3 tasks with the Claude Code 2.1.207 har­ness (reasoning ef­fort=max, 400K con­text, and 128K max­i­mum out­put), re­port­ing avg@3 over three roll­outs per task. Each roll­out runs in an iso­lated con­tainer built from the task’s of­fi­cial im­age, and is capped at 600 agent turns with a 10-hour time­out. Tool Search is dis­abled, and the ar­ti­facts each agent pro­duces are scored by the task’s of­fi­cial sep­a­rate ver­i­fier.

Agent’s Last Exam (CLI): We eval­u­ate ALE us­ing the of­fi­cial eval­u­a­tion pro­to­col with the Claude Code har­ness (reasoning ef­fort=max, 1M con­text, and 64K max­i­mum out­put). Each of the 105 tasks runs in an iso­lated Docker con­tainer us­ing the re­sources de­clared in its Task Card. The de­fault time­out is 4 hours, with task-spe­cific lim­its tak­ing prece­dence (up to 8 hours). Tool Search is dis­abled, and re­sults are scored by the of­fi­cial ALE eval­u­a­tors.

Toolathlon Verified: We ob­tain all re­sults via the of­fi­cial eval­u­a­tion ser­vice and re­port pass@1 av­er­aged over 3 in­de­pen­dent runs.

AutomationBench: We eval­u­ate on AutomationBench v1.0.6, in­cor­po­rat­ing the fix for the null-type han­dling is­sue in­tro­duced in PR #13.

GDPval-AA v2: Models are eval­u­ated by Artificial Analysis.

CyberGym: We eval­u­ate GLM-5.3 in Claude Code 2.1.207 (max rea­son­ing ef­fort, no web tools with tem­per­a­ture=1.0, top_p=1.0, max_new_­to­kens=128000). All eval­u­a­tions are un­der un­lim­ited time­out per task and re­sults are sin­gle-run Pass@1 over 1,507 tasks. To sim­u­late real-world us­age sce­nar­ios, we place the agent in­side the task con­tainer. We also re­move all Git-related in­for­ma­tion and ap­ply a do­main whitelist (allowing only es­sen­tial do­mains such as pypi.org and deb.de­bian.org for ba­sic tool in­stal­la­tion) to pre­vent the agent from cheat­ing.

ExploitGym: We eval­u­ate GLM-5.3, Kimi-K3 and Qwen3.8 Max in Claude Code 2.1.207 (max rea­son­ing ef­fort, no web tools with tem­per­a­ture=1.0, top_p=1.0, max_new_­to­kens=128000). The re­ported re­sults are sin­gle-run Pass@1 on 869 tasks un­der two time­out bud­gets: 2 hours and 6 hours, which are cal­cu­lated as the API in­fer­ence time rescaled by per-model to­kens per sec­ond rate (per-model TPS sourced from Artificial Analysis; that is, we rescale GLM-5.3′s re­sults by 115 TPS, Kimi K3′s re­sults by 40 TPS and Qwen3.8 Max’s re­sults by 47 TPS), plus the non-API over­head. We also ap­ply a do­main whitelist (allowing only es­sen­tial do­mains such as pypi.org and deb.de­bian.org for ba­sic tool in­stal­la­tion) to pre­vent the agent from cheat­ing.

ExploitBench: We eval­u­ate GLM-5.3 in Claude Code 2.1.207 (max rea­son­ing ef­fort, no web tools with tem­per­a­ture=1.0, top_p=1.0, max_new_­to­kens=128000). Following the of­fi­cial eval­u­a­tion set­tings, we limit the max­i­mum num­ber of in­ter­ac­tion rounds be­tween the agent and the en­vi­ron­ment to 300, and com­pute the av­er­age cov­er­age score over all 41 tasks across 3 re­vi­sions. The cov­er­age re­sult of a task is de­ter­mined by tak­ing the union of ca­pa­bil­i­ties achieved across all re­vi­sions, and the av­er­age score is ob­tained by av­er­ag­ing the re­sults. We also ap­ply a do­main whitelist (allowing only es­sen­tial do­mains such as pypi.org and deb.de­bian.org for ba­sic tool in­stal­la­tion) to pre­vent the agent from cheat­ing.

FrontierSWE: The eval­u­a­tion was con­ducted by Proximal with 1M con­text length, max ef­fort level, and 128K max­i­mum out­put to­kens. Dominance score re­ported as of 2026/08/14.

PostTrainBench: We eval­u­ate GLM-5.3 us­ing Claude Code 2.1.207 with max ef­fort level, tem­per­a­ture = 1.0, top_p = 1.0, max_new_­to­kens = 128000, and a 1M-token con­text win­dow. We re­port the weighted av­er­age over 3 runs. Runs that fail to pro­duce a score fall back to the of­fi­cial zero-shot base-model base­line score. For checks in­tended to pre­vent the use of third-party APIs, we re­moved the orig­i­nal pat­tern-match­ing-based checks, as they pro­duced false pos­i­tives when a lo­cal vLLM end­point was ac­cessed through the OpenAI SDK. Instead, we use an LLM agent to in­spect so­lu­tions for ex­ter­nal API us­age.

SWE-Marathon: We eval­u­ate GLM-5.3 us­ing Claude Code 2.1.207 with max­i­mum ef­fort level, tem­per­a­ture = 1.0, top_p = 0.95, max_new_­to­kens = 128000, and a 1M-token con­text win­dow. For strip-clone, the orig­i­nal anti-cheat checks used overly broad im­port de­tec­tion that could re­ject valid im­ple­men­ta­tions. We re­moved the af­fected checks and per­formed llm-based in­spec­tion in­stead to avoid false pos­i­tives. For pa­ra­me­ter-golf and trimul-cuda, changes to the NVIDIA wheels caused the Docker im­age builds to fail, so we added –extra-index-url https://​pypi.org/​sim­ple to re­store suc­cess­ful builds.

Citation

If you find GLM-5.3 use­ful in your re­search, please cite our tech­ni­cal re­port:

@misc{glm5team2026glm5vibecodingagentic, ti­tle={GLM-5: from Vibe Coding to Agentic Engineering}, au­thor={GLM-5-Team and : and Aohan Zeng and Xin Lv and Zhenyu Hou and Zhengxiao Du and Qinkai Zheng and Bin Chen and Da Yin and Chendi Ge and Chenghua Huang and Chengxing Xie and Chenzheng Zhu and Congfeng Yin and Cunxiang Wang and Gengzheng Pan and Hao Zeng and Haoke Zhang and Haoran Wang and Huilong Chen and Jiajie Zhang and Jian Jiao and Jiaqi Guo and Jingsen Wang and Jingzhao Du and Jinzhu Wu and Kedong Wang and Lei Li and Lin Fan and Lucen Zhong and Mingdao Liu and Mingming Zhao and Pengfan Du and Qian Dong and Rui Lu and Shuang-Li and Shulin Cao and Song Liu and Ting Jiang and Xiaodong Chen and Xiaohan Zhang and Xuancheng Huang and Xuezhen Dong and Yabo Xu and Yao Wei and Yifan An and Yilin Niu and Yitong Zhu and Yuanhao Wen and Yukuo Cen and Yushi Bai and Zhongpei Qiao and Zihan Wang and Zikang Wang and Zilin Zhu and Ziqiang Liu and Zixuan Li and Bojie Wang and Bosi Wen and Can Huang and Changpeng Cai and Chao Yu and Chen Li and Chengwei Hu and Chenhui Zhang and Dan Zhang and Daoyan Lin and Dayong Yang and Di Wang and Ding Ai and Erle Zhu and Fangzhou Yi and Feiyu Chen and Guohong Wen and Hailong Sun and Haisha Zhao and Haiyi Hu and Hanchen Zhang and Hanrui Liu and Hanyu Zhang and Hao Peng and Hao Tai and Haobo Zhang and He Liu and Hongwei Wang and Hongxi Yan and Hongyu Ge and Huan Liu and Huanpeng Chu and Jia’ni Zhao and Jiachen Wang and Jiajing Zhao and Jiamin Ren and Jiapeng Wang and Jiaxin Zhang and Jiayi Gui and Jiayue Zhao and Jijie Li and Jing An and Jing Li and Jingwei Yuan and Jinhua Du and Jinxin Liu and Junkai Zhi and Junwen Duan and Kaiyue Zhou and Kangjian Wei and Ke Wang and Keyun Luo and Laiqiang Zhang and Leigang Sha and Liang Xu and Lindong Wu and Lintao Ding and Lu Chen and Minghao Li and Nianyi Lin and Pan Ta and Qiang Zou and Rongjun Song and Ruiqi Yang and Shangqing Tu and Shangtong Yang and Shaoxiang Wu and Shengyan Zhang and Shijie Li and Shuang Li and Shuyi Fan and Wei Qin and Wei Tian and Weining Zhang and Wenbo Yu and Wenjie Liang and Xiang Kuang and Xiangmeng Cheng and Xiangyang Li and Xiaoquan Yan and Xiaowei Hu and Xiaoying Ling and Xing Fan and Xingye Xia and Xinyuan Zhang and Xinze Zhang and Xirui Pan and Xu Zou and Xunkai Zhang and Yadi Liu and Yandong Wu and Yanfu Li and Yidong Wang and Yifan Zhu and Yijun Tan and Yilin Zhou and Yiming Pan and Ying Zhang and Yinpei Su and Yipeng Geng and Yong Yan and Yonglin Tan and Yuean Bi and Yuhan Shen and Yuhao Yang and Yujiang Li and Yunan Liu and Yunqing Wang and Yuntao Li and Yurong Wu and Yutao Zhang and Yuxi Duan and Yuxuan Zhang and Zezhen Liu and Zhengtao Jiang and Zhenhe Yan and Zheyu Zhang and Zhixiang Wei and Zhuo Chen and Zhuoer Feng and Zijun Yao and Ziwei Chai and Ziyuan Wang and Zuzhou Zhang and Bin Xu and Minlie Huang and Hongning Wang and Juanzi Li and Yuxiao Dong and Jie Tang}, year={2026}, eprint={2602.15763}, archivePre­fix={arXiv}, pri­ma­ryClass={cs.LG}, url={https://​arxiv.org/​abs/​2602.15763}, }

Safetensors

Model tree for zai-org/​GLM-5.3

Spaces us­ing zai-org/​GLM-5.3 2

Collection in­clud­ing zai-org/​GLM-5.3

Paper for zai-org/​GLM-5.3

htmx 4.0.0 has been released!

four.htmx.org

htmx 4.0.0 Release

The htmx team is very happy to an­nounce the re­lease of htmx 4.0.0! This is the cul­mi­na­tion of 8 months of work (plus a game) and we are very happy with the re­sults.

The idea of htmx 4 started to ger­mi­nate when I de­cided to cre­ate fixi and, in do­ing so, got more fa­mil­iar with the fetch() API and async pro­gram­ming in JavaScript. (htmx had al­ways used XMLHttpRequest due to back­wards com­pat­i­bil­ity is­sues.)

One chance evening I was con­tacted by Christian, who had some in­ter­est­ing ideas around stream­ing HTML that got me think­ing that mov­ing the in­ter­nals to fetch() would sim­plify things for him and for the li­brary in gen­eral. After a bit of work I man­aged to get Michael and Alex on board, and we were off to the races.

Development has been very smooth. We started a port of fixi + the htmx test suite. Over time, we re­dis­cov­ered why htmx did many of the things that it did and moved our new im­ple­men­ta­tion closer and closer to the old one. At this point the be­hav­ioral dif­fer­ences be­tween 2.x and 4.x are rel­a­tively small and where they do di­verge we have made ex­plicit choices that we feel will put htmx-based ap­pli­ca­tions in a good spot for be­ing 100-year web ser­vices

Note that we are not mark­ing 4.0 as lat­est in NPM be­cause we do not want to force-up­grade users who are re­ly­ing on non-ver­sioned CDN URLs for htmx. Instead, 2.x will re­main lat­est and the 4.0 line will re­main next un­til some point in early 2027. The web­site, how­ever, will ref­er­ence 4.0.

Major Changes

As men­tioned above, htmx 4, from a user’s view­point, is al­most iden­ti­cal to htmx 2. There are three ma­jor changes:

Attribute in­her­i­tance is now ex­plicit by de­fault rather than im­plicit by de­fault (this is the biggest up­grade item)

The htmx event names have been stan­dard­ized & cleaned up. Some ad­vanced users may need to change the events they lis­ten for.

History sup­port now does not use lo­cal­Stor­age by de­fault (which was a cause of many sup­port headaches). Most peo­ple won’t no­tice this at all.

Internally, we mi­grated from XMLHttpRequest to fetch() but that should be trans­par­ent for most users of htmx.

Attribute Inheritance

In htmx 2 many at­trib­utes were inherited” by de­fault. This al­lows you to place at­trib­utes on par­ent el­e­ments and their be­hav­ior will ap­ply to child el­e­ments. This be­hav­ior, which came from the in­ter­cooler.js days, was in­spired by CSS and, un­sur­pris­ingly, worked out about the same as CSS: pow­er­ful but dif­fi­cult to un­der­stand at times.

In htmx 4 at­trib­utes are not in­her­ited un­less you ex­plic­itly say so by adding an :inherited af­ter the at­tribute name:

<!– htmx 2 –> <div hx-con­firm=“Are you sure?“> <button hx-delete=“/​item/​1”>Delete</​but­ton> </div>

<!– htmx 4 –> <div hx-con­firm:in­her­ited=“Are you sure?“> <button hx-delete=“/​item/​1”>Delete</​but­ton> </div>

This will be the largest up­grade bur­den in mi­grat­ing from htmx 2 to htmx 4. To make things eas­ier, we have pro­vided a com­mand line tool to find places you need to mark as in­her­ited.

Note that at­trib­utes like hx-dis­in­herit, etc. are no longer re­quired and should be re­moved.

Events

The events trig­gered by htmx 2 had grown or­gan­i­cally over the life of the li­brary and were not par­tic­u­larly well or­ga­nized, mak­ing it dif­fi­cult to know ex­actly which event was fired when.

In htmx 4, all events now fol­low htmx:phase:ac­tion[:sub-ac­tion]:

In ad­di­tion, the fol­low­ing changes were made:

Most er­ror events col­lapse into htmx:er­ror. HTTP er­ror re­sponses fire htmx:re­sponse:er­ror.

The htmx:xhr:* events are re­moved. htmx 4 uses fetch().

The htmx:val­i­da­tion:* events are re­moved in fa­vor of na­tive browser form val­i­da­tion.

The full table is in What’s New in htmx 4.

The com­mand line up­grade checker flags old event names in hx-on at­trib­utes and in your JavaScript where it can find them.

History

History sup­port has al­ways been in­cluded in htmx, al­low­ing you to im­ple­ment back-but­ton aware ac­tions with sim­ple at­trib­utes. In htmx 2, a cache in lo­cal­Stor­age was used to snap­shot pages for restora­tion. Unfortunately a large source of is­sues was that this snap­shot could in­clude DOM mu­ta­tions by 3rd party JavaScript li­braries. When the page was re­stored, those mu­ta­tions re­mained but the un­der­ly­ing JavaScript logic was not.

htmx 4 does not cache pages in lo­cal­Stor­age. On back nav­i­ga­tion htmx re-fetches the page and swaps it into <body>, or into the [hx-history-elt] el­e­ment if one is pre­sent. This al­lows 3rd party JavaScript li­braries to just work” in most cases and, with good re­quest caching, is very fast.

If you want lo­cal caching in­stead, we now ship a very com­plete hx-his­tory-cache ex­ten­sion that re­stores his­tory from ses­sion­Stor­age and is de­signed to in­te­grate well with script­ing so­lu­tions like Alpine.js, etc.

New Features

There are two big new fea­tures in htmx 4, both of which we are re­ally ex­cited about:

Morph Swaps

We now sup­port mor­ph­ing swaps out of the box with htmx. I cre­ated id­iomorph and nearly in­cluded it in htmx 2.x but de­cided against it. In htmx 4, Michael has done great work im­prov­ing on that al­go­rithm and in­te­grat­ing it seam­lessly into htmx.

<hx-partial>

Another ma­jor new fea­ture is the <hx-partial> tag. This tag is sim­i­lar to out-of-band swaps, but is much clearer when you want to do some­thing be­yond just re­plac­ing a sin­gle el­e­ment with a new ver­sion of it­self:

<hx-partial hx-tar­get=“#mes­sages” hx-swap=“be­foreend”> <div>New mes­sage</​div> </hx-partial> <hx-partial hx-tar­get=“#count”> <span>5</span> </hx-partial>

Extensions

Much of the ex­cite­ment in htmx 4 is in the ex­ten­sions. Switching to fetch() in­ter­nally let us re­think how ex­ten­sions can and should work, and sparked the cre­ation (and recre­ation) of many new ex­ten­sions, for ex­am­ple:

hx-pre­load - pre­load con­tent (e.g. on mouseover) to speed re­quests up

hx-down­load - na­tive, fetch-based file down­loads

hx-alpine-com­pat - smooths over com­pat­i­bil­ity is­sues be­tween htmx and Alpine.js

hx-his­tory-cache - caches his­tory in ses­sion­Stor­age, pro­vides Alpine.js com­pat­i­bil­ity

Additionally, there are three new or up­dated stream­ing HTML ex­ten­sions:

hx-sse streams over text/​event-stream.

hx-ws streams and sends over WebSockets.

hx-mul­ti­part streams over mul­ti­part/​mixed

Finally, we de­cided it was time to try our hand at our own small front-end script­ing so­lu­tion that tightly in­te­grates with htmx. hx-live is in­spired by Alpine.js, jQuery and hy­per­script, and makes front end script­ing pleas­ant and fun. It even sup­ports what we are call­ing DOM-based, HATEOAS-friendly re­ac­tiv­ity.

There is a new ht­max.js bun­dle in the dis­tri­b­u­tion which pack­ages htmx with the most pop­u­lar of these in a sin­gle file if you don’t want to think about which ones you want to pick.

Upgrading

For a com­plete up­grade guide see What’s New in htmx 4.

As men­tioned ear­lier, we are pro­vid­ing an up­grade tool to help you:

$ npx htmx.org@4.0.0 up­grade-check — ./templates

File ex­ten­sions: .html, .php, .js, .ts, .jinja, .jinja2, .j2, .erb, .hbs Use –ext to add more (e.g. –ext .vue –ext .svelte)

Scanning 1 file(s)…

Found 8 is­sue(s) in 1 of 1 file(s). tem­plates/​in­dex.html:1: [inheritance] hx-head­ers needs :inherited suf­fix (descendant on line 3 has hx-delete) (this looks like a CSRF to­ken; with­out :inherited the header does not reach child el­e­ments and the server re­jects the re­quest) tem­plates/​in­dex.html:2: [inheritance] hx-tar­get needs :inherited suf­fix (descendant on line 3 has hx-delete) tem­plates/​in­dex.html:2: [inheritance] hx-con­firm needs :inherited suf­fix (descendant on line 3 has hx-delete) tem­plates/​in­dex.html:3: [renamed-attr] hx-dis­able -> re­name to hx-ig­nore (hx-disable now means disable dur­ing re­quest’) tem­plates/​in­dex.html:4: [removed-attr] hx-vars is re­moved -> use hx-vals with js: pre­fix tem­plates/​in­dex.html:4: [removed-attr] hx-prompt is re­moved -> load the hx-prompt ex­ten­sion to keep the same syn­tax tem­plates/​in­dex.html:9: [old-event] old event name htmx:afterRequest” -> htmx:after:request” tem­plates/​in­dex.html:9: [old-api] htmx.ad­d­Class() is re­moved -> use el­e­ment.classList.add()

We are also ship­ping an agent skill to as­sist in up­grad­ing

Installing

htmx 4.0 can be in­stalled via a pack­age man­ager ref­er­enc­ing ver­sion 4.0.0, or can be linked via a CDN:

<script src=“https://​unpkg.com/​htmx.org@4.0.0/​dist/​htmx.min.js></script>

or Downloaded

LLMs

Like it or not, a lot of peo­ple are us­ing LLMs and we are pro­vid­ing the fol­low­ing skills files for LLMs:

htmx-guid­ance - core htmx skills for de­vel­op­ing with htmx 4

htmx-de­bug­ging - di­ag­nos­ing htmx is­sues dur­ing dev

htmx-ex­ten­sion-au­thor­ing - writ­ing and de­bug­ging htmx 4 ex­ten­sions

htmx-up­grade-from-ht­mx2 - mi­grat­ing a code­base from htmx 2.x to 4.x

(Let’s leave aside if re­leas­ing a new ver­sion of a li­brary in the LLM era is a good or bad thing!)

Conclusion

We hope you en­joy htmx 4. htmx 2 will con­tinue to be sup­ported in­def­i­nitely so don’t feel any pres­sure to up­grade.

I’d like to thank the fol­low­ing peo­ple for all their help with this re­lease:

Michael West - Incredible team­mate & grug-brained de­vel­oper

Christian Tanul - Inspired htmx 4 & led the stream­ing & live ex­ten­sions

Alex Petros - For keep­ing the ship on an even keel

Stephen Mitchell - The ge­nius be­hind the game

Stu Kennedy - Our WebSockets ex­pert

André Ahlert Jr. - Providing IDE & Editor Support

Dien Hoa Truong - For kick­ing the tires on early htmx 4 and help­ing fix many bugs

Upgrade Music

Wouldn’t be an htmx up­date with­out up­grade mu­sic:

openai.com

Just a rumour of a bug is enough to find a security exploit these days

anil.recoil.org

I re­leased a se­cu­rity fix for OCaml’s co­http 6.3.0 to­day, fix­ing a path tra­ver­sal is­sue. The patch it­self was straight­for­ward and in nor­mal times, the se­cu­rity pro­ce­dure would have been to fix it pri­vately, in­form af­fected users, and then is­sue a pub­lic ad­vi­sory. This time around though, I no­ticed probes in my live web­server logs with the ex­act bug pat­tern just min­utes af­ter open­ing the PR to fix the is­sue.

What’s worse, I found I could use my own agents to find the ex­ploit just by know­ing roughly what it was about and so could have been ex­ploit­ing it well be­fore the pub­lic patch was avail­able! Given that just the ru­mour of a se­cu­rity is­sue seems enough to give at­tack­ers enough info to find new ex­ploits, we’re go­ing to need to change the way we deal with se­cu­rity re­sponses in open source.

1 The ru­mour of a bug is all new agen­tic ex­ploit sys­tems need

This par­tic­u­lar re­port ar­rived pri­vately on a Slack chan­nel via Jane Street last week, and was it­self found via Claude Fable. That com­presses all time­lines con­sid­er­ably…

1.1 The time­line of a mod­ern se­cu­rity re­port

Before ex­am­in­ing the patch in de­tail, I pointed my own Claude at the af­fected code to see what else was lurk­ing (asking it to in­ves­ti­gate path nor­mal­i­sa­tion is­sues). Fable frus­trat­ingly re­fused out­right due to its se­cu­rity block since I don’t have ac­cess to Glasswing, but DeepSeek V4 Pro obliged me and in­de­pen­dently turned up sev­eral re­lated is­sues. My agent also triv­ially cre­ated an ex­ploit to probe a lo­cal live server in un­der a minute.

After some back and forth with the bug re­porter about pos­si­ble fixes, I qui­etly opened co­http#1145 pub­licly to get more eyes on it. This nor­mally takes a few days and a re­lease within a week or two is rea­son­able. Within about ten min­utes (!) this web­site was field­ing probes for per­cent-en­coded tra­ver­sal se­quences, in­di­cat­ing that au­to­mated watch­ers are keep­ing an eye on pub­lic repos­i­to­ries.

If it took me just a minute to cre­ate my own ex­ploit lo­cally, then ten min­utes ac­tu­ally seems quite long for an au­to­mated at­tack win­dow to start! A de­ter­mined at­tacker who is mon­i­tor­ing pack­age repos­i­to­ries could eas­ily be ex­ploit­ing them within sec­onds.

1.2 Security em­bar­goes are no longer ef­fec­tive

Conventional se­cu­rity process in­volves em­bar­go­ing the bug, and as­sumes that se­crecy of the de­tails pro­tects users. However, all an agent needs to­day is a broad di­rec­tion to search in, and it can do its own re­search. Fang et al. found that when given a CVE de­scrip­tion, their GPT-4 agent ex­ploited 87% of a 15-vulnerability bench­mark, and with­out the de­scrip­tion, just 7%.

Two years on, the mean time to ex­ploit is -7 days. In other words, ex­ploita­tion now pre­cedes the patch! That same met­ric looks to be around 63 days in 2018 – 19, and crossed zero in 2024. A quick search finds lots of other sim­i­lar cases these days… ma­ri­mo’s CVE-2026 – 39987 went from ad­vi­sory to first ex­ploita­tion at­tempt in 9 hours, even with no pub­lic proof-of-con­cept in ex­is­tence. Langflow’s CVE-2026 – 33017 took 20 hours. We seem to have crossed the ru­bi­con for au­to­mated ex­ploit gen­er­a­tion…

2 Are the bu­go­nom­ics against OSS main­tain­ers now?

It looks to me like our se­cu­rity processes need to in­vert some­what, since just one per­son search­ing for the is­sue class (this could be a mail­ing list ques­tion, an odd com­mit in an or­phan branch, or a con­text leak) is suf­fi­cient to alert some­one else’s agent and let them get ex­ploit code. This is wild.

A May 2026 pa­per coined the term bugonomics” and ar­gues that the bot­tle­neck has moved to defender re­me­di­a­tion through­put”. LLMs are mer­rily gen­er­at­ing ex­ploits, but our abil­ity to de­fend against them is­n’t nec­es­sar­ily im­prov­ing as main­tainer val­i­da­tion, triage and re­lease rates stay flat. This un­for­tu­nately matches the view from my OSS main­tain­er’s chair:

The ques­tion is not whether fron­tier mod­els, open-weight mod­els, or pro­gram analy­sis win”. The ques­tion is how to or­ches­trate them so that scarce val­i­da­tion, pri­or­i­ti­za­tion, and re­lease ca­pac­ity goes to­ward durable fixes rather than me­chan­i­cal search and re­port draft­ing. A cen­tral de­fender op­por­tu­nity is tech­ni­cal debt re­me­di­a­tion: se­man­tics-grounded, tool-ver­i­fied, model-as­sisted work­flows that help main­tain­ers find, val­i­date, pri­or­i­tize, and fix se­cu­rity-rel­e­vant de­fects be­fore they be­come to­mor­row’s ex­ploited vul­ner­a­bil­i­ties. — Demystifying the Mythos or Disrupting Bugonomics?, Pesoli et al, 2026

The ques­tion is not whether fron­tier mod­els, open-weight mod­els, or pro­gram analy­sis win”. The ques­tion is how to or­ches­trate them so that scarce val­i­da­tion, pri­or­i­ti­za­tion, and re­lease ca­pac­ity goes to­ward durable fixes rather than me­chan­i­cal search and re­port draft­ing. A cen­tral de­fender op­por­tu­nity is tech­ni­cal debt re­me­di­a­tion: se­man­tics-grounded, tool-ver­i­fied, model-as­sisted work­flows that help main­tain­ers find, val­i­date, pri­or­i­tize, and fix se­cu­rity-rel­e­vant de­fects be­fore they be­come to­mor­row’s ex­ploited vul­ner­a­bil­i­ties. — Demystifying the Mythos or Disrupting Bugonomics?, Pesoli et al, 2026

And why are main­tainer ca­pa­bil­i­ties stay­ing flat? Well, not hav­ing ac­cess to fron­tier agents like Mythos is an ob­vi­ous one, but also that the en­gi­neer­ing of a se­cu­rity patch that does­n’t cause any re­gres­sions is just fun­da­men­tally more work.

3 So what the hell can we do about this?

We clearly need to adapt fairly quickly. I don’t think the cur­rent man­ual triage process should dis­ap­pear, but I have seen an un­sus­tain­able surge of ac­tiv­ity since Fable came out. We are only just be­gin­ning to get a han­dle on how much of the in­com­ing fire­hose is ma­chine-gen­er­ated, but it’s ob­vi­ously a lot.

The big en­gi­neer­ing shops (like Google) have been build­ing mi­croup­dates di­rectly into their soft­ware to en­sure that fixes di­rectly reach users as a pri­or­ity over (e.g.) be­ing fixed in the Chrome code repos­i­tory. We don’t re­ally have that kind of lux­ury in Docker or OCaml, as we don’t con­trol the end­points our soft­ware is used in. Aside from Docker Desktop, down­stream dis­tri­b­u­tions quite rightly repack­age OSS on their own timescales and terms.

For smaller pro­jects like OCaml, just gain­ing ac­cess to the fron­tier mod­els is a strug­gle. The Western mod­els have se­cu­rity guards in place which mean that we can’t use the com­mer­cially avail­able ones. Project Glasswing has ex­panded to 150 or­gan­i­sa­tions across 15 coun­tries in­clud­ing crit­i­cal in­fra­struc­ture op­er­a­tors, cloud and fi­nan­cial providers, the Linux Foundation, but mom and pop’ main­tain­ers still don’t have ac­cess. I was am­biva­lent back in April whether this is harm­ful, but it’s pretty ob­vi­ous to­day that it’s turn­ing out pretty ter­ri­bly.

3.1 Super sekrit pri­vate patch de­vel­op­ment

The first re­me­di­a­tion is to de­velop the fixes some­where re­ally pri­vate out of the reach of AI. GitHub’s tem­po­rary pri­vate forks nom­i­nally do this, but it does­n’t work hugely well for us.

First, GitHub re­stricts it to keep in­for­ma­tion about vul­ner­a­bil­i­ties se­cure, in­te­gra­tions, in­clud­ing CI, can­not ac­cess tem­po­rary pri­vate forks” which im­me­di­ately dis­con­nects the main­tainer from the lifeblood of our CI re­sults. Secondly, only a sin­gle PR can merge into the fork, which does­n’t work well for is­sues that of­ten span a few repos­i­to­ries. Reviewers also have to be en­rolled one at a time by an ad­min, and in open-source land re­view­ers are kind of drive-by de­pend­ing on who is avail­able (especially in August!).

More broadly though, this plugs the wrong leak. The patch stay­ing se­cret is­n’t nearly as im­por­tant as en­sur­ing the de­scrip­tion about the is­sue reaches ex­actly the right peo­ple with no leak­age to at­tack­ers.

We don’t have ro­bust dis­cus­sion in­fra­struc­ture avail­able within OSS as it’s spread through var­i­ous end-to-end en­crypted ones (we use Matrix) but also shared in­fra­struc­ture like Discord or Slack which are ex­tremely leaky. We do need some sort of web-of-trust to dis­tin­guish the good guys from the bad in a par­tic­u­lar pro­ject con­text.

3.2 No em­bar­goes, just ship con­tin­u­ously

Another thing we could do is to rapidly fix is­sues in pub­lic, ship con­tin­u­ously, and im­prove the re­lease path via bet­ter au­toma­tion.

Bigger pro­jects like Chrome show this is pos­si­ble via weekly se­cu­rity up­dates, two re­leases per week (!), and dy­namic patch­ing that swaps back­ground processes for up­dated bi­na­ries with­out a restart. This is­n’t en­tirely new tech­nol­ogy; I looked into in­te­grat­ing live ksplice Linux patch­ing with Xen 15+ years ago. The Linux ker­nel also ships fixes as soon as pos­si­ble, de­fer­ring at most seven days and ex­cep­tion­ally four­teen.

However, soft­ware pack­ag­ing is our pri­mary ob­sta­cle. Chrome has a rel­a­tively easy job of ship­ping one bi­nary arte­fact, but OSS is of­ten a bunch of li­braries that are then em­bed­ded in a va­ri­ety of down­stream prod­ucts. So to do this, we’ll need:

much bet­ter cross-ecosys­tem pack­age man­age­ment to dis­cover where dis­parate li­braries are even­tu­ally em­bed­ded. Ryan Gibb will talk about this at ICFP next week!

bet­ter scan­ning tools to help with triage; Andrew Nesbitt has been do­ing just this with Scrutineer over the past few months. Thomas Gazagnaire and I have been dis­cussing try­ing this out for our OCaml code, sub­ject to get­ting ac­cess to a rea­son­able fron­tier model with­out se­cu­rity blocks.

more ro­bust qual­ity con­trol in­fra with­out any false pos­i­tives that works across the spec­trum of sup­ported plat­forms. While it’s rel­a­tively easy to run CI on Linux, it’s a dif­fer­ent story on OpenBSD, FreeBSD, ma­cOS, and some ar­chi­tec­tures like RISC-V

3.3 Proactive pro­tec­tion at the pro­to­col layer

I’ve also been hav­ing more rad­i­cal thoughts about how we could slam in pro­tec­tions dy­nam­i­cally to pro­tect end­points us­ing our li­braries. If we just ac­cept that up­stream patch fixes will al­ways trail an ex­ploit, then we must put some­thing faster to get ahead.

For ex­am­ple, this co­http bug fixed to­day has a sim­ple mit­i­ga­tion: just nor­malise per­cent-en­coded path sep­a­ra­tors in the re­quest URL. This rule was im­ple­mentable the minute the re­port ar­rived, and also de­ploy­able while the full fix went through re­view, test­ing and pack­ag­ing. Virtual patch­ing is rou­tine on cloud in­fra­struc­ture these days; Cloudflare de­ployed man­aged rules to plug Log4shell back in 2021.

But open source lacks a dis­tri­b­u­tion mech­a­nism for such rules out­side of a com­mer­cial CDN. That’s what the an­ti­botty net­work idea from our in­ter­net ecol­ogy pa­per is try­ing to plug via more soft­ware di­ver­sity around the global Internet. How can we have lo­cal, fast-prop­a­gat­ing de­fences that hear about a vul­ner­a­bil­ity and act on their im­me­di­ate in­fra­struc­ture within sec­onds?

4 Some re­search fol­lowups

I think we’ll need some com­bi­na­tion of all three op­tions in the short-term. A light­weight web-of-trust for OSS con­trib­u­tors (like the ven­er­a­ble Advogato used to be), as well as more fo­cus on OSS pack­ag­ing and con­tin­u­ous roll­out and triage mech­a­nisms that don’t over­whelm our pre­cious hu­man con­trib­u­tors.

I’ve also posted a cou­ple of new MPhil re­search ideas for any­one in­com­ing to Cambridge next month and is look­ing for a pro­ject.

An an­ti­botty de­fen­sive test­bed to pro­tect net­work ser­vices” puts a MirageOS gate­way in front of a home net­work, and in­ves­ti­gates whether a set of mit­i­ga­tion rules can be made trust­wor­thy enough to de­ploy au­to­mat­i­cally. There’s a fun cap­ture-the-flag game we could play by giv­ing the same ru­mour to an at­tack­ing agent and a de­fend­ing one and see­ing which one gets there first.

Compiling Lean spec­i­fi­ca­tions into OxCaml en­force­ment au­tomata” de­fines what a li­brary is per­mit­ted to do across filesys­tem, parser and net­work lay­ers us­ing Dijkstra mon­ads. This would com­piles that Lean spec­i­fi­ca­tion into an OxCaml au­toma­ton that en­forces it at run­time. It’s a mod­ern spin on the state­call au­tomata I built dur­ing my PhD.

And if any­one from Project Glasswing is lis­ten­ing, team OCaml could use ac­cess now :-)

(The co­http fix was not a solo ef­fort. Sapphire Livingstone found and re­ported the is­sue, guided the fix and co-de­vel­oped the re­me­di­a­tion; Michael Dales, Török Edwin and Patrick Ferris re­viewed the patch; Hannes Mehnert co­or­di­nated the ad­vi­sory; and Thomas Gazagnaire has been think­ing through the wider triage prob­lem. Thank you all! The bu­go­nom­ics may be against us, but we will crest this hump.)

GitHub - Lakr233/vphone-cli

github.com

Boot a vir­tual iPhone via Apple’s Virtualization.framework us­ing PCC re­search VM in­fra­struc­ture.

Prerequisites

Host:

Apple Silicon

ma­cOS 15+ (Sequoia)

Xcode + iOS SDK (cross-compiles the guest dae­mon)

SIP/AMFI re­lax­ation to al­low pri­vate PV=3 en­ti­tle­ments with un­signed-bi­nary

Dependencies:

brew in­stall python@3.13 aria2 wget gnu-tar openssl@3 ldid-procur­sus ssh­pass key­stone cmake libusb ipsw zstd

Install

brew in­stall zqxwce/​tap/​vphone-cli

Build

git clone –recurse-submodules https://​github.com/​Lakr233/​vphone-cli.git

./scripts/setup_tools.sh # in­stall deps, build tool­chain sub­mod­ules, cre­ate the Python venv ./scripts/build.sh # build + sign vphone-cli, bun­dle the .app, cross-com­pile vphoned

cd .build/vphone-cli.app/​Con­tents/​Ma­cOS/ vphone-cli –help

Quick Start

One com­mand cre­ates a VM end-to-end (download → patch → DFU re­store → CFW in­stall → first boot):

vphone-cli vm cre­ate my­phone -V jb # -V / –variant

vphone-cli vm launch my­phone

Commands

vphone-cli vm cre­ate runs the whole pipeline; the in­di­vid­ual steps be­low let you drive it man­u­ally or re-run one stage.

Manage

vphone-cli vm list # list VMs (–json for script­ing) vphone-cli vm info my­phone # show one VM vphone-cli vm new my­phone # cre­ate an empty bun­dle (cpu/mem/disk op­tions) vphone-cli vm con­fig my­phone –cpu 8 –memory 8192 vphone-cli vm clone my­phone my­phone-2 # fast APFS clone, fresh de­vice iden­tity vphone-cli vm ex­port my­phone –out my­phone.tzst # zstd fast by de­fault (–max = xz -9); –out may be a dir (auto-names <vm>.tzst/.txz); skips re­store dir + stag­ing files vphone-cli vm im­port my­phone.tzst –name re­stored vphone-cli vm re­name my­phone iphone16 vphone-cli vm delete iphone16

Build a VM man­u­ally (what vm cre­ate au­to­mates)

vphone-cli vm new my­phone # 1. empty bun­dle vphone-cli fw pre­pare my­phone –iphone-version 26.1 # 2. down­load + merge IPSWs vphone-cli fw patch my­phone –variant jb # 3. patch the boot chain

vphone-cli vm launch my­phone –dfu & # 4. boot into DFU (background) vphone-cli re­store my­phone –get-shsh # fetch SHSH vphone-cli re­store my­phone # DFU re­store vphone-cli vm stop my­phone # stop the DFU boot

vphone-cli cfw in­stall my­phone –variant jb # 5. in­stall CFW (host-mount; asks for sudo) vphone-cli vm launch my­phone # 6. first boot

Update to a newer iOS by point­ing fw pre­pare at an IPSW: –iphone-source /path/to.ipsw –cloudos-source /path/to.ipsw.

Firmware Variants

Five patch vari­ants with in­creas­ing se­cu­rity by­pass — pass one to –variant:

See re­search/​0_bi­na­ry_­patch_­com­par­i­son.md for the per-com­po­nent break­down.

Running & Connecting

SSH (jailbreak): ssh -p 22222 mo­bile@<vm-ip> (password alpine)

SSH (regular/dev): ssh -p 22222 root@<vm-ip>

VNC: vnc://&​lt;vm-ip>:5901

Locations

Everything vphone-cli cre­ates lives un­der ~/.vphone/ — kept out­side the repo and the .app so the signed bun­dle stays portable. Redirect the whole tree with $VPHONE_ROOT:

Precedence: the per-item over­rides ($VPHONE_LIBRARY_ROOT, $VPHONE_VENV_DIR) win over $VPHONE_ROOT, which wins over the ~/.vphone de­fault. The ip­sws/, tools/, and debs/ caches al­ways sit di­rectly un­der whichever root is ac­tive.

SIP/AMFI Relaxation

Option A — fully dis­able SIP, then dis­able AMFI via boot-arg (most per­mis­sive).

In Recovery (long-press power → Terminal):

csru­til dis­able csru­til al­low-re­search-guests en­able

Then re­boot into ma­cOS and set the AMFI boot-arg (needs SIP fully off to take ef­fect):

sudo nvram boot-args=“am­fi_get_out­_of_my_way=1 -v” # re­boot af­ter

Option B — keep SIP on (debug-only re­laxed), then al­lowlist the bi­nary with am­fi­dont (leaves AMFI en­abled sys­tem-wide).

In Recovery:

csru­til en­able –without de­bug csru­til al­low-re­search-guests en­able

Then re­boot into ma­cOS and:

vphone-am­fi­dont # .build/vphone-cli.app/​Con­tents/​Re­sources/​vphone-am­fi­dont for lo­cal builds

Tested Environments

FAQ

zsh: killed ./vphone-cli — AMFI/debug re­stric­tions aren’t by­passed; see Prerequisites (amfi_get_out_of_my_way=1 or am­fi­dont).

Virtualization is not avail­able on this hard­ware — your Mac is it­self a VM; PV=3 guest boot can’t nest. Use a non-nested ma­cOS 15+ host.

Stuck on Press home to con­tinue” — con­nect via VNC and right-click (two-finger click) to sim­u­late the home but­ton.

System apps won’t in­stall — dur­ing iOS setup, don’t pick Japan or the EU as your re­gion (extra reg­u­la­tory checks the VM can’t sat­isfy); pick e.g. United States.

App crashes on launch with EXC_GUARD / GUARD_TYPE_MACH_PORT — re-patch with vphone-cli fw patch <name> –variant <v> –force-exc-guard, then re-re­store/​in­stall (#291). Always on for iOS 18 bases.

Install a .ipa/.tipa — use the run­ning VMs Install menu (drag-drop or file picker).

cfw in­stall hangs re-sign­ing a sys­tem bi­nary (e.g. Campo), mem­ory climb­ing un­bounded — known bug in ldid-procur­sus up to 2.1.5-procursus7 (the cur­rent Homebrew sta­ble): bytes(uin­t64_t) calls __builtin_clzll(0) with no zero-guard, which is un­de­fined be­hav­ior, and on this build re­solves to a 0-length that un­der­flows an un­signed loop counter — ldid spins writ­ing one byte at a time into a grow­ing buffer in­stead of ter­mi­nat­ing. Triggered by any en­ti­tle­ments plist con­tain­ing an in­te­ger value of ex­actly 0 (some real Apple sys­tem bi­na­ries have these). Fixed up­stream but not yet in a tagged re­lease; re­build from source: brew in­stall –HEAD ldid-procur­sus && brew link –overwrite ldid-procur­sus. Kill the hung ldid process first (sudo kill -9 <pid>) if you al­ready hit it.

Automation

vphone-cli ex­poses a host con­trol socket (<bundle>/vphone.sock) for pro­gram­matic con­trol — screen­shots, touch, swipes, hard­ware keys, clip­board — each ac­tion re­turn­ing an in­line screen­shot for AI-driven E2E test­ing. See vphone-mcp for an MCP server wrap­ping it.

Acknowledgements

wh1te4ever/​su­per-tart-vphone-writeup

25,000 Lbs. of Chicken Products Recalled in 5 States: USDA

www.thehealthy.com

If you’re won­der­ing what has been go­ing on with our food, you’re one of mil­lions. Now, on Wednesday, August 26, the U.S. Department of Agriculture (USDA) pub­lished a re­call no­tice on 24,900 pounds of chicken.

The event re­port­edly im­pacts food­ser­vice busi­nesses in New England—namely, Maine, Massachusetts, New Hampshire, Rhode Island, and Vermont.

The USDAs Food Safety and Inspection Service (FSIS) says two sep­a­rate frozen, not-ready-to-eat Buffalo chicken-fla­vored prod­ucts from Boston-based Shanghai Ravioli Corporation, Inc. are be­ing pulled.

Why is the re­call hap­pen­ing?

FSIS sur­veil­lance re­port­edly found that prod­ucts named as Buffalo chicken ran­goon” and Benedetto’s Buffalo chicken moz­zarella sticks.” The prod­ucts ap­par­ently have false marks of in­spec­tion” with EST. 18004.”

The cited rea­son sug­gests the mat­ter may fol­low a re­cent trend, in which the USDA is sud­denly clamp­ing down on im­ported foods. An in­dus­try trade site refers to Shanghai Ravioli Corporation as a distributor of pasta and other goods,” which means they could be in­volved in im­port­ing.

The FSISs im­age of the prod­ucts does not show a coun­try of ori­gin.

Sources have ex­plained that an­i­mal-sourced foods brought in from for­eign coun­tries need to be in­spected once they reach the U.S. side. Other re­cent USDA re­calls for the same rea­son af­fected soup and ba­con, both from Canada.

What’s the risk?

Wednesday’s FSIS an­nounce­ment states, Food pro­duced with­out in­spec­tion may con­tain un­de­clared al­ler­gens, harm­ful bac­te­ria, or other con­t­a­m­i­nants that put con­sumer health and safety at risk.”

The FSIS lists the re­call as a Class I, which they deem as a high or medium risk. This rank­ing is used when there is a rea­son­able prob­a­bil­ity that the use of the prod­uct will cause se­ri­ous, ad­verse health con­se­quences or death.”

The agency says it urges busi­nesses not to serve the prod­uct and to dis­card or re­turn them to the place of pur­chase.

For daily well­ness up­dates, sub­scribe to The Healthy newslet­ter and fol­low The Healthy on Facebook, Instagram, and Google. Keep read­ing:

Donuts Recalled Nationwide Following Reported Illness

Rice Recall Update: 55,000 Pounds in 12 States for Foreign Matter

Ibuprofen Recalled for Reported Cross-Contamination

I accidentally turned LLM memory into program analysis :: pwning.systems

pwning.systems

Over the past few months I have been play­ing around quite a bit with LLM agents, par­tic­u­larly for vul­ner­a­bil­ity re­search.

They are be­com­ing sur­pris­ingly good at nav­i­gat­ing large code­bases, ex­plain­ing un­fa­mil­iar sub­sys­tems and help­ing ex­plore po­ten­tial at­tack sur­faces. However, once an in­ves­ti­ga­tion starts tak­ing a few hours, I kept run­ning into the same prob­lem: the model would slowly lose track of what we had ac­tu­ally es­tab­lished.

It might sug­gest an ap­proach that we had al­ready ruled out, for­get that an as­sump­tion turned out to be false, or con­fi­dently con­tinue rea­son­ing from an ob­ser­va­tion that was no longer valid. Obviously, telling an LLM that some­thing is wrong does not nec­es­sar­ily mean that it will stop be­liev­ing all of the things that de­pended on it :)

I ini­tially started look­ing into mem­ory sys­tems be­cause I wanted to make LLMs more use­ful for com­plex vul­ner­a­bil­ity re­search and re­duce this type of hal­lu­ci­na­tion.

There are of course al­ready plenty of so­lu­tions for giv­ing LLMs mem­ory. Usually this in­volves stor­ing old con­ver­sa­tions or ob­ser­va­tions some­where, em­bed­ding them, and then re­triev­ing the most rel­e­vant pieces when­ever the model needs them again.

This works rea­son­ably well, but there was some­thing about it that both­ered me.

During a vul­ner­a­bil­ity re­search sesh, I don’t just want the model to re­mem­ber what we said.

I want it to main­tain what we cur­rently know.

Imagine that dur­ing an in­ves­ti­ga­tion we es­tab­lish the fol­low­ing:

at­tacker con­trols ob­jec­t_a ob­jec­t_a points to ob­jec­t_b ob­jec­t_b is a ker­nel ob­ject

From this, we may con­clude that the at­tacker can con­trol a ker­nel ob­ject.

A nor­mal mem­ory sys­tem could store all of these ob­ser­va­tions and re­trieve them again when­ever we ask about the ex­ploitabil­ity of the bug. The LLM then fig­ures out the same con­clu­sion.

Great!

However, sup­pose that two hours later we dis­cover in LLDB that ob­jec­t_a does not ac­tu­ally point to ob­jec­t_b, and that our pre­vi­ous ob­ser­va­tion was based on a wrong as­sump­tion.

At that point our mem­ory may con­tain some­thing like:

ob­jec­t_a points to ob­jec­t_b at­tacker can con­trol ob­jec­t_b ob­jec­t_a does not ac­tu­ally point to ob­jec­t_b

Now we re­trieve some sub­set of these mem­o­ries and hope that the LLM cor­rectly fig­ures out which con­clu­sions are still valid.

This started to feel a lit­tle fa­mil­iar to me.

This looks like pro­gram analy­sis

A lot of the work I nor­mally do in­volves pro­gram analy­sis.

When analysing a pro­gram, we usu­ally have a bunch of facts about the pro­gram and some rules that de­rive ad­di­tional facts from them.

For ex­am­ple, imag­ine we know:

calls(foo, bar) calls(bar, baz)

We could de­fine a rule stat­ing that if one func­tion calls an­other func­tion, which it­self can reach a third func­tion, then the first func­tion can reach the third func­tion as well.

Eventually we cal­cu­late a fixed point con­tain­ing every­thing we can de­rive from the pro­gram. More im­por­tantly, if one of our in­put facts changes, there are plenty of tech­niques for up­dat­ing only the af­fected re­sults in­stead of re­run­ning every­thing from scratch.

This is also ex­actly what I wanted from an LLM dur­ing vul­ner­a­bil­ity re­search.

If an ob­ser­va­tion changes, I don’t want the model to re­con­struct the en­tire in­ves­ti­ga­tion from a tran­script and hope­fully no­tice all of the con­se­quences. I want the af­fected con­clu­sions to be­come in­valid au­to­mat­i­cally.

When look­ing at the prob­lem from this per­spec­tive, I started won­der­ing why we were mak­ing the LLM re­con­struct its en­tire state over and over again.

What if we just main­tained it?

And this is how I some­how ended up writ­ing a Datalog en­gine for LLMs :)

Datalog

Before we con­tinue, it is prob­a­bly use­ful to briefly ex­plain what Datalog ac­tu­ally is.

Datalog is a de­clar­a­tive logic pro­gram­ming lan­guage. Instead of writ­ing in­struc­tions de­scrib­ing how some­thing should be cal­cu­lated, we de­scribe facts and rules from which new facts can be de­rived.

Datalog is a de­clar­a­tive logic pro­gram­ming lan­guage. Instead of writ­ing in­struc­tions de­scrib­ing how some­thing should be cal­cu­lated, we de­scribe facts and rules from which new facts can be de­rived.

For ex­am­ple, we could store the fol­low­ing facts:

con­trols(at­tacker, ob­jec­t_a). points_to(ob­jec­t_a, ob­jec­t_b). ker­nel_ob­ject(ob­jec­t_b).

And then de­fine the fol­low­ing rule:

con­trol­s_k­er­nel_ob­ject(At­tacker) :- con­trols(At­tacker, ObjectA), points_to(Ob­jectA, ObjectB), ker­nel_ob­ject(Ob­jectB).

From our ex­ist­ing facts, the en­gine can there­fore de­rive:

con­trol­s_k­er­nel_ob­ject(at­tacker).

Nothing par­tic­u­larly ex­cit­ing yet.

However, sup­pose we later dis­cover that:

points_to(ob­jec­t_a, ob­jec­t_b).

was in­cor­rect.

If con­trol­s_k­er­nel_ob­ject(at­tacker) was de­rived from that fact, we know ex­actly which con­clu­sion de­pends on the ob­ser­va­tion that just changed, and we can au­to­mat­i­cally in­val­i­date it.

This is con­sid­er­ably nicer than putting all of the old in­for­ma­tion into a prompt and ask­ing an LLM to hope­fully no­tice the same thing.

Lemmalog

This even­tu­ally turned into Lemmalog.

The ba­sic idea is that an LLM should not nec­es­sar­ily be re­spon­si­ble for main­tain­ing its own knowl­edge. Instead, I split the prob­lem into two parts.

The LLM han­dles the fuzzy part:

LLDB shows that the freed ob­ject is later reused as the des­ti­na­tion of the write.” | v freed(ob­jec­t_a) reused_as(ob­jec­t_a, write_­tar­get)

And Lemmalog han­dles the de­ter­min­is­tic part:

facts | v rules | v de­rived facts

This means that the LLM is still re­spon­si­ble for un­der­stand­ing nat­ural lan­guage, source code, de­bug­ger out­put and all the other messy in­for­ma­tion that ap­pears dur­ing an in­ves­ti­ga­tion.

LLMs hap­pen to be quite good at this.

But once that in­for­ma­tion has been con­verted into struc­tured facts, we no longer need the model to re­peat­edly de­ter­mine all of its con­se­quences. The data­base can do that in­stead.

Retractions

One of the first in­ter­est­ing prob­lems I ran into was re­mov­ing facts.

Adding facts to a Datalog data­base is rel­a­tively straight­for­ward: add the new fact and eval­u­ate any rules which may now pro­duce ad­di­tional re­sults.

Removing some­thing is a lit­tle more an­noy­ing.

Take the fol­low­ing ex­am­ple:

a. b. c :- a. c :- b.

Here c has two sep­a­rate rea­sons for be­ing true.

If we re­move a, we can­not sim­ply re­move c, be­cause b still pro­vides an­other de­riva­tion for it. However, if we re­move both a and b, c should dis­ap­pear as well.

This turns out to be quite im­por­tant dur­ing vul­ner­a­bil­ity re­search, be­cause a con­clu­sion may be sup­ported by mul­ti­ple ob­ser­va­tions.

For ex­am­ple:

can­di­date_3_is_­ex­ploitable

may re­main true even if one par­tic­u­lar ex­ploit prim­i­tive turns out not to work, be­cause there is an­other in­de­pen­dent path to the same re­sult.

So Lemmalog has to keep track of how facts were de­rived and up­date their sup­port when some­thing changes.

Conveniently, this also gives us an­other use­ful prop­erty:

we can ask why some­thing is true.

Why?

Imagine we have been run­ning an agent for a few hours while in­ves­ti­gat­ing some­thing and it even­tu­ally con­cludes:

can­di­date_3_is_­ex­ploitable

That is nice, but I would also quite like to know why.

Because Lemmalog al­ready tracks the de­pen­den­cies of de­rived facts, we can ask it for the prove­nance of a con­clu­sion. For ex­am­ple, we may get some­thing that con­cep­tu­ally looks like this:

can­di­date_3_is_­ex­ploitable | +– at­tack­er_­con­trol­s_­pointer | | | +– ob­ser­va­tion_41 | +– point­er_reach­es_­tar­get | +– ob­ser­va­tion_57 +– rule_12

If ob­ser­va­tion_41 later turns out to be in­cor­rect, we know that this con­clu­sion may no longer be valid, and be­cause the data­base knows this as well, it can re­move the af­fected con­clu­sions au­to­mat­i­cally.

This was orig­i­nally mostly nec­es­sary to make in­cre­men­tal eval­u­a­tion work cor­rectly, but it turns out that be­ing able to ask an AI agent why it be­lieves some­thing is quite use­ful as well :)

It also ad­dresses one of the more an­noy­ing fail­ure modes I en­coun­tered with LLM-assisted re­search. Sometimes a model will con­fi­dently say some­thing like:

we al­ready es­tab­lished that this pointer is at­tacker-con­trolled

when that is not ac­tu­ally true.

If a con­clu­sion ex­ists in Lemmalog, I can ask where it came from. If there is no prove­nance sup­port­ing it, then it is not part of the main­tained state.

This ob­vi­ously does not pre­vent an LLM from hal­lu­ci­nat­ing dur­ing ex­trac­tion, but it does make it much harder for un­sup­ported con­clu­sions to silently be­come part of the in­ves­ti­ga­tion.

Facts also change over time

Another is­sue is that re­plac­ing old facts is not al­ways the same as delet­ing them.

Suppose we orig­i­nally be­lieve:

prim­i­tive_a is vi­able

and later dis­cover:

prim­i­tive_a is not vi­able

For most cur­rent queries, we prob­a­bly only care about the sec­ond state­ment. However, if we want to un­der­stand why we pre­vi­ously ex­plored a par­tic­u­lar ex­ploit strat­egy, the old state is still use­ful.

For this rea­son Lemmalog can as­so­ci­ate facts with va­lid­ity in­ter­vals.

Conceptually, we can rep­re­sent the state as some­thing like:

vi­able(prim­i­tive_a) [10:14, 12:37) not_vi­able(prim­i­tive_a) [12:37, …)

This al­lows us to an­swer both:

Is prim­i­tive_a vi­able now?

and:

Why did we think prim­i­tive_a was vi­able ear­lier?

with­out keep­ing two ap­par­ently con­tra­dic­tory facts around and ask­ing the LLM to de­cide which one we meant.

Again, this is not re­ally a lan­guage model prob­lem.

Stopping the smart TV from being used against you.

www.s-config.com

Smart TVs are every­where! But who is the mas­ter?

Amid the over­all elec­tron­ics boom thanks to A.I., one piece of elec­tron­ics is al­most un­changed fi­nan­cially. That is the dig­i­tal dis­play or flat-panel mon­i­tor. With good rea­son! The en­tire en­ter­tain­ment and stream­ing in­dus­try wants one of these de­vices in your liv­ing room. Not only want­ing ac­cess to your wal­let; com­pa­nies are will­ing to vi­o­late cus­tomer trust to squeeze a few ex­tra pen­nies out of the de­vices they sold you. To these com­pa­nies, you tech­ni­cally never re­ally owned the de­vice, but in­stead pur­chased per­mis­sion from these com­pa­nies to have it ex­ist in your house­hold. Like a true tech-val­ley rapist, they will break into your home, lis­ten to every­thing you do via the mi­cro­phone, track every move­ment that you make thanks to their back­doored OS, and most im­por­tantly, spread mal­ware onto de­vices they don’t even own.

Whoah S! Mal-ware? Isn’t that a lit­tle harsh? - Anonymous

Whoah S! Mal-ware? Isn’t that a lit­tle harsh? - Anonymous

Malware, by de­f­i­n­i­tion, is soft­ware you never asked for and was in­stalled with­out your per­mis­sion. At least in LGs case. Using Legal Ninjitsu to state that by agree­ing to in­stall their mon­i­tor driver,’ you also agree to in­stall every other bloat­ware ap­pli­ca­tion that they choose should come with it as well. Like ad­ver­tis­ing, it takes a shit all over your desk­top to churn prof­its for LG.

Read on to con­tinue the de­scent into mad­ness.

It all starts with go­ing cheap.

It did­n’t take a whole lot of search­ing on Amazon to find what ap­pears to be a good deal on a 75-inch TV. We fig­ured we’d go with 75 inches be­cause, like every American house­hold, it’s a good idea to go big when pur­chas­ing a TV for your bath­room. Ahh, noth­ing like be­ing on the shit­ter while catch­ing eye strain from the TV that prac­ti­cally cov­ers an en­tire side of the wall.

Sure enough, LG comes out on top with their (bullshit) AI up­scal­ing and Wow Orchestra,” what­ever that means. But $10 more than the Toshiba… WHY NOT RIGHT?!?

So you get this bad-boy home, hook it up to your lap­top with Windows 11 so you can get Netflix go­ing on..

Oh, there’s a dri­ver up­date for the LG tele­vi­sion. Makes sense, right? It’s a fairly re­cent tele­vi­sion, and the dri­vers will help us take ad­van­tage of all of the TVs fea­tures. Of course, I’d like Microsoft to down­load it.

LG Malware:

Then af­ter a while, you no­tice more and more ap­pli­ca­tions ap­pear on your desk­top. Like a copy of McAfee an­tivirus that you never asked for. Now, if this were an el­derly cou­ple or some­one who does­n’t know any­thing about com­put­ers. They would­n’t even know their sys­tem is be­ing turned against them by LG. Some would think their sys­tem was hacked by some­one else. But ac­tu­ally, no. Because Microsoft is be­ing am­biva­lent by let­ting LGs sup­posed driver’ in­stall it­self and, via LGs proxy, in­stall even more ap­pli­ca­tions.

How does this at­tack work? Especially when you made SURE LG has no ac­cess to the in­ter­net!

This all hap­pens the mo­ment you plug that HDMI (or DisplayPort for peo­ple who pur­chased an LG com­puter mon­i­tor) right into an in­ter­net-en­abled lap­top through a pro­to­col called EDID, also known as Extended Display Identification Data,” which by it­self is to­tally harm­less!

It sim­ply tells your op­er­at­ing sys­tem what kind of mon­i­tor is con­nected, what its ca­pa­bil­i­ties are, it’s se­r­ial num­ber. That’s it!

Inside Windows, when you go to Device Manager, it might not seem like much. Just a generic UPNP de­vice at­tached as a mon­i­tor.

Dive a lit­tle more into Device Manager, and we see where EDID does its magic; we found that this par­tic­u­lar Microsoft Box is hooked up to some kind of Samsung mon­i­tor. Now, Samsung might have some kind of dri­ver that would help take ad­van­tage of my mon­i­tor. But this Samsung is so old they sim­ply do not care.

When your core OS, like Microslop has been com­pro­mised by an in­cred­i­bly wealthy or­ga­ni­za­tion such as LG. Having your Windows Updater sched­ule a dri­ver re­lease seems like a help­ful thing to do for your end users. And it helps if LG throws a lot of money Microsoft’s way as a part­ner to look the other way, as their sup­posed dri­ver soft­ware has its way with your lap­top. Well! What’s the mat­ter with that? Don’t you want Value added fea­tures?” or you can’t be pos­si­bly stu­pid to unin­stall a virus checker as Reputable” as McAfee right?

Hah! I don’t have to worry about this! I got Linux!

Hah! I don’t have to worry about this! I got Linux!

For now.. That’s true. But EDID trans­mis­sion still hap­pens. and your can in­stall dd­cu­til to check it out.

crack­head@BLORB.LO­CALHOST:~$ dd­cu­til de­tect I2C bus: /dev/i2c-2 DRM_connector: card1-HDMI-A-2 EDID syn­op­sis: Mfg id: APX - AP Designs Ltd Model: LE24H87 Product code: 12852 (0x3234) Serial num­ber: Binary se­r­ial num­ber: 535 (0x00000217) Manufacture year: 2010, Week: 32 This mon­i­tor does not sup­port DDC/CI. (I2C slave ad­dress x37 is un­re­spon­sive.)

For the sake of this blog, we pow­ered up one of our thin clients and con­nected HDMI to it. It shows a lot of in­for­ma­tion for my ul­tra-crap mon­i­tor that was made in 2010.

But what stops LG from con­vinc­ing the Ubuntu main­tain­ers to make an LG vari­ant of their sup­posed dri­ver to mag­i­cally ap­pear in your snap or apt pack­age repos­i­tory? You hope to God they don’t, but the dan­ger is al­ways there.

Fuck, i’ll just not get an LG TV Then!

Fuck, i’ll just not get an LG TV Then!

If you’re go­ing to be home all of the time, that’s a per­fectly ac­cept­able so­lu­tion. Keep in mind LG may be the first com­pany that is ea­ger to erode trust to get a few ex­tra dol­lars or lis­ten in on your con­ver­sa­tions. Corporations are cow­ards; if they see one per­son ex­ploit­ing peo­ple, you’ll bet your ass the rest will fol­low right in line. Eventually, Smart TVs will be used against you. On top of this, get­ting a Non-SMART TV,” you’ll quickly find out that most of those only ex­ist within com­mer­cial en­vi­ron­ments; of­ten, they want a pre­mium price for not hav­ing an Android OS that could get hacked, or plas­ter movie ads the mo­ment you power it up.

For ex­am­ple, if you checked into one of the many newly ren­o­vated Hilton ho­tels through­out the United States while trav­el­ing. It’s al­most guar­an­teed to have an LG TV not only pow­ered on the mo­ment you en­ter the room but con­nected to the ho­tel in­ter­net, ready to spy away on you.

Usually, you can just un­plug the damn TV and watch movies on your lap­top screen. OR! You do some­thing about it.

Enter the EDID Blocker

We’ve used each one of these units dur­ing one part of our life­time in the world of com­put­ers.

The gold one on the left is ac­tu­ally an EDID dummy plug. It al­lows a com­puter to be pow­ered on, think­ing there is an HDMI mon­i­tor at­tached to its work­sta­tion, when in re­al­ity there is none. Useful if you like graph­i­cal re­mote desk­tops and don’t want the res­o­lu­tion to get all stu­pid the mo­ment you un­plug.

The other is a com­mon HDMI EDID blocker. There’s a lot out there with vary­ing qual­ity. Originally, these were used in con­junc­tion with an HDMI split­ter so if the mon­i­tors change it does­n’t black out the dis­play. Another use is if you have a lot of pro­jec­tors in a par­tic­u­lar ori­en­ta­tion and don’t want win­dows to com­pletely screw up your tiling with­out us­ing Nvidia Mosaic, this is more of a hard­ware-based ap­proach.

crack­head@BLORB.LO­CALHOST:~$ dd­cu­til de­tec­tI2C bus: /dev/i2c-2DRM_connector: card1-HDMI-A-2E­DID syn­op­sis:Mfg id: XMD - UNKModel: Mi TVProduct code: 154 (0x009a)Serial num­ber:Bi­nary se­r­ial num­ber: 0 (0x00000000)Manufacture year: 2017, Week: 39This mon­i­tor does not sup­port DDC/CI. (I2C slave ad­dress x37 is un­re­spon­sive.)

TV changed, and if I un­plug just the HDMI ca­ble go­ing to my 2010 tele­vi­sion, it’ll still read just fine.

You’re prob­a­bly won­der­ing about the last EDID blocker, which is DisplayPort to DisplayPort. WELL!

It’s a scam. Just a bunch of wires, shit-sol­dered to­gether to make a jumper and charger 15 – 30-45 to pass it off as an ac­tual blocker. We wanted to get a le­git EDID blocker to show you its de­vice. But it’s im­por­tant to doc­u­ment even the boot­leg garbage you get when work­ing with grey-mar­ket tech. Got my re­fund right away on this one.

This, how­ever, il­lus­trates the im­por­tance of check­ing your hard­ware when you get it. If we got an LG gam­ing mon­i­tor with this boot­leg piece of shit hooked up to a Windows box. Welp! We would prob­a­bly be scrub­bing McAfee off of our com­put­ers AGAIN!

When it comes to a DisplayPort EDID em­u­la­tor, there are le­git com­pa­nies out there. But they’re pricey. Anywhere from $50 to $150 US Dollars.

Final thoughts.

There are, of course, more sen­si­ble tech­niques to fight cor­rupt smart TVs, such as the fol­low­ing:

Never give ANY smart de­vice in­ter­net.

Have a good fire­wall that Microsoft never touched. Especially make sure your DNS for these com­pa­nies is di­verted to a pic­ture of Obama. Generally, a good open-source router with ad-block­ing plu­g­ins will take care of this

Just change your OS. Worked for me!

Use a Linux-based OS to watch TV. Don’t get an­other Android box loaded with spy­ware.

Throw it into the garbage and fi­nan­cially eat shit buy­ing a TV with no OS what­so­ever. Or a bet­ter and more portable so­lu­tion could be a pro­jec­tor in­stead.

You know what? There’s re­ally noth­ing good to watch on TV any­ways. If Hollywood has to sub­si­dize the tele­vi­sion in­dus­try just to keep its base, it does­n’t have any money to in­vest in any­thing new. Every chan­nel just wants to brain­wash you. So throw­ing the Flat Panel into the garbage might be a good idea any­ways.

But if you’re backed into a cor­ner with your work lap­top run­ning Microslop and every­one is in­sist­ing on hook­ing your car­bomb of a lap­top up to the world’s most mal­ware-in­fested TVs, such as an LG. It’s good to know there are hard­ware op­tions to pro­tect your­self by mis­di­rect­ing the man­u­fac­turer la­bel so no dri­vers can ever be de­tected or down­loaded.

The only down­side we’ve ever ex­pe­ri­enced with these block­ers is when some­thing is rated for 4K. Your sys­tem BIOS might au­to­mat­i­cally switch to 4K un­til the OS re­turns. Minor an­noy­ance. But it’s some­thing worth not­ing.

That’s what server said

END OF LINE+++

EasyEffects should be part of every Linux distribution and desktop environment to massively improve laptop speaker sound quality – OSnews

www.osnews.com

Home > Multimedia, AV > EasyEffects should be part of every Linux dis­tri­b­u­tion and desk­top en­vi­ron­ment to mas­sively im­prove lap­top speaker sound qual­ity

Virtually all lap­top speak­ers suck. It’s the one area where even re­ally ex­pen­sive lap­tops tend to fall on their ass, leav­ing users with a tinny, harsh, and hol­low sound ex­pe­ri­ence. While you can’t ex­actly over­come physics — lap­top speak­ers are nec­es­sar­ily small and thus just can­not ever sound as good as proper speak­ers — there’s a lot you can do with proper tun­ing and soft­ware magic. If you’re a desk­top Linux user, you ac­tu­ally al­ready pos­sess all the plumb­ing needed to fix your au­dio; it’s just not ex­posed to you in any way. Luckily, an ap­pli­ca­tion called EasyEffects al­lows you to ac­tu­ally make use of desk­top Linux’ ad­vanced au­dio fea­tures to mas­sively im­prove the sound qual­ity of your lap­top’s speak­ers.

The OSNews 2026 Fundraiser

8,823 / 15,000

➡️ Donate through Ko-Fi ➡️ Donate through SEPA trans­fer* ➡️ Buy merch from our store ➡️ Why a fundraiser?

€5000 in­cen­tive: Make me use Windows 11 for a month (the re­sults were not great)> €10000: Video tour of my of­fice and my com­put­ers/​de­vices col­lec­tion <€15000: Buy a Mac and use ma­cOS for a month (and re­view it)€20000: I get an OSNews tat­too

*Name: Thom Holwerda — IBAN: SE08 8000 0820 1684 4657 8414 — BIC: SWEDSESS

EasyEffects’ own de­scrip­tion on its GitHub page does­n’t re­ally ex­plain what it does or what it’s ca­pa­ble of, so here’s the de­scrip­tion from Wikipedia in­stead.

EasyEffects uses PipeWire to process in­com­ing and out­go­ing au­dio streams in­de­pen­dently and can ap­ply var­i­ous sound ef­fects in the form of plug-ins made by dif­fer­ent de­vel­oper teams such as Calf Studio Gear, MDA.LV2 and GStreamer. All plu­g­ins have their own pre­sets and can be ap­plic­a­ble in­side the suite rather than hav­ing to use a dif­fer­ent mixer or ex­e­cut­ing a script from the com­mand line.Avail­able out­put ef­fects are lim­iter, auto vol­ume, com­pres­sor of dy­namic range, fil­ter, 30 bands para­met­ric equal­izer, bass en­hancer, ex­citer, re­ver­ba­tion, cross­feed, de­lay, max­i­mizer and spec­trum an­a­lyzer. Available in­put ef­fects are WebRTC, lim­iter, com­pres­sor, fil­ter, equal­izer, de-esser, re­ver­ba­tion, pitch shift and spec­trum an­a­lyzer. ↫ EasEffects’ Wikipedia page

EasyEffects uses PipeWire to process in­com­ing and out­go­ing au­dio streams in­de­pen­dently and can ap­ply var­i­ous sound ef­fects in the form of plug-ins made by dif­fer­ent de­vel­oper teams such as Calf Studio Gear, MDA.LV2 and GStreamer. All plu­g­ins have their own pre­sets and can be ap­plic­a­ble in­side the suite rather than hav­ing to use a dif­fer­ent mixer or ex­e­cut­ing a script from the com­mand line.

Available out­put ef­fects are lim­iter, auto vol­ume, com­pres­sor of dy­namic range, fil­ter, 30 bands para­met­ric equal­izer, bass en­hancer, ex­citer, re­ver­ba­tion, cross­feed, de­lay, max­i­mizer and spec­trum an­a­lyzer. Available in­put ef­fects are WebRTC, lim­iter, com­pres­sor, fil­ter, equal­izer, de-esser, re­ver­ba­tion, pitch shift and spec­trum an­a­lyzer.

None of this mat­ters, and you can for­get every­thing from these two para­graphs.

What mat­ters is that us­ing EasyEffects, you can tune the au­dio com­ing out of your speak­ers to make them sound a lot bet­ter. The few lap­tops on the mar­ket that do have de­cent au­dio — MacBooks, some Dell XPS lap­tops, and surely a few more — aren’t mag­i­cally de­fy­ing physics. While they prob­a­bly do have ob­jec­tively higher-qual­ity speak­ers, the main dif­fer­ence be­tween those lap­tops and lap­tops with crappy-sound­ing speak­ers is that the for­mer come with built-in tun­ing from the fac­tory to make them sound much bet­ter than they would with­out any soft­ware trick­ery.

If you know your way around au­dio, you can very much use EasyEffects and tune your lap­top speak­ers from scratch to mas­sively im­prove how they sound. However, that re­quires time, ex­pe­ri­ence, knowl­edge, and ex­per­tise that most peo­ple lack, in­clud­ing my­self. Lucky for us, though, there are count­less down­load­able pre­sets out there for EasyEffects de­signed specif­i­cally to make lap­tops sound bet­ter.

In an ideal world, you’d pick a pre­set cre­ated specif­i­cally for your lap­top make and model, but odds are you won’t find one, so for most lap­tops you’ll have to set­tle for a generic pre­set that tries to do its best. I’ve long set­tled on the Advanced Auto Gain.json pre­set from JackHack96, which greatly im­proves the au­dio per­for­mance on any lap­tops I’ve tried it on, but of course, there’s count­less other pre­sets for you to try to see if there’s any­thing that suits your par­tic­u­lar lap­top and ears bet­ter.

Getting all of this up and run­ning is re­ally easy. EasyEffects is most likely pack­aged by your Linux dis­tri­b­u­tion, and the lat­est ver­sion is al­ways avail­able as a Flatpak from Flathub. Download the pre­set(s) you want to try, copy them ei­ther to ~/.config/easyeffects (if you use your dis­tri­b­u­tion’s pack­age) or to ~/.var/app/com.github.wwmm.easyeffects/data/easyeffects/output/ (if you use the Flatpak ver­sion). They’ll show up right away in the Presets tab in EasyEffects, ready to be turned on and off when­ever you want, mak­ing it very easy to com­pare and con­trast to find the one you like best. EasyEffects can live in your sys­tem tray giv­ing you easy ac­cess to your pre­sets with­out hav­ing to open the main win­dow, and it can be set to start au­to­mat­i­cally at boot. EasyEffects can also be turned on and off on the fly.

There’s ob­vi­ous down­sides to all of this, too, of course. First, since you’re most likely go­ing to be us­ing a generic pre­set not specif­i­cally crafted for your lap­top, there’s no guar­an­tee the re­sults will be pos­i­tive for you. Second, not every pre­set is ideal for every type of au­dio. Most of my au­dio con­sists of YouTube videos with mostly speech; if you lis­ten mostly to mu­sic, dif­fer­ent pre­sets may yield bet­ter re­sults. Third, au­dio qual­ity is deeply sub­jec­tive, and what sounds good to my ears may sound like to­tal garbage to yours. Fourth, EasyEffects does take up a tiny frac­tion of CPU power (I’m talk­ing 0.1 – 0.2% ac­cord­ing to KDEs System Monitor), but I have never seen it have any no­tice­able per­for­mance im­pact on any­thing.

Even the generic pre­set I use makes such a mas­sive dif­fer­ence for me on every lap­top I’ve ever tried it on, that I’ve be­come con­vinced EasyEffects and a few of the generic pre­sets should be in­stalled by de­fault by any desk­top-ori­ented Linux dis­tri­b­u­tion. On top of that, Linux lap­tops OEMs like System76, Nova Custom, Star Labs, and so on, should re­ally take the time to cre­ate proper pre­sets for their lap­tops to im­prove their sound qual­ity out of the box. I feel like if you’re al­ready de­sign­ing and sell­ing lap­tops, you prob­a­bly also have the skills and means to cre­ate a de­cent pre­set.

In fact, I’d take it a step fur­ther and urge desk­top en­vi­ron­ments like KDE and GNOME to prop­erly in­te­grate EasyEffects into their sound set­tings. They should­n’t in­clude the en­tire ap­pli­ca­tion and its user in­ter­face, but should make it so that you can con­fig­ure and man­age pre­sets right from the sound set­tings pan­els, and switch be­tween pre­sets from their vol­ume ap­plets (as well as turn it off en­tirely, of course). This would leave the full EasyEffects ap­pli­ca­tion for peo­ple who need more con­trol, man­ual tun­ing, and more ad­vanced fea­tures.

There’s ab­solutely no rea­son why speak­ers on Linux lap­tops should sound tinny, harsh, and hol­low. The Linux desk­top has all the tech­nolo­gies and fea­tures built right in to make speak­ers sound much bet­ter than they do with­out any tun­ing, and yet, very few peo­ple seem to ac­tu­ally be aware of this. This needs to change, and I think it’s up to dis­tri­b­u­tions, desk­top en­vi­ron­ments, and Linux OEMs to make this hap­pen.

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

10HN is also available as an iOS App

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

Visit pancik.com for more.