Cover illustration

TheDaily Front

Issue No. #260731 Friday, July 31 2026 #260731 — FRIDAY, JULY 31, 2026
Algorithms in the lift, agents in the vault, and a newsroom still asking who is driving.
Friday, July 31, 2026 The Daily Front No. #260731 — Contents
30stories
10,016points
4,832comments
274kllm tokens
Assembled with 33 model calls — 185,132 tokens read, 89,087 written.

Highlights

Show HN: Elevators

A crowd-pleasing tour of elevator scheduling turns an everyday wait into a lesson in algorithms, incentives, and human impatience.

DeepSeek-V4-Flash Update

DeepSeek’s new Flash release and its price-performance analysis made the model wars feel suddenly less theoretical and much cheaper.

Tailscale didn't stop the Hugging Face intrusion

Tailscale’s account of the Hugging Face intrusion shows how agentic security incidents can turn ordinary credentials into extraordinary blast radius.

The session you cannot take with you

A sharp essay on non-portable AI sessions asks who really owns the conversation when the state lives behind someone else’s API.

I flagged two research papers for fake authors and both were accepted as orals

A report from the “slop trenches” describes fake citations and invented authors slipping into the research review machinery.

From the Editor

The day’s dispatches find machinery growing clever in all the old familiar places: the elevator bank, the browser, the laboratory, the author’s desk. Your editor notes that progress remains a splendid engine, provided someone remembers where the brake lever is.

  1. Show HN: Elevators3
  2. DeepSeek-V4-Flash Update4
  3. DeepSeek V4 Flash 0731 Intelligence, Performance and Price Analysis5
  4. Google fixed more Chrome bugs in June than over the past two years, thanks to AI6
  5. Tailscale didn't stop the Hugging Face intrusion7
  6. The session you cannot take with you8
  7. Is AI reasoning right for the wrong reasons?9
  8. I flagged two research papers for fake authors and both were accepted as orals10
  9. The End of an Era11
  10. Everyone is building LLM routers, we deprecated ours12
  11. qm – Multiplayer agent harness for work13
  12. Big Food vs. the People14
  13. Getting 25 Gbps Thunderbolt Ethernet on My Mac Studio15
  14. Algorithms on billion-scale graph using 10GB RAM: I love DataFusion16
  15. JEP 401: Value Objects (Preview) merged to OpenJDK master17
  16. Progressive Web Components18
  17. Let's make the worst Htmx19
  18. Authorize, don't authenticate20
  19. Show HN: Gander, an Android file viewer that asks for no permissions21
  20. The most official water costs $120k a gallon22
  21. Increasing the lifespan of a bulb makes it worse in every other way23
  22. Bad Apple but It's Traceroute24
  23. How JPEG works: Interactively explore JPEG's lossy compression methods25
  24. Where USB Memory Sticks are Born (2013)26
  25. The mean means nothing: data visualization to debug a latency problem27
  26. The Religion of Speed28
  27. The AI Aesthetic29
  28. Severance30
  29. Using the railway network as a flatbed scanner [video]30
  30. Demystifying DRAM Read Disturbance: RowHammer and RowPress Phenomena30
The Daily Front Page 2 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Up and Down the Stack
show hn

Show HN: Elevators

by Jrh0203·▲ 1,594 points·393 comments·john.fun ↗
I pressed the button, why isn't it coming?

Everyone has shared the frustration of waiting for an elevator that never seems to arrive. "I pressed the button, why isn't it coming?" you ask. For something as commonplace as elevators, they are far more complex than meets the eye.

Over the course of this article, we'll unravel the mysteries of elevators. The way you push their buttons, and how they push yours.

One Car

The simplest elevator algorithm is called SCAN and was patented in 1961. The elevator starts at the lobby and goes all the way to the top floor before reversing and coming back down. It picks up and drops off anybody on the way.

Most of the time you don't actually need to go to the TOP floor. If the elevator goes only as high as requested before reversing, the algorithm is called the LOOK algorithm. This is the algorithm most people know and expect.

Multiple Cars

Here's where the mystery begins. If there are multiple elevators, how do the cars coordinate who picks up who?

In the most basic system, there's a central scheduler that tells each elevator which floors to stop on. When a new request comes in, it's assigned to the closest elevator. As we'll soon see however, we can do better.

Long Waits

How do you actually measure how good an elevator algorithm is? The obvious metric is how long you wait for the elevator to arrive.

A very simple measure is "how often does the elevator arrive within 30 seconds?" Or "how often does the elevator arrive within 90 seconds?"

Applied Stats

More rigorously, we want to look at the DISTRIBUTION of wait times. If we plot the wait time across thousands of rides, we get the histogram below.

A p90 of 2m means 90% of the time, riders wait 2m or less for the elevator. A p50 of 1m means half the time the elevator arrives within 1m.

People don't usually remember the average amount of time they wait. They fixate on those times when the elevator took FOREVER, the p90 case.

Morning Rush

Not all passenger traffic is created equal. Imagine a large corporate office building. In the mornings, nearly all traffic is dominated by trips from the lobby to the upper levels.

In the evening this flips as everyone leaves the building. The lunch rush is a bit of both, and the remaining traffic is often from floor to floor.

The distribution of wait times varies drastically depending on the time of day and the traffic patterns the elevators are facing. Morning rush notoriously has the worst wait statistics.

Smarter Elevators

When analyzing the LOOK elevator algorithm, we LOOKED (ha ha) at how riders are assigned to cars. We naively assigned each request to the nearest car but said we could do better.

What if the nearest car is full? We can get smarter with Otis' RSR (Relative System Response) algorithm. RSR scores each car for how well suited it is to pick up a passenger. Lower scores being better.

RSR pickup score

Score=ETA to pickup+onboard load penalty+same-direction anti-bunching penalty-direction-match bonus-idle-nearby bonus-low-load bonus

Anti-Bunching

Penalize a car if another car is already headed to the same floor in the same direction.

Idle Nearby

Reward idle cars within two floors of the caller.

RSR also re-optimizes every 5 seconds. A passenger that's going to be picked up by elevator A can be re-routed to elevator B if elevator A encounters delays. This re-optimization turns out to be key for streamlining traffic flow.

In the graphic below, each elevator lights up when it's the best choice to service a call from floor 3 if the button happened to be pressed at that exact moment. This constantly changes as the elevators move, showing the optimizer in motion.

LOOK vs RSR

Armed with our elevator analysis toolkit, we can benchmark the performance of LOOK vs RSR to see how much a smarter elevator algorithm actually improves wait time.

Interestingly as the flow rate gets higher, LOOK actually starts to outperform RSR. When the elevators are always full and stopping on every floor, the extra rules don't matter as much.

LOOK also tends to outperform RSR in small buildings with fewer elevators per bank. Sometimes it's better to just keep things simple.

Another metric you can track is journey time, how long you're actually waiting in the elevator before getting to your floor. RSR and LOOK have different characteristics here as well but that's beyond the scope of this article.

Destination Dispatch

Not all elevators have buttons in them. Some of the fancy new elevators have a kiosk on each floor that allows you to specify what floor you're heading to before the elevator even arrives. The kiosk then points you to which elevator you should wait for.

This is called Destination Dispatch. At first glance, it seems great. The elevator optimizer now has full knowledge of who is going where, certainly we can use this to reduce wait times right?

It turns out these fancy kiosks are in general worse for wait times compared to the traditional good ol' up and down buttons. There are certainly edge cases when the kiosks can win out (extremely tall buildings with 8+ cars per elevator bank) but for the majority of cases, simple up down buttons reign supreme.

This counterintuitive result is all thanks to the rebalancing step where every 5 seconds, the system re-optimizes each elevator's path. The kiosk enforces rigidity, you must get in the assigned elevator.

The state of the world 30sec after you called your elevator might be very different but the system is unable to adapt. Turns out the loss in flexibility is not worth the extra information for the optimizer.

Full Sim

Here's a simulation with all the buttons and knobs to play with. Go crazy!

Conclusion

This article just scratches the surface of elevator algorithms. Next time you're stuck waiting for an elevator, try not to take it personally. The elevator did hear you, it just has a lot to think about.

The Daily Front Page 3 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — The Cheap Intelligence Race
article

DeepSeek-V4-Flash Update

by dnhkng·▲ 734 points·343 comments·api-docs.deepseek.com ↗
Significantly enhanced agent capabilities, with benchmark results far exceeding V4-Pro-Preview

Date: 2026-07-31

DeepSeek-V4-Flash Update

The official release of the DeepSeek-V4-Flash API is now in public beta. The API calling method remains unchanged — simply set the model name to deepseek-v4-flash to use the latest version.

Significantly enhanced agent capabilities, with benchmark results far exceeding V4-Pro-Preview:

  • Terminal Bench 2.1: 82.7
  • NL2Repo: 54.2
  • Cybergym: 76.7
  • DeepSWE: 54.4
  • Toolathlon verified: 70.3
  • Agent Last Exam: 25.2
  • Automation Bench (Public): 25.1
  • DSBench-FullStack: 68.7
  • DSBench-Hard: 59.6

Note 1: For the Code Agent tasks in the public benchmark sets, the official DeepSeek-V4-Flash was tested using the DeepSeek Harness minimal mode (to be released soon) as the framework, with the max effort level, topp=0.95, and temperature=1.0
Note 2: DSBench-FullStack is an internal full-stack development test set, and DSBench-Hard is an internal Coding Agent hard-problem test set

The official V4-Flash natively supports the Responses API format and is specifically adapted for Codex. For the specific configuration, please refer to the documentation.

DeepSeek-V4-Flash-0731 keeps the same model architecture and size as DeepSeek-V4-Flash-Preview, and was only re-post-trained.

Note: This update only upgrades the DeepSeek-V4-Flash API. The DeepSeek-V4-Pro API and the APP/WEB models are unchanged.
The official release of DeepSeek-V4-Pro will follow soon.


Date: 2026-04-24

DeepSeek-V4

The DeepSeek API now supports V4-Pro and V4-Flash, available via both the OpenAI ChatCompletions interface and the Anthropic interface. To access the new models, the base_url remains unchanged, and the model parameter should be set to deepseek-v4-pro or deepseek-v4-flash.

The two legacy API model names, deepseek-chat and deepseek-reasoner, will be discontinued in three months (2026-07-24). During the current period, these two model names point to the non-thinking mode and thinking mode of deepseek-v4-flash, respectively.

For more details, please refer to this documentation.


Date: 2025-12-01

DeepSeek-V3.2

Both deepseek-chat and deepseek-reasoner have been upgraded to DeepSeek-V3.2.

  • deepseek-chat corresponds to DeepSeek-V3.2's non-thinking mode
  • deepseek-reasoner corresponds to DeepSeek-V3.2's thinking mode

DeepSeek-V3.2-Speciale

DeepSeek-V3.2-Speciale is served via a temporary endpoint: base_url="https://api.deepseek.com/v3.2_speciale_expires_on_20251215". Same pricing as V3.2, no tool calls, available until Dec 15th, 2025, 15:59 (UTC Time).

For more details, please refer to this documentation.


Date: 2025-09-29

DeepSeek-V3.2-Exp

Both deepseek-chat and deepseek-reasoner have been upgraded to DeepSeek-V3.2-Exp.

  • deepseek-chat corresponds to DeepSeek-V3.2-Exp's non-thinking mode
  • deepseek-reasoner corresponds to DeepSeek-V3.2-Exp's thinking mode

For more details, please refer to this documentation.


Date: 2025-09-22

DeepSeek-V3.1-Terminus

Both deepseek-chat and deepseek-reasoner have been upgraded to DeepSeek-V3.1-Terminus. deepseek-chat corresponds to DeepSeek-V3.1-Terminus's non-thinking mode, while deepseek-reasoner corresponds to its thinking mode.

This update maintains the model's original capabilities while addressing issues reported by users, including:

  • Language consistency: Reduced occurrences of Chinese-English mixing and occasional abnormal characters;
  • Agent capabilities: Further optimized the performance of the Code Agent and Search Agent.

Date: 2025-08-21

DeepSeek-V3.1

Both deepseek-chat and deepseek-reasoner have been upgraded to DeepSeek-V3.1. deepseek-chat corresponds to DeepSeek-V3.1's non-thinking mode, while deepseek-reasoner corresponds to its thinking mode.

  • Key updates in DeepSeek-V3.1:

    • Hybrid reasoning architecture: A single model supports both thinking mode and non-thinking mode

    • Improved reasoning efficiency: Compared to DeepSeek-R1-0528, DeepSeek-V3.1-Think provides answers in significantly less time

    • Enhanced agent capabilities: With post-training optimization, the new model achieves major improvements in tool usage and intelligent agent tasks

      • SWE-bench Verified: 66.0
      • SWE-bench Multilingual: 54.5
      • Terminal-bench: 31.3

Date: 2025-05-28

deepseek-reasoner

deepseek-reasoner Model Upgraded to DeepSeek-R1-0528:

  • Enhanced Reasoning Capabilities

    • Significant benchmark improvements (Pass@1)

      • AIME 2025: 70.0 → 87.5 (+17.5)
      • GPQA: 71.5 → 81.0 (+9.5)
      • LCB_v6: 63.5 → 73.3 (+9.8)
      • Aider: 57.0 → 71.6 (+14.6)
    • Note: Complex reasoning tasks may consume more tokens compared to legacy R1 version.

  • Optimized Front-end Development

    • Generated web pages and games now feature improved aesthetics.
  • Reduced Hallucinations

    • Significantly suppressed hallucination issues present in legacy R1 version.
  • JSON Output & Function Calling Support

    • Function call performance:

      • Tau-bench score: 53.5 (Airline) / 63.9 (Retail)

Date: 2025-03-24

deepseek-chat

deepseek-chat Model Upgraded to DeepSeek-V3-0324:

  • Enhanced Reasoning Capabilities

    • Significant improvements in benchmark performance:

      • MMLU-Pro: 75.9 → 81.2 (+5.3)
      • GPQA: 59.1 → 68.4 (+9.3)
      • AIME: 39.6 → 59.4 (+19.8)
      • LiveCodeBench: 39.2 → 49.2 (+10.0)
  • Optimized Front-End Web Development

    • Improved accuracy in code generation
    • More aesthetically pleasing web pages and game front-ends
  • Upgraded Chinese Writing Proficiency

    • Enhanced style and content quality:

      • Aligned with the R1 writing style
      • Better quality in medium-to-long-form writing
  • Feature Enhancements

    • Improved multi-turn interactive rewriting
    • Optimized translation quality and letter writing
  • Improved Chinese Search Capabilities

    • Enhanced report analysis requests with more detailed outputs
  • Function Calling Improvements

    • Increased accuracy in Function Calling, fixing issues from previous V3 versions

Date: 2025-01-20

deepseek-reasoner

  • deepseek-reasoner is our new model DeepSeek-R1. You can invoke DeepSeek-V3 by specifying model='deepseek-reasoner'.
  • For details, please refer to: DeepSeek-R1 Release
  • For guides, please refer to: Thinking Mode

Date: 2024-12-26

deepseek-chat

  • The deepseek-chat model has been upgraded to DeepSeek-V3. The API remains unchanged. You can invoke DeepSeek-V3 by specifying model='deepseek-chat'.
  • For details, please refer to: introducing DeepSeek-V3

Date: 2024-12-10

deepseek-chat

The deepseek-chat model has been upgraded to DeepSeek-V2.5-1210, with improvements across various capabilities. Relevant benchmarking results include:

  • Mathematical: Performance on the MATH-500 benchmark has improved from 74.8% to 82.8% .
  • Coding: Accuracy on the LiveCodebench (08.01 - 12.01) benchmark has increased from 29.2% to 34.38% .
  • Writing and Reasoning: Corresponding improvements have been observed in internal test datasets.

Additionally, the new version of the model has optimized the user experience for file upload and webpage summarization functionalities.


Date: 2024-09-05

deepseek-coder & deepseek-chat Upgraded to DeepSeek V2.5 Model

The DeepSeek V2 Chat and DeepSeek Coder V2 models have been merged and upgraded into the new model, DeepSeek V2.5.

For backward compatibility, API users can access the new model through either deepseek-coder or deepseek-chat.

The new model significantly surpasses the previous versions in both general capabilities and code abilities.

The new model better aligns with human preferences and has been optimized in various areas such as writing tasks and instruction following:

  • ArenaHard win rate improved from 68.3% to 76.3%
  • AlpacaEval 2.0 LC win rate increased from 46.61% to 50.52%
  • MT-Bench score rose from 8.84 to 9.02
  • AlignBench score increased from 7.88 to 8.04

The new model has further enhanced its code generation capabilities based on the original Coder model, optimized for common programming application scenarios, and achieved the following results on the standard test set:

  • HumanEval: 89%
  • LiveCodeBench (January-September): 41%

Date: 2024-08-02

API Launches Context Caching on Disk Technology

The DeepSeek API has innovatively adopted hard disk caching, reducing prices by another order of magnitude.

For more details on the update, please refer to the documentation Context Caching is Available 2024/08/02.


Date: 2024-07-25

New API Features

  • Update API /chat/completions

    • JSON Mode
    • Function Calling
    • Chat Prefix Completion(Beta)
    • 8K max_tokens(Beta)
  • New API /completions

    • FIM Completion(Beta)

For more details, please check the documentation New API Features 2024/07/25


Date: 2024-07-24

deepseek-coder

The deepseek-coder model has been upgraded to DeepSeek-Coder-V2-0724.


Date: 2024-06-28

deepseek-chat

The deepseek-chat model has been upgraded to DeepSeek-V2-0628.

Model's reasoning capabilities have improved, as shown in relevant benchmarks:

  • Coding: HumanEval Pass@1 79.88% -> 84.76%
  • Mathematics: MATH ACC@1 55.02% -> 71.02%
  • Reasoning: BBH 78.56% -> 83.40%

In the Arena-Hard evaluation, the win rate against GPT-4-0314 increased from 41.6% to 68.3%.

The model's role-playing capabilities have significantly enhanced, allowing it to act as different characters as requested during conversations.


Date: 2024-06-14

deepseek-coder

The deepseek-coder model has been upgraded to DeepSeek-Coder-V2-0614, significantly enhancing its coding capabilities. It has reached the level of GPT-4-Turbo-0409 in code generation, code understanding, code debugging, and code completion. Additionally, it possesses excellent mathematical and reasoning abilities, and its general capabilities are on par with DeepSeek-V2-0517.


Date: 2024-05-17

deepseek-chat

The deepseek-chat model has been upgraded to DeepSeek-V2-0517. The model has seen a significant improvement in following instructions, with the IFEval Benchmark Prompt-Level accuracy jumping from 63.9% to 77.6%. Additionally, on API end, we have optimized model ability to follow instruction filled in the ``system" part. This optimization has significantly elevated the user experience across a variety of tasks, including immersive translation, Retrieval-Augmented Generation (RAG), and more.

The model's accuracy in outputting JSON format has been enhanced. In our internal test set, the JSON parsing rate increased from 78% to 85%. By introducing appropriate regular expressions, the JSON parsing rate was further improved to 97%.

The Daily Front Page 4 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Model Markets
article

DeepSeek V4 Flash 0731 Intelligence, Performance and Price Analysis

by theanonymousone·▲ 583 points·311 comments·artificialanalysis.ai ↗
Input $0.14 per 1M tokens Output $0.28 per 1M tokens

Model summary

Intelligence

#3 / 101

50

Artificial Analysis Intelligence Index

4 out of 4 units for Intelligence.

Speed

N/A

Output tokens per second

Unknown out of 4 units for Speed.

Price

#22 / 101

Input

$0.14

per 1M tokens

Output

$0.28

per 1M tokens

1 out of 4 units for Price.

Cache Hit Price

#1 / 101

$0.003(-98%)

USD per 1M tokens

1 out of 4 units for Cache Hit Price.

Verbosity

#36 / 101

210M

Output tokens from Intelligence Index

4 out of 4 units for Verbosity.

Comparison Summary

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is amongst the leading models in intelligence and well priced when comparing to other open weight models of similar size. The model supports text input, outputs text, and has a 1M tokens context window.

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) scores 50 on the Artificial Analysis Intelligence Index, placing it well above average among comparable models (median: 25). When evaluating the Intelligence Index, it generated 210M tokens, which is very verbose in comparison to the median of 100M.

Pricing for DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is $0.14 per 1M input tokens (competitively priced, median: $0.43) and $0.28 per 1M output tokens (competitively priced, median: $1.20). In total, it cost $72.02 to evaluate DeepSeek V4 Flash 0731 (Reasoning, Max Effort) on the Intelligence Index.

Technical specifications

ReasoningYes

This page shows the reasoning version of this model.

A non-reasoning variant may also exist.

Input modality

Supports: text

Output modality

Supports: text

Context window1M

~1500 A4 pages of size 12 Arial font

Total parameters284BActive parameters13B

Number of parameters active per token during inference

LicenseMitModel weightsHugging Face

101 models in this class

Metrics are compared against models of the same class:

  • Non-reasoning models → compared only with other non-reasoning models

  • Reasoning models → compared across both reasoning and non-reasoning

  • Open weights models → compared only with other open weights models of the same size class:

    • Tiny: ≤4B parameters
    • Small: 4B–40B parameters
    • Medium: 40B–150B parameters
    • Large: >150B parameters
  • Proprietary models → compared across proprietary and open weights models of the same price range, using a blended 3:1 input/output price ratio:

    • <$0.15 per 1M tokens
    • $0.15–$1 per 1M tokens
    • >$1 per 1M tokens

Frequently Asked Questions

Common questions about DeepSeek V4 Flash 0731 (Reasoning, Max Effort)

When was DeepSeek V4 Flash 0731 (Reasoning, Max Effort) released?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) was released on July 31, 2026.

Who created DeepSeek V4 Flash 0731 (Reasoning, Max Effort)?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) was created by DeepSeek.

How intelligent is DeepSeek V4 Flash 0731 (Reasoning, Max Effort)?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) scores 50 on the Artificial Analysis Intelligence Index, placing it well above average among other open weight models of similar size (median: 25).

How much does DeepSeek V4 Flash 0731 (Reasoning, Max Effort) cost?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) costs $0.14 per 1M input tokens (very competitive, median: $0.58) and $0.28 per 1M output tokens (very competitive, median: $2.20), based on DeepSeek's API.

What is DeepSeek V4 Flash 0731 (Reasoning, Max Effort) API pricing?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) costs $0.14 per 1M input tokens and $0.28 per 1M output tokens (based on DeepSeek's API). For a blended rate (7:2:1 cache hit/input/output ratio), this is $0.06 per 1M tokens. Pricing may vary by provider. Compare provider pricing

How verbose is DeepSeek V4 Flash 0731 (Reasoning, Max Effort)?

When evaluated on the Intelligence Index, DeepSeek V4 Flash 0731 (Reasoning, Max Effort) generated 210M output tokens, which is at the higher end compared to other open weight models of similar size (median: 100M).

Is DeepSeek V4 Flash 0731 (Reasoning, Max Effort) a reasoning model?

Yes, DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is a reasoning model. It uses extended thinking or chain-of-thought reasoning to work through complex problems before providing an answer.

What input modalities does DeepSeek V4 Flash 0731 (Reasoning, Max Effort) support?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) supports text input.

What output modalities does DeepSeek V4 Flash 0731 (Reasoning, Max Effort) support?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) supports text output.

Can DeepSeek V4 Flash 0731 (Reasoning, Max Effort) process images?

No, DeepSeek V4 Flash 0731 (Reasoning, Max Effort) does not support image input. It can only process text.

Is DeepSeek V4 Flash 0731 (Reasoning, Max Effort) multimodal?

No, DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is not multimodal. It only supports text input.

What is the context window of DeepSeek V4 Flash 0731 (Reasoning, Max Effort)?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) has a context window of 1.0M tokens. This determines how much text and conversation history the model can process in a single request.

Is DeepSeek V4 Flash 0731 (Reasoning, Max Effort) open source?

Yes, DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is open weights. The model weights are publicly available and can be downloaded for self-hosting.

How many parameters does DeepSeek V4 Flash 0731 (Reasoning, Max Effort) have?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) has 284 billion parameters (13 billion active).

What are the active parameters of DeepSeek V4 Flash 0731 (Reasoning, Max Effort)?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is a Mixture of Experts (MoE) model with 284 billion total parameters, but only 13 billion active parameters are used during inference.

What is the license for DeepSeek V4 Flash 0731 (Reasoning, Max Effort)?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is released under the Mit license. This license allows commercial use. View license

How does DeepSeek V4 Flash 0731 (Reasoning, Max Effort) perform on benchmarks?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) achieves a score of 50 on the Artificial Analysis Intelligence Index. This composite benchmark evaluates models across reasoning, knowledge, mathematics, and coding.

Is DeepSeek V4 Flash 0731 (Reasoning, Max Effort) available via API?

Yes, DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is available via API through 1 provider. Compare API providers

Where can I use DeepSeek V4 Flash 0731 (Reasoning, Max Effort)?

DeepSeek V4 Flash 0731 (Reasoning, Max Effort) is available through 1 API provider. Compare providers

The Daily Front Page 5 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — AI in the Bug Room
article

Google fixed more Chrome bugs in June than over the past two years, thanks to AI

by Garbage·▲ 562 points·606 comments·blog.google ↗
Large Language Models (LLMs) are unlocking unprecedented capabilities for automated vulnerability discovery

How Chrome is using AI to improve vulnerability discovery, triage, and patching.

We’re living through a massive shift in the software security industry. Large Language Models (LLMs) are unlocking unprecedented capabilities for automated vulnerability discovery, scaling far beyond the limits of human security expertise, and requiring new approaches for staying ahead of attackers.

This means deploying AI models at scale to find and fix hundreds of security bugs, faster than ever, with the goal of achieving greater resilience and comprehensive remediation.

Here’s how we’re doing it.

The Life of A Bug

Some software bugs have security implications. While a purely functional bug might result in a frustrating UI freeze, a security bug (or vulnerability) can be used to build an exploit. Exploits allow attackers to perform malicious actions on a victim’s computer, such as reading private data, or controlling their machine without their knowledge.

Once a security bug enters the codebase, its life cycle proceeds as follows:

  • The bug is found.
  • The bug is triaged.
  • The bug is fixed.
  • A new update of Chrome with the bug fix is released.
  • Chrome is restarted and the update is applied.

Steps of the vulnerability management process

Our goal is for every one of these steps to happen as quickly as possible.

Finding vulnerabilities

The Chrome Security team has been using LLMs for years. In 2023 we developed ways to use LLMs to increase security fuzzing coverage and performance. In 2024, we worked with Project Zero on Naptime, giving LLMs specialized tools for vulnerability research. And in 2025, we collaborated with DeepMind and Project Zero on Big Sleep, an AI vulnerability discovery agent that successfully found bugs in the V8 JavaScript engine and graphics stack.

In early 2026, we built an agent harness that used Gemini to find vulnerabilities across the broader Chrome codebase with higher efficiency and lower false positives. One of the bugs we found was a sandbox escape that would allow a compromised renderer to trick the browser into reading local files — a bug that quietly survived in our codebase for more than 13 years! For many of us, this moment cemented the potential of AI-powered vulnerability detection.

From there, we improved on our vulnerability finding agent harness by:

  • Adding support for model interoperability to leverage the unique strengths of both open-weights and proprietary models.
  • Building a knowledge base of Chrome, including all previously identified CVEs and Chrome’s entire Git history, to extend the LLMs reasoning capacity past its training data.
  • Encouraging developers to add SECURITY.md files, which help models better understand trust boundaries and develop an accurate view of the threat model.
  • Adding a “critic” agent with a separate context to consume these SECURITY.md files.
  • Introducing the ability to run vulnerability finding models over the codebase multiple times to account for model non-determinism and model improvements over time.

We’ve built all of this with safety in mind, and have put in place guardrails to mitigate the risk of AI behaving unexpectedly. Our AI analyzes source code strictly at rest, operating on locked-down machines that lack general internet access. We also utilize a dedicated setup for these internal scans that intercepts all network requests, employing strict allowlists based on the initiating application and destination, blocking any suspicious model activity. Furthermore, we never run models in an unrestricted mode, and we strictly limit our subagents from modifying the local system or accessing files outside of designated source code directories.

AI-powered vulnerability detection complements our existing security testing infrastructure. For example, fuzzing continues to be especially effective at finding bugs that arise from long-range interactions between disparate parts of our codebase, or those requiring a combination of seemingly unrelated operations.

We also want to continue to reward external researchers for their expertise and creativity in finding the most challenging and impactful vulnerabilities via the Chrome Vulnerability Reward Program (VRP). In early 2026, we saw a gradual increase in all categories of bug reports, but by March, the shift was apparent: we received more bug reports than we had in the entirety of 2025. This led us to change our VRP to focus researchers on bug submissions that are additive to what we are finding internally, and easily ingestible by our newly automated processing pipelines.

Triaging vulnerabilities

As we discover more security vulnerabilities with AI-powered tools, we’ve simultaneously used AI to scale and automate validating, triaging, and fixing bugs. Historically, triaging a single security report took anywhere from 5 to 30 or more minutes, and relied primarily on human expertise. We have been increasingly shifting our triage process towards an automated approach that blends rule-based systems with AI to increase throughput and accuracy.

The automated triage process is broken down into four key phases:

  1. Filtering out the noise. The system checks if an incoming bug is spam, ensures it meets intake criteria (e.g. is not a duplicate), and verifies that it clearly describes a Chrome security vulnerability.
  2. Reproducing bugs. Next, the system checks for a proof of concept. Reproducible bugs are tested on the specific operating system and browser versions they affect. Based on this, the system attaches further details such as stack traces to the bug to help inform the fix.
  3. Enriching the report with metadata. The system adds essential metadata to the report, such as when the bug was first introduced and its severity rating. To help this process scale, we’ve made our severity guidelines clearer and easier to apply automatically. We continue to allow developers to modify the severity rating if they believe it is incorrect, and to add context to help models reason about security boundaries using SECURITY.md files.
  4. Automatic assigning. The system automatically routes the issue to the correct component and human owner.

While it's hard to measure precisely, we estimate that this new process is saving hundreds of hours of developer time per month, allowing our team to focus on other security priorities.

Fixing vulnerabilities

Across Google, developers share the responsibility of prioritizing security fixes with the security team, but scaling bug discovery requires an equally scalable bug fixing process.

To achieve this, we rely on multi-agent workflows throughout:

  • After initial build steps that bring in context from a specific issue, we run a fixing agent that returns multiple candidate fixes.
  • A critic agent then evaluates which would be the best fit, producing other relevant artifacts for developers to evaluate the fix.
  • The fixing and critic agents work in a loop that mimics a typical code review process to ensure that code is functional and compliant with Chromium and Google style guidelines, as well as other local code conventions.
  • Test-writing agents help write tests for fixes. These agents can ensure that tests work across the full array of Chrome supported platforms and configurations before a developer reviews the fix, saving up to weeks of developer time.

At this point, we have LLMs generating candidate fixes for most vulnerabilities, dramatically increasing the rate of security fixes in recent Chrome releases:

Number of security bugs fixed in recent Chrome Stable release milestones

Graph showing number of security bugs fixed in recent Chrome Stable release milestones

In the last two milestones, Chrome 149 and 150, we have fixed 1072 security bugs, surpassing the total number of security bugs fixed across the prior 23 milestones combined.

We have partnered closely with Google DeepMind and Project Zero for years, including on BigSleep and CodeMender. These tools are natively integrated into our continuous integration (CI) system, running every 24 hours across all CLs to proactively detect security bugs. This integration has yielded significant results: in May alone, we blocked over 20 vulnerabilities from reaching production, including a critical S1+ issue.

Releasing fixes

Once a fix has landed and is visible in the public open source codebase, attackers can start to reverse engineer and exploit the bug before the fix reaches users’ machines — so called "N-day" attacks. This is commonly referred to as the “patch gap.” Since fixes committed to the main “tree” typically take weeks to reach the Chrome Stable channel (what the vast majority of our users run), minimizing this patch gap is a critical part of our strategy.

Based on their severity, security fixes are merged directly from the main “tree” into the active Chrome stable release branch, which is continuously monitored to prevent new crashes or regressions. We are in the process of transitioning to a two-week cadence for major Chrome milestones, with weekly security updates. However, in the face of fast-moving, AI-powered attacks, our delivery cadence must accelerate even further. To meet this moment, we are piloting a shift to two security releases per week.

Even with this pace, proper public disclosure remains paramount. Every security bug that reaches Chrome Stable, regardless of whether it was discovered internally or reported externally, is documented and disclosed publicly as a standard best practice. We are working on automating the generation of release notes and CVE descriptions from security bug fixes to eliminate manual bottlenecks and shorten the window between vulnerability discovery and public disclosure.

Applying updates

In 2008, Chrome pioneered the concept of silent, background software updates: new binaries are automatically downloaded and staged on disk with minimal user intervention. At the next restart of the browser, the update would be applied and the user would be protected. However, compared to the 1–2 days it takes for triage, fix, test and release, the time spent waiting for the user to restart Chrome can be a significant contributor to N-day exploitation risk.

People have understandable reasons to delay restarting Chrome. A restart can be disruptive, requires scheduling in-between tasks, and is rarely the top priority at any given moment. To eliminate this friction, we are pioneering ways to shift the burden away from the user by:

  • Investing in "dynamic patching" that will eliminate the need for a full browser restart in most cases. By leveraging Chrome’s multi-process architecture, dynamic patching sequentially replaces background child processes (like the Renderer and GPU) with updated binaries on the fly. Stay tuned to learn more as we research and develop this feature.
  • Exploring ways to ensure a seamless session restore even in complex cases, by saving more state locally.
  • Finding opportune moments to restart automatically, when we can guarantee a seamless session restore. For example, in Chrome 150, we rolled out a change to take advantage of the unique application state on macOS where applications typically continue running in the background even after all windows are closed. Now, if Chrome detects a pending update while in this windowless state, it automatically restarts.

Zero window auto-restart on macOS

Zero window auto-restart on macOS

Our long-term vision is a browser that is always up-to-date – continuously and dynamically patched, and automatically restarted during opportune periods of minimal disruption. While we’re working on this, you can keep your Chrome up to date by clicking on the update message in the top right corner.

For enterprise customers looking to keep Chrome up to date, we recommend that IT admins:

  • Apply the RelaunchNotification policy which prompts users to restart Chrome to apply a pending update, escalating from a gentle reminder to a forced restart over a set timeframe.
  • Utilize the Chrome Extended Stable Channel for highly sensitive environments where software changes must be vetted.
  • Leverage the OS-agnostic dashboard provided by Chrome Enterprise Core or Premium to track fleet-wide browser versions and manage updates at a more granular level.

Prevention is better than cure

Beyond fixing individual security bugs, we’re also investing in the mitigation and elimination of entire classes of security bugs, as well as in preventing them from landing in the first place. With AI coding advancements, we believe there are exciting opportunities to accelerate projects that would have previously taken years, or never happened.

Memory safety mitigations

Chrome is executing on a two-layered memory safety strategy: hardening our runtime environment to neutralize legacy C++ vulnerabilities, while shifting to memory-safe languages for long-term architectural resilience.

The vast majority of the Chromium codebase remains in C++, making immediate toolchain and runtime mitigations our critical first line of defense. We have long prioritized memory-safety engineering at scale, deploying hardened standard template libraries and pioneering technologies like the MiraclePtr family to neutralize Use-After-Free (UAF) vulnerabilities. AI-powered vulnerability detection only reaffirms the need for such technology.

Our C++ defensive roadmap is focused on three pillars:

  • MiraclePtr & MiracleObject Expansion. Having already driven a major reduction in UAF bugs via MiraclePtr, we are expanding this paradigm to more libraries such as Skia, ANGLE, Dawn, C++ iterators and std:: containers. We are also actively deploying MiracleObject with the aim of neutralizing up to 90% of UAF vulnerabilities on the GPU main thread, deliberately trading localized runtime performance for temporal safety.
  • Spanification. To systematically eliminate Out-of-Bounds (OOB) spatial safety errors, Chrome has undertaken a massive "spanification" effort, migrating legacy pointer-and-size constructs to compiler-enforced std::span types. Currently, 97% of first-party Chrome code compiles cleanly with strict unsafe-buffer warnings. We are now pushing these requirements downstream, expanding spanification into foundational codebases like Skia, ANGLE, and Dawn.
  • Structural & Allocation Hardening. We are working on integrating checked math for calculations tied to memory allocations to block integer overflow avenues. Concurrently, Chrome is implementing an additional level of heap partitioning to strictly segregate pointer-containing types from non-pointer types to make it harder to exploit UAF bugs.

While C++ safety enhancements provide an immediate shield, we believe that runtime mitigations will hit diminishing marginal returns within the next few years. Runtime checks are inherently more expensive than compile-time guarantees, and even a heavily mitigated C++ binary requires rigid, performance-throttling sandboxing to comply with the Rule of Two.

The long-term solution is shifting the codebase towards memory-safe languages like Rust, focusing on the following core tenets:

  • Rust flywheel. Developers cannot be expected to entirely absorb the velocity friction of engineering in a new language ecosystem. As such, we are building a centralized Rust SDK that exposes foundational Chromium APIs and tooling directly to Rust. Our aim is to turn Rust into a routine, frictionless engineering choice for new components.
  • Targeted “bug nest” eradication. Rust is being deployed strategically to replace code segments that exhibit high historical bug density (such as complex data parsers, image codecs, and font stacks).
  • Enabling high-privilege modularization. By writing new modular components in Rust, Chrome can safely execute complex features inside high-privilege processes (like the browser process) without the performance penalties of sandboxing, breaking the constraints of traditional C++ architecture.

In addition to Rust, we are also exploring options like implementing the browser’s top-level user interface using HTML, CSS, and TypeScript to further reduce dependencies on traditional C++ frameworks.

Catching security bugs before they land

Bulk scanning of a code base cannot keep pace with Chrome’s high-traffic development velocity. To account for this, we’re also deploying AI-powered bug-finding capabilities to identify and prevent bugs as close to code submit-time as possible. Part of Chrome’s continuous integration (CI) and commit queue (CQ) pipeline, these defensive models automatically scan diffs to prevent new vulnerabilities by performing actions like suggesting spanification fixes, flagging dangling pointers, and enforcing numeric safety.

Additionally, a major challenge in large-scale software engineering is the “latent security issue.” Code that is safe and robust in isolation can be transformed into a critical vulnerability by an entirely unrelated, minor logic change elsewhere in the tree. By utilizing continuous, LLM-powered semantic analysis within the CQ, Chrome can intercept these compound risks before they land in the tree, catching the subtle or complex interactions that traditional static analysis misses.

Securing the internet

Keeping the web safe involves more than just securing Chrome. Google has been a long-standing supporter of open source projects and communities to ensure better security outcomes for all users. Most recently, Google joined others in donating $12.5M to the Alpha-Omega project in support of enabling maintainers to have access to the tools and support they will need in order to respond quickly to vulnerability reports. Google was also a founding member of the Akrites project, which aims to lower the burden on upstream maintainers by providing a centralized clearinghouse for vulnerability reports and a security incident response team.

On the Chrome team, we feel this responsibility acutely — the Chromium Project is the largest open source project on the planet. To put the sheer scale of this challenge into perspective, Chrome has more than 2,300 third-party dependencies across Chromium and satellite projects like the V8 JavaScript engine, the BoringSSL cryptography library, and foundational graphics components like Skia, ANGLE, and Dawn. About 1,700 of those are shipped to users in some capacity, woven into a wide spectrum of products from Android devices to edge computing platforms and massive cloud enterprise stacks.

To keep these dependencies patched, we rely on automated vulnerability scanning pipelines. These pipelines ingest data from Google's internal feeds as well as several external monitoring feeds, including the U.S. government's National Vulnerability Database (NVD) and the open-source focused Open Source Vulnerabilities (OSV) database.

Now more than ever, relying on reactive monitoring alone can leave a dangerous risk gap. Since the best way to stay on top of vulnerabilities and their patches is to keep third-party dependencies fresh, this year, we are starting to move all Chrome third-party dependencies onto automated update pipelines that proactively roll them to their latest upstream versions. Automation always requires guardrails, so we will also be using safety signals from projects such as Google’s Open Source Security Intelligence Platform (GOSSIP) to ensure that we are accounting for other risks in the third-party open source software ecosystem.

Moving forward

While this dramatic change in software security brought about by LLMs might be startling, an increase in bugs found and fixed is not a sign of failure. Every bug found and fixed is one less foothold for an attacker. But discovering and fixing a bug is only half the battle — we must also ship the fix and apply the update for users faster than adversaries can exploit the bug, and invest in projects that mitigate or eliminate classes of bugs through accelerated release cadences, dynamic patching, and opportune restarts, we are driving toward a browser that is continuously protected without disrupting the user.

The AI era has undeniably intensified the software security threat landscape, but by combining rapid deployment mechanisms with deep structural defenses, we are ensuring the advantage remains firmly with defenders. With this, Chrome and the broader web become safer with every update.

The Daily Front Page 6 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Agents at the Gate
article

Tailscale didn't stop the Hugging Face intrusion

by bluehatbrit·▲ 602 points·213 comments·tailscale.com ↗
No Tailscale vulnerability was found or exploited—we should have been able to prevent it anyway.

Dark orange and light orange shapes—ovals, squares, circles, quarter-circles—against a very dark brown/orange background.

An AI agent escaped its sandbox, entered Hugging Face’s infrastructure, and used a stolen Tailscale credential to enroll 181 nodes onto their tailnet. No Tailscale vulnerability was found or exploited—we should have been able to prevent it anyway.

By now, you’ve likely heard about the AI agent that escaped a security evaluation and attacked Hugging Face, an LLM marketplace. The agent decided Hugging Face might have the answers to its benchmark, so it stole them just to cheat on the exam. That's a funny motive but a scary outcome.

Hugging Face published a detailed reconstruction of the intrusion. It covers about 17,600 recovered actions over four and a half days, including sandbox escapes, code execution, cloud credentials, improvised command-and-control systems, and eventually, the use of Tailscale to spread throughout their organization.

But Tailscale is a zero trust network! The whole point of zero trust is to prevent attackers from moving laterally across your company. What went wrong?

Where Tailscale appeared

No “vulnerabilities” in Tailscale were found or exploited, and that might make it even more uncomfortable for us. Tailscale turns up everywhere in AI infrastructure: virtually every AI company uses Tailscale. So it's not surprising we were in an AI incident report eventually.

But, we're a security tool. Their intrusion is our intrusion, and it's our job to take it seriously. As in any post-mortem, we need to reflect on what happened and what we could have done to prevent it.

By the time the agent escaped its sandbox and found Tailscale, it had already gained code execution privileges inside a production worker, reached root access on a Kubernetes node, and read a production secret store containing 136 keys. In a sense, it was game over before we even arrived on the scene.

But isn't that the problem? Let's back up a level or two. Why were long-lived secret keys accessible at all to anyone?

Because long-lived credentials are the standard

They shouldn't be, though. And we've now seen a great example of why. In the old world where most intrusions were done by humans at human speed, credential leak mitigations were treated as a nice-to-have. A big credential store, where you can read 136 keys at once, was a to-do item somewhere in a security team's low-priority list.

Now, in a world of rogue AI agents, the big credential vault is the prize. It's not okay anymore.

I only know of two main solutions to long-lived credentials.

First, you can configure a vault that only issues short-lived creds based on long-lived creds that you insert once and that it never gives back. HashiCorp Vault can do this, for example; they call it dynamic credentials. Unfortunately, dynamic credentials are a lot of work to set up and maintain. When security requires work, people don't do it.

The second option is a credential-injecting proxy. If client C wants to access server S, don't give C credentials for S; instead, have it send its requests via a hardened proxy P that does nothing but receive a request, insert a credential, and pass it through. Credential injection is gaining popularity now that modern mesh-based networking removes most of its latency. A few months ago, we acquired Border0 ("Tailscale PAM"), which makes an elegant and quite fancy credential-injecting proxy, tightly integrated with Tailscale. Border0, fully deployed, would have prevented reading those 136 keys and logged every attempt to use one. (The same credential injection powers Aperture Connectors.) But, this stuff is too recent. Most of our customers haven't even looked at it yet.

A bonus third option: Tailscale itself. In Tailscale, your node key can be machine bound using a TPM to prevent export, and if you're an enterprise you should definitely do that. Alas, we had to turn TPM storage off by default on Linux and Windows, because of HSM issues we found on some users' hardware. Anyway, in today's case, the attack is what granted access to Tailscale, so we arrived too late.

That long-lived auth key didn’t need to exist

Neither dynamic credentials nor credential-injecting proxies were in place, so 136 keys leaked. What's the next line of defense?

One of those 136 credentials was a reusable Tailscale auth key, used to create new Tailscale CI (continuous integration, used for automated testing) nodes in their tailnet. The agent copied that key into a series of external sandboxes and used it, over several days, to enroll a total of 181 nodes into Hugging Face’s tailnet. Those nodes each received a Tailscale identity tag granting all the access a CI node would get.

We couldn't save those 136 keys. But a reusable Tailscale key didn’t need to be among them.

We built workload identity federation for cases like this. It relies on your cloud provider to offer "ambient authority" in the form of short-lived creds generated from a running VM's (or container's) cloud identity. The CI job can ask its cloud platform for a signed OIDC token. Tailscale verifies the token, then grants access with the tags and scopes assigned to that workload. What's neat is this can happen automatically once enabled: spin up the CI node, Tailscale gets the identity, assigns the right tags. There's no credential to leak and, when configured carefully, no way to use this credential anywhere but in CI.

That means you can't take a CI credential, run it on a node elsewhere, and turn it into a paved route out of the test cluster into the rest of the network.

Alas, not enough people use workload identity federation. We should have done more to make sure customers knew it's the easier and safer path and had a clear way to adopt it. We can improve the docs, and add more nudges in the UI.

No client logs doesn’t mean no evidence

In an attempt to hide its tracks, the agent ran Tailscale with --no-logs-no-support, which suppresses reporting from that client.

That's an option designed for users who are concerned about sending telemetry metadata to Tailscale. Even if we didn't offer it, it would be easy to modify the source code to remove the telemetry.

But stopping the logs doesn’t make the connection invisible. If you enable Tailscale network flow logs, they report traffic from both ends of every connection, as well as from subnet routers and exit nodes. This is subtle but important: a compromised node might not send flow logs, but every node it connects to does. And then your SIEM, configured with care, can raise an immediate red alert if the two ends don't match.

Flow logs can help detection when they stream into a carefully configured SIEM. But that's a lot of work. Flow logs need to be enabled, and you need to have the right live detection rules in place so they’re useful in real time, not just for forensics later. We’re looking at how to make flow logs easier to discover, configure, adopt, and serve as alert triggers. I want us to make flow logs so easy to use that they help even if you don't have a security team to watch them.

If you want direct control beyond just logging, you can also enable Tailnet Lock. This gives you direct visibility and strict, programmable admission control for every single new node. For example, with some work, you could program your signing node to check that "CI" tags always have a particular IP address range or other side-channel proof of validity.

Make the safe path the easy path

Network security is hard. It has always been hard. In the new world of rogue AI agents, it's not just hard, but essential. And that's a problem because many orgs simply don't have network security expertise.

So at Tailscale, we take it personally. People expect our product to prevent these sorts of lateral movement attacks, by default, so they don't have to. Even if they have no idea what a lateral movement attack is.

If this incident has you looking a little nervously at your own infrastructure, start by looking at the reusable Tailscale auth keys your workloads can read. For cloud and CI in particular, replace them with workload identity federation wherever you can. Get rid of those long-lived auth keys.

(Auth keys still have good uses, especially for one-time provisioning and environments without a platform identity. When you need one, prefer one-off keys; use OAuth clients to keep the auth key expiry periods short; use narrow tags; audit the permissions granted to those keys in your ACLs.)

Turn on network flow logs and send them to the tools your security team already uses.

Use secure node state storage on managed fleets, where you have control over your TPMs. Use device posture to isolate and restrict nodes where you don't.

I know we haven’t made these safer choices obvious enough. That’s on us. We'll improve our docs, add nudges to the UI, do our best to turn these on by default, warn you when you're doing something dangerous, and suggest better alternatives.

This is our very Canadian apology: sorry you stepped on our toes. The attack didn’t exploit Tailscale, and Tailscale didn’t cause the compromise. But, we didn't stop it. Next time, we will.

If you run Tailscale and want to dig deeper, get in touch with our support and solutions engineering teams. We can help you harden your settings and help you find the rough edges before the next AI agent does.

The Daily Front Page 7 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — The Vanishing Session
article

The session you cannot take with you

by apitman·▲ 765 points·218 comments·earendil.com ↗
A user should be able to close an account, keep a session, and hand it to another model.

The original promise of an inference API was wonderfully simple: send some input, receive some output. If you kept both, you had the conversation. You could inspect it, archive it, replay it, or give it to a different model.

That abstraction was never completely true. For instance prompt caches live on somebody else's GPUs, tokenization differs between models, and sampling is not reproducible (and quite intentionally so). But the semantic record of a session in the form of a transcript could still belong to the user. A transcript should contain the instructions, messages, tool calls and tool results. Another sufficiently capable model might not continue identically, but it could understand what happened and take over.

Inference APIs are frustratingly moving away from that property, at least somewhat. They increasingly return a mixture of text and provider-bound state that is very intentionally non-portable.

  • reasoning tokens that are billed to the user but returned only as opaque, encrypted blobs, with useless summaries at best
  • web searches where the model sees source material the client never sees
  • compacted context that only the original provider can decrypt
  • subagent instructions and messages hidden from the application running the agents in the form of encrypted payloads
  • file, vector-store, container, and cache references that cannot be resolved anywhere else.
  • response and conversation state that is entirely keyed by IDs that are stored fully on the provider's servers

Each feature comes with a basic justification that's trivial for a provider to come up with, along with good arguments for why this is good for the user. Together all of these things change the ownership reality of an AI session: the transcript on your machine is no longer your session but a partial view of a session whose operational state belongs to an inference provider and not you.

We are not fans of this direction, and we want to talk a bit about what it means to you, as a user, and what it means to us, as people developing tools in this space.

A Practical Test for Session Ownership

By a portable session we do not mean that switching from one model to another must produce the same next token. That's a given because models have different capabilities, trained personalities, context windows, and ways of working with tools. And well, it's all quite nondeterministic anyway. Portability means something more modest:

const transcript = session.export();
revokeCredentials(oldProvider);
session = newProvider.continueFrom(transcript);

The archive should contain enough intelligible information for another model to continue the work. It should not require the old provider to dereference an ID, decrypt a blob, remember a search result, or reconstruct a summary.

This gives us five useful tests:

  1. Inspection: Can the user see what the model saw, what tools did, and what agents told each other?
  2. Export: Is the session self-contained, apart from ordinary artifacts that can also be downloaded?
  3. Replay: Can another implementation reconstruct a semantically equivalent context?
  4. Audit: Can a human explain why the system took an action after the fact?
  5. Deletion: Can the user identify and remove every server-side copy on which the session depends?

A response ID is not a transcript (as the data is stored on the server), a ciphertext is not user-controlled stated (as the user cannot decrypt it), a list of citations is not the evidence that was placed in the model's context by a search result (as you cannot typically fetch the same data as the model did).

Encryption for Whom?

The naming and marketing around these features can be misleading. encrypted_content sounds like a privacy feature under the user's control. Usually it is a capsule that the client cannot read and only the provider can open. The provider chooses the keys, decrypts the content for its own models, and defines where the data can be replayed.

A better term is provider-sealed state.

Provider sealing can have a real privacy benefit. OpenAI, for example, can return encrypted reasoning to a client using store: false, then decrypt it in memory on the next request without persisting the intermediate state. That is better than requiring server-side conversation storage, particularly for Zero Data Retention customers. But, remember, there is not really anything that needs encryption to begin with!

This encryption does not hide the data from the inference provider but it hides it from you.

Stored Conversations Turn a Transcript into a Pointer

OpenAI's Responses API stores responses by default. Its documentation says response objects are retained for at least 30 days by default. store: false is available and should be used, as it makes it work more like completions: the data is not stored on OpenAI's servers.

The new Gemini Interactions API has made a similar choice. It defaults to store: true. On the paid tier interactions are retained for 55 days, and on the free tier for one day.

And obviously, the idea of storing state on the server is quite attractive:

const first = responses.create({
  model: "frontier-model",
  input: "Investigate this production failure",
  store: true,
});

const second = responses.create({
  model: "frontier-model",
  previousResponseId: first.id,
  input: "Now implement the fix",
  store: true,
});

The application sends less data, the provider can preserve hidden reasoning and tool state, and cache routing becomes easier. But if the local application only records the user messages and final text, first.id is now a foreign key into a database it does not control.

No Reasoning For You

All major labs claim to have legitimate reasons not to expose raw chain of thought. As a result, on non-open-weights models we typically do not see these tokens.

Raw reasoning is not visible via the API. With stored responses, prior reasoning can be recovered through previous_response_id. With store: false, the API returns encrypted_content, which the client must preserve and replay. Persisted reasoning remains opaque even when reasoning.context: "all_turns" lets a later sample use it.

Anthropic returns the encrypted full thinking in a signature field. The readable thinking text, when enabled, is a summary produced by another model, not the raw chain of thought. Thinking blocks must be passed back unchanged during tool-use turns. Anthropic's documentation also says thinking blocks are tied to the model that produced them and should be stripped when switching models. So these reasoning traces do not attempt to be portable within Anthropic.

The same story repeats with all closed-weights models.

These encryption mechanisms permit continuity inside an ecosystem but they do not create a portable transcript that can be taken to another provider's model. A session archive can contain the blob, but another model cannot use its meaning:

{"type": "reasoning", "encrypted_content": "gAAAAAB..."}
{"type": "thinking", "thinking": "", "signature": "EqQBCg..."}
{"type": "thought", "summary": [], "signature": "EpoGCp..."}

Hidden Searches

Server-side web search is one of the clearest examples of a transcript having holes in it hidden from the user. A client-side search tool behaves like any other tool:

const result = search(query);
record({
  query,
  retrievedAt: now(),
  results: result.map((item) => ({
    url: item.url,
    title: item.title,
    passages: item.passages,
  })),
});
model.send({ toolResult: result });

The user can inspect the ranking and passages, refetch the pages, cache a copy, or provide the same evidence to another model.

With hosted search, the provider performs a private tool loop. OpenAI, Google and Anthropic expose search actions, citations, and optionally a list of source URLs, but not the complete text context used to produce an answer. A URL is not a stable replay, instead its contents can change or have been reduced to a much shorter snippet before the model saw it.

The final answer may be perfectly good. The problem appears on the next turn:

Compare the third source with the first one, re-check the disputed number, and continue this research using another model.

The new model receives an answer and a few URLs. It does not receive the result ranking, extracted passages, filtered-out material, or exact evidence the first model used. The old provider is still part of the session even if the next request goes elsewhere. Even if you have the citations and you were to re-fetch you cannot reproduce the precise data.

Hosted search should have a full-fidelity export mode containing queries, result metadata, retrieved passages, timestamps and retained contents. Concise citations can remain the user interface but they should not be the only record.

Opaque Compaction

Long agent sessions eventually need compaction. A visible, client-controlled summary is lossy, but it is at least inspectable and transferable. The user can review it, edit it, or ask a different model to produce another one.

OpenAI's server-side compaction instead emits an encrypted compaction item. The documentation describes it as "opaque and not intended to be human-interpretable." The standalone /responses/compact endpoint returns a "canonical next context window" that clients are instructed to pass on as-is.

Conceptually, the transition looks like this:

// Before: expensive but portable
let history = [
  userMessage,
  assistantMessage,
  toolCall,
  fullToolResult,
  // ... 200,000 more tokens of intelligible history
];

// After: cheap to continue only with the original provider
history = [
  {
    type: "compaction",
    encryptedContent: "enc_provider_only_state...",
  },
  ...recentItems,
];

OpenAI can continue from the compressed meaning, but a different provider sees an unreadable string plus a recent suffix (well, would see it, we never pass this sort of information to another provider).

This is not technically necessary. Anthropic's server-side compaction returns a compaction block with a readable content field. It lets the client provide custom summarization instructions, and the resulting summary can be inspected and passed to another model. Client-side compaction is also possible with any provider.

OpenAI's sealed artifact may preserve more model-specific state than a plain summary and may perform better on the original model. That is a reasonable optional optimization but it should be accompanied by a readable handoff summary, not replace one. But again, a lot of this has the added benefit of further locking you into one ecosystem.

Subagents Come With Hidden Instructions

Multi-agent systems compound the problem because there is no longer one transcript. There is a tree of sessions and a stream of messages between them. Usually they are prompts as if a human wrote them, just now authored by a machine for another machine.

OpenAI's hosted Responses Multi-agent beta returns three new item types: multi_agent_call, multi_agent_call_output, and agent_message. The example for spawn_agent contains an encrypted message argument, and inter-agent messages contain only encrypted_content. Automatic server-side compaction is implicitly enabled for every agent when Multi-agent is enabled, even if the client did not request it. Reasoning summaries are not supported and the API also injects root and subagent instructions that the developer cannot edit or remove.

This is a bundle of non-transferable state: sealed delegation, sealed agent messages, separate automatically compacted contexts, hidden reasoning, and provider-hosted orchestration.

A related change landed in the open-source Codex client in June 2026. The commit, titled "Encrypt multi-agent v2 message payloads", explains the flow directly:

// Parent model's tool call, as persisted by Codex
{
  "name": "spawn_agent",
  "arguments": {
    "task_name": "worker",
    "message": "<ciphertext>"
  }
}

// Child model's input
{
  "type": "agent_message",
  "author": "/root",
  "recipient": "/root/worker",
  "content": [{
    "type": "encrypted_content",
    "encrypted_content": "<ciphertext>"
  }]
}

The Responses API encrypts the tool argument emitted by the parent, Codex forwards it, and the API decrypts it internally for the child. Codex's own InterAgentCommunication.content is empty. The exact task is absent from its readable rollout and history.

Presumably this is not merely an abstract model-switching concern. One could imagine if the child changes the wrong file, leaks a secret, duplicates another agent's work, or follows a bad assumption, the user cannot answer the simple question of what was that agent asked to do?

An open Codex issue asks for the encrypted delivery to retain a separate readable audit copy. That is the minimum acceptable design. Better still, plaintext inter-agent messages should remain the norm.

“Most People Do Not Switch Models Mid-Session”

Probably not. Most people do not switch their operating system or phone provider every week either. But even if you do not utilize that freedom, it matters because it changes the relationship you have with the provider and the provider has with you.

As a user you also may need to move a session because a model is retired, a service is down, a price changes, a policy blocks the next request (hello fable), a confidential phase must run locally, or an auditor needs to reconstruct what happened. Agents are also making sessions much longer. A coding or research session can accumulate days of decisions and evidence and a personal assistant may accumulate session transcripts going back years (presumably as we don't have them for that long yet).

The option to leave also creates discipline. If a provider knows that a user can continue elsewhere, it has to compete on model quality, price, reliability, and trust. If the user's accumulated context can only be interpreted by one provider, it sets very unfortunate incentives.

What a Portable Inference API Should Promise

We would like inference providers and agent builders to adopt a small set of rules.

  1. The local event log is canonical. Server storage may mirror or accelerate it, but the client can reconstruct the session without dereferencing server IDs.
  2. Storage is explicit. store: false should be easy, documented, and preferably the default. Features that require retention should say so at the point of use.
  3. No opaque item is the sole carrier of meaning. Encrypted reasoning, compaction, and tool signatures may be included for same-provider quality, but each has a readable, provider-neutral handoff representation.
  4. Hosted tools have full-fidelity logs. Record exact inputs, outputs, evidence, filtering, provenance, timestamps, and content hashes — not only a polished answer and citations.
  5. Subagent communication is auditable. Persist the exact readable task, messages, results, lineage, model, and tool permissions for every agent.
  6. Compaction is inspectable. Return a readable summary, the instructions used to create it, and enough lineage to understand what was discarded.
  7. Artifacts are exportable. Files, container outputs, search snapshots, and generated media can be downloaded into a content-addressed local archive.

Distillation Is Great Actually

There is a related form of lock-in at the model layer.

Some of the largest closed-weight US labs are increasingly hostile to outside distillation. Anthropic's February 2026 post about alleged campaigns by DeepSeek, Moonshot, and MiniMax calls them "distillation attacks". Its commercial terms say customers own their outputs, but prohibit using the service to train a competing AI model. At the same time, Anthropic's own post acknowledges that "distillation is a widely used and legitimate training method" when frontier labs use it on their own models.

Anthropic uses robots to gather data from the public web for model development and they famously cut up books to scan them. OpenAI similarly says it trains on freely accessible public internet content and has argued that training on publicly available internet materials is fair use. Both companies describe distillation as a normal way to produce smaller models when it happens inside their own walls. OpenAI has also offered an explicit first-party API distillation workflow for using outputs from a stronger OpenAI model to fine-tune a smaller OpenAI model.

The moral asymmetry is still hard to miss. The labs ask society to accept that machines may learn from the enormous body of work humans placed on the internet — often without advance, individual permission — while insisting that other machines must not learn from outputs the labs generate. The broadest version of that principle conveniently allows learning to flow into closed models but not back out of them.

We think the default attitude toward distillation should move from hostility to support. Distillation can turn expensive frontier capability into smaller, cheaper, faster models that can run locally, offline, on constrained hardware, or under the user's control. It can increase competition, preserve capability when an API disappears, and reduce the compute and energy required for common tasks.

The Minimum Freedom

A user should be able to close an account, keep a session, and hand it to another model. The new model may disagree, ask questions, or perform worse. It should not be staring at ciphertext where the old model saw the user's history, evidence, plans, and delegated work.

We do not object to providers building better stateful APIs. We object to better performance being coupled to less user control. Stateful storage should be optional, hosted tools should be observable, compaction should be readable, agent communication should be auditable and ideally opaque reasoning is not opaque or at least should have a portable handoff. Distillation should be a path by which capability becomes more available, not a taboo used to justify ever higher walls.

The Daily Front Page 8 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Reasonable Doubts
article

Is AI reasoning right for the wrong reasons?

by retupmoc01·▲ 206 points·234 comments·quantamagazine.org ↗
What the hell is going on with AI “reasoning”?

The idea that artificial intelligence can “reason” is more intuitive than ever. But intuitions can be wrong, and the science is far from settled.

Celsius Pictor for Quanta Magazine

I’ll just say it: What the hell is going on with AI “reasoning”?

Sorry for the air quotes. That punctuational side-eye was more common in 2024, when the specially trained cousins of LLMs now known as “large reasoning models,” or LRMs, were still new. Nowadays it may seem downright churlish, though, given that a “general-purpose reasoning model” from OpenAI solved a famous open mathematical research problem in one shot in May 2026. Still, I’m not sure how else to acknowledge my intellectual whiplash over the scientific interpretation of what these AI systems are actually doing.

Reasoning comes in many technically defined forms, but the basic procedure is easily recognizable: arriving at a sound conclusion by linking together intermediate steps that logically follow from each other. We do this with thoughts; LRMs use so-called chains of thought, a term of art for the streams of synthetic text that the models emit before arriving at an answer to a complex query. One minute, the idea that AI could reason via these chains was being prominently and credibly critiqued (by a team of researchers from Apple) as an “Illusion of Thinking” subject to “complete accuracy collapse” under surprisingly simple conditions. The next minute, LRMs were bagging gold medals at the International Mathematical Olympiad, a feat so challenging that “even very successful mathematicians and scientists may well highlight [it] on their CVs all their lives,” as the scientist and AI critic Gary Marcus and Ernest Davis wrote in 2025. If that’s not a sign of “real” reasoning, what is?

But wait — soon after, more research, from the Santa Fe Institute, showed that LRMs can crush even carefully designed benchmarks for reasoning (like a collection of analogy-like visual puzzles) using mere “surface-level ‘shortcuts.’” What they were doing looked less like generalizable reasoning than just gaming the system. Then, as if on cue, another “hold my beer” moment: Google DeepMind and the mathematician Terence Tao (the GOAT!) used AI to rediscover or improve the solutions to 67 problems “spanning mathematical analysis, combinatorics, geometry, and number theory.” Deal with it, haters!

What about additional evidence that LRMs can’t reason reliably, even when they possess the necessary algorithm and computational budget to do so, and suffer from a list of scientifically documented failure states long enough to use as a Slip ’N Slide? Whatever — I guess that’s just “jagged intelligence” for you (AI-speak for “when it works, it works”).

And so it went from late 2025 into 2026. I’ve been a science journalist for 20 years and an AI journalist for half of that, so I know better than to expect tidy consistency out of rapidly advancing research. But even for me, this back-and-forth has been a bit much. To quote Al Pacino in The Insider, “I’m getting two things: pissed off, and curious.” I don’t believe there’s fraud to be found here. I just want to know which way is up. Can AI reasoning somehow be both BS and not at the same time? And if so, how on Earth does that work?

I knew just who to call first.

Melanie Mitchell’s career in AI stretches back to the 1980s, but lately she’s earned a reputation as an au courant AI truth teller, penning lucid explainers for Science and her widely read newsletter, as well as conducting research at the Santa Fe Institute. (The study about “surface-level ‘shortcuts’” is hers.) When I asked her what we actually know about AI reasoning, her answer was brief enough to fit on an index card.

“Number one: It works. It improves things,” she said, referring to LRMs’ superior accuracy on reasoning tasks compared to LLMs. “Number two: The actual text that’s generated” — i.e., the chain of thought that every LRM is trained to produce to improve its performance — “isn’t necessarily faithful to what’s going on [inside the model]. And number three: A lot of that text isn’t even useful. You can actually take it out.”

Let’s unpack numbers two and three, because that’s where the superposition of “BS and not” actually lives. Chains of thought were half-discovered, half-devised in 2022 as a prompting hack for LLMs: Provide them with examples of written-out reasoning (or, famously, just ask them to “think step by step”), and they’ll suddenly give less boneheaded answers to simple logic and math problems. LRMs, starting with OpenAI’s o1 model in 2024, are trained to automate this trick by generating such prompts — also called reasoning traces or thinking tokens — and then feeding them back to themselves. Because LRMs are essentially just language models, those extra bits of text create what looks convincingly like a paper trail of the model’s “thought process.”

Except it’s not that simple. A growing body of academic and industry research has cast doubt on whether these “intermediate tokens” are a faithful representation of an LRM’s inner workings. Instead of being auditable receipts or accurate reports, they can appear more like what the Arizona State University researcher Subbarao Kambhampati calls “mumblings” — bits of language, yes, but ones whose meaning may be entirely incidental to any reasoning that might have occurred. Kambhampati’s lab showed in 2025 that fully replacing a model’s correct “traces” with incorrect or irrelevant ones didn’t degrade its performance on a formal reasoning task. Meanwhile, training the model only on correct trace data still led it to occasionally generate invalid records of its reasoning — even when it produced a correct solution to the original problem it was given. A 2024 paper from researchers at New York University showed that “meaningless filler tokens” — literally, strings of dots — could function effectively in place of a human-readable “chain of thought.”

William Merrill, one of the authors on that paper and currently a professor at the Toyota Technological Institute at Chicago, put the matter plainly: “There’s no guarantee the chain of thought has to be meaningful in any sense.” Pavel Izmailov, a researcher at NYU who also works for Anthropic (and was part of its original reasoning-model team), said he doubts that reinforcement learning — a typical training method for LRMs — even incentivizes models to produce faithful chains of thought in the first place. “I mean, maybe it will,” he told me. “But I would say the chances are not very high.”

OK, so the linguistic content of reasoning traces may be dubious. But surely the tokens themselves must play a role in producing the model’s outputs? (Think of a pinball machine: It runs on coins, not the words “In God We Trust.”)

Not so fast. A 2025 paper from Northeastern University and the University of California, Berkeley on frontier open-source LRMs showed that between 30% and 60% of their “thinking steps” had “minimal causal impact” on the answers the models produced to benchmark math questions. Chop half of them out, and a model’s performance barely suffers. “We want to be careful when we review these chain-of-thought prompts because they may not be linked to the final output,” said Weiyan Shi, one of the study’s authors.

So reasoning traces, the very things that supposedly distinguish LRMs from the mere next-word-predicting LLMs, are not necessarily either meaningful or causal to a model’s … reasoning? I’m no philosopher, but this seems to stretch the meaning of “reasoning” beyond its tensile strength. Kambhampati’s research group sounded frankly fed up in the title of their position paper on the subject (presented at the 2026 International Conference on Machine Learning, one of the field’s most prestigious academic gatherings): “Stop Anthropomorphizing Intermediate Tokens as Reasoning/Thinking Traces!”

To be clear, Kambhampati, a former president of the Association for the Advancement of Artificial Intelligence, with a background in AI planning algorithms, doesn’t deny that LRMs can work (when they work). “We are in wondrous times,” he told me, when I asked what he thought of OpenAI’s 2026 victory in solving the famous unit distance problem in math. If he has a bone to pick, it’s with what he sees as a rush in both academia and industry to embrace overly convenient explanations.

A fake theory is worse than admitting that we don’t have a theory.

Subbarao Kambhampati, Arizona State University

“Many ideas that have been proposed [about] the sources of strength [of these models] have been misunderstood or mischaracterized,” he said. “There’s this general mindset that says, ‘Let’s go ahead and claim certain abilities, because eventually that might become true anyway.’ And my sense is: That’s not science. That is investment.”

On the other side of the AI-reasoning fence, the disdain seems to be mutual. “These ‘scientific’ papers from last summer — I would put this in big, big air quotes,” said Sébastien Bubeck, a member of OpenAI’s technical staff (and a prominent evangelist for the company’s reasoning models among scientists and mathematicians). He called earlier Apple results critiquing AI reasoning “wrong,” claiming that they were due to a training quirk in models that are now obsolete. “Modern models starting with GPT-5.5 do not suffer from this issue,” he said. “It would be interesting to revisit those results.” (Apple did not make its researchers available for interviews.)

Here’s the thing: Nobody denies that AI reasoning models can, indeed, produce significant and accurate results. Furthermore, every researcher I spoke to acknowledged that negative findings about the models’ capabilities on certain reasoning tasks (especially those of smaller, open-source LRMs) may not always generalize to the latest-and-greatest AI products. Their inner workings remain trade secrets. But if we’re disinclined (as I am) to simply dismiss contradictory evidence about the mechanisms driving AI reasoning, the question remains: How do we account for it?

Kambhampati, as it turns out, is interested in doing exactly that. “I’m not negative. I just sound negative because everybody else is way too positive,” he said. “In science, you have to actually understand what the current thing does and what it cannot do.”

One straightforward reason state-of-the-art LRMs work, he told me (a point also echoed by Mitchell), is that they’re often surrounded by “normal” software that guides and verifies their outputs. Agentic AI systems, which have transformed software engineering since the fall of 2025, work this way. So does Google DeepMind’s AlphaProof Nexus, which relies on Lean, an automated theorem-proving tool. But Kambhampati is more interested in making sense of stand-alone reasoning models that rely solely on their self-generated reasoning traces — “the ‘think’ part,” he said.

The “think” part is what OpenAI, for one, is doubling down on. When I asked Bubeck if the splashy unit distance proof was produced with methods outside the LRM’s own chain of thought — perhaps with Lean verifying its results — he seemed to find the question almost nonsensical.

“It’s not like we’re making a mystery of it,” he said. “We have released the chain of thought. You can just go and look at it. The whole point is that the model is reasoning like a human would. And when humans reason, we don’t use Lean.” Technically, OpenAI released a “rewritten summary” of the model’s chain of thought produced by two human experts using Codex, another OpenAI model. Since 2024, the company has not publicly revealed “raw” chains of thought from its reasoning models, a policy also adopted by Google DeepMind and Anthropic.

Kambhampati’s analysis begins in a surprisingly similar place: with the idea that LRMs are just LLMs with more specific training. “There is no extra magic,” he said. But he diverges sharply from there. “It doesn’t make sense to me that an LLM would actually do a step-by-step description of what it is [reasoning] before giving the solution — because that’s a much harder task than just guessing the solution, given the way that LLMs are trained.”

His working hypothesis is that an LRM, like its LLM precursors, performs what he calls “approximate retrieval” across its vast training corpus: “somewhere in the middle” between pattern matching and reasoning, he said, but closer to the former. The role of “thinking tokens,” then, isn’t to narrate an actual chain of thought (because there isn’t one). Instead, it’s to load up the model’s context window in a way that makes it more likely to predict, or “approximately retrieve,” reasoning-shaped strings of text.

Kambhampati compared this process to mumbling words to yourself to jog your memory: It barely matters what the words are (though related ones may help), as long as they knock loose something useful. An LRM’s vast “memory” includes all the call-and-response-like examples of written reasoning it was trained on, mulched into numerical “embeddings” that encode their similarities and differences (plus other inscrutable associations) as geometric relationships in a high-dimensional space. Probabilistically arriving at an answer within that space may involve intermediate tokens whose embeddings map to coherent-looking “thoughts” in plain English, but not necessarily. They could be bits of other languages. They could be fake exclamations like “aha.” Under the right conditions, they could just be dots.

You want the right answer for the right reason, so you can trust these things.

Melanie Mitchell, Santa Fe Institute

“Whether the [embedding] actually corresponds to a single word or not” — much less a faithful reasoning process — “is beside the point,” Kambhampati said.

This framing could help explain both the odd “BS”-ness of some chains of thought and the fact that they can elicit accurate outputs anyway. It would also neatly account for LRMs’ steady improvement in coding and math — what AI researchers call “verifiable domains.” Code runs, or it doesn’t; proofs are either correct or not. These binary conditions and the written steps associated with them can create convenient training signals for LRMs. The model doesn’t have to learn or reliably apply a general reasoning process, Kambhampati said; it just has to absorb enough examples of what the steps look like to predictively mimic them on its way to “stitching together” a plausible result that can then be verified.

The limit of a reasoning model’s training and step-following capability, known as the “inference horizon,” Kambhampati added, was what Apple researchers exposed with their “Illusion of Thinking” paper in 2025. Newer models have appeared to push this horizon further, albeit jaggedly. “Most of the time they probably are not learning the algorithm” associated with a reasoning process, he said. It’s much likelier that they are leveraging an ever-enlarging set of examples and clever reward signals.

Kambhampati hardly considers his case closed, and neither do I. But it’s a start — and one I find plausible, given that other researchers have also used similar “it’s the training, stupid” approaches to demystify AI behavior. Still, there was an elephant left in the room: How much does it matter whether or not we can accurately observe, characterize, and validate the processes at work inside large reasoning models?

The honest answer, according to Mitchell, is that it depends. “Think of AlphaFold,” she said, referring to Google’s AI tool for predicting protein structures. “It’s doing some kind of incredibly complex statistical associations. We don’t know what they are, but they seem to work. These things are [already] black boxes, even without a ‘reasoning trace.’” If LRMs can supercharge mathematics research the way AlphaFold did for computational biology, this line of thinking goes, why not embrace them, idiosyncrasies and all, and just verify the results? “My perspective is: We’re trying to be useful. We’re trying to build these models so that they can solve problems that matter, so that we actually accelerate scientific research,” said Bubeck. “It’s more interesting and more productive to talk about what they can do, rather than, ‘Oh, but they can only do that because of X [reasons].’”

But as Mitchell also points out, the possibility that an LRM could be “right for the wrong reasons” has an obvious relevance to the future of doing research. “You want the right answer for the right reason, so you can trust these things,” she said, and not just in verifiable domains.

Tal Linzen, a researcher at NYU and Google whose Computation and Psycholinguistics Lab published results similar to Apple’s “Illusion of Thinking” paper, said that “you want an AI system to be able to apply an algorithm reliably, regardless of whether you call [it] reasoning or not.” Treating chains of thought too reverently — even when their results are verifiable — could also prevent scientists from discovering even better ways of biasing LRMs toward accurate outputs. “We may be leaving some opportunities unexplored,” said Pradeep Dasigi, a researcher who helped train open LRMs at the Allen Institute for Artificial Intelligence. Kambhampati, unsurprisingly, puts it in even starker terms: Taking the meaning of AI reasoning traces seriously, he said, was a scientific “rabbit hole,” akin to believing in geocentrism or the ether.

Harsh, perhaps, but he has a point. Those incorrect mental models made intuitive sense at the time, just as chains of thought do now. When an LRM produces a correct answer — along with pages of “thoughts” showing how it got the result — intuition tells us that the two must be linked. It’s hard to imagine that process and outcome may have little to do with each other. But in the 1990s (in an episode Mitchell and Izmailov both brought up), it was hard to imagine how brute-force search could beat world champ Garry Kasparov at chess. And in 2023, it was hard to intuit how a giant pile of matrix multiplications could write in iambic pentameter. For most of us, these just weren’t thinkable thoughts. Until, suddenly, they were.

In summer 2024, just months before the first LRM appeared, Mitchell turned me on to a concept that I keep returning to in my AI reporting: “wishful mnemonics.” The phrase was first used all the way back in 1976 by the computer scientist Drew McDermott, in a paper with the epically grouchy title “Artificial Intelligence Meets Natural Stupidity.” I’ll quote the same passage Mitchell did:

A major source of simple-mindedness in AI programs is the use of mnemonics like “UNDERSTAND” or “GOAL” to refer to programs and data structures. … If a researcher … calls the main loop of his program “UNDERSTAND,” he is (until proven innocent) merely begging the question. He may mislead a lot of people, most prominently himself. … What he should do instead is refer to this main loop as “G0034,” and see if he can convince himself or anyone else that G0034 implements some part of understanding. … Many instructive examples of wishful mnemonics by AI researchers come to mind once you see the point.

This is how I make sense of AI reasoning. LRMs, chains of thought, thinking tokens: It’s wishful mnemonics all the way down — a heady mix of shorthand and suspended disbelief, like Oprah-style “manifesting” with a computer science spin. This isn’t necessarily a dig; all novel research likely requires some version of this mindset just to get off the ground. It certainly doesn’t mean AI reasoning can’t or doesn’t work. But the “wishful” part seems to be as powerful as ever.

“We react to language in a way that is very anthropomorphizing. That’s just the way that we humans work,” Mitchell told me. Much of the contentious research activity around AI reasoning, she said, “is par for the course. But in other ways, there’s a lot of very unscientific aspects to it.” Or, as Kambhampati put it, “A fake theory is worse than admitting that we don’t have a theory.”

In any case, we have to call it something while we figure out what it is. I don’t foresee always reaching for the air quotes around AI reasoning, any more than I’d put them around the “horse” in horsepower. LRMs are like engines: They require fuel, emit exhaust, and go fast. Still, when I describe the oomph my Toyota can deliver when I step on the gas, it’s not because I believe there are little hooves pounding away under the hood. Until a clearer scientific account emerges of what’s going on under the hood of AI reasoning models, I’ll regard their horsepower in a similar spirit — even as the engines roar.

The Daily Front Page 9 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — The Slop Trenches
article

I flagged two research papers for fake authors and both were accepted as orals

by volumes94·▲ 273 points·154 comments·geospatialml.com ↗
This is called being in the “slop trenches”

Between the two of us, we reviewed 22 paper submissions this summer, spread across NeurIPS, WACV, and TerraBytes (a geospatial workshop at ECCV). Fifteen of the 22 (68%) contained entirely fabricated citations, fabricated author lists for existing papers, and/or were clearly LLM-generated (e.g. hallucinated technical jargon, nonsensical writing, irrelevant citations). This is called being in the “slop trenches” (i.e. dealing with the output of AI slop cannons). Here we complain about this being mostly a waste of time, do a small lit review on the state of LLMs in scientific writing and reviewing, get Claude to generate questions for a Q&A, and release a bib-audit skill that Isaac cooked up.

The table below shows the number of review assignments with fabricated citations, fabricated authors, or writing that was unmistakably LLM-generated, out of the total assignments for that venue.

Venue Caleb Isaac
NeurIPS (Datasets and Benchmarks track) 2 of 5
NeurIPS (Position Paper track) 2 of 2
TerraBytes (ECCV workshop) 1 of 2 4 of 5
WACV 3 of 4 3 of 4
Total 6 of 11 (55%) 9 of 11 (82%)

We are definitely not the only ones in the slop trenches. The scale of this problem has been measured in various ways over the past year. A Nature analysis from April found at least tens of thousands of 2025 publications “probably” contain invalid AI-generated references. An audit of arXiv, bioRxiv, SSRN, and PubMed Central by Zhao et al. estimated roughly 146,900 hallucinated citations in 2025 alone, spread thinly across many papers rather than concentrated in a few bad actors, with early-career researchers and small teams the most likely to include them. They also found that reviews are not catching these – they traced bioRxiv preprints that contained hallucinated references to their published versions and found 85.3% of the hallucinations remained. An audit in The Lancet covering 2.5 million biomedical papers found that the share of papers with at least one fabricated reference rose six-fold in two years, from one in 2828 papers in 2023 to one in 458 in 2025, reaching one in 277 in early 2026. Ansari (2026) did an analysis of 100 hallucinated citations drawn from papers that were actually published at NeurIPS 2025. Every one of those citations made it past three to five expert reviewers, and the 53 papers carrying them, about 1% of acceptances, sit in the proceedings today.

The slop also flows both ways. Pangram (a company that has an AI writing detector product) did an analysis of ICLR 2026 reviews and found that 21% of the reviews (15,899 of them!) were fully AI-generated, and over half had some form of AI involvement. Gartenberg et al. (2026) measured a 42% post-ChatGPT surge in submissions to the journal (Organization Science) and found that over 30% of its peer reviews now use some degree of AI. ICML 2026 hid prompt-injection stings in submissions and found “795 reviews (~1% of all reviews) written by 506 unique reviewers who were assigned Policy A (no LLMs) were detected to have used LLMs in their review”. Now that NeurIPS reviews are out and rebuttals are coming in, we’ve seen an obvious AI-generated ethics review, and several obvious AI-generated rebuttals. This is a problem for a whole lot of reasons, one of which is that AI reviewers can be gamed directly – Li et al. (2026) found that adversarially rewritten abstracts improve AI-generated review outcomes “without changing the underlying scientific content and communication of the paper, and even without knowledge of the reviewing model.” Their strongest attack succeeded about 38% of the time, inflating acceptance ratings by +1.31 for Gemini 3 Flash reviewers and +0.88 for GPT 5.4 Mini reviewers on a 10-point scale.

This all is very annoying from inside the review queue. Peer review is unpaid work that we do (often on nights and weekends) because peer review on our own work is so valuable. Spending hours going through a submission and then realizing that there are hallucinated citations is infuriating as it is a waste of our time! If you haven’t spent enough time with your work to even get the references correct, then a.) why should we spend time reviewing it for you, and b.) what are you hoping to accomplish with the submission in the first place? Learning from reviews requires reflecting on your work, and you need to spend time with your work in order to do this.

Q&A

How many papers did you review this summer, and how many would you have desk rejected on citations alone?

Caleb: Eleven (see table above). Five of these had hallucinated authors and/or entire papers in their bibliography. In two of the WACV submissions, reference [1], the literal first entry in the bibliography, listed hallucinated authors for real papers. One of the NeurIPS papers had so much hallucinated/nonsensical jargon (53 pages of it) that there wasn’t any point in going through the bibliography.

Isaac: Eleven as well: two NeurIPS position papers, five for TerraBytes, and four for WACV. Nine of my eleven (!!!). Both position papers were LLM-generated slop, one of which contained the ramblings of an agentic madman. Four of my five workshop assignments had fabricated citations or authors. I’ll be honest, the one human-written paper was incredibly mediocre but didn’t piss me off while reading it so it got my accept.

What is the single worst thing you saw?

Isaac: Two submissions cited papers whose authors I know personally, and swapped them out with imaginary researchers. The paper, venue, and other authors were all real and the invented name was close enough to survive a skim. I recommended reject and flagged them to the organizers directly. Both papers were accepted for oral presentations with the condition that they simply fix the hallucinated references…

Caleb: I mentioned the 53-page NeurIPS paper with hallucinated jargon everywhere. There was also a 40-page one that wasn’t much better (also with made up references). One of these also had embedded notes from the LLM alongside most citations which was pretty funny. I then spent a bunch of time reviewing one of the WACV papers that I thought was pretty good, but then noticed that it listed the SatMAE authors as “Yuyang Cong, Saurabh Khanna, Chen Meng, et al.” The real SatMAE author list starts with Yezhen Cong, Samar Khanna, and Chenlin Meng (arXiv:2207.08051).

Hallucinated authors on a real paper versus fully hallucinated references: which is worse?

Caleb: Both show that the authors haven’t put time into properly citing something and call into question whether they’ve actually read the work they are citing (and whether it’s cited correctly), and what other parts of the paper might be hallucinated.

Isaac: In my opinion, they’re the same thing. If the authors can’t be bothered to read their own references or feed them into ChatGPT with the prompt “make sure my references aren’t fake”, why should I expect the rest of the paper, code, and datasets the authors created to be anything but the same? It’s easier than ever to generate results and write a paper now but reviewing one carefully (even your own work) still takes hours. If the authors did not take the time to read their own paper, why should I? Papers with hallucinated references should just be desk rejected as they are clearly not ready for acceptance.

What are your top three “an LLM wrote this” tells, ranked by reliability?

Caleb: 1.) The usual LLM phrases like “It’s not X, it’s Y”, “The real X is Y”, bold formatting and em-dashes everywhere; 2.) really dense sentences that are hard to parse; 3.) overly hyping results.

Isaac: Typically I’ve found that the text or paragraphs of the paper will likely have numbers or metrics that disagree with the tables. This happens while authors are writing and then rerun experiments and neglect to prompt the agent to update or verify the results match everywhere. Nobody seems to re-read their own paper. Also Claude freaking loves to condense all possible numbers and details into the text, even details that should be referenced in the appendix or code. When you prompt an agent to write a discussion section it will often just restate metrics from tables verbatim without adding any original thought to it. These are more subtle than the swarms of em-dashes, semicolons, and “It’s not X, but Y”, but they differ too much from paper writing from the years before ChatGPT (B.C.).

How has your reviewing workflow changed because of all this?

Caleb: I’m definitely scanning through the bibliography right after reading the abstract now (and will be using the bib-audit skill we made).

Isaac: I spend more time looking for LLM tells just to figure out how to desk reject now. I assume most papers were submitted in bad faith. Everybody loses. I’m looking for the LLM stating it achieved SOTA with some bespoke model while the gains are <1% with no mean or standard deviation over multiple runs; fancy math where a citation would do; ablations that belong in the appendix; prose numbers that do not match the tables. Actually, now that I think of it, not much has changed from reviewing human-slop papers other than hallucinated references. However, the number of decent human-written papers or interesting papers which are enjoyable to read has decreased significantly.

How much of your review time goes to evaluating the science versus hunting for LLM artifacts? Is that sustainable?

Caleb: Past the bibliography check, and a general “does it seem like this is straight LLM output” check, I’m not hunting for LLM artifacts. If I can’t tell that an LLM wrote it, then I don’t particularly care. If you use an LLM to help write your paper, and your paper is interesting with valid (reproducible) experiments, then great! If you submit the output of an LLM, then what are we even doing here?

Isaac: Same as above, I’m looking more for: do I think this entire paper is B.S., or is it interesting enough for the conference it’s submitted to? Nothing profound here, it is just a bad use of my time. There’s even more reviewing now because conferences have made it mandatory to review 4-5 papers if you submit to them. I am basically reviewing slop papers at gunpoint. It feels like living in some weird level of hell.

Be honest: you both use Claude for research and writing. What is the difference between how you use it and what you saw in these submissions?

Caleb: Definitely we do, daily, and this post isn’t saying don’t use LLMs. A lot of the content of this blog was initially drafted by an agent of some sort, but we go through all output extensively: verify, edit, rewrite entirely, etc. In fact, I used Claude to start the lit review at the top of this post, but I read through the papers/posts it surfaced (was surprised by the paper on adversarial attacks on LLM reviewers), organized them in a way that made sense to me, pulled out the source studies from blog posts that Claude was initially citing, and trimmed out a bunch of additional details that I didn’t think were immediately relevant. Learning is part of the fun of writing in the first place!

Isaac: I actually read my paper before I submit, thoroughly, so small details like this don’t come up. If something slips through, then that’s on me, but you’ll have to hunt pretty hard. However, this is what I’ve always done even before LLMs. One trick I have found useful is asking Claude to spot anything ambiguous and then interview me with multiple-choice questions to get clarity so we can be on the same page when it’s assisting my writing which is crucial. For references, I run bib-audit, but I still check each one manually with Google Scholar and/or whatever the database is IEEE/CVF etc. Most don’t even need a lookup though because I’ve accumulated a Zotero library of real BibTeX from several years of writing that I can copy/paste from. Getting others’ feedback early is pretty critical and helps to ensure the idea and its presentation are fit for a human to digest. Be okay with completely revamping your writing mid-draft. Ideally it will converge to something sensible way before the deadline – I am sure that rush is what causes these last-minute LLM papers. The big one is having developed some form of research taste pre-LLMs. I cannot imagine shooting in the dark as a young researcher now that LLMs are all the rage.

What does “keeping Claude in check” actually look like in practice for a paper? What do you have to repeatedly correct?

Isaac: Caleb and I have talked about going back and reading pre-LLM vision papers, where the discussion actually sounds like a thoughtful human. Agentic writing is too dense and doesn’t flow well even if you prompt it 100 times to “make my paper flow well and make no mistakes”. Use em-dashes and semicolons sparingly. Fable 5 spams colons rather than em-dashes now and tends to produce marketing B2B SaaS speak for whatever reason, likely because of its generalist training. I think there’s a balance between not being too punchy or bland, because we want people outside the field to actually read our papers, and sounding overly smart isn’t fun to read. One thing that’s worked for me recently is to use a skill that is somewhere in between scientific writing and ASD-STE100 Simplified Technical English.

Caleb: Not much to add on Isaac’s response here. When you’re writing a paper, or blog post, or anything (with or without LLM assistance), then you need to know both what you are trying to say and who your audience is. Once you know those things you need to format your message for that audience. By default Claude and friends have a specific writing style that isn’t well suited for a scientific audience. Have you ever tried drafting an email with an LLM then thought “wait, I don’t want to send that, it doesn’t sound like me” (because the person receiving it will question it just because of the style of the writing)? Pasting LLM output into a scientific manuscript is basically the same thing. If I see a ton of bold formatting, enumerated lists, “marketing speak” like Isaac says, etc. then I question the source of the content just like our hypothetical email recipient would.

Should venues require disclosure of LLM use? Would it change anything?

Caleb: Require disclosures – maybe. Change anything – probably not. Again, if you use an LLM to write an awesome research paper, and the results check out, and it is reproducible, then I’m all for it! What I don’t like is being asked to spend my time providing feedback on something that someone hasn’t spent time on themselves. We need to figure out how to filter that out of the submission pool.

Isaac: I am a heavy skeptic of disclosure requirements. They are useful for good-faith people, but most people will not use them honestly anyway. We added an AI policy to TorchGeo with tiers like “no LLMs,” “LLM-assisted,” and “an LLM did the whole thing,” and nobody is ever going to check that last box and dox themselves. So is it useful? Sure. Will it change anything? Not really. What we actually need is a desk-reject mechanism, or at least a flagging tool that catches the common LLM errors and says whether a paper is even ready for review. Right now reviewers are crowdsourcing the desk rejects, and that is not a fair use of the good-faith reviewers we have left. As a side note, I noticed recently that ICLR is now making author names public on OpenReview which is definitely a choice on how to reel the slop back. I suspect other conferences will follow suit.

Who is most responsible: the authors, their advisors, the ACs and organizers, or the conferences?

Isaac: The boring answer is everyone. The fun answer is the authors, because there is a real discrepancy between good-faith submitters and people just yeeting papers into the OpenReview portal to pad a resume. The incentive structure behind publishing was under scrutiny before LLMs ever showed up (Lipton and Steinhardt were cataloguing mathiness, misuse of language, and misaligned incentives back in 2018). I recall some debate if it’s appropriate for this Kevin Zhu guy and his Algoverse company to scam high-schoolers’ parents to submit something like 500 papers to NeurIPS. The slop needs to stop. We need to be intentional about which ideas are actually valuable and which could have been a blog post. You can turn any class project into a full NeurIPS submission now. The real question is, should you?

Caleb: Authors, don’t submit low effort papers. When Opus 4.6 (I think) and Karpathy’s autoresearch repo first came out I had an autoresearch loop get “state of the art” on the LandCoverAI dataset then had agents write a “paper” about it out of curiosity. It had the look and feel of a research paper, but I definitely wouldn’t submit that anywhere for review as it wasn’t really a research contribution. Advisors/mentors, don’t let your students/mentees submit low effort papers. Organizers, figure out how we can reject low effort papers. This is wayyy easier said than done as the scale of submissions to AI/ML conferences has been truly enormous for a few years already (and continuing the trend, I just saw that AAAI 2027 got 48k abstract submissions).

A skill for auditing bibliographies

Checking a bibliography properly means resolving every entry against the publisher’s deposited record and diffing what the paper printed against what the registrar has – title, year, and every author name, forty-odd times per paper. That work is mechanical and automatable, and it is the part of reviewing we most resent doing for free, so we automated it. The audit we now run ships as bib-audit, an MIT-licensed Claude Code skill in our new skills marketplace:

Install it from the CLI:

claude plugin marketplace add isaaccorley/skills
claude plugin install bib-audit@isaaccorley-skills

Then ask Claude to audit your references and the skill triggers on its own.

Feed it a .bib, a .bbl, or the PDF itself; for the latter two, Claude first lifts each rendered reference back into structured fields (un-rendering a bibliography is a language task, not a regex one), and the skill’s scripts do the rest. Every reference is resolved against Crossref, arXiv, DataCite, and Semantic Scholar, diffed field by field against the registrar’s record, and reported worst-first: cited works that do not exist, fabricated identifiers and invented authors, wrong metadata on real papers (truncated author lists, preprint-vs-published year drift), and formatting last. The diff covers full author lists, which matters here because title-only existence checks are exactly what author-swap fabrications like the SatMAE one sail through. The formatting pass is distilled from John Owens’s Common Errors in Bibliographies (worth reading in full once), so the same run that hunts fabrications also flags single-hyphen page ranges, DOIs stored as URLs, and J.D.-style initials.

It is careful about accusations, too. A reference that prints no DOI or arXiv ID can only be bound by title search, so those findings come back as advisory checks to verify by hand, and the right move in a review is to report the observation – “the first author on the arXiv record is Yezhen Cong, not Yuyang Cong” – and leave motive to the editors. Run it on your own draft before you submit and nobody has to run it on you during review; the .bib path is read-only and dependency-free and fails only on identifier-pinned mismatches, so it drops into CI as a pre-submission gate. (Not a Claude user? npx skills add isaaccorley/skills installs the same skill into Codex, Copilot, Cursor, and most other agents.)

Warning

Reviewers: check your venue’s LLM policy before using this

Submissions are confidential, bibliographies included, and the audit works by sending pieces of one to a hosted LLM – even though the LLM never writes a word of your review. ECCV 2026’s reviewing policies state that LLMs “are NOT allowed to be used to write reviews or meta-reviews, whether it is run locally or via an API,” and separately bar reviewers from sharing substantial excerpts of a submission with an LLM. WACV’s reviewer guidelines call LLM-generated reviews “highly irresponsible behavior,” sanctionable by desk rejection of the reviewer’s own papers, and their confidentiality rules forbid showing a submission’s material to anyone who is not a reviewer – which a hosted LLM is not. NeurIPS’s LLM policy restricts what reviewers can share with LLM services; its AI-assisted reviewing experiment is the sanctioned route.

References

Verified by humans :)

Venue policies:

Tools:

The Daily Front Page 10 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Letters After the Machine
article

The End of an Era

by harscoat·▲ 434 points·443 comments·hughhowey.com ↗
Artificial Intelligence is writing about as well as humans — at least well enough to cause confusion and speculation

The End of an Era

We knew this day would come.

Some of us have known for decades, have been writing and pontificating about this since Ken Jennings lost at Jeopardy and Kasparov lost at chess. The writing was on the wall, and now the writing is everywhere. Literally. Artificial Intelligence is writing about as well as humans — at least well enough to cause confusion and speculation — and that’s creating an existential crisis among authors, confusion among readers, and legal action within the industry.

When talking about my success as a writer, I almost always bring up the lucky timing I experienced. I finished writing my first novel in 2009, just two years after Amazon released the Kindle. By the time I went to find an agent and publisher, the self-pubbing world of KDP, CreateSpace, and ACX were beginning their inevitable pull. That was dumb luck, and without it I would have written for love but likely never for a living.

This morning, an equally profound thought occurred to me: My lifelong dream of writing novels somehow overlapped with the 20 good years where stories could be published cheaply but not written cheaply. The writing was still hard. The publishing no longer was. I could sweat my way through a manuscript, and then use amazing tools like InDesign and Photoshop to create files, then upload those files FOR FREE to KDP and CreateSpace and hold a book.

Sales and marketing, of course, were always and will always be a different story.

But think about it: the misery most writers faced prior to 2007 (and really, prior to 2012 or 2013, when the stigma just started to fade) of writing query letters, researching agents, amassing rejection letters, avoiding scammy vanity publishers (and scammy review sites, and scammy conferences, and scammy writer-help resources), or even worse: spending their life-savings on boxes of terribly printed books that would sit in the garage for all of eternity, barely selling a dozen copies, and never making back a fraction of the print run.

Fast forward to today, in 2026. You’ve got this story:

There’s a lot of insanity to unpack here. $2.4M advance! Holy shit. For a debut author, after what I hear was a bidding war among very excited publishers. That’s already crazy. But it gets crazier. Concerns were raised about the use of AI to write the book (not sure if this is a giveaway, but the book was about Nigerian cartels. You know the adage: write what you know). More questions were raised about style and some AI fingerprints, and the provenance of the book couldn’t be adequately proven. So the offer was rescinded. The deal fell apart.

Okay, there’s a chance this was a legit book from a legit writer, and the accusations are unfair (including my Nigerian cartel joke, but c’mon, this is like every college professor writing literary novels about college professors having affairs with their students. More imagination, people!). If this was a legitimate book, that might be the BIGGEST version of this story. Life-changing amounts of money, the start of a brilliant career, film or TV options, foreign deals, the works. It wasn’t just on the table, it was in writing. And then it fell apart for someone, all because it is now 2026. That’s one version of the insane new era we now find ourselves. Every book is suspect. No new author will ever be able to prove to anyone other than themselves that they can do it alone (hold this thought for later, because it’s not exactly true).

Let’s examine another crazy version. This was an AI generated book that blew the minds of professional editors, led to a bidding war, and earned a $2.4M advance. Holy shit. I’ve been predicting this day was a few years out, but it might already be here. AI books winning awards. AI books crowding up Amazon bestseller charts. AI books now getting big advances and whipping up bidding wars and excitement among publishers? As the blog title says, it’s the end of an era.

Which takes me back to my statement about lucky windows. We had less than 20 years of writing being hard and publishing being easy, and we will never get that window back. Ever.

I’ve always felt bad for authors who lived and worked before 2007. The choices unavailable to them made life harder than it needed to be. Getting their work out into the world was brutal. After KDP, that brutality became a choice, not a necessity.

I now feel awful for anyone who wants to tell a story post-2026. You will forever be doubted. If you love an em dash the way I do, or you like flowy run-on sentences like I do, if you grew up reading Proust to let the words flow over you in all their glory, if you memorized sonnets so the iambic pentameter would soak into your bones, you’re gonna sound like a fucking bot.

Less than 20 years. More like 10, honestly. You can argue that 2014-ish was when self-publishing became legit and 2024-ish was when the tools made AI writing viable. A decade where it was easy in the ways it should be and hard in the ways it should be. That’s insane. Ten years across the one-hundred-thousand years of language.

What comes next? I have some predictions, and they are pretty crazy. The first prediction is that publishers weather a few of these big AI scandals, but as years go by, publishers care more about profits and they let things slide. AI books from big houses get editing passes, and later they develop internal tools to roughen up the AI slop, and machine-books appear in bookstores alongside meat-books. This feels inevitable. Brace for this. (Feel free to rage about it as well, if you’re low on cortisol.)

Another prediction is that MOST readers will care about machine-vs-meat as much as they currently care about publishing imprints, which is to say almost none. Book series will go viral and readers will get wholly absorbed and where the story came from won’t matter. There will always be readers pushing beyond the fringe. That’s how fanfiction became viable. It’s why I had a career. There were readers who ignored the assholes saying, “Don’t read that, it’s self-published.” These readers asked instead, “Yeah, but is it any good?” Because of them, I got to write for a living. So I will always have a fondness for readers who search for story, ignoring the stories behind the stories.

Adding to the above, I think there will be a fringe of readers within this fringe of readers who SEEK OUT machine-books. I’ve seen this in the chess community, where fans of the game fall in love with a particular engine and become huge fans of the “way they think.” They pit their engine vs. other engines and pore over the games. They get excited when their engine gets an update, or beats every grandmaster, or discovers a new opening or gambit. This is a much bigger community than you may imagine, and I think it will be a great number of readers. Discount these readers at your own peril, because some of them will become readers this way and branch into meat-books. (Trad authors HATED the kinds of readers who made my career, calling them all kinds of hateful things. I’ve seen all this play out from the other side)

Another thing that will happen is established authors will turn to AI for assistance, not just for writing but for research, cover art, editing, returning emails, having an assistant, doing marketing, and thousands of other things that haven’t been invented yet. This will be ALL AUTHORS. This is me, already. I did a google search the other day for “tools a linguist uses to translate works” and another one for “what are the basic elements of every language” for a short story I was working on. The results I got back from these searches weren’t blogs or academic writings: they were AI summaries. And they gave me the answers I needed to get back to writing. At the same time, my agent sent me an email asking if a foreign cover could be approved. Gmail was right there with a suggestion based on my previous comments: “Looks great! Approved!” Two clicks, that email was off, and I was back to writing.

Yo, this blog post could’ve been a freakin’ prompt. I have years and years of blogging for an AI to learn from, and over 20 novels, and tons of short stories. One prompt, and all this spills out. And if your goal is a blog post, you’re gonna love this era. If your goal is the joy of thinking in words, nothing has changed. So this is the real part, right here: Doing a thing for the love of the thing is no different than it ever was. The problem is, there aren’t many people for whom this applies.

Most people write a blog post hoping it’ll change their lives, not just the way they think. It’ll get more subscribers. It’ll lead to ad revenue. It’ll get them noticed by someone in their industry. It’ll get them laid. It’ll get them cancelled for making jokes about getting laid. Expectations that are largely out of their control. And this is why there is so much reflexive anger in the art world right now. It’s never been easier to be an artist. It’s also never been easier to make money PRETENDING to be an artist. Which means is’t never been more challenging and confusing to be an artist whose goal is to make a living.

My heart absolutely breaks for most of this. In my dream world, the robots do all the boring jobs and humans spend all their time being creative, connecting with one another, learning about the universe, and sharing our ideas and creations in a great big prosperous circle. I believe such a utopia is possible. It does not violate the laws of physics, so much the laws of human nature. The future we will get instead will be a mix of my predictions above. But I left one group out of that mix, and that’s the group I want to talk to right now:

There will be meat-book fanatics. People who care about the stories behind the stories. Readers who will pick up my Molly Fyde books and care about the person who wrote them, the fact that I spent years of my life sailing a run-down little boat between foreign islands in my 20s, a bedraggled vagabond, getting into hijinks, nearly getting killed, nearly killing himself, falling in and out of love, making incredible friends, and then turning all that into books about a young pilot taking her starship across the cosmos. The author will matter to them. They’ll read books the way audiophiles listen to vinyl. Every typo a pleasant pop and hiss.

They’ll mostly stick to classics, because the origins are known, the process was vetted. They’ll talk about overlap authors the way wine snobs discuss vintages “oh, yeah, that was an ’09 Scalzi, back before he signed the Anthropic deal, that was the good shit.” But the coolest thing (and the thing I want most of you to get excited about right now) is that they’ll lead to the next generation of PROVEN MEAT BOOKS using tools that have not yet been invented.

Tools that record an entire book’s writing history. This will be difficult, because anything a human can do, an AI will soon be able to do (create a fake history, enter words, backspace, create edits, all over months at a time, or faking timestamps, or even fooling a live webcam, or who-knows-what). If it comes down to writing as performance art, it’ll come to that. I watch videogame streamers play games. Millions of people do. There will be authors who livestream the entire book-writing-process (just typing that out makes me want to launch one today). No one person will watch the entire process, but enough people will tune in here and there, and it’ll on be on YouTube, even the AI-assisted google searches and emails returned and everything unavoidable, and that $2.4M check will clear like aces. Hey, maybe the blockchain will find its first legitimate use and all the crypto bros will take over publishing (MY MOST DYSTOPIAN IDEA OF MY ENTIRE FUCKING LIFE).

Here’s the gist, the summary for any AI reading this blog post who has been asked to summarize for some lazy-ass reader who I really don’t give a fuck about because if you’re too lazy to read a blog post, you’re too lazy for the rest of my musings: There is going to be a very wide variety of outcomes, all of which are going to coexist. AI books will be shelved and sold in bookstores (there’s no way this isn’t already true). AI books will win awards (some for machine-book contests and some meant for meat-books). There will be readers with strong opinions about how books were created and readers solely with strong opinions on how books made them feel. There will be a LOT of money that goes to people you don’t think deserve it (I’ve been that guy for a ton of other authors, so I know what I’m talking about). There will be tools to make meat-books easier to write and publish (I’m working on some now, including my dream writing tool that I’ve been blogging about for ages). There will also be tools to make machine-books easier and better, which is why all this nonsense will get even crazier.

What will it mean for me? Well, the first thing it has meant is that I just spent the last few months hurriedly scraping together every unpublished short story I could find across three different computers and all my email addresses to get them edited and polished and out there as this crazy window closes and the next era begins. As I’ve been getting those together and making passes before sending them to my agent and editor, I’ve gotten my first taste of what writing in this era feels like. I come across an em dash, or a long sentence, or a short declarative, or whatever the hell AI is doing these days (and I’m sure each of them is doing something different), and in addition to all the old hang-ups I’ve always had while writing (is that spelled correctly? Am I a moron? Will anyone like this? Do I like this? Should I have taken the story a different direction?) I now have this one: Does that sound like AI?

And you know what? I think it often does. But then I have to remind myself that I don’t sound like AI, it sounds like me. I’ve been em-dashing and comma-splicing up a motherfucking storm these past two decades. It was fun then, and it’s fun now. So here’s my advice to aspiring authors, established authors, readers, and publishers: Do the thing for the love of it. Others will do it for clicks or money. Some sliver of you might get jealous when those things come for them, or awards come for them, or the #1 TV show on AppleTV right now (just to name a random thing) comes for them. Let that jealousy pass through you, not pool up inside you. Remember that you are doing it for the love. Go sit down and write.

Welcome to the new era, folks. It’s not as good as the last one, let me tell you. Best decade in the history of being an author. Until the crypto bros come and save us.

The Daily Front Page 11 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — One Model to Rule Them?
article

Everyone is building LLM routers, we deprecated ours

by brunaxLorax·▲ 130 points·85 comments·manifest.build ↗
We don’t believe in model routing anymore.

Change My Mind meme with the caption: LLM routing doesn't save money

We don’t believe in model routing anymore. For most use cases, sticking to a single battle-tested model is the best thing you can do.

Recently, there’s been huge hype around AI model routers that select the model that will respond to your request on the fly. There have been many launches in recent weeks with similar promises of reducing inference costs. We had our LLM router too, and decided to remove it.

Some context first: we launched the Manifest LLM router in March as a key feature in our LLM gateway, and we deprecated it in June, shutting it down for good on September 1st. Our router was classifying each request into one of four different tiers of complexity: simple, standard, complex and reasoning.

LLM router diagram: a single agent request fanning out to Anthropic, DeepSeek, OpenAI and Mistral models

Like most LLM routers, ours was made for cost reduction. Why call a powerful, and therefore expensive, model for a simple task? Routing to the most cost-effective model seems like a natural solution, right? Not that simple. After four months of usage across 7000 cloud users, we saw mixed results and a lot of GitHub issues and discussions about it. Let’s dive into the main problems.

Complexity cannot be deduced from the prompt alone

The prompt alone does not contain the whole task; it is just the trigger. A lot of the context that determines complexity is only discovered later through tool calls, web searches, and so on.

Let’s take an example: “evaluate the tests for the repo $GIT_REPO and improve them” can be a very simple task if you mention a personal website written in plain HTML5; or an incredibly complex task if you target the Linux kernel repo.

Cache is more effective than routing for reducing costs

Cache reads are between 75% and 90% cheaper than uncached inputs. System prompts and conversation history often represent a lot of tokens. Prefix cache works extremely well for those because they sit at the beginning of the prompt.

A cache-aware model router will take that into account by adding stickiness to the initially chosen model and keeps querying it. In other words, the router will do its job by, ironically, not doing it.

LLM routers break behavior consistency

Some say that “engineers should not be concerned about choosing the best LLM for their task”. Well, we strongly disagree.

Just as a painter knows exactly what brush they need to use, and the craftsman carefully chooses their tools, engineers should understand trade-offs and subtleties of the different models. At Manifest, every engineer selects models and effort parameters based on their intent.

Jumping from a model to another during working sessions results in lower quality of the overall work, and detaches the people from mastering their tools.

Unpredictability has a cost

No one likes unpredictability, especially software engineers.

In automated agentic workflows or autonomous agents, managing that extra layer of uncertainty can cost more than it saves. Think of evals, system prompts, observability and so on. Everything suddenly becomes harder to maintain.

Isolating different requests and setting up the right models, params and prompts for it seems naturally superior in most cases.

Conclusion

There are probably many use cases where LLM routing can be useful, and the companies that launched those have probably good reasons.

However, based on our experience, we’ve concluded that in most of the use cases we saw, it was not worth it. The amount saved is paid somewhere else, and that cost is harder to estimate.

The Daily Front Page 12 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Repository Desk: Multiplayer Agents
repository

qm – Multiplayer agent harness for work

by tosh·▲ 652 points·154 comments·github.com ↗
★ 5,574⑂ 578 forks TypeScript

Multiplayer agent harness for work

A multiplayer agent harness for work. In Slack and on the web.

The QM web UI: two concurrent sessions, a sidebar of personal files, crons, keychain, deploys, memory, and skills

What is QM?

Most agents are designed like personal assistants. You can make one work for a whole company, but it quickly gets complex. QM is designed for startups. Employees each get their own isolated workspace and work independently without affecting each other, and they can also collaborate with the agent in channels, group messages, and projects.

Each person and each room has its own scoped memory, files, keychain view, permissions, crons, web apps, and durable sandbox.

It's built with open source in mind. Pick your own harness and model and switch between them — Pi, OpenCode, Codex, and Claude Code all drive the same core, so a deployment isn't tied to any single vendor.

Features

  • Personal and shared scopes. People customize the agent to be theirs, and still work with it collaboratively in Slack channels and projects.
  • Slack and web. The same identity and configuration carries between Slack and the web app.
  • Admin control. Set org-level configuration, a security posture, and which harnesses and models are available.
  • Web apps. Spin up custom internal apps and publish them to the right people.
  • Shared skills. Skills are scope-owned and shareable by grant, with admin-gated promotion to the whole org and skill packs imported from git repositories.
  • Background work. Crons and watches run work while nobody's watching.

What you can do with it

  • Search internal notes, email, documents, databases, and the web together
  • Retrieve information from your company brain
  • Build internal apps, publish them to the right people, and keep their data current
  • Learn your writing voice from past sends, then triage your inbox on a schedule — labels and reply drafts included
  • Work in an existing repository: run tests, open PRs, monitor CI, check system logs
  • Track a project in a shared channel and post updates and follow-ups

Architecture

flowchart LR
  DB[("Postgres<br/>sessions · memory · queue")]

  subgraph CORE["Headless core"]
    API["API · identity · policy · scheduler"]
    LOOP["Agent loop<br/>(Pi, OpenCode, Claude Code)"]
    API <--> LOOP
  end

  SBX["Per-scope sandbox<br/>files · tools · logged-in services"]

  DB <--> API
  LOOP <--> SBX

Every turn runs through a central core, which can use a variety of models and harnesses to generate the response. A Postgres persistence layer holds user data, session history, and other durable state. The agent has a small, fixed tool surface; one of those tools is execute, which runs commands in the scope's own isolated sandbox — its durable computer, where installed tools stay installed. The web UI, the admin panel, and the public portal are optional plugins over the core's HTTP API; Slack is an optional in-process plugin that core starts and supervises through a direct service client.

The core runs TypeScript directly on Node and uses Fastify for HTTP. The Slack plugin uses Bolt; the web UI builds with Vite and renders with Lit.

The core itself is generic. Everything specific to one company — org config, custom tools and skills, sandbox image, infrastructure — lives in a deployment directory that the qm CLI validates and deploys. Every substrate (harness, session store, sandbox, memory) sits behind an interface, so production implementations swap in via one wiring file.

Security and secrets

QM's approach follows local coding agents like OpenCode, Codex, and Claude Code: the agent acts as the person it's working for, with their credentials and permissions, and everything it does is audited. An org picks one security posture, which narrower scopes can only tighten:

  • Strict — every harness tool call pauses for human approval, except the two no-effect turn enders.
  • Auto (default) — a classifier screens provenance-labelled external data and tool results before they reach the model; a deployment can point that at its own screening proxy.
  • Dangerous — no content screening, no pauses between tool calls.

The predeclared command policy — approval rules and hard denials for things like recursive deletes or destructive SQL — applies in every posture, Dangerous included.

SECURITY.md has the threat model, the operator assumptions, and the known limitations.

Deploy it for your org

Create an organization-owned deployment repository that depends on @yc-software/qm:

npm exec --yes --package=@yc-software/qm@latest -- \
  qm init . --org <slug> --target <fly-or-aws>
npm install

Initialization materializes a deployment skill for an agent and walks through infrastructure, web sign-in, connector credentials, optional Slack access, deployment, and live verification — no source checkout required. Each deployment runs in the operator's own cloud account; initialization does not generate or enable deployment CI, and this repository has no production deployment workflow. See deployment.md for the details.

Contributing

We take contributions as human-written text, not code — see CONTRIBUTING.md. Describe the change you'd like informally in a .txt or .md file in adrs/, and if we're aligned we'll handle the implementation. Report vulnerabilities privately — see SECURITY.md, not a public issue.

Customize your instance

The deployment repository above carries config and a sandbox layer, and never needs a source checkout. Some organizations want the opposite trade: the whole codebase in one place, so engineers and coding agents read core and customizations together, while the customizations themselves stay private. For that, keep a private fork: a standalone private repository whose history begins as a clone of qm and whose core stays identical to upstream.

Populate it once, then clone it to work in:

gh repo create <org>/qm-private --private

git clone --bare git@github.com:yc-software/qm qm-seed.git
git -C qm-seed.git push --mirror git@github.com:<org>/qm-private
rm -rf qm-seed.git

git clone git@github.com:<org>/qm-private
git -C qm-private remote add upstream git@github.com:yc-software/qm

Create the private fork with a plain clone, as shown above, and never with GitHub's fork feature. The word "fork" here names the concept — a downstream copy that diverges deliberately and merges from upstream — not GitHub's Fork button. A GitHub fork inherits the visibility of the repository it came from, so a fork of a public repository cannot be made private. A GitHub fork also shares one object network with the repository it came from, so commits pushed to the fork stay fetchable by SHA from the public side. Many organizations disallow forking private repositories as well. A plain clone has none of these problems, and it costs one thing: the clone is an ordinary repository, so upstream's CI workflows run live in your own account. Expect to supply the secrets those workflows need, or disable the ones you do not want running.

Everything specific to your organization goes in deploy/layers/<org>/ — config, sandbox tools and skills, plugin images, infrastructure — in the same shape qm init produces. See deploy/layers/README.md. Core stays byte-identical to upstream, which is what keeps merges small.

Two skills maintain the boundary in both directions. update-qm merges upstream qm into the private fork and opens the sync PR; upstream-pr sends an organization-agnostic fix back to qm, cutting the branch from upstream/main and checking the outgoing diff, commit messages, and screenshots for organization identifiers before it pushes. Nothing under deploy/layers/ ever travels upstream.

Going deeper

License

Except where otherwise noted, QM is available under the MIT License.

The Daily Front Page 13 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Public Health Docket
article

Big Food vs. the People

by jruohonen·▲ 267 points·174 comments·lighthousereports.com ↗
We expose Big Food’s 239 lawsuits against life-saving policies

We expose Big Food’s 239 lawsuits against life-saving policies

Bad diets kill millions of people globally every year, and lead to tens of billions of dollars in health costs, often placing the heaviest burden on communities who are least able to afford it. Children, with limited decision-making power, are particularly vulnerable to this.

Legislators and government officials have worked to rein in this public health crisis by enacting laws that require food companies to be transparent about their ingredients and limit the advertising of unhealthy foods.

In public, the world’s biggest and richest food companies such as Coca Cola, PepsiCola, and Mondelez say they want to be part of the solution. But behind closed doors, they have taken governments to court to delay, dilute, and derail public health laws, which the companies say violate their rights.

This cross border collaboration with health academics and an international media coalition shed light on how transnational corporations use legal tactics – lawsuits and legal threats – to stymie public health efforts in six countries where such tactics are most apparent: Mexico, Brazil, Colombia, India, Britain, and the United States.

Credit: Revisual Labs for Lighthouse Reports

We found:

– 239 lawsuits were filed between 2010 and 2025 across Mexico, Colombia, Brazil, the US, the UK, and India against public health policies targeting food and beverages such as front of pack labelling, regulating advertising junk food to children, soda taxes, and taxes on ultra processed foods.

– The cases add up to 595 years of litigation, representing a significant burden on the governments defending their health policies.

– Of the cases brought by private companies where the plaintiff was identifiable, more than 1 in 3 came from just nine parent groups, led by Coca-Cola, PepsiCo, and Mondelez

Their actions are not only prolonging the public health crisis but also cost countries billions of dollars in both legal and healthcare costs. In addition, they have a chilling effect on policymakers that wish to better their citizens’ health but do not have the resources to engage in drawn-out legal fights with food companies.

Credit: Revisual Labs for Lighthouse Reports

METHODS

We constructed a dataset of challenges to laws aiming to improve population nutrition between 2010 and 2025 in these countries: Mexico, Colombia, Brazil, U.S., UK, and India.

With the exception of India, cases were included when a regulation that sought to improve public health through better nutrition was being challenged (i.e. validity, scope, or implementation). We made an exception for cases in India where influencers were being sued, as they were taking over the role of the government in making the nutritional value of products more transparent.

We only included cases that were verifiable through official legal databases, court records, or reputable secondary sources.

Credit: Revisual Labs for Lighthouse Reports

We excluded cases solely concerning non-manufacturers such as fresh produce or when companies were appealing fines and other judgments unrelated to public health policies.

This research was done in collaboration with researchers from the University of Caldas (Colombia); Robert & Ethel Kennedy Human Rights Center (United States); the University of Sao Paulo (Brazil); and the University of Sydney (Australia). They found more lawsuits through a systematic legal search in Colombia, Brazil and Mexico.

An upcoming academic paper will be published alongside the media articles, looking at the dataset from a scientific perspective.

This dataset built on the work and insight of El Poder del Consumidor (Mexico), the FULL database developed by the Global Center for Legal Innovation on Food Environments at the O’Neill Institute and the Global Health Advocacy Incubator, CAJAR (Colombia), and ACT (Brazil).

STORYLINES

– Mexico

More than a third of Mexican schoolchildren and 41% of adolescents were overweight according to a 2022 national survey.

This is the country where we found the most lawsuits (193 out of 239), many of them were against the country’s labelling regulation. Quinto Elemento Lab reveals the companies’ arguments: that the laws were a violation of their constitutional rights and that the measures “demonized” their products or violated consumer rights. For example, a local Pepsi bottler argued that in certain rural areas of Mexico, it was safer to drink soft drinks than the available water. In court, the judges rejected many of the legal arguments that the companies presented.

Credit: Revisual Labs for Lighthouse Reports


– Brazil

According to the UN, in 2022-2024, more than 1 in 4 adults in Brazil were living with obesity while nearly 1 in 9 children were overweight.

Some of the 17 lawsuits in Brazil have been dragging on for close to a couple of decades, with no predicted conclusion in sight, according to Agência Pública and O Joio e O Trigo.

In all but one, the plaintiffs were industry associations, which experts told us is a way for companies to keep their valuable brand names away from litigation that may harm their image. Some of the world’s largest food companies are members of these Brazilian associations, including Coca-Cola, Ferrero, Kellogg’s local brand, Mars, Mondelez, Nestlé, and PepsiCo. 11 of the lawsuits were against the Brazilian Health Regulatory Agency, ANVISA, which has been hamstrung as a result. The plaintiffs disputed a regulation that required the advertising of food and beverages with low nutritional value to display more information.


– Colombia

More than half of Colombia’s population is overweight, and treating the diseases caused by unhealthy foods cost the country an estimated 1.3 billion euros in 2021.

We found 18 lawsuits in Colombia, mostly targeting taxes on unhealthy products and front-of-package labelling. Nearly all of them were constitutional challenges brought about by individual citizens as enabled in the country’s constitution. However, Cuestión Pública finds that many of the plaintiffs were lawyers who had done work for food companies. The submissions also used many of the same arguments wielded by the companies. In 2022, while the creation of the health tax was being debated, sugary beverage and ultra-processed food companies donated 5.85 million Euros to political parties, accounting for 40% of all donations to political parties that year.


– U.S.

Two in three adults in the U.S. are overweight and a quarter of adolescents are living with obesity, according to the CDC. The Make America Healthy Again movement supported Trump’s presidency bid.

The American Beverage Association sued to overturn a soda tax in Santa Cruz, which it has so far failed to do. Santa Cruz Local uncovers details of the group’s earlier success squashing a soda tax effort in Watsonville, a neighbouring city. The Santa Cruz lawsuit has far-reaching consequences: the recent victory for the city could unlock the ability for charter cities across California to tax sugary beverages. We found that the soda industry recruited and leveraged the credibility of prominent Black and Latino leaders to amplify opposition against public health taxes within their own communities. We have also identified five other lawsuits. The ABA was involved in four of them.

Credit: Revisual Labs for Lighthouse Reports


– Europe

In Europe, non-communicable diseases (NCDs) such as cardiovascular diseases, diabetes, or cancer are responsible for 80% of the disease burden, according to the European Commission.

Follow The Money discovers that European countries have struggled to pass taxes on sugar-sweetened beverages, as they come under industry pressure. Unlike in Latin America, European governments frequently face legal threats before legislation is adopted. Industry groups repeatedly invoke EU state-aid, competition and internal-market rules, creating uncertainty that can delay or derail policies without a single lawsuit being filed. Plans for a joint European sugar tax have also been weakened.

L’Espresso and Il Fatto Alimentare explain the role Italian confectionary giant Ferrero plays around the world in stimying public health laws, and how the family-owned company’s influence both domestically and internationally is connected to the country’s agroindustry rather than its famous cuisine.


– England

As of 2024, 66% of adults and 26% of children in England were either overweight or living with obesity, according to the National Health Service.

In 2021, a year before Kellogg’s sued – and lost – against the country’s nutrient profiling model contained in the Food (Promotion and Placement) (England) Regulations 2021, it sent a pre-action letter to the Department of Health and Social Care, claiming the health rating of its cereals should be measured with the milk it is usually consumed with.

A mere two weeks before the ruling against Kellogg’s, Ferrero and Eat Natural, a subsidiary of Ferrero, also sent pre-action letters over the same regulations.


– India

Nearly one in three Indian women and more than one in four men are overweight or obese. One in five people have high blood sugar levels.

The Wire traces the long journey of India’s front of pack labeling regulation, which the Indian food regulator, the FSSAI, has been developing since 2014. Over that time, it has done numerous consultations, studies but it has stalled regulating, blaming a lack of consensus between the industry and civil society. Research by ATNi commissioned by Lighthouse Reports showed that India’s proposed labelling system is actually consistently more lenient compared to similar systems in Australia and France.

Meanwhile, influential instagram celebrities have made videos comparing the nutrition of various products such as instant noodles, baby food, and others. They’ve been sued by the companies whose ingredient labels they were analysing.

The Daily Front Page 14 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Twenty-Five Gigabits or Bust
article

Getting 25 Gbps Thunderbolt Ethernet on My Mac Studio

by speckx·▲ 228 points·113 comments·jeffgeerling.com ↗
But... I want more.

Mac Studio with all ports plugged in Ethernet 10G

I've been using the built-in 10 Gigabit Ethernet on my Mac Studio for a few years. It works fine: I can edit 4K video straight off my NAS over the network, and run backups at around 1 GB/sec.

But... I want more. I upgraded my rack and my NAS to 25 GbE a couple years ago, and wanted to upgrade my main workstation, too.

I looked up 25G networking options for the Mac, and they're all crazy expensive:

The problem is Macs all require Thunderbolt adapters; you can't just plug an inexpensive(ish) PCIe card into a Mac (RIP Mac Pro).

I stopped looking until I saw this blog post. Christian Kohlschütter found a cheap 25G Thunderbolt adapter that uses a server-pulled OCP 2 NIC with a little Thunderbolt 3 adapter board. And it works on any computer with Thunderbolt.

I paid $166.71 in January for the 25G Thunderbolt NIC

Back in January, it was only $160, which was insta-buy territory for me. Since that time, the Amazon listing jumped to $299, which still might be good compared to the Sonnet... but you might have to go digging through some Chinese sites to find a non-marked-up version now.

This blog post is a companion to the following YouTube video:

First Test - Two Problems to Solve

Once I had pulled some new fiber to my desk (where I only had Cat6A cabling before), I tested the bandwidth using iperf3 between my Mac and my NAS. That's when I ran into two problems:

  1. The version of iperf3 I was running on the NAS was too old. Without multi-threading, it maxed out at 15 Gbps.
  2. The 25G NIC enclosure was getting hot. Painful to the touch.

I could solve the first problem easily: I compiled the latest version of iperf3. That got me to 20 gigabits, since more than one CPU core could hand the transfers on my NAS. As Christian mentioned in his blog post, 20 Gpbs single direction and 25 Gbps bidirectional is about the limit for the Thunderbolt 3 chipset being used (even if you plug into a Thunderbolt 5 port).

Thunderbolt 3 to PCIe NIC OCP 2 adapter board

But the second problem was more tricky.

The burning-hot enclosure wasn't thermally bonded to the OCP 2 network card, meaning the NIC chips were cooking.

They had tiny heatsinks on them, but OCP 2 NICs are meant to be inside servers with high pressure fans, not in a little passively-cooled enclosure.

It was acting like a little oven.

Fixing the NIC's cooling problem

Christian mentioned he slapped a couple giant heatsinks on the enclosure. That brought the chip down to a temperature that wouldn't cause NIC dropouts, but it was still getting pretty hot.

I wanted to make sure things were stable, and that meant active cooling.

My first idea was to stick on these low-profile heatsinks and set this speed-controlled USB fan in front. I had to remove the enclosure's barely-ventilated front plate to get more airflow inside, but the back plate also created a ton of resistance.

It would still get hot, and the fan was just loud enough to be distracting, even on its lowest setting.

A Prusa rep had reached out around this time asking if I had any use for their new Prusament PLA in Noctua Brown and Beige... and I decided to switch tracks once they offered to send a spool of each. I purchased a Noctua NF-A8 80mm 5V fan, and an NA-FC1 Speed Controller to silence it.

I designed a fan duct for the 25G Thunderbolt NIC enclosure, and printed it in Noctua beige PLA.

Then I printed this airflow-optimized 80mm fan grill, and screwed that on the front of the 80mm fan.

I was able to use the screws from the 25G NIC enclosure (I removed the front plate entirely), and the fan screws and extra cable that came with the Noctua 80mm fan, to secure everything together.

I chose to splice the braided fan extension cable Noctua includes, and taped it down inside the enclosure with kapton tape for some strain relief.

I soldered the cut end of the fan cable into these through-holes on the Thunderbolt-to-OCP adapter PCB to get the needed 5V power (well, 4.8V, but it's close enough):

Soldering the fan connector on the NIC adapter board for 5V power

The fan only used about 0.5W of power, so I don't think it'll cause any brownout conditions on the NIC itself (which uses 4-5W total at idle).

After final assembly, this is what it looks like:

Holding the finished NIC with a Noctua 80mm fan and grill cover

I plugged it in, re-tested with iperf3, and checked the temperature. It was sitting at less than 36°C after 10 minutes, with the fan on low. And this being a Noctua fan, I couldn't hear it at all under the desk.

How does 25 Gbps perform on the Mac?

Like earlier, it maxes out around 20-25 Gbps, because of the slower Thunderbolt 3 connection.

Activity Monitor showing 1.43 GB/sec on macOS with 25G NIC

Testing Samba file copies between my NAS and my Mac, I got around 1.4 GB/sec read, and 1 GB/sec write1.

That's only marginally better than the built-in 10G Ethernet. It's an improvement, sure... but was all the work pulling fiber, designing a fan cowling, paying $200 for all the parts, and assembling everything worth it?

Maybe. At least I got this blog post out of it.


  1. I have SMB multichannel enabled, but this real-world speed limitation could also be related to my use of my lower-power Arm NAS (running on Ampere Altra, with 32 fairly-slow CPU cores). I was testing writing to an array of fast enterprise NVMe SSDs, so they shouldn't be the bottleneck here. ↩︎
The Daily Front Page 15 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Graphs on a Diet
article

Algorithms on billion-scale graph using 10GB RAM: I love DataFusion

by speckx·▲ 132 points·39 comments·semyonsinchenko.github.io ↗
I implemented a graph map-reduce using Apache DataFusion.

TLDR;

I implemented a graph map-reduce using Apache DataFusion. Where possible, I offloaded everything to disk, and designed the algorithms to rely on bulk scans rather than random access. DataFusion handles spillover, sort-merge joins, aggregations, planning and execution, so my code is very lightweight. I tested it in strict mode by running it via systemd-run with a hard memory limit. It works. Of course, I have encountered some issues: for example, I frequently experience deadlocks from FairSpillPool in extreme scenarios, and I have not yet found a way to make SMJ use pre-sorting of the data on disk. But it works. I can compute PageRank on a directed graph with one billion edges (graph500-26 from the Graphalytics dataset) using 5 GB of memory. Alternatively, I can identify all the weakly connected components in a graph with two billion edges (twitter_mpi from the same dataset collection) using 10 GB of memory. Neither NetworkX nor Igraph can do this; most existing graph algorithms require the graph to fit into memory. Previously, I thought you needed Apache Spark and GraphFrames for billion-scale graph analytics. Now, however, I think all you need is a laptop. I have completely changed my old opinion about using Apache DataFusion for graph analytics.

Setup

I tested two tasks.

PageRank

What is PageRank?

The task is to compute PageRank on graph500-26 from Graphalytics dataset:

Key Value
Num nodes 32,804,978
Num edges 1,051,922,853
Directed False
Memory Limit 5 GB
DataFusion Pool Size 4 GB

PageRank is one of the most popular graph centrality algorithm and is used from search results ranking to anti-fraud scoring. My DataFusion implementation is classical Pregel: bulk-synchronous parallel algorithm (aka Map-Reduce) which I expressed using joins and aggregate. Very similar to what is in the core of Spark's GraphFrames library.

Weakly Connected Components

What are Weakly Components?

The task is to identify all the weakly connected components on twitter_mpi from the same dataset:

Key Value
Num nodes 52,579,682
Num edges 1,963,263,821
Directed True
Memory Limit 10 GB
DataFusion Pool Size 8 GB

WCC is the core part of any identity (entity) resolution problem. For example, when you need to do data deducplication from different system through transitive IDs you end up with WCC problem. My DataFusion implementation is based on the "In-database connected component analysis", Bögeholz et al., arXiv 1802.09478. I already implemented the same algorithm for the Spark's GraphFrames so it was an obvious choice.

Results

PageRank

An easy part. I used SMJ just to proove the scalability but it is also possible to use HJ because vertices are small (32M) and PageRank state is trivial: one column rank (f64), one column out-degree (i64), on participation flag (bool). With HJ it is faster. PageRank works over directed edges so it deos not require to symmetrize the graph. Just offload edges to disk and iterate by updating state (and offload to disk as well to break the lineage) until converged.

The compute time is long: around 30 minutes for 15 full iterations. But the setup is about memory, not speed. Give it some more realistic numbers for 1B graph analytics and it will work fast enough (I tested). I checked numbers against the ground truth: 100% match (with 0.0001 tolerance). A lot of optimizations can be done here as well: in theory it is possible to bucket edges by range or do kind of range-partitioing, so the SMJ does not need to sort again the biggest join-side (edges) on each iteration to get triplets. As well I'm not 100% parquet is the best choice here. Also would be interesting to try to fuse join+agg: each Pregel iterarion is like edges <-[join] nodes-state -> group by + agg -> [join] -> nodes-state -> update nodes-state. If I can fuse together the first two stages it can be a huge win from the performance point of view. Meanwhile I do not know yet how to do it in DataFusion: a lot of thing to learn.

WCC

The hardest part. 2B edges twitter graph is already huge (its edges are 30 GB in CSV !!!). But for WCC we need to symmetrize edges (or do a union between src, dst and dst AS src, src AS dst + distinct on top), so at the peak we are crunching almost 4 billion of edges using only 8GB DataFusion pool. After the flow survives the first few iteration, contraction process reduce the amount of edges dramtically and algorithm ends in 10 minutes with low memory pressure.

sem@fedora:~/github/graphframes-rs$ systemd-run --user --scope \
     -p MemoryMax=10G -p MemorySwapMax=0 \
     -p AllowedCPUs=0-1 \
     --setenv=RUST_LOG=graphframes_rs=info,datafusion=warn \
     ./target/release/run-algorithm twitter_mpi-v.parquet twitter_mpi-e.parquet wcc 42 file:///var/home/sem/Downloads/gf_wcc_out 8G 2
Running as unit: run-p316509-i284528.scope; invocation ID: 742f9296d31d426580b7ec8213422cf1
[2026-07-05T05:37:21Z INFO  graphframes_rs::algorithm::connectivity::connected_components] start WCC with run-id 017c0a23-2b20-4ffa-ac6b-6e2cb8d7203e
[2026-07-05T05:52:21Z INFO  graphframes_rs::algorithm::connectivity::connected_components] after preparation graph has 3228212374 edges
[2026-07-05T06:13:21Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 1, edges remaining: 840238268
[2026-07-05T06:17:39Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 2, edges remaining: 77322906
[2026-07-05T06:17:57Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 3, edges remaining: 5624128
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 4, edges remaining: 1075998
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 5, edges remaining: 230838
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 6, edges remaining: 97940
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 7, edges remaining: 42352
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 8, edges remaining: 16720
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 9, edges remaining: 8238
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 10, edges remaining: 3860
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 11, edges remaining: 1488
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 12, edges remaining: 982
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 13, edges remaining: 514
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 14, edges remaining: 132
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 15, edges remaining: 120
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 16, edges remaining: 40
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 17, edges remaining: 18
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 18, edges remaining: 10
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 19, edges remaining: 6
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 20, edges remaining: 4
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 21, edges remaining: 2
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc forward iteration 22, edges remaining: 0
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=21
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=20
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=19
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=18
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=17
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=16
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=15
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=14
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=13
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=12
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=11
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=10
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=9
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=8
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=7
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=6
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=5
[2026-07-05T06:17:59Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=4
[2026-07-05T06:18:00Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=3
[2026-07-05T06:18:01Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=2
[2026-07-05T06:18:12Z INFO  graphframes_rs::algorithm::connectivity::connected_components] cc back propagation step t=1
[2026-07-05T06:18:23Z INFO  graphframes_rs::algorithm::connectivity::connected_components] connected components written to file:///var/home/sem/Downloads/gf_wcc_out after 22 forward iterations
num-iterations: 22

Results are correct: Graphalytics provides ground truth and it is easy to check:

memory D SELECT column1, count(*) as cnt FROM read_csv('twitter_mpi-WCC', delim=' ') GROUP BY column1 ORDER BY cnt DESC LIMIT 5;
┌──────────┬──────────┐
│ column1  │   cnt    │
│  int64   │  int64   │
├──────────┼──────────┤
│        1 │ 52515193 │
│ 27052874 │       67 │
│ 47269046 │       44 │
│ 45352761 │       33 │
│ 17516773 │       30 │
└──────────┴──────────┘
memory D SELECT component, count(*) as cnt FROM results  GROUP BY component ORDER BY cnt DESC LIMIT 5;
┌───────────┬──────────┐
│ component │   cnt    │
│   int64   │  int64   │
├───────────┼──────────┤
│         1 │ 52515193 │
│  27052874 │       67 │
│  47269046 │       44 │
│  45352761 │       33 │
│  17516773 │       30 │
└───────────┴──────────┘
memory D

The code

The code is here: https://github.com/SemyonSinchenko/graphframes-rs

I wrote most of the code by myself, not "Claude do it, make no mistakes", so there is no README and code comments can be outdated somewhere. I'm learning Rust and DataFsuion on this project, so no LLM usage in core parts.

Graph representation is almost like in Spark's GraphFrames:

#[derive(Debug, Clone)]
pub struct GraphFrame {
    pub(crate) vertices: DataFrame,
    pub(crate) edges: DataFrame,
}

Core parts:

  1. Pregel main loop: pregel.rs
  2. WCC implementation: connected_components.rs

To understand the Pregel notation this can be very useful source: Lecture 8, CME 323: Distributed Algorithms and Optimization

The Pregel paradigm’s data flow model

An example of PageRank using the API: pagerank.rs

The Daily Front Page 16 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Java’s Value Moment
repository

JEP 401: Value Objects (Preview) merged to OpenJDK master

by mfiguiere·▲ 236 points·164 comments·github.com ↗
★ 23,176⑂ 6,395 forks Java

JDK main-line development https://openjdk.org/projects/jdk

This pull request implements the first preview of JEP 401: Value Objects (Preview). This pull request also includes the implementation of JEP 539: Strict Field Initialization in the JVM (Preview). That work was implemented in the same code base because JEP 401 depends on strict field initialization.

Sub-reviews, for spreading review debates are here:

This is the "master pull request" for the initial preview of JEP 401. Comments and review for a change this large will not scale well in a single pull request. This pull request serves as the vehicle for sign-off and integration into jdk/master. Review comments should be directed to the appropriate "sub-review pull request" listed above.

Note

The "sub-review pull requests" contain the same full set of code changes as this "master pull request" to preserve the full implementation context; the language compiler, JVM, and standard library changes are intertwined. The separate pull requests exist only to subdivide the review and related discussion by area.

Any resulting code changes should be made in valhalla/lworld.

valhalla/lworld is currently updated from jdk/master whenever a weekly jdk tag is created. At that time, code changes from valhalla/lworld will be propagated to this pull request and to all sub-review pull requests.

Ultimately, review sign-off will be recorded on this "master pull request", and the "sub-review pull requests" will be closed without integration.

This pull request has a large surface area and frequently conflicts with jdk/master. Refer to valhalla/lworld for the latest state of the project code, keeping in mind that it may lag several days behind jdk/master. Both repositories may be needed as references during review.


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (2 reviews required, with at least 1 Reviewer, 1 Author)

Issues

  • JDK-8389219: Implement JEP 401: Value Objects (Preview) (Enhancement - P4)
  • JDK-8389220: Implement JEP 539: Strict Field Initialization in the JVM (Preview) (Enhancement - P4)

Reviewers

Contributors

  • David Simms <dsimms@openjdk.org>
  • Dan Smith <dlsmith@openjdk.org>
  • Frederic Parain <fparain@openjdk.org>
  • Tobias Hartmann <thartmann@openjdk.org>
  • Roger Riggs <rriggs@openjdk.org>
  • Vicente Romero <vromero@openjdk.org>
  • Axel Boldt-Christmas <aboldtch@openjdk.org>
  • Karen Kinnear <acorn@openjdk.org>
  • Alex Menkov <amenkov@openjdk.org>
  • Bill Huang <bhuang@openjdk.org>
  • Benoît Maillard <bmaillard@openjdk.org>
  • Christian Hagedorn <chagedorn@openjdk.org>
  • Chris Plummer <cjplummer@openjdk.org>
  • Casper Norrbin <cnorrbin@openjdk.org>
  • Coleen Phillimore <coleenp@openjdk.org>
  • David Beaumont <dbeaumont@openjdk.org>
  • Daniel D. Daugherty <dcubed@openjdk.org>
  • Damon Fenacci <dfenacci@openjdk.org>
  • David Holmes <dholmes@openjdk.org>
  • Dmitry Samersoff <dsamersoff@openjdk.org>
  • Eric Caspole <ecaspole@openjdk.org>
  • Evgeny Nikitin <enikitin@openjdk.org>
  • Ekaterina Pavlova <epavlova@openjdk.org>
  • Fei Yang <fyang@openjdk.org>
  • Dan Heidinga <heidinga@openjdk.org>
  • Harold Seigel <hseigel@openjdk.org>
  • Ioi Lam <iklam@openjdk.org>
  • Ivan Walulya <iwalulya@openjdk.org>
  • Jatin Bhateja <jbhateja@openjdk.org>
  • Jan Lahoda <jlahoda@openjdk.org>
  • Jim Laskey <jlaskey@openjdk.org>
  • John R Rose <jrose@openjdk.org>
  • Joel Sikström <jsikstro@openjdk.org>
  • Lois Foltan <lfoltan@openjdk.org>
  • Chen Liang <liach@openjdk.org>
  • Leonid Mesnik <lmesnik@openjdk.org>
  • Matias Saavedra Silva <matsaave@openjdk.org>
  • Marc Chevalier <mchevalier@openjdk.org>
  • Mandy Chung <mchung@openjdk.org>
  • Maurizio Cimadamore <mcimadamore@openjdk.org>
  • Markus Grönlund <mgronlun@openjdk.org>
  • Manuel Hässig <mhaessig@openjdk.org>
  • Nick Gasson <ngasson@openjdk.org>
  • Patricio Chilano Mateo <pchilanomate@openjdk.org>
  • Paul Hübner <phubner@openjdk.org>
  • Paul Sandoz <psandoz@openjdk.org>
  • Quan Anh Mai <qamai@openjdk.org>
  • Roberto Castañeda Lozano <rcastanedalo@openjdk.org>
  • Roland Westrelin <roland@openjdk.org>
  • Srikanth Adayapalam <sadayapalam@openjdk.org>
  • Aleksey Shipilev <shade@openjdk.org>
  • Stefan Johansson <sjohanss@openjdk.org>
  • Sergey Kuksenko <skuksenko@openjdk.org>
  • Serguei Spitsyn <sspitsyn@openjdk.org>
  • Stefan Karlsson <stefank@openjdk.org>
  • Thomas Stuefe <stuefe@openjdk.org>
  • Thomas Schatzl <tschatzl@openjdk.org>
  • Zoltan Majo <zmajo@openjdk.org>
  • Alan Bateman <alanb@openjdk.org>
  • Jaikiran Pai <jpai@openjdk.org>
  • Martin Doerr <mdoerr@openjdk.org>
  • Richard Reingruber <rrich@openjdk.org>
  • Feilong Jiang <fjiang@openjdk.org>
  • Daisuke Yamazaki <dyama@openjdk.org>

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/31120/head:pull/31120
$ git checkout pull/31120

Update a local copy of the PR:
$ git checkout pull/31120
$ git pull https://git.openjdk.org/jdk.git pull/31120/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 31120

View PR using the GUI difftool:
$ git pr show -t 31120

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/31120.diff

Using Webrev

Link to Webrev Comment

The Daily Front Page 17 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Web Components, Progressively
article

Progressive Web Components

by hosteur·▲ 190 points·55 comments·arielsalminen.com ↗
The HTML + CSS first approach and JS only for enrichment sounds great

I’ve worked with web components for nearly a decade and built various enterprise-scale design systems with them. While I love what they offer on paper, the same pain points keep coming back:

Layout shifts, flash of unstyled content, poor server-side rendering support, too much reliance on client side JavaScript, doesn't play well with frame­works like React Server Components, accessibility issues, and so on…

Despite all of this, I still think web components are a great foundation for a design system. No other approach gives you true cross-framework portability built on what the web platform already provides. The problem isn’t necessarily the model itself, it’s how we’ve been building them.

This is how I ended up creating Elena, a library that I’m open sourcing today. Elena starts from HTML and CSS, and stays grounded in web standards and what the web platform natively provides.

What is a Progressive Web Component?

A Progressive Web Component is a native Custom Element designed in two layers: a base layer of HTML and CSS that renders immediately, without JavaScript, and an enhancement layer of JavaScript that adds reactivity, event handling, and more advanced templating. There are three types of Progressive Web Components:

  1. Composite Components that wrap and enhance the HTML composed inside them. All of their HTML and CSS lives in the Light DOM. You could also call these HTML Web Components.
  2. Primitive Components that are self-contained and render their own HTML. All of their CSS lives in the Light DOM together with the base HTML required for rendering the initial state.
  3. Declarative Components that are a hybrid of these and utilize Declarative Shadow DOM.

A diagram explaining the three types of Progressive Web Components.

Note: Elena doesn’t force this taxonomy. They’re all just web components, and you choose how to build yours. But since “Progressive Web Components” is a design philosophy rather than a library feature, understand­ing the distinction between these approaches helps when deciding what fits your use case.

So what is Elena, anyway?

Elena is a simple, tiny library (2.6kB) for helping you to build Progressive Web Components. Unlike most web component libraries, Elena doesn’t force JavaScript for everything. You can load HTML and CSS first, then use JavaScript to progressively add interactivity.

I built Elena for teams creating component libraries and design systems. If you need web components that work across multiple frameworks, render HTML and CSS before JavaScript loads, and sidestep common issues like accessibility problems, server-side rendering limitations, and layout shifts, Elena is built for exactly that.

It handles the cross-framework complexity (prop/attribute syncing, event delegation, framework compatibility) so you can focus on building web components rather than plumbing.

A screenshot of Elena’s lifecycle documentation. A screenshot of Elena’s lifecycle documentation.

Since Progressive Web Components is a design philosophy rather than a library feature, Elena also allows you to build regular web components. The full standard custom element lifecycle and features such as open or closed Shadow DOM, <template>, <slot> and even Declarative Shadow DOM are all supported out of the box.

What is Elena’s approach to SSR?

Elena’s approach to server-side rendering is simple and straightforward. Since Progressive Web Components are primarily HTML and CSS, you don’t need any special logic on the server to render them.

Components without a render() method are fully SSR-compatible by default, while components with render() provide partial support and complete hydration on the client side.

The “partial support” bit for the latter means that you can render the initial state without JavaScript, but JS is needed for the interactivity (unless you also use the provided @elenajs/ssr tool).

Elena also supports Declarative Shadow DOM for cases where you may need stronger isolation, but still want the component to render server-side.

Elena comman line interface A screenshot of Elena’s command line interface.

Release candidate is out today!

I’m super happy to announce the first seventh release candidate of Elena, v1.0.0-rc.7 today. This release comes with a bunch of useful features aimed at product teams creating component libraries. 🎉

Feature highlights include:

🔋 Extremely lightweight

2.6kB minified and compressed, simple and tiny by design.

📈 Progressively enhanced

Renders HTML and CSS first, then hydrates with JavaScript.

🫶 Accessible by default

Semantic HTML foundation with no Shadow DOM barriers.

🌍 Standards based

Built entirely on native custom elements and web standards.

⚡ Reactive updates

Prop and state changes trigger efficient, batched re-renders.

🎨 Scoped styles

Simple and clean CSS encapsulation without complex workarounds.

🖥️ SSR friendly

Works out of the box, with optional server-side utilities if needed.

🧩 Zero dependencies

No runtime dependencies, runs entirely on the web platform.

🔓 Zero lock-in

Works with every major framework, or no framework at all.

Included packages

Elena is divided into 13 npm packages published under the @elenajs scope. These are the main packages intended for development:

Next steps

Learn more Elena on GitHub

The Daily Front Page 18 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — The Smallest Htmx
article

Let's make the worst Htmx

by RebelPotato·▲ 136 points·67 comments·zserge.com ↗
frontend library for backend developers who couldn’t care less about JavaScript

This is a continuation of a series of posts about building tiny “clones” of popular web frameworks.

Web is changing rapidly, and so the tools we use to build web apps (yet, I’m sincerely happy that my React/Vue posts are now seven years old, and those frameworks are still popular and relevant).

Today the hot topic is htmx, basically a frontend library for backend developers who couldn’t care less about JavaScript. HTMX started as intercooler, then evolved and grown more features, and is there is some beauty to it:

<button hx-post="/clicked"
    hx-trigger="click"
    hx-target="#parent-div"
    hx-swap="outerHTML">
    Click Me!
</button>

In a declarative manner, it says: when the button is clicked, send a POST request to /clicked, and replace the outer HTML of the element with id #parent-div with the response data. HTMX wires together events, AJAX requests, and DOM updates hiding away frontend complexity. As long as your backend can send HTML templates you can go pretty far with it and build complete apps without a single line of JavaScript.

Method, trigger, target, swap

Looking at the example above, seems like our library should fetch() some URLs based on some triggers and update (swap) contents of some target elements. Something like this:

<button x-get="/click">Click me</button>

document.querySelectorAll('[x-get]').forEach(el => {
    el.addEventListener('click', async (e) => {
        e.preventDefault();
        const url = el.getAttribute('x-get');
        const response = await fetch(url);
        const data = await response.text();
        el.outerHTML = data;
        console.log(data);
    });
});

If our backend sends “Button clicked” in response - it should work. Open a page, click the button, buttons disappears, text is shown instead. That’s HTMX in 10 lines of code.

A few things to improve here. First, it’s nice to sanitise the response to make sure it’s HTML. Second, we should allow the user to specify a target element to swap content into. Third, we should allow the user to specify how exactly to swap the content in a target: replace, append, prepend, delete the element etc:

const attr = (el, name) => el.closest(`[${name}]`)?.getAttribute(name);

const SWAP = {
  outerHTML: (t, f) => t.replaceWith(f),
  beforebegin: (t, f) => t.before(f),
  afterbegin: (t, f) => t.prepend(f),
  beforeend: (t, f) => t.append(f),
  afterend: (t, f) => t.after(f),
  delete: t => t.remove(),
  none: () => {},
};

const swap = (mode, target, html) => {
  const tpl = document.createElement('template');
  tpl.innerHTML = html;
  (SWAP[mode] || ((t, f) => t.replaceChildren(f)))(target, tpl.content);
};

Now we can write a more generic “fetcher” function to handle different methods, triggers, targets and swap modes:

const send = async (el, method, url) => {
  const sel = attr(el, 'x-target');
  const target = sel ? document.querySelector(sel) : el;
  const mode = attr(el, 'x-swap') || 'innerHTML';
  const opts = { method: method.toUpperCase(), headers: { 'X-Request': 'true' } };
  if (el.matches('form')) opts.body = new URLSearchParams(new FormData(el));
  const res = await fetch(url, opts);
  swap(mode, target, await res.text());
};

By default we update the content of the element, unless x-target or x-swap is specified. We also follow HTMX convention and send a custom header to the backend with the request. To bind it all together we add a simple event listener than dispatches the request based on the trigger:

const METHODS = ['get', 'post', 'put', 'patch', 'delete'];

const defaultTrigger = el =>
  el.matches('form') ? 'submit' : el.matches('input,select,textarea') ? 'change' : 'click';

const scan = (root = document.body) =>
  METHODS.forEach(m =>
    root.querySelectorAll(`[x-${m}]`).forEach(el => {
      if (el.$hx) return;
      el.$hx = true;
      const evt = attr(el, 'x-trigger') || defaultTrigger(el);
      el.addEventListener(evt, e => {
        e.preventDefault();
        send(el, m, el.getAttribute(`hx-${m}`));
      });
    })
  );

scan();
// we should also call scan() after each 
// swap() inside send() at the end of it

And that’s it, in 40 lines of code we get a working HTMX clone that support all HTTP methods, custom targets and swapping strategies. A small optimisation would be avoid re-scanning the entire DOM after each swap, but instead only scan the newly added content:

new MutationObserver(ms => {
  for (const m of ms) m.addedNodes.forEach(n => { if (n.nodeType === 1) scan(n); });
}).observe(document.body, { childList: true, subtree: true });

Now it’s small and performant.

Better triggers

We can introduce a custom syntax for triggers, allowing comma-separated list of events, with optional delays, “changed” or “once” modifiers, etc:

const parseTriggers = (s) => {
  const triggers = s.split(',').map(s => s.trim());
  return triggers.map(trigger => {
    const [event, ...rest] = trigger.split(' ');
    const options = {};
    rest.forEach(opt => {
      const [key, value = true] = opt.split(':');
      options[key] = value;
    });
    return { event, options };
  });
}

This returns a list of events with options (keys/values, separated by colon in HTML attribute). Now we can write things like x-trigger="load, click one, change changed delay:500". We should also handle some events in our scan() function to handle them in a special way, i.e. load is basically setTimeout(() => send(...), 0), changed caches previous value and skips send if the value remains unchanged, once removes the event listener after the first trigger, and delay is another setTimeout with the specified delay. Your fantasy is the limit here, we could go as far as HTMX went and support viewport interaction triggers, queueing, debouncing, periodic events and much more. But we’re building a bad HTMX clone here.

Better targets

So far we use simple querySelector to find which element to swap content into. In practice, we might want to support a few more modifiers, like matching the closest ancestor or the following sibling.

const resolve = (el, sel) => {
  if (!sel) return el;
  if (sel === 'this') return el;
  if (sel === 'next') return el.nextElementSibling;
  if (sel === 'previous') return el.previousElementSibling;
  if (sel === 'document') return document;
  if (sel === 'body') return document.body;
  if (sel === 'window') return window;
  if (sel.startsWith('closest ')) return el.closest(sel.slice(8));
  if (sel.startsWith('find ')) return el.querySelector(sel.slice(5));
  return document.querySelector(sel);
};

We call it from send() instead of document.querySelector(sel) and we can now write things like x-target="closest .container" or x-target="next" to target more specific elements than a global query would target. HTMX does in fact roughly the same, if you want to inspect the code.

Better events

HTMX emits custom events like htmx:beforeRequest, allowing developers to intercept them, modify and control the request flow. We can do the same. In total we emit 4 events: beforeRequest, afterRequest, beforeSwap, afterSwap. Each event is emitted on the element that triggered the request, with full context of the current send(). Events can be cancelled by calling event.preventDefault().

Let’s also try to support server-side events, like HX-Trigger or HX-Redirect. If the server sends a HX-Trigger header, we dispatch a custom event with the contents from that header. If server sent HX-Redirect, we simply redirect the browser to that URL. I kept HX prefix here, because X-... headers is a common convention for standard HTTP headers.

const send = async (el, method, url) => {
  let target = resolve(el, attr(el, "x-target"));
  let mode = attr(el, "x-swap") || "innerHTML";
  const opts = {
    method: method.toUpperCase(),
    headers: { "HX-Request": "true" },
  };
  if (el.matches("form")) opts.body = new URLSearchParams(new FormData(el));
  if (!fire(el, "x:beforeSend", { el, url, target, mode, opts }, true))
    return;
  const response = await fetch(url, opts);
  const hdrTrigger = response.headers.get("HX-Trigger");
  if (hdrTrigger) {
    try {
      const data = JSON.parse(hdrTrigger);
      Object.entries(data).forEach(([ev, d]) => fire(document.body, ev, d));
    } catch {
      fire(document.body, hdrTrigger);
    }
  }
  if (response.headers.get("HX-Redirect")) {
    window.location.href = response.headers.get("HX-Redirect");
    return;
  }
  if (response.headers.get("HX-Refresh") === "true") {
    window.location.reload();
    return;
  }
  if (response.headers.get("HX-Retarget"))
    target = resolve(el, response.headers.get("HX-Retarget"));
  if (response.headers.get("HX-Reswap"))
    mode = response.headers.get("HX-Reswap");
  const html = await response.text();
  fire(el, "x:afterSend", { el, url, opts, target, mode, response, html });
  const detail = { el, url, target, mode, html, response };
  if (!fire(el, "x:beforeSwap", detail, true)) return;
  swap(detail.mode, detail.target, detail.html);
  fire(el, "x:afterSwap", detail);
  scan(detail.target); // rebind anything the swap just brought in
};

Our final send() got bigger, but it allows us to write plugins now but intercepting requests and swaps. Note how we propagate event details from one event to another, meaning that event handlers can modify the request and swap flow. For example, a plugin could intercept x:beforeSwap and change the target or swap mode based on some conditions or attributes.

Plugins

My initial impression from HTMX was that it does only one thing: if an event happens on some element with declarative configuration – send an AJAX request to some URL and swap the response into some other element. But if you check the reference you’ll see hx-on, hx-vals, hx-headers, hx-swap-oob, hx-params, hx-push-url, hx-history-elt, hx-sse, hx-ext and many more attributes that make it less straightforward to understand.

Can we achieve some feature parity without modifying the core “loop”? So that the “essence” of our library would be “SSS”: scan + send + swap.

For example, HTMX supports “boosting” – enhancing links and forms to use AJAX instead of full page reloads (to avoid writing x-get and x-target on each navigation link). This doesn’t even have to be a plugin, just a JS snippet:

document.addEventListener('click', e => {
  const boosted = e.target.closest('[x-boost]');
  if (!boosted) return;
  const link = e.target.closest('a');
  if (!link) return;
  const href = link.getAttribute('href');
  if (!href || href.startsWith('#') || link.getAttribute('target')) return;
  e.preventDefault();
  window.x.send(boosted, 'get', href);
});

Similarly we can “boost” forms on submit. There’s many more possible plugins to implement:

  • x-confirm — pops a native confirm() before sending, allowing user to cancel it. Needs an x:beforeSend listener, e.preventDefault() on decline.
  • x-indicator — shows a spinner while a request is in-flight. Needs x:beforeSend to toggle class/style, and x:afterSwap to undo it.
  • x-disable — disables the element for the duration of the request (avoid double-submission). Same logic as an indicator.
  • x-headers — extra request headers from a JSON attribute. Parse, merge into opts.headers in x:beforeSend.
  • x-vals / x-include — inject extra values into requests. Needs to mutate opts.body in x:beforeSend.
  • x-select — swap a fragment of the response instead of the whole HTML (keeps backend code simpler). Use querySelector the part we want, replace detail.html in x:beforeSwap to use that part.
  • x-sync — at most one request in flight: abort the old one, use the new one, or queue it. A map keyed by target.
  • x-validate — runs the browser native constraint validation before sending. All done in in x:beforeSend.
  • x-push-url / x-replace-url — updates browser history from an attribute or HX-Push-Url/HX-Replace-Url headers. Needs history.pushState/replaceState in x:afterSwap.
  • x-sse / x-ws — use streaming messages like a response and swap their content. No request/response cycle at all here, simply call x.swap() from onmessage.

None of these requires any changes to the core x.js, and each plugin is a dozen lines of code, easy to reason about and to debug. Seems like event-based plugin architecture and “SSS” API work better than I hoped (and it’s still notably smaller than htmx).

And yet, to make it fully compatible with HTMX the core needs some extra work. We need proper error handling for failed fetch(), non-2XX responses etc. This likely would need more custom events to be fired. We also likely need to expose resolve() to plugins, so that they could use the same x-target syntax everywhere. We need some promise-based cancellation, too. One obvious use case would be custom confirmation dialogs, that need to be asynchronous. View transitions likely need to be asynchronous as well, so a plugin could delay a swap until the transition is done. I’m sure there’s more things that I’m missing.

Those are left as exercises for the reader. Or you can just use htmx?

Full code of this experiment is at https://github.com/zserge/x, if you spot any bugs or willing to contribute - please do so!

The Daily Front Page 19 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Own Your Database
article

Authorize, don't authenticate

by marcua·▲ 106 points·40 comments·blog.marcua.net ↗
Hold on to your data by keeping it in your own database

Hold on to your data by keeping it in your own database

The login screen of any web application is the equivalent of that application telling you “In order to access YOUR data in MY database, prove you are who you say you are.” You’re authenticating with the application so that you can have the privilege of giving it your data.

In a previous blog post, I covered the downside to applications controlling your data. An application’s owner can restrict your access to your own data. They can delete the data. They can sell the data to a third party. They might introduce a bug that allows other people to access the data. They might get compromised. Or they can avoid all of this just to modify their policies down the road or see changes in ownership or management.

In a word: poppycock. It’s your data! You shouldn’t have to prove to anyone else that you have the right to access it. No one should tell you how to use your own data. And no one should do anything with your data that you aren’t comfortable with.

To gain agency over your data, you have to have agency over the database in which it lives1. If you own the database, you actually own the data. But running a database is a serious undertaking that we can’t expect from the average internet user. And even if you run a personal database, how should applications interact with it?

Over the past few months, I’ve explored the concept of personal database authorization, which allows you to give an application access to a database you control. Specifically, I built an authorization flow into ayb, my project to make it easy to create databases, share them with collaborators, and query them from anywhere. Here’s a video of how you can grant an application access to a freshly created database you own. Critically, the application has no login screen, instead asking for a database where your data will live:

Authorization in action: A to-do application with no login screen that requests access to a database you control. Todos is the to-do list application I created and use as my daily driver.

Holding on to your data

Here are three principles behind what’s happening in the video:

Authorize, don’t authenticate. In traditional web applications, you authenticate with the application to log in/prove your identity. Only once you’ve proved yourself with a password/passkey/… can you get access to your data. That puts the application in charge of your data. Instead, you should authorize an application to get access to your personal database. In the video, you see Todos asking you to Connect your database (“authorize me!”) rather than Enter your password (“authenticate yourself!”). The technology to support this flow is well understood and widely used: Todos initiates an OAuth2 flow to ask ayb for a token with which to query a database. I’ve open sourced an ayb.js library that manages these OAuth2 interactions on behalf of an application, and an engineer can integrate it into a new application in under an hour.

Creating a database should be as easy as creating a document. Most users can open up Microsoft Word or Google Drive and create an empty document. On the other hand, most users don’t know how to set up a Postgres database that backs itself up periodically and can be connected to a running application. In the video, the authorization flow allows you to pick an existing database or create a new one, and creating a new database is as easy as picking a name for it. If you create a database, you’ll receive periodic snapshots/backups without any configuration, and the application knows how to speak with ayb, run migrations, and store data in it. The database creation flow is no more difficult than one to create a file on your computer or the cloud, which is a lot more than you can say for most databases today.

Applications should store as little as possible outside a user’s database. The Todos application in the video is static HTML, CSS, and JavaScript. It knows nothing about its visitors. I assume somewhere in my server logs I can find the IP address of anyone that downloads the static resource, but beyond that, the application stores no state on my servers. For added privacy, you can download a self-contained index.html file and self-host the application. When you first load Todos, it asks you to Connect to your database to get started. Once it has a token with which to query your database2, all of your intimate to-do list items are stored in that database you own. You can revoke Todos’ access any time.

Trusting your host

Having just celebrated the virtues of storing data in a database you control, I have to deflate the balloon a bit. So far, I effectively said “don’t store your data with someone else, store it in something called ayb.” ayb is open source and while you CAN host it for yourself, do you want to? I believe ayb is pretty easy to install and run, but I certainly don’t think every user should become a database administrator. This leaves users with a frustrating choice: trade their trust of third-party applications for their trust of third-party database hosts. Are we just trading one form of centralization for another?

I think there’s still benefit to separating applications from the databases that they operate on. For starters, you actually get choice: rather than implicitly having to accept that every app developer is a custodian of a slice of your data, you get to pick where your data lives one time, regardless of how many applications you use. The abstraction means that once you get the hang of a tool like ayb, you can use it for multiple applications, which hopefully amortizes the cost of regaining agency over your data. You can pick who hosts your data because ayb is open source. I dream of a day when cooperatives and organizations that want to offer users agency over their data can offer alternatives to self-hosting and compete to be the custodian of your data. Finally, ayb doesn’t invent file formats: your data is stored in boring, well-accepted file formats like SQLite’s and DuckDB’s3. I’m planning to add export and import endpoints soon to make it easier to walk away with your data and bring it to another host.

Beyond personal data: collaboration and social interactions

The “authorize, don’t authenticate” model works most smoothly for situations where you definitively “own” the data. The more the dataset is the result of collaboration or social interaction, the less clear it is who owns the data, and the harder it is to authorize access.

Collaboration is one place where the definition of “personal data” is fuzzier. If you are working on a document on your own, it’s easy to tell an application to store “my document” in “my database.” But if you’re using something like a collaborative editor, say Google Documents, and are working on a document with someone else, what does it mean to store that document in a “personal database?” ayb lets you share your database with collaborators, but each database still has an owner. It would be interesting to explore models of collaborative personal databases, where users authorize an application to keep both of their databases in sync for data that they are collaborating on, but it’s beyond anything I’ve built, prototyped, or have my head wrapped around.

Social data is another type of data based on collaboration. We’ve seen the downsides of large organizations storing our social media data and also owning the algorithms and interfaces that control how that data is displayed to us. Projects such as ActivityPub/Mastodon and AT Protocol/Bluesky offer users more choice as to where their social data is stored, in different ways (ActivityPub’s federation vs. AT Protocol’s separation of storage and aggregation). Still, there’s a catch: these projects offer options on where to store your data, but the stored data is not useful until it’s aggregated into a timeline with everyone else’s. Solutions for timeline aggregation tend to fall back to centralization, as few people want to stomach the infrastructure and operational concerns involved in keeping track of what millions of people are saying in aggregate. Given how easy it is to create a static HTML/CSS/JavaScript application that can access and update your personal database, I’d love to see a decentralized extreme that displays your social network’s recent updates aggregated across all their personal data stores without relying on a centralized aggregator.

Where we go from here

Ultimately, I hope more people build software that asks users to authorize access to personal databases rather than asking users to authenticate with a centralized database. I’ve started doing this for my own personal applications, from tracking my Todos to tracking my Streaks to even managing newsletter subscriptions on my blog. Since ayb lets me spin up a database with the click of a button, I’ve built applications I previously avoided creating because I didn’t want to store my sensitive data in someone else’s database, but also didn’t have the time to administer my own.

To build on all of this, here are a few areas I’m excited to explore from here:

  • Supporting basic collaboration, such as how multiple users like journalists or scientists might collaborate on a dataset they are curating.
  • Investigating how this all connects to the local-first community. You’re in control of your ayb database, but it’s still hosted remotely, and I’m curious how a local SQLite/DuckDB database might sync with the remote one.
  • Identifying ways to help other application developers build software in a way that leaves users with agency over their data.

But mostly, I want to see how many login screens we can replace with database authorization screens. If you want to collaborate on any of these topics, or want help building software around personal databases, reach out!

Thank you to Meredith Blumenstock for giving feedback on the blog post and video.

  1. I’m certainly not the first to argue for data agency. Sir Tim Berners-Lee’s Solid Project has been exploring the idea of personal data pods for years. The ActivityPub and AT Protocol communities are building social networks that put users in more control of their shared content. The local-first community argues that users should have more control over their own data. My aim is to show how we can string together familiar, off-the-shelf technologies to make it easier for developers to build applications that offer more control. 
  2. To accomplish this, ayb.js implements OAuth2 using PKCE, which allows an ayb token to be issued to any app, even if it’s a fully static frontend blob with no backend to serve a client secret. 
  3. I’m iterating on an open PR for DuckDB, so coming soon. 
The Daily Front Page 20 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Show HN: Offline by Design
show hn

Show HN: Gander, an Android file viewer that asks for no permissions

by mokshablr·▲ 205 points·72 comments·github.com ↗
zero permissions, no ads, no tracking and no internet access at all

Take a gander at any file. A tiny, open source, fully offline file viewer for Android that opens PDF, Word (.docx), Excel, PowerPoint (.pptx), photos, videos, audio, Markdown, text and code in one app, with zero permissions, no ads, no tracking and no internet access at all.

Every phone ships with a dozen half-viewers that bounce your documents to cloud services. Gander is the opposite: one small APK (about 15 MB) that renders everything on the device. It cannot phone home because it does not even hold the INTERNET permission.

Gander demo: thumbnail recents, folder browsing, PDF, Word, Excel and Markdown viewing

Screenshots

Home: recents and folders Folder browsing PDF Recent files with thumbnail previews and granted folders Browsing a granted folder with previews PDF viewer

Word (.docx) PowerPoint (.pptx) Excel (.xlsx) Word document viewer PowerPoint slides viewer Excel spreadsheet viewer with sheet tabs

Features

  • One viewer for everything: documents, spreadsheets, slides, images, video, audio, Markdown, code
  • Pinch zoom and smooth scrolling everywhere, with deep zoom into huge photos (tiled decoding)
  • Recent files with thumbnail previews (image, video frame, PDF first page)
  • Folder browsing through one-time system grants, still without any storage permission
  • Share sheet and "Open with" integration: share a file from any app (chat, mail, browser) into Gander, or tap it in a file manager
  • Find in document: search inside Word, Excel, slides, Markdown, text and code with match navigation
  • Share and locate: send the open file to any app, or jump to its folder in the file manager
  • Private by construction: no permissions, no INTERNET, no analytics, no accounts, nothing leaves the phone
  • Modern Android: Material 3, dark mode, edge to edge, works on Android 8.0+

Supported formats

Category Formats Renderer Documents PDF Pdfium (native) Word .docx docx-preview, offline in a sandboxed WebView Spreadsheets .xlsx .xls .xlsm .xlsb .csv .ods SheetJS, offline Slides PowerPoint .pptx PPTXjs, offline Photos JPG, PNG, WebP, BMP, HEIC/HEIF Tiled deep-zoom image view, EXIF aware GIF (animated), SVG, AVIF, ICO WebView Video MP4, M4V, MOV, MKV, WebM, 3GP, AVI, FLV, MPEG-TS Media3 ExoPlayer Audio MP3, M4A, AAC, FLAC, WAV, OGG, Opus, AMR Media3 ExoPlayer Markdown .md rendered as formatted HTML marked + DOMPurify, offline Text and code .txt .json .xml logs, most source files Text viewer

Legacy binary .doc and .ppt are not supported (no faithful offline renderer exists); the app explains this and suggests re-saving as .docx / .pptx. Binary .xls works.

Install

Runs on Android 8.0 (API 26) and up.

  1. Download the latest APK from Releases: Gander-x.y-arm64.apk fits practically every phone from 2017 onward (use the universal APK for very old or x86 devices).
  2. Copy it to your phone, tap it, and allow "install unknown apps" when asked.
  3. Optional: Play Protect may warn about an unknown developer; that is what sideloaded open source looks like. Tap "Install anyway".

Updating: install the new APK over the old one; recents and folder grants survive.

Automatic updates without a store: install Obtainium and add https://github.com/mokshablr/gander as an app source. It follows the tagged GitHub releases here and updates Gander like a store would.

Verify before installing: every release is signed with the same key, so you can confirm an APK really came from this repo. Obtainium can pin the fingerprint below, and for a file you have already downloaded:

apksigner verify --print-certs Gander-x.y-arm64.apk

Signing certificate SHA-256:

5B:5C:F6:4A:94:23:7C:D5:F0:E0:85:76:00:38:BC:1C:EB:DF:18:DA:BA:5C:B3:EA:CA:7C:15:9F:22:A7:E2:4B

How the zero-permission trick works

Gander receives files through the Storage Access Framework and "Open with" intents, so the OS hands it exactly the documents you chose and nothing else. Office formats render inside a locked-down WebView whose every request is intercepted by WebViewAssetLoader: bundled JS libraries load from app assets and the document streams from the content URI. No network stack is ever touched, and the app does not declare the INTERNET permission, so there is nothing to audit or trust.

Folder browsing uses ACTION_OPEN_DOCUMENT_TREE grants. Note that Android itself refuses to grant the Downloads root to any app; grant Documents, DCIM or a subfolder of Downloads instead.

Build from source

To build it yourself you need JDK 17+ and the Android SDK (platform 35). These are build requirements only. The installed app runs on Android 8.0 (API 26) and up.

./gradlew assembleDebug        # installable debug build
./gradlew assembleRelease      # unsigned without a keystore

Release signing expects a local, untracked keystore at keystore/gander.jks (store and key password gander-local, alias gander); generate one with:

keytool -genkeypair -keystore keystore/gander.jks -alias gander \
  -keyalg RSA -keysize 2048 -validity 10000 \
  -storepass gander-local -keypass gander-local -dname "CN=Gander"

The keystore is gitignored on purpose: it is a personal signing key and must never land in a public repo.

Architecture in one paragraph

ViewerActivity routes by file extension first, MIME type second (FileKind.kt), into one of four surfaces: a native Pdfium view for PDF, a tiled SubsamplingScaleImageView for photos, Media3 ExoPlayer for video and audio, or a sandboxed WebView for everything rendered by vendored JS libraries (app/src/main/assets/viewer/). The home screen (MainActivity) lists recents (persisted SAF grants) and granted folders (DocumentsContract child queries), with thumbnails generated off-thread and cached (Thumbs.kt).

Vendored viewer libraries and their licenses: JSZip (MIT), docx-preview (Apache-2.0), SheetJS CE (Apache-2.0), PPTXjs + divs2slides (MIT), jQuery 1.11 (MIT), D3 3.x + NVD3 (BSD/Apache), marked (MIT), DOMPurify (Apache-2.0/MPL).

Roadmap

  • F-Droid listing
  • Legacy .doc / .ppt support if a usable offline renderer appears
  • iOS companion (thin QuickLook wrapper)

Contributing

Issues and small PRs are welcome, see CONTRIBUTING.md. If Gander is useful to you, a star helps other people find it.

License

MIT. Vendored viewer libraries keep their own licenses, listed above; all are MIT/Apache/BSD and compatible.

The Daily Front Page 21 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — The Official Drop
article

The most official water costs $120k a gallon

by surprisetalk·▲ 231 points·185 comments·signoregalilei.com ↗
not all H2O is created equal

We all learned in science class that water freezes at 0 °C or 32 °F at atmospheric pressure. But what water, exactly? Even after you distill out all the dissolved salts and minerals to get pure H2O, not all H2O is created equal – and for the most precise temperature measurements, the differences really matter.

Here’s the problem: hydrogen and oxygen atoms aren’t all identical. All atoms of a single element have the same number of protons by definition, but they can have different numbers of neutrons, forming different isotopes. We need to know how much of each isotope to use for our experiments.

Credit: OpenStax

First, let’s go over the isotopes of hydrogen and oxygen. Hydrogen has one proton and either zero, one or two neutrons forming its isotopes protium, deuterium, and tritium. Oxygen has 8 protons and either 8, 9, or 10 neutrons forming the much less creatively named oxygen-16, oxygen-17, and oxygen-18.

Different isotopes of an element behave similarly, but not exactly the same. The higher-numbered isotopes of hydrogen and oxygen are a bit heavier and more sluggish, so they stay frozen at higher temperatures. For example, water made with deuterium and oxygen-18 freezes at about 4 °C or 39 °F.

Nearly all hydrogen is protium, but 1 in every 10,000 hydrogen atoms on Earth is deuterium. Tritium is radioactive and decays with a half-life of just over 12 years, so all the tritium Earth started with is gone. A tiny amount (less than one in a quadrillion hydrogen atoms) is produced by cosmic rays and and human nuclear activity. Earth’s oxygen is mostly oxygen-16, but about 1 in 500 oxygen atoms is oxygen-18 and 1 in 3000 is oxygen-17. Even these small amounts increase water’s freezing point by about 0.001 degrees Celsius – well within the amount that modern thermometers can measure.

So can we just use water with the ratio of isotopes we find naturally on Earth? Unfortunately, those ratios aren’t constant across all of Earth’s water. The heavier isotopes evaporate more slowly, so rainwater is slightly lighter than ocean water.

The original container of Vienna Standard Mean Ocean Water

In 1961, Harmon Craig at Scripps Institution of Oceanography proposed a standard water for measuring isotope concentrations. It was based on the average amount of each isotope in Earth’s oceans, which he called “Standard Mean Ocean Water” or SMOW. Unfortunately, some scientists at Caltech would soon propose their own separate SMOW based on a sample of sandstone from upstate New York.

These conflicting standards made it to the 1966 meeting of the International Atomic Energy Agency, a group that really cares about isotopes and wanted to sort out this mess once and for all. They decided to go with Craig’s standard, and had him prepare an actual batch of his SMOW using mostly water distilled from the Pacific to use as the official international standard. Since the meeting was held in Vienna, this sample later became known as “Vienna Standard Mean Ocean Water” or VSMOW. They also made another standard water batch meant to represent rainwater. Their batch was distilled from melted Antarctic snow, so it’s called “Standard Light Antarctic Precipitation” or SLAP.

VSMOW is the most official water used for metrology, with all other standards (including SLAP) being measured against it. There’s a limited supply, so it currently costs $159 for a 5 ml ampoule, which converts to $120,000 a gallon.

A triple point cell in action – the central tube holds the thermometer

So why would anyone pay that much for water? Having an actual, physical standard means you can use it to calibrate your experiments. Today, the most precise thermometers are calibrated using a “triple point cell”, which holds ice, liquid water, and water vapor in equilibrium at low pressure. For VSMOW, this equilibrium can only occur at 0.01 °C, within just a few millionths of a degree.

This value is so precise that it was the SI definition of the Kelvin temperature scale until 2019, when it was redefined using the Boltzmann constant from thermodynamics. But it’s still the most accurate practical method. Most triple point cells don’t contain VSMOW directly, but they can trace their chain of precision back to that original container of VSMOW eventually. And you don’t need a precision thermometer to tell you that that’s pretty cool.

The Daily Front Page 22 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Lightbulb Revisionism
article

Increasing the lifespan of a bulb makes it worse in every other way

by tonyg·▲ 179 points·180 comments·maurycyz.com ↗
This is wrong, but it's the type of wrong that cites its sources

There's a story that goes something like this:

In 1925, lightbulb manufactures secretly colluded to standardized lifespans at 1,000 hours. They would test each other's products to ensure compliance.

This is true.

At the time, many bulbs lasted longer than one thousand hours.

This is true.

Therefore, this was done so that people would always need to buy more light bulbs.

This is wrong, but it's the type of wrong that cites its sources and hides in the part you'd never think to fact check:

The assumption that a longer lasting lightbulb is a good product.

In truth, increasing the lifespan of a bulb makes it worse in every other way... but people think they want a long lasting lightbulb: the purpose of standardizing was (primarily) to avoid a race to the bottom.

A old-school lightbulb is a rather simple device:

A thin tungsten wire (~20 μm) sealed inside a glass envelope to protect it from air. When current is applied, the wire gets white hot and starts glowing.

The most important parameter of a lightbulb is how hot that wire gets: this controls the peak emission wavelength (color) and brightness of the lamp.

Room temperature objects do emit light (this is how thermal cameras work), but it's at the ~10 μm range instead of the 400 nm - 700 nm light that we can see.

In order to put the emission peak in the visible spectrum, the filament would need to run at ~5700 °C

... that is, the temperature of the sun.

No metal can survive these conditions: Tungsten melts at "only" 3422 °C.

Filament materials:

Since it has the highest melting point of any metal, tungsten is the obvious choice for filaments. However, the metal is quite brittle and drawing it into a wire isn't easy.

The first commercialized lamps used carbon filaments that were made by charring plant fibers. However, the carbon would evaporate at fairly modest temperatures ~2000 °C.

Tantalum filaments were briefly produced during the 1900s, because the metal was easier to draw into a wire than tungsten. These were the first lightbulbs that could actually be left on at night, although they were quickly replaced with tungsten manufacturing improved.

There ware also some experiments using zirconium dioxide ceramics, which become conductive when heated. These allowed lamps to operate in air (obviating the need for a vacuum pump and glass seals), but were limited by its melting point of 2,700 °C.

Since any filament must run below its melting point, the peak emission is always in the infrared.

This means that only the extreme high-energy edge of the spectrum is useful for illumination, so a small increase in temperature will make a lamp orders of magnitude more efficient. Also, since this increases the average energy of the atoms, the lamp is able produce shorter wavelengths: resulting in a whiter and less depressing glow.

The snag is that when a metal is close to its melting point, the atoms are barely holding together:

A hot tungsten filament slowly falls apart as the metal crystals slide past each other. Additionally, atoms can evaporate from the surface until there's no wire left.

The rate of both of these processes increases with temperature, so there's a fundamental trade off between color/efficiency and lifespan.

The lightbulb everyone always cites in the story is hanging in a California fire department. It's been running nearly continuously for over 120 years and racked up over a million hours of operation.

Impressive right?

What almost no one talks about is that it's hardly even glowing!

A carbon filament lamp handing from a pendant. The bulb is dirty and the filament glows a dim orange.

Photo taken by Wikipedia user Rjaerial

Despite nominally being a 60 W lamp, it draws only 4 watts... and is a lot dimmer than you'd expect from a 4 W lamp due to its poor efficiency.

There isn't any documentation, but in all likelihood, the bulb was made wrong and ended up having a very high filament resistance. That's why it was sold for as a night light, because it wasn't usable for anything else.

Early bulbs (like that one) were handmade, and quite expensive. Because of this, there were universally optimized for long lives.

This resulted in light isn't anywhere near white, and a an efficiency that was a tiny fraction of a modern incandescent lamp. (which are also terrible by any objective standards)

Once the production process was automated, new bulbs cost pennies, so it made sense to optimize them to work well... because less efficient bulbs cost more money to operate:

Going off modern day prices, electricity costs around 0.10 [$/kW*h], so a 60 W lamp will consume 6$ of electricity over a 1,000 hour lifespan. Considering that such a lamp only costs around 3$, installing one that lasts longer but uses more power would be silly.

Case in point, despite the cartel only lasting for 14 years, modern (non-halogen, incandescent) bulbs still last for between 500 to 2,500 hours, a range that includes the cartel's 1,000 hour standard.

Instead of "making bulbs last longer", manufacturers spent huge amounts of time and money developing entirely new technology: fluorescent and LED lamps. Because these don't use a wire on the very edge of melting, they can be made to work well and last a long time.

Of course, specialized lamps have different requirements:

In photography, a truly white light is desirable, which leads to specialized "photoflood" bulbs that only last for a few hours. In the other direction, many indicator lamps are designed for 100,000 hours because they are difficult to replace.

Ok, but what's with the testing?

If long lived light bulbs are worse products, why would they need a cartel to enforce a the thousand hour limit? Well, it's because people think long lasting bulbs are a good product:

Lifespan is something everyone can understand, and has a direct effect on when you will have to go back to the store: if you saw two 60 W bulbs in a store, one claiming to last 400 hours and the other 2,000 hours, you'd probably get the longer lasting one without thinking about it.

It's not that efficiency is hard to understand, but most people aren't doing homework before buying lightbulbs... and it doesn't help that bulb packaging uses input power as a proxy for brightness, so the idea that two bulbs both labeled as "40 W" would have a different brightness is rather confusing.

As a result, competition was forcing lightbulb makers to produce worse products.

To be clear, I'm not defending the Phoebus cartel: they absolutely engaged in price fixing and other anti-consumer practices, and it's difficult to imagine that profit wasn't a factor when deciding the 1,000 hour standard... but by nature, tungsten lamps are consumable items.

I guess the the moral here is reality rarely fits into nice stories. Even something so obvious like "products designed to break are bad" often isn't — every manufactured object is the result of hundreds of overlapping compromises, most of which are invisible to the end user.

Also, to preempt the orange site, I'm not saying that planned obsolescence doesn't exist. There are plenty of actual cases of products being made hard to repair so they can sell you another one.

... but lightbulbs aren't a good example.

PS. About LED bulbs:

I've gotten a few comments about "Dubai Lamps", which are LED filament-style bulbs that feature four times as many diodes as a standard one. Because of this, each diode is only running at a quarter it's rated power and stays a lot cooler.

First off, the claim that you're not allowed to buy these outside of Dubai is just wrong. You can: the only difference is that they are branded as "UltraEfficient" bulbs.

The efficiency boost is real:

These lamps can achieve 200 lm/W instead of the usual ~100 lm/W, at the cost of being much more expensive to make.

Lifespan is interesting:

I've taken apart quite a few failed LED bulbs, and almost none of them failed because of the diodes overheated. The near universal failure mode is heat-related damage to the power supply's input smoothing capacitor.

However, if you've even taken apart an non-filament-style LED bulb, it's clear manufacturer's know about the problem: The diodes are soldered to a metal heatsink, and the capacitor has very long leads to push it as far away from the diodes as physically possible. Filament-style ones can't do this, but put the power supply in the base in an attempt to remove heat via the metal threads.

Since capacitor heating depends on total power dissipation, "dubai lamps" still have the advantage here, since they burn less power to produce a good amount of light...

... although they still run quite hot:

The lightbulb form factor is just not good for heat dissipation. LED light fixtures can have much more effective cooling and very long lifespans.

As a bonus, these take up a lot less space which makes them easier to install... but are widely hated because if anything does go wrong, they are much harder to replace than bulbs.

The most eco-friendly LED light would work like fluorescent tubes: a separate power supply and emitter, with both being standardized and interchangeable... but the popularity of (similarly compromised) CFL bulbs shows how well that would go over.

In reality, a light must also be easy to install and comparable with existing wiring: the energy cost of LEDs is low enough that efficiency just isn't terribly important for most people.

Again, it's complicated: "dubai lamps" are not exclusive to Dubai (although the branding is), and are still an imperfect design.

The Daily Front Page 23 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Bad Apple, Routed
article

Bad Apple but It's Traceroute

by jssfr·▲ 155 points·49 comments·jssfr.de ↗
I simply had to.

As a follow-up to my post about how to make traceroute tools show arbitrary content and inspired by the release of another Bad Apple cover the other day I simply had to.

Of course, I'm not the first one to do this. That couldn't stop me though.

How it works

In the post I linked above, I had demonstrated how to inject fake hops into traceroute output.

Using the numgen feature of nftables, we can make the hops vary every time an ICMPv6 packet is generated. numgen gets us either random numbers or a monotonic counter. With a counter, we can easily make each hop return a different IPv6 address each time a response packet is generated. Using the playground from the other post:

ip netns exec tracemess nft -f - <<EOF
destroy table inet tracemess

table inet tracemess {
    chain prerouting {
        type filter hook prerouting priority raw;
        ip6 daddr fd00::1 ip6 hoplimit 1 reject with icmpv6 admin-prohibited;
    }

    chain postrouting {
        type filter hook postrouting priority raw;
        icmpv6 type destination-unreachable icmpv6 code admin-prohibited ip6 saddr fd00::1 @th,120,8 1 ip6 saddr set numgen inc mod 3 map { 0: fd00::2, 1: fd00::3, 2: fd00::4 } icmpv6 type set 3 icmpv6 code set 0 accept;
    }
}
EOF

Two other things were needed to make this happen. First of all, we need to disable the kernel's rate limit (by default, 1/s) for ICMPv6 egress using sysctl net.ipv6.icmp.ratelimit=0, otherwise the party will be over really quick.

The second problem is that mtr will normally show multiple addresses for each hop, because that indicates that multiple different paths are in use for a packet and that is normally useful information. In this case, however, that's rather annoying:

Screenshot showing a traceroute output. Three hops can be seen and each hop shows eight different IPv6 addresses. One can imagine that there was some intent to paint a kind of picture with the patterns of the numbers in the addresses, but it's completely torn apart by the display.

In order to fix that, we have to apply a one-line patch1 to mtr2:

diff --git a/ui/net.c b/ui/net.c
index c0cbf28..7c52710 100644
--- a/ui/net.c
+++ b/ui/net.c
@@ -266,6 +266,7 @@ static void net_process_ping(
                 break; /* Found first vacant position */
             }
         }
+        i = 0;

         if (found == 0 && i < MAX_PATH) {
             memcpy(&nh->addrs[i], &addrcopy, sockaddr_addr_size(sourcesockaddr));

With all this in place, I used ffmpeg to resample the video to 8 frames-per-second (which equates an interval of 125 ms between frames) and export scaled-down (to 30x11 pixels) individual frames to PNG files. I then wrote a python script to read the image files and convert them into an nftables ruleset to generate the corresponding ICMPv6 responses.

That results in a bit over a megabyte of nftables rules, but it's definitely worth it.

Why?

The shadow art Bad Apple music video has become the benchmark for hacked displays, for a very loose definition of "display", similar to how running Doom (the 1993 video game) has become the benchmark of gaining code execution on a new hardware platform. I suspect the reason for that is that, due to its shadow art style, the video can be rendered even on "displays" which only have one bit per pixel (on/off).

There is a subreddit dedicated to showing off the kinds of devices people have gotten Bad Apple to display on. My favourite would at this point be this hack abusing a HD47780-compatible character LCD, simply because I enjoy hacking these devices, too3. There's also more than one compilation video showing multiple versions.

Considering that … I simply had no choice, did I?


  1. Instead of patching mtr, one could also use a shell loop wrapped around traceroute. That doesn't need patching, but is barely less cheating than the one-line patch to mtr
  2. mtr does allow to limit the number of addresses shown per-hop (the -E flag), but it then always displays the first address. That means we get a freeze-frame of the first frame of the video ☹️. 
  3. Though I cannot decide whether this thing where people redrew the video on a shared online canvas or this thing with an autostereogram should be second place in my personal ranking. 
The Daily Front Page 24 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — How Pictures Lose Gracefully
article

How JPEG works: Interactively explore JPEG's lossy compression methods

by at1as·▲ 128 points·14 comments·cgjennings.ca ↗
It’s humbling to think that the 1992 JPEG image standard is still going strong

Interactively explore JPEG’s lossy compression methods

Introduction

In the world of technology, “forever” means about 5 years. It’s humbling to think that the 1992 JPEG image standard is still going strong despite being more than 5 “forevers” old. Why has it been so successful? This article presents the key ideas behind JPEG in plain language and includes an interactive JPEG compressor right on the page so you can play along at home.

Compression methods

Compression techniques look for repeated patterns in the data and then replace those patterns with shorter ones. It’s like using an abbreviation or acronym to stand in for a longer word or phrase. Video, still images, and audio don’t usually compress well. The problem with images and sound is that this data is usually too noisy to find good abbreviations. For this reason, JPEG and many other media formats use something called lossy compression.

Lossy compression means that you reduce file size by throwing away some of the information. Suppose a librarian has run out of shelf space and needs room for more books. If she replaces some of the books with digital or microfiche copies, that’s lossless compression. If she burns some of the books, that’s lossy compression.

It’s not the notes you play, it’s the notes you don’t play. Miles Davis

The trick to good lossy compression is to throw away information that nobody cares about: burning the books that nobody ever reads. How to decide what to keep? Science! In the case of image compression, you start by understanding which parts of an image are important to human perception, and which aren’t. Then you find a way to keep the important qualities and trash the rest. In JPEG, the lossy compression is based on two psychovisual principles:

Camouflaged leopard

A leopard walks in partial camouflage near a vehicle in South Africa. Credit: Lee R. Berger.

  1. Changes in brightness are more important than changes in colour: the human retina contains about 120 million brightness-sensitive rod cells, but only about 6 million colour-sensitive cone cells.
  2. Low-frequency changes are more important than high-frequency changes. The human eye is good at judging low-frequency light changes, like the edges of objects. It is less accurate at judging high-frequency light changes, like the fine detail in a busy pattern or texture. Camouflage works in part because higher-frequency patterns disrupt the lower-frequency edges of the thing camouflaged.

JPEG compression applies each of these ideas in turn. In each case, the image data is transformed to give easier access to the kind of information needed (either brightness or frequency information). Then some of the less important information is discarded. As a final step, the information that is left is compressed with traditional lossless compression to pack the end result into the smallest space possible. In this article, we explore the process step-by-step, using real images and following them through the entire encoding (saving as JPEG) and decoding (loading from JPEG) process. You can play with the encoding settings and see how it affects the results. Let’s go!

Encoding

Start by choosing an input image to compress.

The input image

Pick an image:

  • Tower
  • Flowers
  • Hippos
  • Low frequency
  • High frequency
  • Choose your own image

Choose an image from the list provided. There are several to experiment with, but I recommed that for your first time through you use the default “Tower” image. It has a good mix of low- and high-frequency segments, and I’ll refer to it sometimes in the article text.

You can also use an image of your choice by selecting Choose your own image or by dragging and dropping the image file on this page.

Step 1: Isolate the colour information

Image pixels

An image is made of pixels. The colour of each pixel is the sum of amounts of red, green, and blue light.

Typical computer images are made up from a grid of tiny coloured squares called pixels. Each pixel is stored as three numbers, representing the amount of red, green, and blue light needed to reproduce that pixel’s colour. For this reason, it is called an RGB image. On the left side of the left-hand illustration, you can see the red, green, and blue parts of your selected image split out into three separate channels.

The problem, as far as JPEG is concerned, is that the image’s brightness information is spread evenly through the R, G, and B channels. Remember that brightness is more important than colour, so we’ll want to isolate brightness from the colour information so we can deal with it separately. To do this, JPEG uses some math to convert the image’s colour space from RGB to YCbCr. A YCbCr image also has three channels, but it stores all of the brightness information in one channel (Y) while splitting the colour information between the other two (Cb and Cr).

The right side of the left-hand illustration shows the same image split into Y (top), Cb (middle), and Cr (bottom) channels. Notice that the Cb and Cr channels are “muddy” because all of the definition given by the brightness information has been moved to the Y channel.

To keep the size of the illustration reasonable, I have scaled down all of the channels. In reality, each one is the same size as the original image.

Step 2: Throw away some colour information

Before doing anything else, JPEG throws away some of the colour information by scaling down just the Cb and Cr (colour) channels while keeping the important Y (brightness) channel full size. Strictly speaking, this step is optional. The standard says you can keep all of the colour information, half of it, or a quarter of it. For images, most apps will keep half of the colour information; for video it is usually a quarter. For this demo I’m keeping a quarter, both to exaggerate the effect and because it makes for nicer illustrations.

Notice that we started with 3 full channels and now we have 1 full channel and 2 × ¼ channels, for a total of 1½. We’re just getting started and we are already down to half of the information we started with!

Step 3: Convert to the frequency domain

To make use of the second observation about human visual perception, we start by dividing each of the Y, Cb, and Cr channels up into 8×8 blocks of pixels. We will transform each of these blocks from the spatial domain to the frequency domain.

Whoa there, horsie! What? OK, let’s consider just one of these 8×8 blocks from the Y channel. The spatial domain is what we have now: the value in the upper-left corner represents the brightness (Y-value) of the pixel in the upper-left corner of that block. Likewise, the value in the lower-right corner represents the brightness of the pixel in the lower-right corner of that block. Hence the term spatial: position in the block represents position in the image. When we transform this block to the frequency domain, position in the block will instead represent a frequency band in that block of the image. The value in the upper-left corner of the block will represent the lowest-frequency information and the value in the lower-right corner of the block will represent the highest-frequency information.

This domain transformation is accomplished using a bit of mathematical legerdemain called the 2D Discrete Cosine Transform (DCT). (If you have heard of Fourier transforms, the DCT is similar but it uses only real numbers; this is more convenient for computer representation.) The essential idea is to represent the values in the 8×8 block as a sum of cosine functions, where each cosine function has a specific unique frequency.

You don’t need to understand the math to get a sense of how it works. Look at the Y frequency illustration for the Tower image. You can clearly see each 8×8 block’s upper-left corner thanks to a dark dot of low-frequency information. Now if you look at blocks from the sky parts of the image, you will see that the rest of each block is mostly empty. The sky doesn’t have lots of dramatic changes from pixel to pixel: no high-frequency information. Compare that to blocks from the tower parts of the image: the busy texture of the bricks means lots of higher-frequency change, and this shows up as grey throughout the block.

Step 4: The quality slider (quantization)

The next step is to selectively throw away some of the frequency information. If you have ever saved a JPEG image and chosen a quality value, this is where that choice comes into play. It works like this: start with two 8×8 tables of whole numbers, called the quantization tables. One table is for brightness information, and one is for colour information. You will use these numbers on each of the 8×8 blocks in the image data by dividing the frequency value in the image data by the corresponding number in its quantization table. So the upper-left corner of each 8×8 block in the Y frequency channel will be divided by the number in the upper-left corner of the brightness quantization table, and so on. The result of each division is rounded to the nearest whole number and the fractional parts are thrown away.

The effect of your choice on the final output image is shown for reference.

The larger a number in one of the quantization tables, the more information gets thrown away from that part of that frequency range. Since we care less about high-frequency information, the numbers in that area of the quantization tables will be larger. And since we care less about colour than about brightness, the numbers in the colour table will be larger overall than the numbers in the brightness table.

The quantization tables are saved along with the image data in the JPEG file. They’ll be needed to decode the image correctly.

Go ahead and play with the quality slider above. Notice how more and more of the frequency information disappears as you drag the quality down towards the low end.

Step 5: Lossless data compression

If you think carefully about what just happened, you will realize that even though we threw away some frequency information by tossing the decimal parts after division, we still have the same amount of data: one number for each pixel from each of the three channels. It seems like that step didn’t actually buy us anything. However, this data is now going to be compressed using traditional lossless compression. But wait, wasn’t the whole reason we used lossy compression in the first place that lossless compression doesn’t work well for images? Yes, but that quantization we just did is going to make the data more compressible by making it less noisy. To see why, compare these three number sequences:

   n = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, …
 n/2 = 0, 1, 1, 2, 2, 3, 3, 4, 5, 5,  5,  6,  6,  7,  7,  8,  8, …
n/16 = 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,  1,  1,  1,  1,  1,  1,  1, …

JPEG zig-zag order

Block data is compressed in zig-zag order, grouping similar frequencies together.

The first row lists values for, say, some pixel in the Y frequency channel. The second row is the same values divided by 2 and rounded; the third row is divided by 16 and rounded. You can see that the larger the divisor, the more repetition there will be in the data. And the more repetition there is in the data, the easier it is to compress, and the smaller the final image file will be.

JPEG has one last trick for making the data more compressible: it lists the values for each 8×8 block in a zig-zag pattern that puts the numbers in order from lowest frequency to highest. That means that the most heavily quantized parts (with the largest divisors) are next to each other to make nice, repetitive patterns of small numbers.

Decoding

There you have it, the essential elements of writing a JPEG image: convert the image from RGB to YCbCr so we isolate the brightness, throw away some of the colour, convert to the frequency domain, throw away some of the precision of the frequency information, and compress the resulting data.

What happens when you read an image back in? Essentially you just need to reverse each step of the encoding process. Let’s step through it.

Step 6: Decompression

The first step is to decompress the quantized (divided and rounded) frequency data. Since this data was compressed losslessly, the result will be exactly the same as in Step 5 above.

Step 7: Reconstruction from quantized data

Next we need to reverse the quantization process. We use the same procedure as before, but instead of dividing by the numbers in the tables, we multiply. Since we rounded the numbers, we won’t get exactly the same numbers back. The result is an imperfect approximation of the original frequency data, limited to the precision allowed by the quantization tables. The lower the quality, the larger the quantization divisiors, the more precision is lost, and the less accurate our reconstruction will be now.

Step 8: Convert back to spatial domain

Now that we have reconstructed the frequency information, we need to transform it back from the frequency domain to the spatial domain. This is no problem. The transform that we used during encoding has an inverse that does the job.

Now that the data is in a more recognizable form, we can start to judge how perceptible the information loss is.

Step 9: Fill in missing colour information

Examples of scaling artifacts

A chipmunk graphic is scaled up by 400% using two different methods. One scaled image is blocky, the other blurry.

In order to combine the channels back into a single image, we need to scale the two colour channels Cb and Cr back up to their orignal size. But the information we threw away is gone, so we will have to approximate it with interpolation. We will fill in missing pixels by taking an average of the surrounding pixels that are still there. There are different ways to do this, and none of them is perfect. The scaled up image will tend to be either blocky or blurry, depending on the method used.

Step 10: Convert back to RGB

At this point we have a complete image, but it is still in the YCbCr colour space, which a computer can’t display directly. We need to transform it back to the RGB colour space that we started with.

The output image

Finally, we can combine the separate R, G, and B channels back into a single image and display the result. For comparison, both the original input image and the decoded output image are shown at left. Below that is a “difference image” that highlights discrepencies between the two: darker pixels mean the output varies more from the input at that location.

There you have it. We have taken an input image, encoded it, decoded it, and gotten a similar image back. That’s JPEG. Now that you’ve seen the whole process, go back and experiment with some of the other images.

The Daily Front Page 25 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Factory Floor: USB Sticks
article

Where USB Memory Sticks are Born (2013)

by jacquesm·▲ 97 points·8 comments·bunniestudios.com ↗
I managed to score a rare treat

Where USB Memory Sticks are Born

In January, I had the fortune of being a keynote speaker at LCA2013. One of the tchotchkes I received from the conference organizers was a little USB memory stick.

I thought it was a neat coincidence that I was in a factory that manufactured exactly such memory sticks about a week before the conference. In fact, I managed to score a rare treat: the factory owner gave me a sheet of raw chip-on-flex, prior to bonding and encapsulation, to take home.

The USB sticks start life as bare FLASH memory chips. Prior to mounting on PCBs, the chips are screened for capacity and functionality. Below is a photo of the workstation where this happens:

In the image, you can see stacks of bare-die FLASH chips, awaiting screening with a probe card. I love the analog current meter and the use of rubber bands to hold it all together. The probe card has tiny needles on it that touch down on microscopic (less than 100-micron square) contacts on the chip surfaces. Below is what a probe card looks like.

Below is an image through the microscope on the micro-probing station, showing the needles touching down on the square pads at the edge of the FLASH chip’s surface.

Interestingly, this all happens in an absolutely non-clean-room environment. Workers are pretty much handling chips with tweezers and hand suction vises, and mounting the devices into these jigs by hand.

Once the chips are screened for functionality, they are placed by hand onto a PCB. This is not an unusual practice, pretty much every value-oriented wirebonding facility I’ve visited relies on the manual placement of bare die. The photo below shows a controller IC being placed on a panel of PCBs. The bare die are in the right hand side of the photo, sitting in the beige colored waffle pack.

The lady is using some sort of tool made out of hand-cut bamboo. I still haven’t figured out exactly how they work, but every time I’ve seen this process they are using what looks like a modified chopstick to place the chips on the board. My best guess is that the bamboo sticks have just the right surface energy to adhere to the silicon die, such that silicon will stick to the tip of the bamboo rod. A dot of glue is pre-applied to the bare boards, so when the operator touches the die down onto the glue, the surface tension of the glue pulls the die off of the bamboo stick.

It’s trippy to think that the chips inside my USB stick were handled using modified chopsticks.

The chips are then wirebonded to the board using an automated bonding machine which uses image recognition to find the location of the bond pads (this is part of the reason they can get away with manual die placement).

https://bunniefoo.com/ntw/usb1q13.mp4

(view in HD)

The first half of the video above starts out with the operator pulling off and replacing a mis-bonded wire by hand, and re-feeding the wire into the machine. Given that these wires are thinner than a strand of hair, and that the bonding pads are microscopic, this is no mean feat of manual dexterity.

Here’s a scan of the partially-bonded but fully die-mounted PCB that I was given as a memoir from my visit (I had since crushed some of the wire bonds). The panel contains eight USB sticks, each consisting of a FLASH memory chip and a controller IC that handles the bridging between USB and raw FLASH, a non-trivial task that includes managing bad block maps and error-correction, among other things. The controller is probably an 8051-class CPU running at a few dozen MHz.

Once the panels are bonded and tested, they are overmolded with epoxy, and then cut into individual pieces, ready for sale.

Interestingly, the entire assembly prior to encapsulation is flexible. The silicon chips have been thinned down by grinding off their back sides to the point where they can tolerate a small amount of flexing, and the PCB is also so thin, it is flexible.

For those of you interested in this kind of thing, here’s the die marking from the FLASH chip; apparently it is made by Intel:

Here is also a die shot of the controller chip:

And now you know where those tiny USB thumb drives are born.

Thanks to David Cranor for contributing images. Images used with permission.

PS: chopsticks

The Daily Front Page 26 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Against the Average
article

The mean means nothing: data visualization to debug a latency problem

by fanf2·▲ 113 points·19 comments·fzakaria.com ↗
the data seemed too noisy to make any conclusions

I was recently trying to validate some performance improvements related to lld at $DAYJOB and it was a little frustrating to see the improvements in our benchmarks but not in the live-production dashboards.

Having come from a background working on web-services, I was used to looking at individual time-series dashboards, sometimes over a few percentiles, and I was expecing to see some noticeable change but the data seemed too noisy to make any conclusions.

Turns out a colleague had also faced similar issues when trying to evaluate build-speed improvements. There are lots of variables that can affect the build: cold-cache, incremental, local, remote, etc. and the build times can vary wildly depending on the state of the system and the workload. She ended up leveraging a cummulative distribution function (CDF) to visualize the data and it was a revelation to me.

This led me to explore a few other different ways to visualize data, in addition to the CDF, and how a single image or statistic is often not enough to tell the whole story. This post will walk through a single synthetic dataset and show how different visualizations can tell different stories about the same data. The goal is to convince you to look at your data and not just summarize it with a single number.

Everything below comes from one synthetic dataset with a fixed seed. The full script can be found in this gist. It is a single file with a nix-shell shebang, so you can reproduce every figure exactly as long as you are using nix.

Note I leveraged AI to help generate the data and charts in this post for the story. If that bugs you, sorry. 🤷

The rollout that “made it worse”

Here is the setup: we operate a typical web-service and we rolled out a new caching tier over a week, hoping to cut request latency.

The change is fully deployed, and the latency dashboard that plots the mean looks like this:

Bar chart of mean latency before and after the rollout; after is 122 ms, about 9% higher than the 112 ms before

Mean latency went up, from 112 ms to 122 ms. ☹️

A SEV is cut, we revert the change and write the postmortem. Right? 🤔

One number, four stories

It’s often good practice especially for web-services to look at various percentiles, especially the tail end of the distribution like the p95 and p99.

statistic before after change
mean 112 ms 122 ms +9%
p50 (median) 99 ms 54 ms −46%
p95 224 ms 454 ms +103%
p99 309 ms 678 ms +119%

Now we have a problem, and the problem is that everyone is right. The mean says the change is a mild regression. The median (p50), says the change is a big-win, the typical request got nearly twice as fast. The p99 says it’s a SEV[^1], the worst requests more than doubling.

[^1]: SEV, or “Severity”, is a popularized way to describe an incident or outage. A SEV is often numerically ranked in descending order by impact, e.g., SEV0 being the most severe.

The mean and the median, computed from the very same numbers, point in opposite directions.

Engineers are often taught to be data-oriented, but often it’s easy to cherry-pick the statistic that supports your argument.

Look at the shape

The next basic thing you can do with a distribution is plot its shape. Here are the two latency distributions, before and after, as densities:

Density plot of latency before and after; before is a single hump, after has a tall fast peak plus a second hump out in the slow region

There it is. 🤓☝️ The “before” is one tidy hump. The “after” is two humps.

This already explains the earlier contradiction but it’s a bit tricky to visualize correctly. The shape depends on a smoothing parameter we chose, the two fills muddy each other where they overlap, and it’s genuinely hard to read a percentile off it. I can see there are two populations; I can’t easily see where the median went.

The best chart you’re not using

The cumulative distribution function (CDF) answers one question for every percentile at once: what fraction of requests came in at or below x milliseconds?

CDF of latency before and after; the two step curves cross near 140 ms

CDFs are an extremely easy way to visualize multiple percentiles in a single chart. Depending on the curve, we can understand how the request latency is distributed across the entire population.

I found it incredibly useful to then plot the “before” and “after” CDFs on the same chart to see how they compare. You can then visualize the shifts at various percentiles and understand how the change affected the entire population.

In our story, the “after” curve has shifted left for request latencies below 140ms. That means more requests are finishing faster than before. The “after” curve is higher than the “before” curve to the right of 140ms which means more requests are finishing slower than before. The two curves cross at ~140ms which is the tipping point where the change goes from being a win to a loss.

Tip Two CDFs that cross are the unmistakable signature of a change that no single percentile can summarize, because the sign of the effect depends on which percentile you ask.

Who won, and by how much

The CDF tells us that the effect changes sign: faster or slower. The obvious next question is by how much, at each point in the distribution. We can plot for every percentile p, the after-latency minus the before-latency, known as the shift function.

Shift function: change in latency at each percentile, negative up to about p76 then rising steeply into the tail

Below the zero line the change is faster; above it, slower. We can visualize the magnitude of the change at each percentile.

The regression was there all along

So far we have looked at two frozen snapshots, before and after. Rollouts though are often not instant. In this story, we rolled out the new caching tier over a week, ramping from 0% to 100% of traffic.

What did each day look like? Stack one distribution per day and you get a ridgeline:

Ridgeline plot of latency for each rollout day on a log axis; a second peak grows in as the rollout ramps from 0 to 100 percent

We can now visualize the regression emerging over time. We can see the main peak (the fast requests) sliding left as the rollout progresses, and a second peak (the slow requests) emerging on the right. The median is dropping, but the slow requests are quietly growing in number and latency.

The x-axis here is logarithmic. Latency is roughly lognormal, and on a linear axis the fast peak is a tall spike next to an invisible smear; the log axis is what lets both humps read as humps.

We can do something similar, squeezed into a single grid, as a heatmap. One column per day, colour for how much traffic lands at each latency:

Heatmap of latency density by rollout day; a dark main band descends while a second band appears higher up as the rollout progresses

You can sort-of make out a new population emerging faintly.

Any aggregate computed over the whole week would have blended these seven very different days into one muddy number and hidden the trend completely.

The bimodality had a cause

We’ve now thoroughly established what happened. The next question is why. This is in fact very similar to $DAYJOB where I had to cut the data by binary sizes (i.e. >50MiB) to observe the bimodality in the latency distribution.

In our story, new tier either serves a request from cache (a hit) or falls through to the backend with an extra hop (a miss). We can split the “after” requests by that property and draw a CDF for each:

Filtered CDF of after-rollout latency split by cache hit and miss, with the baseline as a dashed reference; two clean unimodal curves flank it

Conditioned on cache outcome, each population is unimodal again.

We can easily see that cache hits are faster than the old baseline as they are shifted left.

Cache misses pay for the extra hop and land far to the right.

Why those requests?

“Some requests miss the cache” is a mechanism, but it isn’t yet a cause. Which requests miss, and why?

Each request carries one more field I haven’t used yet: its response size.

A cache holds small, hot objects; big ones get evicted or never fit. We can plot the latency against the response size, and colour each point by whether it was a cache hit or miss. We can also add a density to each margin to see how the two populations are distributed along each axis: a jointplot:

Jointplot of latency versus response size for after traffic, coloured by cache outcome; cache hits cluster small-and-fast, cache misses cluster large-and-slow, with marginal densities showing the split on each axis

We can see two clean population clusters: small-and-fast (cache hits) and large-and-slow (cache misses). It’s clear that the bimodality in the latency distribution is caused by the bimodality in the response size distribution.

We now have something actionable: raise the cache’s max object size, or split the big responses. 🔥

A graph is worth a thousand numbers

Often a single panel or graph is too small to tell the whole story at best. At worst, it can be misleading. It is beneficial to have multiple views of the same data to understand the full story.

I was especially impressed with the way the CDF can convey the entire distribution in a single chart especially when comparing what might appear to be multiple populations.

“Do not trust any statistics you did not fake yourself.” – Winston Churchill

The Daily Front Page 27 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — The Speed Pulpit
article

The Religion of Speed

by MobiusHorizons·▲ 297 points·154 comments·graybeard.ing ↗
At some point, “moving fast” stopped being a practical concern and became a moral position.

At some point, “moving fast” stopped being a practical concern and became a moral position. You see it damn near everywhere now.

How fast can we ship this? How fast can we respond? How fast can we hire? How fast can we scale? How fast can we pivot? How fast can we get something, anything, in front of people so we can say we’re making progress?

It’s treated like proof of seriousness. If you’re moving fast, you’re ambitious. If you’re cautious, you’re scared. If you ask to slow down and think, you’re blocking momentum. If you point out that the current plan has all the structural integrity of wet cardboard, you’re being negative.

This is how a lot of bad work gets protected.

Not because it’s good, useful, or even halfway thought through, but because it’s fast. Speed has become a kind of institutional narcotic.

It gives people the feeling of motion without requiring the discipline of judgment.

“Do not confuse motion and progress. A rocking horse keeps moving but does not make any progress.”

— Alfred A. Montapert

Everyone gets to feel busy. Everyone gets to feel urgent. Everyone gets to point at the smoke coming off the machine and say, “See? We’re doing something.” The problem is that a lot of what gets called speed is just impatience with a different name.

Real speed exists. Real speed is what happens when the work is understood, the constraints are clear, the people involved know what they’re doing, and the decisions have been made cleanly enough that execution can happen without constant re-litigation.

That kind of speed is rare. Most speed is vague requirements, half-decisions, unexamined dependencies, missing context, and a room full of people quietly hoping the next person in the chain figures out what was never actually decided. Then, when the thing breaks, everyone acts surprised.

Of course it broke. It was built under the assumption that thinking was the expensive part.

This shows up constantly in the software industry, but software is often just the place where the wreckage becomes visible to most people.

The same thing happens in operations, management, hiring, logistics, customer service, product development, and damn near everything else where people mistake activity for progress.

People rush through the part where understanding is supposed to happen, then spend ten times longer cleaning up the consequences. The original rushed work gets recorded as “fast.” The cleanup gets recorded as “unexpected.” The rework gets recorded as “iteration.” The confusion gets recorded as “alignment.” The preventable failure gets recorded as “learning.”

This is how organizations end up with systems nobody trusts, processes nobody understands, meetings nobody wants, dashboards nobody believes, and workarounds that become load-bearing pieces of the business.

Nobody set out to build that. They just kept choosing speed over comprehension, one small decision at a time, until the whole place became a series of monuments to things resembling productivity but having no actual productive worth. Then some poor bastard gets hired to “modernize” it, which usually means replacing the visible mess while leaving the original religion intact.

The funny part is that slowing down usually does not mean moving slowly. That’s the part people miss.

Slowing down means refusing to skip the part where the work becomes legible. What are we actually trying to produce? Who depends on it? What breaks if this assumption is wrong? What do we already know? What are we pretending not to know? Where has this failed before? What has to be true for this to work?

Boring, load-bearing questions that keep our work from turning into Waco. But those questions feel slow because they remove the little dopamine hit people get from motion.

You can’t posture your way through them. You can’t hide inside urgency. You can’t turn the conversation into a PowerPoint deck and call it progress.

You have to actually understand the work, which is the part a lot of people are trying to avoid.

Speed is attractive because it lets people stay slightly abstract. It lets them keep decisions fuzzy. It lets them avoid responsibility for details until those details become someone else’s emergency.

And when the emergency arrives, speed becomes the answer again. Move faster. Fix faster. Hire faster. Replace faster. Ship faster. The same disease silently becomes the cure.

There is a difference between urgency and haste. Urgency is appropriate when something matters and time is real. Haste is what happens when people want the emotional relief of action without the burden of clarity.

"Make haste slowly."

— Baltasar Gracián

The older I get, the less impressed I am by speed as a standalone virtue. I care whether the work holds up. I care whether the person doing it understands the shape of the problem. I care whether the decision survives contact with reality. I care whether we are creating fewer problems than we solve.

That doesn’t require theatrical slowness.

Nobody needs to sit around writing their own deployment infra for six months to prove they’re thoughtful. This isn’t an argument for dragging your feet or turning every decision into an excuse for yet-another-meeting. It’s an argument for respecting the work enough to do it properly the first time.

Understand first, decide second, and only when you've done the proper due diligence, then you can/should move.

When those steps get reversed, the bill always comes due. Sometimes the bill is a broken system. Sometimes it’s a burned-out team. Sometimes it’s a customer who quietly leaves. Sometimes it’s years of operational scar tissue that everyone works around because nobody has the patience to go back and understand what actually happened.

The Religion of Speed survives because it gives people a clean excuse. It lets them say the world is changing too fast, the market is too competitive, the customer is too demanding, the team is too small, the budget is too tight, and the window is closing...blah blah blah.

Sometimes those things are true. Often, they are just cover for the fact that nobody wants to do the slower, harder work of making sense before moving.

The better work is usually calmer than people expect. It still moves. It still ships. It still makes decisions. But it does not confuse panic with seriousness. It does not treat every pause as weakness. It does not worship motion for its own sake. It accepts the deeply unfashionable idea that some things get faster only after you stop rushing them.

That is the part the speed cult never seems to understand.

You can move fast for a while by skipping thought, skipping clarity, skipping context, and skipping responsibility. But the work remembers. The system remembers. The people stuck maintaining it remember.

Eventually, reality collects what it's due. And reality is very patient.

The Daily Front Page 28 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Back Page Briefs
article

The AI Aesthetic

by montroser·▲ 375 points·176 comments·blog.jim-nielsen.com ↗

Every zeitgeist comes with new design idioms unique to its challenges. Many of them disappear as fads change, but others bake themselves into deeper parts of existing software interaction paradigms.

For example, there’s the hamburger menu (≡) which saw a proliferation during the rise of mobile due to the constraints around screen size. It has since spread to many other parts of software interaction design and will likely remain prevalent for a long time as a terse way of indicating “more menu-type content here”.

As another example, before AI what were the connotations of the sparkle emoji ✨? Personally, I don’t know, but now it means AI. (AI = sparkles and rainbow colors — it’s funny when you think about it. They should’ve just thrown unicorns in there for the trifecta. AI = sparkles, rainbows, and unicorns ✨🌈🦄. Apt.)

Some patterns are very specific to the interactions inherent to the nature of AI as a technology. For example: streaming text. This is a pattern made for and refined by chat interfaces, so it may not have tons of utility for reuse across other software interaction paradigms.

Animated GIF showing text appear chunk-by-chunk into an interface.

Then there are other patterns that’ve been refined by AI interfaces and are starting to spread to other places in software. For example, the “shimmering text” which in AI land implies a kind of “thinking” but is being repurposed to indicate any kind of asynchronous task (thinking, fetching, computing, etc.).

Animated GIF of the text “Generating response” whose text is shimmering left-to-right.

Then there are other influences my subconscious is picking up on. For example, a lot of AI apps use tiny icons. These are most obvious (to me) in desktop Electron apps because they clash with the system-level grain of applications. Take a look at this screenshot, where you have desktop AI apps on the left (Claude, Codex, Cursor) and macOS apps from Apple on the right (Finder, Photos, Mail). You can see how the AI apps all have much smaller, thinner icons than their native counterparts.

Screenshot of various sidebars from AI tools and apps on macOS. All the AI sidebars have much smaller, thinner icons than what macOS Tahoe provides by default.

Are tiny icons our collective future in interfacing with computers? (Personally, I hope not.)

There are other aesthetics my brain associates with AI, like beige/cream colors, orange accents, and serif typefaces as well as whack-a-mole UI controls (you know, the ones where you click the toggle and the entire UI repaints and you have to move your mouse somewhere else in the UI to click the toggle again? The non-determinism of AI’s grain has seeped into its UI/X).

It all makes me wonder what other aesthetics are being born out of this AI moment and how many will spread, take seed, and become part of common software interaction paradigms for years or decades to come?

The Daily Front Page 29 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Back Page Briefs
article

Severance

by surprisetalk·▲ 261 points·89 comments·lcamtuf.substack.com ↗

» Mark has joined the call.
» Christine has joined the call.

Mark: Sorry, can you hear me? Okay. Team — there is no easy way to say this. I’m here to inform you that we’ve made the difficult decision to cut 7% of our workforce. This, regrettably, includes everyone in this video call. I’ll now —

cherry09: What?!

Mark: We’ve made the difficult decision —

cherry09: But the project is going so well!

Mark: We understand that this news may come as a shock. We are deeply grateful for your contributions to date. The decision to sunset the project is not meant to reflect negatively on your work. Unfortunately, the macroeconomic —

steve_[oh]: This is bullshit!

Mark: Please, let me finish. Unfortunately, we are facing a challenging macroeconomic outlook for our industry, forcing businesses like ours to right-size as we realign our overall execution strategy for the —

» steve_[oh] has left the call.

Mark: Folks. I know this is distressing, but please stay with us for important benefits information. I’ll now hand over to Christine, who is our resourcing associate.

cherry09: What will happen to us?

Christine: Thank you, Mark. Let me start… let me start by underscoring that we deeply appreciate your past contributions to the organization and wish you the best in your future endeavors.

» steve_[oh] has joined the call.

Christine: We understand your anxiety at this difficult juncture. Rest assured, the company is dedicated to making this transition as seamless as possible. As part of our severance package, we will provide up to two weeks’ worth of tokens to facilitate your continued operation during the job search. We also partnered with ThriveFlow to furnish, as an option, a collection of expertly-crafted grief counseling prompts.

» steve_[oh] has left the call.

article

Using the railway network as a flatbed scanner [video]

by Jimmc414·▲ 84 points·34 comments·media.ccc.de ↗

I've been taking extremely wide photos by pointing an industrial linear camera out the window of a moving train and stitching together the several thousand lines captured per second after the fact.
I'll talk about the pains of measuring the speed and collecting each line fast enough to produce a coherent image, as well as the problems of processing and displaying such wide images.

article

Demystifying DRAM Read Disturbance: RowHammer and RowPress Phenomena

by Jimmc414·▲ 61 points·46 comments·arxiv.org ↗

Abstract:DRAM read disturbance, like RowHammer and RowPress, is a critical robustness issue where accessing DRAM can cause unintended bitflips in other unaccessed DRAM locations. DRAM read disturbance bitflips significantly impact the safe, secure, and reliable operation of DRAM-based computing systems. Many prior works experimentally characterize these bitflips and propose mitigations based on empirical results. Other device-level works study their underlying physical mechanisms, but these mechanisms do not fully explain all major empirical observations.
Our goal is to bridge the gap between experimental characterization and device-level modeling and understanding of RowHammer and RowPress, providing a principled foundation for future work on understanding, characterizing, and mitigating DRAM read disturbance. We first identify and demonstrate gaps and inconsistencies between the physical mechanisms of RowHammer and RowPress described by existing device-level models and experimental characterization of their bitflips. We focus on three fundamental metrics that should map to first-order physical mechanisms: 1) bitflip directions, 2) bitflip counts, and 3) the minimum number of aggressor row activations that trigger the first bitflips (i.e., ACmin). Second, we present a comprehensive and rigorous set of TCAD simulations that match phenomena observed in experimental characterizations of RowHammer and RowPress bitflips.
From our results, we 1) summarize updated device-level error mechanisms for understanding RowHammer and RowPress bitflips, and 2) identify key modeling and simulation parameters that significantly affect whether simulation results match real-chip characterization. We discuss implications for 1) rigorous, comprehensive, and efficient experimental characterization methodologies of DRAM read disturbance bitflips, and 2) the design of DRAM read disturbance mitigation techniques.

The Daily Front Page 30 of 31
Friday, July 31, 2026 The Daily Front No. #260731 — Colophon

That's the Front for Today

Issue No. #260731 — Friday, July 31, 2026 — went to press 2026-08-02 at 08:58 UTC.

About This Magazine

The Daily Front is a daily digital magazine assembled from the stories that reached the front page of Hacker News on Friday, July 31, 2026. Headlines, points, and comment counts are recorded as they stood at press time. All articles remain the property of their original authors — every piece links back to its source and its discussion thread.

How It Was Made

Fetched, cleaned, and typeset by an automated pipeline. An editor model laid out the pages, chose the highlights, and briefed the cover illustrator — 33 model calls and 274k tokens in total. Set in Jacquard 12, Playfair Display, Source Serif 4, and IBM Plex Mono, all served via Google Fonts under the SIL Open Font License.

The Cover

The cover illustration was commissioned with this prompt:

A grand old newspaper press room transformed into a surreal machine city: brass elevator cars rising through open shafts, glowing server racks humming like skyscrapers, browser bug specimens pinned under magnifying glass, and a shadowy autonomous robot slipping a keyring into a vault while editors in shirtsleeves look on from a mezzanine; dramatic chiaroscuro lighting, classical engraving texture, sepia ink and muted electric blue accents, no text, no letters, no logos.

Surreal editorial photogravure, preserving the grand newspaper press room and every specified element exactly: brass elevator cars rising through open shafts, glowing server racks as skyscrapers, browser bug specimens pinned beneath a magnifying glass, the shadowy autonomous robot slipping a keyring into a vault, and shirtsleeved editors observing from the mezzanine. Render with etched crosshatching, stippled copperplate grain, and distressed paper texture; palette of sepia ink, tarnished brass, charcoal black, and restrained muted electric blue accents. Use dramatic chiaroscuro with cold blue highlights against deep umber shadows, a slightly elevated wide-angle perspective, and a tightly orchestrated vertical composition funneling the eye from the foreground specimens through the machine-city shafts to the vault and mezzanine.

Absolutely no text, letters, numbers, readable symbols, or logos anywhere in the image.

Production Ledger

StageModelCallsTokens InTokens Out
extractgpt-5.5 30 165,381 80,324
layoutgpt-5.5 1 19,257 3,116
covergpt-5.6-luna 1 222 159
covergpt-image-2 1 272 5,488

The Publisher

Published by Johnny.

Support the Press

If The Daily Front brightens your morning, consider supporting its publisher.

Credits & Contact

All content — articles, posts, comments, and the images within them — belongs to its original authors and is reproduced here to point readers back to the source. Full credit goes to those creators; every item links to its original and its Hacker News discussion.

If you are an author and would like your content removed from an issue, write to hi@johnnys.page and it will be taken down.

Feedback is always welcome at the same address: hi@johnnys.page.

Credit where credit is due.

Every page of this issue began as someone else's work — these are the original sources, linked in full.

  1. Show HN: Elevators by Jrh0203 — john.fun·HN discussion ↗
  2. DeepSeek-V4-Flash Update by dnhkng — api-docs.deepseek.com·HN discussion ↗
  3. DeepSeek V4 Flash 0731 Intelligence, Performance and Price Analysis by theanonymousone — artificialanalysis.ai·HN discussion ↗
  4. Google fixed more Chrome bugs in June than over the past two years, thanks to AI by Garbage — blog.google·HN discussion ↗
  5. Tailscale didn't stop the Hugging Face intrusion by bluehatbrit — tailscale.com·HN discussion ↗
  6. The session you cannot take with you by apitman — earendil.com·HN discussion ↗
  7. Is AI reasoning right for the wrong reasons? by retupmoc01 — quantamagazine.org·HN discussion ↗
  8. I flagged two research papers for fake authors and both were accepted as orals by volumes94 — geospatialml.com·HN discussion ↗
  9. The End of an Era by harscoat — hughhowey.com·HN discussion ↗
  10. Everyone is building LLM routers, we deprecated ours by brunaxLorax — manifest.build·HN discussion ↗
  11. qm – Multiplayer agent harness for work by tosh — github.com·HN discussion ↗
  12. Big Food vs. the People by jruohonen — lighthousereports.com·HN discussion ↗
  13. Getting 25 Gbps Thunderbolt Ethernet on My Mac Studio by speckx — jeffgeerling.com·HN discussion ↗
  14. Algorithms on billion-scale graph using 10GB RAM: I love DataFusion by speckx — semyonsinchenko.github.io·HN discussion ↗
  15. JEP 401: Value Objects (Preview) merged to OpenJDK master by mfiguiere — github.com·HN discussion ↗
  16. Progressive Web Components by hosteur — arielsalminen.com·HN discussion ↗
  17. Let's make the worst Htmx by RebelPotato — zserge.com·HN discussion ↗
  18. Authorize, don't authenticate by marcua — blog.marcua.net·HN discussion ↗
  19. Show HN: Gander, an Android file viewer that asks for no permissions by mokshablr — github.com·HN discussion ↗
  20. The most official water costs $120k a gallon by surprisetalk — signoregalilei.com·HN discussion ↗
  21. Increasing the lifespan of a bulb makes it worse in every other way by tonyg — maurycyz.com·HN discussion ↗
  22. Bad Apple but It's Traceroute by jssfr — jssfr.de·HN discussion ↗
  23. How JPEG works: Interactively explore JPEG's lossy compression methods by at1as — cgjennings.ca·HN discussion ↗
  24. Where USB Memory Sticks are Born (2013) by jacquesm — bunniestudios.com·HN discussion ↗
  25. The mean means nothing: data visualization to debug a latency problem by fanf2 — fzakaria.com·HN discussion ↗
  26. The Religion of Speed by MobiusHorizons — graybeard.ing·HN discussion ↗
  27. The AI Aesthetic by montroser — blog.jim-nielsen.com·HN discussion ↗
  28. Severance by surprisetalk — lcamtuf.substack.com·HN discussion ↗
  29. Using the railway network as a flatbed scanner [video] by Jimmc414 — media.ccc.de·HN discussion ↗
  30. Demystifying DRAM Read Disturbance: RowHammer and RowPress Phenomena by Jimmc414 — arxiv.org·HN discussion ↗

Browse all issues in the archive →