Saturn is revolutionizing financial services with AI, building the operating system for financial advisors. Our mission is to democratize financial advice for one billion people by providing the world's most trusted, intelligent platform for financial planning and compliance.
This is a rare chance to build a category-defining company in a high-stakes, regulated environment. We operate with a
Dual Mandate
: relentless
Speed of Execution
to deliver reliable, robust products today, and dedicated
Speed of Learning
to explore the frontier of AI and unlock the next generation of features.
If you are driven by the pursuit of greatness, thrive on end-to-end ownership, and want to build the gold standard for AI trust and reliability, we invite you to build with us.
Role Overview
As a
Senior AI Engineer
at Saturn, you are the single-threaded owner of critical, customer-facing AI features that form the backbone of the advisory operating system. This is a highly autonomous role requiring robust software engineering fundamentals, deep LLM intuition, and an obsessive focus on product quality in a regulated domain.
You will own the entire feature lifecycle: from defining the Gold Standard with our domain experts (Guardians), architecting the agentic workflow, designing and building the comprehensive evaluation suites, to deploying and operating the solution reliably in production. You are expected to move quickly, making pragmatic, data-backed decisions that drive measurable value.
What You'll Do
1. End-to-End Feature Ownership and Architecture:
Ownership:
Take complete ownership of a product domain or complex feature, making architectural decisions independently and delivering high-quality results from concept through to long-term maintenance.
Defensive Design:
Architect and implement fault-tolerant AI systems, incorporating robust fallbacks (via a model-agnostic gateway), retries, and comprehensive monitoring and tracing, driven by the
Will to Care
about system reliability.
Explicit Orchestration:
Design and deploy complex, multi-step AI agents using explicit orchestration frameworks, ensuring state transitions are visible, testable, and auditable.
2. Drive Evaluation and Quality Discipline:
Design Evaluation Strategy:
Design, implement, and maintain the comprehensive, systematic evaluation framework (Evals Flywheel) specifically for your features to rigorously measure performance, manage regressions, and ensure quality compounds over time.
Domain Partnership:
Work directly with our domain experts to translate complex financial and compliance requirements into executable evaluation rubrics and Gold Standard datasets.
Quality Feedback Loop:
Instrument features end-to-end to rapidly diagnose probabilistic failures, converting production issues into high-priority regression tests.
3. Elevate Engineering Standards:
Technical Excellence:
Write clean, modular, Python code that raises the bar for the team. Actively participate in code review, using the process to mentor peers and reinforce architectural standards.
What You Have
5+ years
of professional experience in a highly demanding engineering environment.
Proven track record
(3+ years) of building, shipping, and operating scaled, impactful products where Generative AI or LLMs are a core component.
Deep Experience with Agentic Systems:
Expertise in RAG pipelines, systematic prompt engineering, agentic workflow orchestration, and defining reliability trade-offs for production systems.
Evaluation Focus:
Direct, demonstrable experience designing, writing, and maintaining automated evaluation frameworks (
evals
) used to rigorously test and improve probabilistic systems.
End-to-End Ownership:
A history of thriving in ambiguity, taking complete ownership of large features, and driving initiatives forward independently with a strong bias for action.
Engineering Excellence:
Mastery of Python and modern backend development practices, including system design, testing, CI/CD, and robust production observability.
Product & User Focus:
Strong product sense and the drive to quickly build domain expertise, translating user needs and compliance context into high-value technical solutions (the expression of
Will to Care
for the customer).
Saturn Values in Practice:
Earn Trust:
Building verifiably correct, explainable systems (Citation-First, Adviser-in-the-Loop).
Pursue Greatness:
Driving our Evaluation-Driven Development flywheel to compound quality daily.
Seek Truth:
Relying on data, traces, and customer feedback (Guardians) to inform every decision.
Be Audacious:
Taking decisive ownership and building intelligent agents that solve previously unsolvable problems in finance.
Will to Care:
Obsessively anticipating customer needs and building systems with extreme attention to detail, ensuring long-term quality, reliability, and the success of our users and peers.
In my previous post, I introduced the
simp
le programming language.
Near the end of that post, I mentioned that the parser isn't very efficient.
Let's try to optimize it!
If you're just curious about the title of the article,
you can skip ahead to
that section
.
(Re-)introducing
simp
First, a quick recap on the programming language being implemented here:
fn fibonacci(n) {
if n < 2 { n }
else { fib(n-1) + fib(n-2) }
}
let result = fibonacci(30);
It's a very
simple
, language consisting of:
Variables
Functions
Various kinds of expressions
Conditionals
It's not particularly useful, but it has enough
stuff
to be a good testing ground
for experimenting with parsers.
Simple AST
Currently, the implementation uses a
recursive descent
parser which produces an
abstract syntax tree
(AST). The AST represents code in a more structured form,
which is easier to work with in subsequent compilation passes.
Here is a small subset of the full AST definition:
Each kind of
Stmt
and
Expr
is in a
Box
, to allow these types to be
recursive
.
Sequences are stored in
Vec
s.
This kind of design is simple and flexible.
Unfortunately, it also uses a lot of memory, and each syntax node requires many separate allocations.
A note on benchmarking
Benchmarking is difficult. It's very easy to introduce bias or measure the wrong thing.
We'll be relying on two metrics:
Throughput in lines of code per second
Maximum memory usage
The scenario will be to parse a set of files with different sizes, starting at a few kB
and going up to 100 MB. We're effectively measuring how much time it takes to
construct
the AST, and how much memory it uses. We won't be measuring anything beyond that.
A 100 MB file has nearly 10 million lines of code! The reason we go that high is to prevent
the CPU from storing the entire file and its AST in CPU cache.
Don't over-interpret these numbers! All they'll tell you is the
relative performance
of different kinds of tree representations.
Simple tree AST results
The Y axis is in
millions
of lines of code per second.
The X axis is
log10(input size)
.
We're using
log10
for the X axis, otherwise all the smaller inputs would be crammed to the
left side of the plot. This way the results are more evenly spread out.
Our memory scales ~linearly with the size of the input, so no
log10
here, as a straight
line tells a better story.
We have nothing to compare this to right now, so these numbers are pretty meaningless.
We're clocking in at ~millions of lines of code per second, so it's not
too
slow...
but we can do better!
Amortizing allocations
Here's
StmtFn
, the syntax node for function declarations:
Each
name
and
parameter
in the declaration turns into a separately heap-allocated string.
String interning
Instead of storing owned
String
s, we'll store an
index
into a string buffer.
That string buffer will also keep track of where each string is in the buffer,
so that we can find equivalent strings later.
Interning
is a "get or insert" operation on that string buffer. It will allow
us to re-use memory for strings and identifiers which appear multiple times in
the same source file.
fn parse_expr_ident(c: &mut Cursor, ast: &mut Ast) -> Result<Expr> {
let token = c.must(LIT_IDENT)?;
let name = c.lexeme(token);
let id = ast.intern_ident(name);
Ok(Expr::Ident(Box::new(ExprIdent { name: id })))
}
This was a relatively painless change!
A nice property of interning all strings is that to compare them, all you need
is their ID, because identical strings are guaranteed to have the same ID.
You don't have to compare the actual string memory.
O(1) string comparisons, nice!
During traversal, we'll have to resolve these IDs to the actual strings if we
want to print them.
Interning results
In this graph, higher is better. So interning strings is actually faster!
This is a bit counter-intuitive, though. Why does it take
less time
to do
more work
? We're now hashing each string, looking it up in a map, and then
either returning an ID after optionally allocating something. Previously we
were always allocating.
The next graph will partially answer that:
A
page fault
is a signal to the operating system that the process is trying
to use a page it has
allocated
, and so the page must be assigned a physical
address in RAM.
The more memory you use, the more page faults there are. And they add up quick!
It turns out that using less memory also means the CPU has to do less work.
So, are we actually using less memory?
Yes, by quite a bit! We peak at ~2.3 GB for the "simple tree" representation,
and at ~1.8 GB when interning strings. That's more than a 20% reduction!
Memory usage and page faults are directly correlated. If you allocate and actually
fill 2 GB of memory, with a page size of 4096 bytes, we can predict ~500 thousand
page faults. That's exactly what we see in the graphs above.
Allocations are expensive!
Can we reduce memory usage even further?
Pointer compression
Enter "flat AST".
Here
is a nice article about this concept. To summarize:
Instead of storing full 64-bit pointers, we'll store 32-bit
indices
.
These indices are relative to the base of a
memory arena
.
The result is that nodes are allocated in contiguous arrays, which allow us to
amortize the cost of those allocations. Instead of doing a
malloc
call per node,
we'll be doing a
malloc
call for every
N
nodes.
I thought about whether to properly separate all the different methods in this article,
but ultimately decided not to. For each kind of AST, I have to copy and update the parser...
That's a lot of work, and I don't believe it would change the conclusion.
Our
StmtFn
no longer directly stores an
ExprBlock
as its
body
, instead it
stores an
ExprId
. That makes the whole struct quite a bit smaller.
We could also just have a single array of
Node
, but that would require combining
Stmt
and
Expr
into a new enum, and I wanted to avoid any extra indirection here.
While parsing, We'll ask the
ast
to store new nodes, instead of wrapping each one
in a
Box
:
fn parse_stmt_fn(c: &mut Cursor, ast: &mut Ast) -> Result<StmtId> {
assert!(c.eat(KW_FN));
let name = parse_ident(c, ast)?;
let params = parse_param_list(c, ast)?;
let body = parse_expr_block(c, ast)?;
Ok(ast.alloc_stmt(Stmt::Fn(StmtFn { name, params, body })))
}
The
ast
gives us a node ID in return. We can store that node ID inside other nodes.
We're not
interning
these nodes. Even if you have two identical syntax trees, they'll
be duplicated in the final AST.
This is pretty close to what
rustc
does! Is it any good?
Flat AST results
Yeah, that is an improvement!
The results are a
bit
noisy on smaller files. At those scales, the overhead of whatever
setup the
flat
AST has to do is higher than the actual parsing. My computer is also
a bit noisy while running these benchmarks.
We've reduced the memory usage again! But not by a whole lot.
The reason is that each variant of the
Stmt
and
Expr
enum is
unboxed
.
rustc
actually boxes the larger variants, which would reduce memory usage.
I didn't do that because it makes the benchmark slower, and it'd negate the whole
point of demonstrating usage of a memory arena.
Still, nice speedup!
...can we do better?
Bump allocation
A memory arena is a contiguous section of the
heap
. But the heap is
already
flat.
Why do we have to flatten it further?
Well, sort of flat. Memory pages used by a process don't actually have to be contiguous.
They could be anywhere in physical memory.
Let's bring in a library:
bumpalo
implements a
bump allocator
,
which can be used to allocate arbitrary types in a contiguous
memory arena
. It provides us with address
stability, as it never
reallocates
an existing block of memory, instead allocating a new block
(twice the size of the previous one) when it runs out of space.
Our AST will go back to looking like it did initially, a tree of pointers. But this time, the
nodes are bump-allocated, amortizing the cost of allocation in much the same way as the
flat AST
representation.
We're in Rust, with compiler-checked lifetimes. Each call to
Bump::alloc
will yield a
reference
to the allocated value. That reference has a lifetime tied to the lifetime of the
Bump
allocator
it came from. So we'll have to annotate each syntax node with a
lifetime
in order to be able to
store them:
This lifetime is now "infectious", and every time we allocate or use our AST, we'll have to contend with it.
Luckily, in most compilers, the lifetimes of intermediate data structures are quite linear:
After you parse, you use the AST, then discard it.
Is it worth it?
Bump allocation results
Yeah! Again, a huge improvement.
What about memory usage?
We're actually using
less
memory. That's because each variant of
Stmt
and
Expr
is significantly smaller.
Note that each
Vec
is a little bit bigger, because it now also stores a reference to the
Bump
allocator.
I'm convinced that just by adding string interning and bump allocation, our AST is already close to optimal,
given a
reasonable
level of effort.
But why stop at
reasonable
? We can do better.
Super flat
Something bothers me about all the previous layouts. We can dictate the relative position of
child nodes
in memory, but we still store an index or pointer to each one separately.
For example, given a
Binary
expression, we need to store two pointers to the
lhs
and
rhs
child nodes.
This gets
much
worse if we need a dynamic number of sub-nodes. Each
Vec
adds 24 bytes to a
syntax node!
We can take advantage of the fact that our AST nodes fit in one of two categories:
Nodes with a
static
number of child nodes
Nodes with a
dynamic
number of child nodes
Going back to the example of
Binary
, it's in the first category, as it always has exactly two child nodes.
What if each node's child nodes were
contiguous
? We would only have to store the index of the
first child
,
and the index of second child and onward would be simply relative to the first. So for
Binary
:
lhs
is at
nodes[index+0]
rhs
is at
nodes[index+1]
To store nodes with a dynamic number of child nodes, we'll also need to store the number of child nodes in some
kind of
length
field.
Each node will still have to store what its type is. For that, we'll use a
tag
field.
We can pack all of this information into just
8 bytes
. In pseudo-Rust with arbitrarily-sized integers:
The
length
and
first_child_index
fields aren't always going to be used. Sometimes a node is just its
tag, like a
Break
expression. For fixed-arity nodes like
Binary
, we don't need the
length
.
We can re-purpose these fields to store additional information:
The
Binary
operator field is a
u8
enum, so we can encode it into the
length
field, since it's unused.
A
String
doesn't have any child nodes. We can store the interned string ID (a
u32
) in the available space!
And so on. We can generalize this as
fixed-arity
nodes can hold a
3 byte
inline value, and
leaf
(tag only)
nodes can hold a
7 byte
inline value.
I don't know if this has a name already. I've been referring to this as a
super flat
representation.
It's inspired by
flat ASTs
, but goes a step further in the flattening.
Super flat implementation
This kind of representation is a
lot
more complex than anything we've looked at previously. We'll need
to set up some infrastructure to define these nodes, as manually indexing into a
nodes
array can be
quite error-prone!
We'll probably want to generate the required code. I've previously done it with
offline
codegen,
by writing a separate program which outputs a source file containing the AST definitions.
Due to the limitations of declarative macros (or maybe just a skill issue on my side),
we'll have to tell the macro which field belongs under which index.
Block expressions have a dynamic number of child nodes:
The macro expands to a function which
packs
the node into the AST,
and a few
getters
which retrieve child nodes.
There's also some miscellaneous runtime machinery to make it all work.
Macro expansion
Block expression
:
#[repr(transparent)]
pub struct ExprBlock {
packed: Packed,
}
impl ExprBlock {
const NUM_STATIC_FIELDS: usize = 1 + 0;
pub fn pack(ast: &mut AstBuilder, tail: Opt<Expr>, body: &[Stmt]) -> Self {
let index = ast.nodes.len() as u64;
if !(index <= u32::MAX as u64) {
::core::panicking::panic("assertion failed: index <= u32::MAX as u64")
}
let index = index as u32;
let len = body.len() as u64;
if !(len <= U24_MASK) {
::core::panicking::panic("assertion failed: len <= U24_MASK")
}
let len = len as u32;
ast.nodes.push(tail.packed);
for node in body {
ast.nodes.push(node.packed);
}
Self {
packed: DynTree::new(Tag::ExprBlock as u8, index, len).packed,
}
}
pub fn tail(self, ast: &impl NodeStorage) -> Opt<Expr> {
if true {
if !Self::check_tag(self.packed.tag()) {
::core::panicking::panic(
"assertion failed: Self::check_tag(self.packed.tag())",
)
}
}
let index = self.packed.dyn_tree().child() as usize + 0;
let node = ast.get(index).unwrap();
unsafe { transmute_node(node) }
}
pub fn body(self, ast: &impl NodeStorage) -> &[Stmt] {
if true {
if !Self::check_tag(self.packed.tag()) {
::core::panicking::panic(
"assertion failed: Self::check_tag(self.packed.tag())",
)
}
}
let index = self.packed.dyn_tree().child() as usize
+ Self::NUM_STATIC_FIELDS;
let len = self.packed.dyn_tree().len() as usize;
let nodes = ast.get_slice(index..index + len).unwrap();
unsafe { transmute_node_slice(nodes) }
}
}
Binary expression
:
pub struct ExprBinary {
packed: Packed,
}
impl ExprBinary {
pub fn pack(ast: &mut AstBuilder, lhs: Expr, rhs: Expr, op: BinaryOp) -> Self {
let index = ast.nodes.len() as u64;
if !(index <= u32::MAX as u64) {
::core::panicking::panic("assertion failed: index <= u32::MAX as u64")
}
let index = index as u32;
let value = op.into_u24();
if !(value <= U24_MASK as u32) {
::core::panicking::panic("assertion failed: value <= U24_MASK as u32")
}
ast.nodes.push(lhs.packed);
ast.nodes.push(rhs.packed);
Self {
packed: Tree::new(Tag::ExprBinary as u8, index, value).packed,
}
}
pub fn lhs(self, ast: &impl NodeStorage) -> Expr {
if true {
if !Self::check_tag(self.packed.tag()) {
::core::panicking::panic(
"assertion failed: Self::check_tag(self.packed.tag())",
)
}
}
let index = self.packed.tree().child() as usize + 0;
let node = ast.get(index).unwrap();
unsafe { transmute_node(node) }
}
pub fn rhs(self, ast: &impl NodeStorage) -> Expr {
if true {
if !Self::check_tag(self.packed.tag()) {
::core::panicking::panic(
"assertion failed: Self::check_tag(self.packed.tag())",
)
}
}
let index = self.packed.tree().child() as usize + 1;
let node = ast.get(index).unwrap();
unsafe { transmute_node(node) }
}
pub fn op(self) -> BinaryOp {
if true {
if !Self::check_tag(self.packed.tag()) {
::core::panicking::panic(
"assertion failed: Self::check_tag(self.packed.tag())",
)
}
}
let value = self.packed.tree().value();
<BinaryOp as Inline24>::from_u24(value)
}
}
String expression
:
#[repr(transparent)]
pub struct ExprStr {
packed: Packed,
}
impl ExprStr {
pub fn pack(value: StrId) -> Self {
let value = value.into_u56();
if !(value <= U56_MASK) {
::core::panicking::panic("assertion failed: value <= U56_MASK")
}
Self {
packed: Leaf::new(Tag::ExprStr as u8, value).packed,
}
}
pub fn value(self) -> StrId {
if true {
if !Self::check_tag(self.packed.tag()) {
::core::panicking::panic(
"assertion failed: Self::check_tag(self.packed.tag())",
)
}
}
let value = self.packed.leaf().value();
<StrId as Inline56>::from_u56(value)
}
}
You may notice some
unsafe code
in the implementation. We're doing something that the Rust
compiler can't handle for us, so we take matters into our own hands. We know that the AST
is correct by construction, so we
assume
that a given node's child nodes exist, and they
are the correct type.
This was a
lot
of work. How does it perform?
Super flat results
To me, this isn't a surprise. Look at the next graph:
A traditional tree representation uses
3x more
memory than our super flat representation.
Of course, you don't usually write 100 MB large files. Luckily, it performs better at every
scale.
Fin
Thanks for reading!
As always, the code is
available on GitHub
,
this time including the benchmarks and plot generation.
Show HN: Mirror_bridge – C++ Reflection powered Python binding generation
Modern C++ meets Multiple Languages: Automatic bindings using C++26 reflection - zero boilerplate, pure compile-time magic.
⚠️
EXPERIMENTAL
: This project requires C++26 reflection (P2996), which is not yet standardized. It only works with
Bloomberg's clang-p2996 fork
. Not recommended for production use until P2996 lands in standard C++26.
One C++ Class, Three Languages
// Write your C++ code oncestructCalculator {
double value = 0.0;
doubleadd(double x) { return value += x; }
doublesubtract(double x) { return value -= x; }
};
No manual binding code. No wrapper macros. Just pure C++26 reflection.
🎉
Supported Languages
Language
Status
API
Example
Python
✅ Stable
Python C API
import my_module
JavaScript
✅ Stable
Node.js N-API
const mod = require('my_module')
Lua
✅ Stable
Lua C API
local mod = require("my_module")
What is Mirror Bridge?
Mirror Bridge is a
header-only library
that uses C++26 reflection (P2996) to automatically introspect your C++ classes at compile-time and generate bindings for Python, JavaScript, and Lua. It discovers:
✅
Data members
- automatic getters/setters with type safety
✅
Methods
(any number of parameters) - variadic parameter support
✅
Constructors
- including parameterized constructors
✅
Method overloading
- automatic name mangling for overloads
✅
Smart pointers
-
std::unique_ptr
,
std::shared_ptr
with automatic conversion
structMathOps {
doubleadd3(double a, double b, double c) { return a + b + c; }
doublesum5(double a, double b, double c, double d, double e) {
return a + b + c + d + e;
}
voidreset() { value = 0; } // Zero parameters work too
};
ops=my_module.MathOps()
ops.add3(1.0, 2.0, 3.0) # 3 parameters ✓ops.sum5(1, 2, 3, 4, 5) # 5 parameters ✓ops.reset() # 0 parameters ✓# ANY number of parameters supported through variadic templates
Keep the Docker Compose workflows you love while deploying anywhere with ease.
Mix and Match Infrastructure
Mix and match cloud and on-premise across regions and providers
Deploy customer-facing apps on reliable cloud VMs
Run resource-hungry background jobs on budget-friendly bare metal servers
Transform that dusty Mac mini into a powerful staging environment
Effortless Operation
Zero-config distributed Docker cluster
No control plane to manage
Keep apps running even when machines go offline
Battle-tested WireGuard networking connects all your machines and workloads
right out of the box
Automatic HTTPS for your domains using Let's Encrypt
Developer Experience You
Love
Deploy and manage across machines with familiar Docker-like commands
Use the same Docker Compose from local dev to production
Find your services instantly with built-in service discovery and DNS
Communicate securely with services across machines without opening ports
How it works
Simple by design,
powerful in practice
Uncloud replaces complex clusters with a simple network of machines working
seamlessly together — no maintenance overhead, just reliable infrastructure.
Secure private network
Each machine joins a WireGuard mesh network with automatic peer discovery
and NAT traversal. Containers get unique IPs and can communicate directly
across machines.
Fully decentralised
Unlike traditional orchestrators, there's no central control plane to
maintain. Each machine maintains a synchronised copy of the cluster
state
through peer-to-peer communication, keeping cluster operations
functional
even if some machines go offline.
Smart CLI
Control your entire infrastructure using intuitive Docker-like commands from
anywhere. Deploy, monitor, and scale applications across all your machines
while the CLI only needs SSH access to a single machine.
Multi-provider cluster of 3 machines
Deploy anywhere
Run your apps on any Linux machine — from cloud VMs and
dedicated servers to bare metal at your office or home.
Automatic HTTPS
Get free TLS certificates and automatic HTTPS for your
domains with zero configuration using built-in Caddy reverse proxy.
Load balancing
Distribute traffic across container replicas running on
different machines for improved reliability and performance.
Service discovery
Access any service by its name from any container using the
built-in DNS that automatically tracks services across your network.
Infrastructure as Code
Define your entire app stack in a familiar Docker Compose
file. No need to learn a new config format.
No vendor lock-in
Mix cloud providers and your own hardware freely to
optimise costs and performance, without changing how you deploy or manage apps.
See in action
Deploy a highly available web app with automatic HTTPS across multiple regions and
on-premises in just a couple minutes.
Be part of the development
journey
Uncloud is an open source project I'm actively developing. I'd love to share this journey
with you. Subscribe to follow the progress, get early insights into new features, and be the
first to know when it's ready for production use. No marketing, ads or spam.
You have a Python codebase. Thousands of lines. It works. Your team knows it. Your tests cover it. Your CI/CD deploys it.
But there’s this one function. It shows up in every profile. It’s eating 80% of your runtime. You
know
it would be faster in C++.
The traditional answer? Rewrite it in C++, then spend sometime writing pybind11 boilerplate to call it from Python. Or just… don’t bother.
Mirror Bridge
is a third option: write C++, run one command, done.
The Setup
Let’s say you have a simple operation - a dot product:
// vec3.hppstructVec3{doublex,y,z;Vec3(doublex,doubley,doublez):x(x),y(y),z(z){}doubledot(constVec3&other)const{returnx*other.x+y*other.y+z*other.z;}// Compute the magnitude (norm) of the vectordoublelength()const{returnstd::sqrt(x*x+y*y+z*z);}};
To use this from Python:
./mirror_bridge_auto src/ --module vec3 -o.
That’s it. No binding code. Mirror Bridge uses C++26 reflection to discover your classes, methods, and fields automatically.
Python class: 0.11s
C++ via Mirror Bridge: 0.04s
Speedup: 2.9x
A ~3x speedup. Not bad, but not eye-popping. What’s going on?
This benchmark can be somewhat misleading
Each call from Python to C++ pays a toll - argument conversion, language boundary crossing, result conversion. For a trivial operation like
dot()
(3 multiplies, 2 adds), that toll dominates the actual work.
This is like benchmarking a Ferrari by measuring how fast it can start and stop at every intersection. You’re measuring overhead, not speed.
Dissecting the Cross-Language Barrier
What actually happens when Python calls a C++ function? Let’s trace through a single
a.dot(b)
call:
Method lookup.
Python sees
a.dot
and searches for the
dot
attribute. It checks
a.__dict__
, then
type(a).__dict__
, walking up the MRO (Method Resolution Order). For our bound C++ class, this eventually finds a descriptor that wraps the C++ method.
Argument boxing.
Python passes
b
as a
PyObject*
- a pointer to a heap-allocated structure containing a reference count, type pointer, and the actual data. The binding layer (nanobind, in Mirror Bridge’s case) must extract the underlying C++
Vec3
from this Python wrapper.
Type checking.
Is
b
actually a
Vec3
? The binding layer verifies this at runtime. In pure C++, the compiler guarantees types at compile time - no runtime check needed.
GIL management.
The Global Interpreter Lock might need to be considered. For simple numeric operations we keep it held, but for longer operations you’d release it to allow other Python threads to run.
The actual call.
Finally, we call the C++
dot()
method. This is the fast part - a few nanoseconds for 3 multiplies and 2 adds.
Result conversion.
The C++
double
result must be wrapped in a Python
float
object - another heap allocation, reference count initialization, and type pointer setup.
Return.
Python receives a
PyObject*
pointing to the new float.
For a function that does microseconds of work, this overhead is negligible. For a function that does
nanoseconds
of work - like our
dot()
- the overhead dominates. You’re spending more time crossing the border than doing actual computation.
Why Is C++ Faster in the First Place?
Even setting aside the cross-language overhead, why is C++ inherently faster for the same operation? Let’s look at what each language actually does for
dot()
:
Return a Python
float
object (heap-allocated, reference-counted)
Each multiplication:
Check types of both operands at runtime
Dispatch to the appropriate
__mul__
implementation
Allocate a new Python
float
for the result
Each addition: same story. We can see this by looking at the Python bytecode:
# Python bytecode for Vec3.dot() - run: python3 -c "import dis; dis.dis(Vec3.dot)"
0LOAD_FAST0(self)2LOAD_ATTR0(x)# dict lookup for self.x
4LOAD_FAST1(other)6LOAD_ATTR0(x)# dict lookup for other.x
8BINARY_MULTIPLY# type check, dispatch, allocate result
10LOAD_FAST0(self)12LOAD_ATTR1(y)# dict lookup for self.y
14LOAD_FAST1(other)16LOAD_ATTR1(y)# dict lookup for other.y
18BINARY_MULTIPLY# type check, dispatch, allocate result
20BINARY_ADD# type check, dispatch, allocate result
22LOAD_FAST0(self)24LOAD_ATTR2(z)# dict lookup for self.z
26LOAD_FAST1(other)28LOAD_ATTR2(z)# dict lookup for other.z
30BINARY_MULTIPLY# type check, dispatch, allocate result
32BINARY_ADD# type check, dispatch, allocate result
34RETURN_VALUE
That’s 18 bytecode instructions, each of which expands to many machine instructions. The
LOAD_ATTR
operations involve hash table lookups. The
BINARY_*
operations involve type checking and dynamic dispatch. By the end, we’ve done 6 attribute lookups, 5 arithmetic operations (each with type checks), and allocated several intermediate
float
objects.
That’s it. Four memory loads (some folded into
mulsd
), three multiplies, two adds, one return. No allocations. No type checks. No dictionary lookups. The entire function is 9 instructions.
This is why the “naive” benchmark shows only 3x speedup instead of 100x: Python’s overhead for calling a function (even a Python function) is substantial, so replacing the
implementation
with C++ helps less than you’d expect. The win comes when you stop crossing the boundary repeatedly.
The Real Benchmark: Surgical Optimization
In practice, you don’t call C++ a million times from Python. You identify a hot loop and move
the entire loop
to C++.
Imagine you have this in your Python codebase:
defhot_loop(n):"""This function showed up in your profiler."""direction=Vec3(1,1,1)dir_len=direction.length()# Magnitude (norm) of direction vector
total=0.0foriinrange(n):v=Vec3(i*0.1,i*0.2,i*0.3)total+=v.dot(direction)/dir_lenreturntotal
It’s called from dozens of places. The
Vec3
class is used throughout your codebase. You don’t want to rewrite everything - you just want
this loop
to go fast.
So you write the C++ version:
// Add to vec3.hppstructVec3{// ... existing code ...staticdoublehot_loop(intn){Vec3direction(1,1,1);doubledir_len=direction.length();doubletotal=0.0;for(inti=0;i<n;++i){Vec3v(i*0.1,i*0.2,i*0.3);total+=v.dot(direction)/dir_len;}returntotal;}};
# Python version
result=hot_loop(1_000_000)# C++ version - ONE call, all work happens in C++
result=vec3.Vec3.hot_loop(1_000_000)
Results (M3 Max MacBook Pro):
Python: 0.26s
C++: 0.004s
Speedup: 67x
That’s
the real number. You pay the Python→C++ toll once. The loop runs at full native speed.
Why Not Just Rewrite Everything in C++?
Because you probably shouldn’t.
Python gives you:
Rapid iteration (no compile step during development)
A massive ecosystem (PyTorch, pandas, requests, FastAPI…)
Readable glue code that connects everything
Easy onboarding for new team members
The Pareto principle applies aggressively here. 80% of your runtime might come from 20% of your code. Often it’s even more skewed - a single hot loop can dominate everything.
Mirror Bridge lets you surgically replace
just the hot 20%
with C++, keeping the other 80% in Python. You get:
Python’s ergonomics for the code that doesn’t need to be fast
C++ performance for the code that does
Zero binding boilerplate connecting them
The Magic: C++26 Reflection
Here’s where it gets interesting. How does Mirror Bridge discover your classes automatically?
The answer is
C++26 static reflection
(
P2996
) - a new language feature that lets code inspect itself at compile time.
When you run
mirror_bridge_auto
, it generates code that looks something like this:
The magic is in
bind_class<Vec3>
. Using reflection, Mirror Bridge can ask the compiler:
// What Mirror Bridge does internallyconstexprautotype_info=^Vec3;// "reflection operator" - get metadata about Vec3// Get all members of Vec3constexprautomembers=std::meta::members_of(type_info);// Iterate over them AT COMPILE TIMEtemplatefor(constexprautomember:members){// What's the name of this member?constexprautoname=std::meta::identifier_of(member);// Is it public?ifconstexpr(std::meta::is_public(member)){// Is it a function?ifconstexpr(std::meta::is_function(member)){// Bind it as a Python methodcls.def(name,/* pointer to member function */);}// Is it a data member?elseifconstexpr(std::meta::is_nonstatic_data_member(member)){// Bind it as a Python attributecls.def_readwrite(name,/* pointer to member */);}}}
The
^
operator (called the “reflection operator”) takes a type and returns a
compile-time value
representing that type’s metadata. From there,
std::meta
functions let you query everything about it: members, their types, their names, whether they’re public, static, const, etc.
This is fundamentally different from runtime reflection (like Java or C#). Everything happens at compile time:
Zero runtime overhead
- the reflection queries are resolved during compilation
Full type safety
- the compiler knows exact types, no casting or dynamic lookups
Works with templates
- you can reflect on template instantiations
Before P2996, generating Python bindings required either:
Manual listing (pybind11) - tedious and error-prone
Code parsing (SWIG) - fragile and limited
Macros (Boost.Python) - ugly and inflexible
Reflection gives us a fourth option:
ask the compiler directly
. It already knows everything about your types. Now we can access that knowledge programmatically.
The Comparison
For context, here’s what the traditional pybind11 approach requires:
Every single method needs to be manually listed. And when you add a new one? Update the Python bindings. Forgot one? Silent failure at runtime.
With Mirror Bridge: write C++, run command, done.
Getting Started
git clone https://github.com/FranciscoThiesen/mirror_bridge
cd mirror_bridge
./start_dev_container.sh # Pre-built Docker image with clang-p2996# Inside container - try the example from this postcd /workspace/examples/blog_vec3
../../mirror_bridge_auto .--module vec3 -o.
python3 benchmark.py
The
examples/blog_vec3
directory contains all the code from this post, ready to run.
The Docker image includes
clang-p2996
, Bloomberg’s experimental Clang fork that implements the reflection proposal. As P2996 moves toward standardization, expect this to land in mainline compilers.
The Bottom Line
The next time you’re staring at a Python profiler, wondering if it’s worth the hassle to optimize that hot loop in C++, the answer is:
yes, and it should take less than five minutes
.
Write the C++ version. Run
mirror_bridge_auto
. Replace the call. Ship it.
Mirror Bridge wouldn’t exist without the work of many others. Thanks to Wyatt Childers, Peter Dimov, Dan Katz, Barry Revzin, Andrew Sutton, Faisal Vali, and Daveed Vandevoorde for authoring and championing P2996. Special thanks to Dan Katz for maintaining the
clang-p2996
branch that makes this all possible today.
Herb Sutter’s
CppCon 2025 talk on reflection
was also a motivating force for this work, making the case of a common language to rule them all, which motivate me to try to make it easier for other languages to call C++.
The idea of using reflection to generate Python bindings isn’t new - it was explored by others before, including in
this ACCU 2025 talk
. Mirror Bridge builds on these ideas and packages them into a tool you can use today.
U.S.-Backed Ceasefire Is Cover for Ethnic Cleansing in Gaza & West Bank
Portside
portside.org
2025-12-04 05:35:52
U.S.-Backed Ceasefire Is Cover for Ethnic Cleansing in Gaza & West Bank
Mark Brody
Thu, 12/04/2025 - 00:35
...
AMY
GOODMAN
:
Israel has announced it will reopen the Rafah border crossing between Gaza and Egypt in the next few days as part of the U.S.-brokered ceasefire. According to the World Health Organization, at least 16,500 sick and wounded people need to leave Gaza for medical care. However, the border will only open in one direction: for Palestinians to exit.
Since the ceasefire began, at least 347 Palestinians have been killed in Gaza and 889 injured, according to the Gaza Health Ministry. In one recent incident, two children were killed by an Israeli drone for crossing the so-called yellow line, which isn’t always well marked. The children were brothers Fadi and Juma Abu Assi, the older of whom was 10 years old. They were gathering firewood for their disabled father. The Israeli military acknowledged the strike, saying, quote, “The Air Force eliminated the suspects in order to remove the threat,” unquote. Palestinians report Israeli forces continue to cross the yellow line on a near-daily basis.
This week, a coalition of 12 Israeli human rights groups concluded in a new report that 2025 is already the deadliest and most destructive year for Palestinians since 1967. On Monday, Israeli forces killed two teenagers in the West Bank in separate attacks as they carried out raids across the territory. In Hebron, soldiers fatally shot 17-year-old Muhannad Tariq Muhammad al-Zughair, whom they accused of carrying out a car-ramming attack that injured an Israeli soldier. Elsewhere, 18-year-old Muhammad Raslan Mahmoud Asmar was shot during a raid on his village northwest of Ramallah. Witnesses say the teen was left to bleed out as Israeli forces barred Red Crescent medics from approaching. The soldiers then seized his lifeless body. Last week, the U.N. reported more than 1,000 Palestinians have been killed by Israeli settlers and soldiers in the occupied West Bank and East Jerusalem since October 7, 2023.
This is Jeremy Laurence, spokesperson for the U.N. High Commissioner for Human Rights.
JEREMY
LAURENCE
:
Killings of Palestinians by Israeli security forces and settlers in the occupied West Bank have been surging, without any accountability, even in the rare case when investigations are announced. … Our office has verified that since the 7th of October, 2023, and up until the 27th of November of this year, Israeli forces and settlers killed 1,030 Palestinians in the occupied West Bank, including East Jerusalem. Among these victims were 223 children.
AMY
GOODMAN
:
For more, we’re joined in Ramallah, in the occupied West Bank, by Sari Bashi, an Israeli American human rights lawyer, former program director at Human Rights Watch. Her
piece
for
The New York Review of Books
is headlined “Gaza: The Threat of Partition.” She co-founded Gisha, the leading Israeli human rights group promoting the right to freedom of movement for Palestinians in Gaza.
Sari, welcome back to
Democracy Now!
Let’s begin with the latest breaking news, that Israel in the next few days will open the Rafah border crossing, but only for Palestinians to go one way: out. What is your response?
SARI
BASHI
:
So, obviously, people need to leave. You mentioned the numbers, in thousands, of patients waiting to leave. There are also students who have been accepted to universities abroad. This is after all of Gaza’s universities have been destroyed. So, it’s half good news.
But the bad news is that the Israeli announcement that it will not allow people to return to Gaza validates the fears that many have, that the ceasefire plan, the American plan and the Israeli plan, is essentially to continue the ethnic cleansing of Gaza. So, in Trump’s ceasefire plan, he committed to allowing Palestinians to return to Gaza, but that’s not what’s happening on the ground. There has been no construction authorized in the half of Gaza where Palestinians actually live. There has been no ability for people to come back home, and there are people who want to come back home.
And life in Gaza is nearly impossible for people because of very, very bad conditions. Eighty-one percent of buildings have been destroyed. People are living in tents that are now flooding in the rains. And it is very difficult to get construction and other materials approved by the Israeli authorities.
JUAN
GONZÁLEZ:
Sari, there have also been reports of secret evacuation flights from Gaza. Who is running these flights, and who determines who goes on those flights?
SARI
BASHI
:
I mean, that’s part of the problem. Early in the war, the Israeli government created what it calls the voluntary immigration administration, which is a secret, nontransparent government body that is supposed to encourage people from Gaza to leave, and make it possible for them to do so. There has been almost no transparency about what that organization is doing. There has been deliberate disinformation by Israeli ministers trying to amplify, artificially amplify, the number of people leaving Gaza to make people afraid. And there have been persistent reports about people paying large sums of money in order to leave, and that is after they have reportedly been asked to sign commitments not to return.
On the other hand, there is a very clear statement in the Trump peace plan, which was incorporated into a U.N. Security Council resolution, that people in Gaza are to be allowed to return home. And it’s perhaps not surprising that following the Israeli announcement this morning that Rafah crossing would be opened for exit only, the Egyptian government reportedly issued an objection and said no. It reminded us that the U.S. had promised that people in Gaza would be allowed to come home, as well.
JUAN
GONZÁLEZ:
And could you talk some about the situation in the West Bank, as we’ve reported these constant attacks by settlers and the military? What’s been the position of the Israeli government on this, and what role have the military played in these attacks?
SARI
BASHI
:
I mean, the concern is that the violence in Gaza, the violence in the West Bank, it’s not random. It’s directed toward ethnic cleansing. It’s directed toward getting Palestinians to leave. Certainly, that’s the case in Gaza, and the devastation and violence there are at a much higher scale than in the West Bank. But in the West Bank, too, just this year, we’ve had 2,000 Palestinians forcibly displaced from their homes through a combination of demolitions as well as settler violence. Every single day, Palestinians are injured or their property is damaged by settler attacks.
And to be clear, settlers are Israeli civilians who have been unlawfully transferred to the occupied West Bank by the Israeli government and have been taking over land that belongs to Palestinians. In the last two years, they have become increasingly emboldened in attacking Palestinians, taking over their olive groves, their flocks, stealing, throwing fire bombs into their homes, to the point where, according to the U.N., in 2025, a thousand Palestinians have been injured by settler attacks.
This is decentralized violence, but it is also state-sponsored violence. It is the government who put those settlers in the West Bank unlawfully, and quite often this violence takes place when Israeli soldiers either stand nearby and do nothing or even participate. The very ultranationalistic, messianic ideology has been infiltrated into the Israeli military, where you have soldiers whose job it is to protect everybody, including Palestinians, who are also settlers. And on the weekends, they come out in civilian clothing and attack and sometimes even kill Palestinians.
AMY
GOODMAN
:
And what about the Jewish and Israeli Jewish activists who stand alongside Palestinians to prevent the olive harvest from being destroyed, to prevent people from being attacked, the response of the Israeli military and the settlers?
SARI
BASHI
:
You know, it’s an indication of just how badly things have gotten, how many red lines have been crossed, because it used to be that Jewish settlers would be reluctant to attack Jews, because their ideology is racialized. They believe that they are acting in the name of the Jewish people. But recently, they have attacked Israeli Jews, as well, when Israeli Jews have come to accompany and be in solidarity with Palestinians.
So, we just finished the olive harvest season. It’s a very important cultural and also economic event for Palestinians. And this year it was particularly violent, with Israeli settlers coming, attacking people as they try to harvest, cutting down and burning down trees, intimidating people. And there have been cases even where Israeli Jewish activists came to be a protective force by accompanying Palestinian harvesters, and they, too, were attacked and even taken to the hospital with injuries. It is much more dangerous to be a Palestinian than to be an Israeli Jew in the West Bank, but the fact that settlers are also attacking Jews is an indication of just how violent, messianic and ultranationalistic this movement has become.
AMY
GOODMAN
:
The death toll from Israel’s more than two-year assault on Gaza has been reported as 70,000. A new study from the Max Planck Institute for Demographic Research in Germany said the death toll likely exceeds 100,000. Our next guest, Ralph Nader, has talked about
The Lancet
report saying it’s probably hundreds of thousands. Life expectancy, says the Max Planck Institute, in Gaza fell by 44% in 2023, 47% in 2024. If you can talk about this? And also, what is going to happen in Gaza now, where the truce stands?
SARI
BASHI
:
I mean, the violence against people in Gaza has been unprecedented. It’s been genocidal. And, you know, we have confirmed 70,000 people whose names and ID numbers have been reported by their families to the Palestinian Ministry of Health. There are thousands more people believed to be buried under the rubble, and thousands or tens of thousands of people who died from indirect causes. So, these are people who, because of a deliberate policy of starvation, died of malnutrition, died of communicable diseases, died of want. And I don’t know that we will ever know how many people died indirectly. This is for a very small population. It’s a population of 2 million people.
In Gaza right now, life has remained nearly impossible. The ceasefire promised reconstruction. It promised the crossings to be open. But the U.S. government introduced a number of caveats, and it actually, unfortunately, got those caveats approved by the U.N. Security Council. And an important caveat was that the reconstruction elements of the ceasefire would be limited to areas where Hamas had disarmed. And if the Israeli military was not able to disarm Hamas and other Palestinian armed groups in two years of war, it’s not clear how they’re going to be disarmed now. So, conditioning reconstruction on Hamas disarmament is basically saying reconstruction will be impossible.
The only place where the U.S. has said it will allow reconstruction is in the more than 50% of Gaza that is directly occupied by Israel, that is off-limits to Palestinians on penalty of death. So, that doesn’t leave people in Gaza with any options. Six hundred thousand schoolchildren have no schools. The hospitals are barely functioning. There’s nowhere to live. A million-and-a-half people are in need of emergency shelter supplies. The concern is that the way the ceasefire is being implemented is only going to contribute to ethnic cleansing, because anybody who can leave Gaza will leave, because it’s very clear that there’s not a future being allowed there.
And the United States has an opportunity to make good on its promise in two ways. First of all, it can make it clear that reconstruction has to be authorized in the part of Gaza where Palestinians live, not in the half of Gaza that’s off-limits to them. And second of all, it should demand, as per the ceasefire agreement, that Rafah be open in both directions, also to allow people to come home.
JUAN
GONZÁLEZ:
And I wanted to ask you how the Israeli media is reporting both the breaches in the cease — the constant breaches in the ceasefire agreement, as well as the constant attacks on Palestinians in the West Bank. And what’s been the impact on public opinion since the ceasefire came into effect, Israeli public opinion?
SARI
BASHI
:
You know, I’m very sorry to say that there’s been a long-term trend of the Israeli media becoming more nationalistic and less critical. And that’s been matched by a number of government moves to coopt and take over both public and private media. So, particularly since October 7th, 2023, the Israeli media, with a few notable exceptions, has been reporting government propaganda uncritically. So, what you will hear in the Israeli media is that suspects were shot in Gaza for crossing the yellow line or approaching troops. You won’t hear that those suspects were 10- and 12-year-old boys who were collecting firewood, and that under international law, you can’t shoot children because they cross an invisible line. In the West Bank, you will hear that terrorists were taken out, when you mean ordinary civilians who were trying to harvest their olive trees.
Haaretz
and a few other media outlets have been offering a different view of what’s happening, a more realistic view. But right now many Israelis choose not to — not to see what’s happening either in the West Bank or Gaza. And interest in what’s happening in Gaza has gone way down since the majority of Israeli hostages have been freed.
AMY
GOODMAN
:
Sari Bashi, we want to thank you so much for being with us, Israeli American human rights lawyer, former program director at Human Rights Watch. We’ll link to your
New York Review of Books
article
, “Gaza: The Threat of Partition.”
Calling dlopen() function from statically-linked binaries is a well-known
(in narrow circles) problem
*
. A common approach is not
supporting it at all. A usual explanation goes along the lines of:
You usually link statically to exclude unneeded functions from the binary.
But if you want to dynamically load a shared library, it likely itself
is linked dynamically against libc (doing otherwise requires extra legwork,
and if you have a few such shared libraries, themselves statically linked,
you duplicate code in each).
But that means you would need to carry around (big) libc.so, which
undermines the original idea of static linking (you could link your app
against dynamic libc and save space).
Alternatively, you could link entire libc into your static executable,
and export dynamic symbols (
ld --export-dynamic
). That avoids carriying
around extra libc.so file, but again requires extra legwork. And it still
undermines the original benefit of static linking, as your app will be
the size of libc.so (+ your app's code).
The summary is that if you want to use dlopen() from static binary,
you would need to do extra legwork, and would lose benefits of static
linking. Ergo, don't use dlopen() with static linking, use dynamic linking
instead! And as no reasonable person would use dlopen() with static linking,
let's remove dlopen() (and that's entire dynamic loader, quite a bloaty
piece of code!) support from statically linked libc, to let people who really
need static linking, reap the maximum benefits of it.
Those are all very valid arguments, but they are all based on a "plugin"
model: you have a common system, sharing a common libc.
That's not the only usage model though. There's another model, which we'll
call "FFI (Foreign Function Interface) model". It goes along the lines of:
Suppose you have a perfect, closed world application. Statically linked
of course.
But you want to go out to
dirty
bustling outside world (in other
words, let your application, or users of your appplication, to dlopen()
outside shared libraries).
There're absolutely no expectations or stipulations about which libc
is used by those shared libraries. In particular, there's no expectations
that libc of your application and external shared lib are the same. Or
that you know which libc is used by external lib at all. For example,
your static binary may be linked against musl libc, but you may want
to load (allow to load) glibc bloat lying in abundance on a typical Linux
system.
Again, the only thing you want is to maintain your static perfect world,
independent from outside hustle. But, at user discretion, you want to
allow this hustling outside world into your address space, by means of
dlopen().
This cute project is a proof-of-concept solution for this usecase.
Implementation idea #1: a (custom) ELF loader. Problem: trying to implement
"full" ELF loader (recursively load dependent .so, etc.) is prolematic, e.g.
because glibc (libc.so) is tightly coupled with dynamic loader aka interpreter
(ld.so). If you just load libc.so, and its dependency ld.so, a lot of
stuff in ld.so will remain uninitialized, then libc.so will call into ld.so,
which will crash. To properly initialize ld.so, it must be "run", i.e.
execution should go into its entry point (and not just ELF INIT func). But
when started that way, ld.so loads an executable, which then terminates.
Idea #2: Make ld.so load an executable which will jump back into our custom
loader. This way, both ld.so will be initialized, and we get back control.
Coupled with setjmp/longjmp, this can be turned into a reusable library.
Its structure is:
Custom ELF loader, built against any libc or lack thereof. This should be
simplified loader, without support for loading shared libs, etc. The only
thing it needs to load is a "helper" target executable and its INTERP.
It calls into INTERP, the call wrapped with
setjmp
. The corresponding
longjmp
is wrapped into a global func, whose address (in ascii) we pass as
a command-line argument to the "helper" target binary.
The "helper" binary should be linked against native libc of the target
environment whose shared libs we want to load (e.g., glibc).
Target binary is also linked agains target libc's libdl. The binary captures
addresses of dlopen()/dlsym()/etc. funcs into an array, and performs function
call into the address passed as a command-line arg, passing array as a
function arg.
That address is, as mentioned above, is a function which performs
longjmp
,
after storing the passed
dlopen()
, etc. function addresses for future use.
After longjmp, we're back to our application, which now has access to
dlopen()
, etc. of the target system.
Building and running
cd src
Build target helper executable:
make -f Makefile.fdlhelper
. As explained
above, it should be built against target system from which you want to load
shared libs dynamically using dlopen(). (If you build this on a typical Linux
system, it will be built against glibc.)
Build static, stdlib-less sample application:
make STDLIB=0
. (You must
pass
STDLIB=0
for full effect. By default, the sample is built against
stdlib, which is helpful during development/debugging.)
Run the sample:
./foreign_dlopen_demo
. While it is static, it will
dynamically load libc.so.6 and call printf() from it. (All this using
fdlhelper
executable built in a previous step.)
Credits
"Foreign dlopen" idea and implementation is by Paul Sokolovsky. The
implementation is based on the ELF loader by Mikhail Ilyin:
https://github.com/MikhailProg/elf
, the original README of that project
follows.
ELF loader
A small elf loader. It can load static and dynamically linked ELF EXEC and DYN (pie) binaries. The loader is PIE program that doesn't depend on libc and calls kernel services directly (z_syscall.c).
If the loader needs to load a dynamically linked ELF it places an interpreter (usually ld.so) and a requested binary into a memory and then calls the interpreter entry point.
Build
Default build is for amd64:
Build for i386:
Small build (exclude all messages and printf):
Load binaries
Load ls:
Load galculator:
$ ./loader /usr/bin/galculator
‘We Must Stop Tinkering Around the Edges’: Van Hollen Makes Case for Medicare for All Amid ACA Fight
Portside
portside.org
2025-12-04 05:22:24
‘We Must Stop Tinkering Around the Edges’: Van Hollen Makes Case for Medicare for All Amid ACA Fight
Mark Brody
Thu, 12/04/2025 - 00:22
...
‘We Must Stop Tinkering Around the Edges’: Van Hollen Makes Case for Medicare for All Amid ACA Fight
Published
Senator Chris Van Hollen (D-Maryland) | Allison Bailey for The New York Times
Democratic US Sen. Chris Van Hollen on Monday became the latest lawmaker to champion
Medicare for All
as the best solution to the country’s
healthcare
woes as tens of millions of Americans face soaring private insurance premiums.
“Yes, let’s extend the [
Affordable Care Act
] tax credits to prevent a huge spike in healthcare costs for millions,” said Van Hollen. “Then, let’s finally create a system that puts your health over corporate profits. We need
Medicare
for All.”
Van Hollen’s remarks came as lawmakers continued to negotiate a possible deal to extend enhanced ACA subsidies that are set to lapse at the end of the year, an outcome that would further drive up healthcare costs for millions.
Politico
reported
late Monday that most senators “believe the chances for a bipartisan breakthrough” before a planned vote next week “are roughly zero.”
“Instead, the most likely outcome is that Senate Democrats put up a bill that has little
GOP
support for a vote, if any, while
Republicans
offer a competing bill of their own,” the outlet noted. “And even those partisan proposals remained in flux as lawmakers returned to
Washington
from a weeklong recess.”
Neither side of the negotiations is offering much more than a Band-Aid on a gaping wound. Democratic leaders want a clean extension of the subsidies to avert
catastrophic cost increases
, while President
Donald Trump
and Republican lawmakers are demanding new restrictions on the ACA that would
make the system worse
.
A handful of progressive lawmakers have used the worsening US healthcare crisis to
make the case for a fundamental overhaul
, one that would replace the for-profit model with a Medicare for All system that guarantees coverage to everyone for free at the point of service—and at a
lower overall cost
than the current system.
It is time for the labor movement to use its organization and resources to fight to stop this wave of repression. As part of that effort, labor needs to propose a freedom agenda for immigrants. | Photo: David Bacon
[This article is part of a Labor Notes roundtable series:
How Can Unions Defend Worker Power Against Trump 2.0?
We will be publishing more contributions here and in our magazine in the months ahead.
Click here to read the rest of the series.
—Editors]
During the Cold War, many of the people with a radical vision of the world were driven out of our labor movement. Today, as unions search for answers about how to begin growing again, and regain the power workers need to defend themselves, the question of social vision has become very important. What is our vision in labor? What are the issues that we confront today that form a more radical vision for our era?
The labor movement needs a freedom agenda. When Zohran Mamdani spoke after his primary election victory in New York City, he declared, "We can be free and we can be fed." Mamdani is telling our unions and workers that we can reject the old politics of agreeing to war abroad and repression at home, in the hopes that at least some workers will gain.
PEACE ECONOMY
We don't have to go back to the CIO and the radicalism of the 1930s to find voices in labor calling for a radical break from the past.
In the 1980s, while Reagan was President, William Winpisinger, president of the Machinists, told his members, thousands of whom worked in arms plants, that they would gain more from peace than from war. Under union pressure in 1992, Congress even passed a bill calling for redirecting a small part of military spending into conversion for a peacetime economy.
One big part of that program is peace. Another is reordering our economic priorities.
Right now working class people have to fight just to stay in the cities. They’re being driven out, and this has a disproportionate impact on workers of color. Unions and central labor councils need to look at economic development, and issues of housing and job creation. That would start to give us something we lack, a compelling vision.
IMMIGRANT RIGHTS
Since 2006 millions of people have gone into the streets on May Day. The marches in 2006 were the largest outpourings since the 1930s, when our modern labor movement was born. In one of the best things our labor movement has done, we began raising the expectations of immigrants when we passed the resolution in Los Angeles in 1999 changing labor’s position on immigration.
We put forward a radical new program: amnesty, ending employer sanctions, reunification of families, protecting the rights of all people, especially the right to organize. That came as a result of an upsurge of organizing among immigrant workers themselves, and support from unions ranging from the United Electrical Workers to the Carpenters.
Congress, however, has since moved to criminalize work and migration, and proposed huge guest worker programs. States have passed bills that are even worse.
Mississippi, for instance, made it a state felony for an undocumented worker to hold a job, with prison terms of up to ten years. Florida has made it a crime to give an undocumented person a ride to a hospital. And administrations from Bush to Trump have implemented by executive order the enforcement and guest worker measures they couldn’t get through Congress.
LABOR’S SILENCE
When Democrats campaigned against Trump by accusing him of sabotaging an immigration bill that would have put billions of dollars into immigration enforcement, they prepared the way for Trump's onslaught once he was elected. Labor, which should have prepared our own members for the fight to come, stayed silent.
In the wave of raids that have followed, hundreds of our own members have been taken, not just for deportation, but on bogus criminal charges or simply swept off the streets by masked agents. Unorganized workers have been terrorized by the raids—a gift to employers as workers are pressured to give up any hope of a union or a higher wage.
Some unions today are part of the network fighting to protect workers and communities from immigration raids. SEIU California leader David Huerta faces misdemeanor federal charges after opposing ICE during a raid in Los Angeles' garment district.
It is time for the labor movement to use its organization and resources to fight to stop this wave of repression. As part of that effort, labor needs to propose a freedom agenda for immigrants that will really give people rights and an equal status with other workers on the job, and their neighbors in their own communities.
DEFEND CIVIL RIGHTS
In the past, the possibility of fighting for our ideals—what we really want—has been undermined by beltway deal making. We have to be consistent in our politics.
Labor needs an outspoken policy that defends the civil rights of all sections of U.S. society, and is willing to take on an open fight to protect them. If Trump's raids and terror campaigns scare unions into silence, few workers will feel confident in risking their jobs (and freedom) to join them.
Yet people far beyond unions will defend labor rights if they are part of a broader civil rights agenda, and if the labor movement is willing to go to bat with community organizations for it.
JOBS FOR ALL
A new direction on civil rights requires linking immigrant rights to a real jobs program and full employment economy. It demands affirmative action that can come to grips with the devastation in communities of color, especially African-American communities.
And none of that can be done without challenging Trump's war policies, but equally the war policies that have come from Democratic administrations.
CLIMATE JUSTICE
Today part of a freedom agenda is not only conversion from military production, but conversion from fossil fuel dependence.
Jeff Johnson, past president of the Washington State Labor Council, drew the connection between labor support for climate conversion legislation and social justice. "I knew we had to educate my members, so that they would understand that we can't support more fossil fuel exploration," he told me. "We have an existential crisis that is social, political and racial, in addition to climate. And we know that the impact of climate change will hit those communities who had the least to do with causing it."
INTERNATIONAL SOLIDARITY
At the heart of any radical vision for our era is globalization—the way unions approach the operation of capitalism on an international scale.
When the old Cold War leadership of the AFL-CIO was defeated in 1996, I heard incoming AFL-CIO Secretary-Treasurer (later President) Richard Trumka say unions should find partners in other countries in order to face common employers. At the time it represented a big change—that unions would cooperate with anyone willing to fight against our common employers. It rejected by implication the anti-communist ideology that put us on the side of employers and U.S. foreign policy, and that shamed us before the world.
Three decades later, however, this idea is no longer radical enough. It’s an example of pragmatic solidarity, although it was, at the time, a good first step out of that Cold
War past.
What’s missing is a response from the labor movement to U.S. foreign policy. International solidarity involves more than multinational corporations. There is no doubt that military corporations benefit from selling the bombs that Israel uses in its Gaza genocide, but political support from Trump, and Biden before him, for Israel is more than just an effort to defend their profits.
Corporate globalization and military intervention are intertwined, and in the labor movement there’s not enough discussion about their relationship. That’s why we got manipulated in the response to 9/11, and by justifications for the Iraq and Afghanistan wars.
WHICH SIDE ARE YOU ON?
Unions in the rest of the world are not simply asking us whether we will stand with them against General Electric, General Motors, or Mitsubishi. They want to know: Will you take action to oppose the Gaza genocide and arms shipments? What is your stand about aggressive wars or coups?
U.S. corporations operating in a country like Mexico or El Salvador are, in some ways, opportunistic. They’re taking advantage of an existing economic system, and trying to make it function to produce profits. They exploit the difference in wages from country to country for instance, and require concessions from governments for setting up factories in their countries.
But what causes poverty in a country like El Salvador? What drives a worker into a factory that, in the United States, we call a sweatshop? What role does U.S. policy play in creating that system of poverty?
In our union movement, we need that kind of discussion. We turn education into simply a technical matter about techniques for grievance-handling and collective bargaining. We don’t really work with our members to develop a framework to answer these questions. So our movement becomes ineffective in fighting about the issues of war and peace, globalization, and their consequences, like immigration.
EDUCATE THE MEMBERS
When the AFL-CIO campaigned in Washington against the Central American Free Trade Agreement (CAFTA), for instance, labor lobbyists went up to Capitol Hill and tried to mobilize pressure on Congress to defeat it. But what was missing was education at the base of the labor movement.
Had we educated and mobilized our members against the Contra war and the counterinsurgency wars in El Salvador and Guatemala (and certainly many of us tried to do that), U.S. workers would have understood CAFTA more clearly a decade later.
The root of this problem is a kind of American pragmatism that disparages education. We need to demand more from those who make the decisions and control the purse strings in our unions.
Since grinding poverty in much of the world is an incentive for moving production, defending the standard of living of workers around the world is as necessary as defending our own. The logic of inclusion in a global labor movement must apply as much to a worker in Mexico as it does to the nonunion worker down the street.
That’s why the debate over the Iraq War at the AFL-CIO convention was so important. From the point when it became clear that the Bush administration intended to invade Iraq, union activists began organizing a national network to oppose it through U.S. Labor Against the War. What started as a collection of small groups, in a handful of unions, became a coalition of unions representing over a million members, the product of grassroots action at the bottom of the U.S. labor movement, not a directive from the top.
That experience was put to work when the genocide began in Gaza, as national and local unions and activists organized the National Labor Network for a Ceasefire. When the United Auto Workers endorsed its call, the union also set up a divestment and just transition working group, "to study the history of Israel and Palestine, the union’s economic ties to the conflict, and to explore how to achieve a just transition for U.S. workers from war to peace."
Opposing intervention and war means fighting for the self-interest of our members. It means being able to identify that self-interest with the interest of Palestinians in Gaza and the West Bank. The same money that pays for bombs for the Israeli government is money that doesn’t get spent on schools here at home. We can’t have a full-employment economy in the U.S. without peace.
The arguments by political centrists in the Democratic Party that we must choose to fight only about workers' immediate economic interests, and stay silent about Gaza or racism, hides the connections workers have to make to challenge the sources of their own poverty and the attacks against them. As veteran organizer Stewart Acuff says, "While economic justice must run through everything our movement does, we cannot deemphasize any strand of injustice."
Union members are not ignorant of this basic fact. In fact, they are becoming more sophisticated, and better at understanding the way global issues, from war to trade, affect the lives of people in the streets of U.S. cities. But the percentage of union members is declining, and the organization they need to put that understanding into practice is getting smaller. Deeper political awareness alone will not create a larger labor movement.
NEED A SOCIAL MOVEMENT
Just after World War II , unions represented 35 percent of U.S. workers. It’s no coincidence that the McCarthy years when the Cold War came to dominate the politics of unions was the moment when that strength began to decline.
By 1975, after the Vietnam War, union membership had dropped to 26 percent. Today only 9.9 percent of all workers, and 5.9 percent in the private sector, are union members.
Declining numbers translate into a decline in political power and economic leverage. California (with one-sixth of all union members) and New York (with one-eighth) have higher union density than any others. But even there, labor is facing an all-out war for political survival.
While the percentage of organized workers has declined, unions have made important progress in finding alternative strategic ideas to the old business unionism. If these ideas are developed and extended, they provide an important base for making unions stronger and embedding them more deeply in working-class communities. But it’s a huge job. Raising the percentage of organized workers in the United States from just 10 to 11 percent would mean organizing over a million people. Only a social movement can organize people on this scale.
In addition to examining structural reforms that can make unions more effective and concentrate their power, the labor movement needs a program that will inspire people to organize on their own, one which is unafraid to put forward radical demands, and rejects the constant argument that any proposal that can't get through Congress is not worth fighting for.
A BROADER VISION
The labor movement has to become a movement that inspires people with a broader vision of social justice. Our standard of living is declining. Workers often have to choose between paying their rent or their mortgage or having health care. There’s something fundamentally wrong with the priorities of this society, and unions have to be courageous enough to say it.
Working families need a decent wage, but they also need the promise of a better world. For as long as we’ve had unions, workers have shown they’ll struggle for the future of their children and their communities, even when their own future seems in doubt. But it takes a radical social vision to inspire the wave of commitment, idealism, and activity.
The 1920s were filled with company unions, the violence of strikebreakers, and a lack of legal rights for workers. A decade later, those obstacles were swept away.
An upsurge of millions in the 1930s, radicalized by the Depression and left-wing activism, forced (relative) corporate acceptance of the labor movement for the first time in the country's history.
There are changes taking place in our unions and communities that can be the beginning of something as large and profound. If they are, then the obstacles unions face today can become historical relics as quickly as did those of an earlier era.
David Bacon is a labor journalist and photographer, author of Illegal People: How Globalization Creates Migration and Criminalizes Immigrants and other books.
New Book Unpacks How Zionism Is Mythologized in Hasbara
Portside
portside.org
2025-12-04 04:54:41
New Book Unpacks How Zionism Is Mythologized in Hasbara
Geoffrey
Wed, 12/03/2025 - 23:54
...
Selling Israel: Zionism, Propaganda, and the Uses of Hasbara
Harriet Malinowitz,
Olive Branch Press/Interlink Publishing Group
ISBN: 9781623715809
Retired English professor
Harriet Malinowitz
spent 20 years writing and researching
Selling Israel
,
and the exhaustively detailed result probes the ways Zionist mythology has been packaged to prop up support for the Jewish state.
She starts by introducing the concept of hasbara, which, she writes, can be “bluntly described as propaganda, but in fact comprises a huge network of government ministries, nongovernmental organizations, nonprofit agencies and charities, campus organizations, volunteer groups, watchdog bodies, professional associations, media networks, fundraising operations, and educational programs that aim to fortify a Zionist-defined notion of Jewishness in persons within Israel, the United States, and other countries.”
Malinowitz’s text deconstructs the ways these entities have been used to support Israel as a place essential to Jewish safety and security. But in addition, she includes the country’s foundational reliance on Biblical history, horrific 19th-century Eastern European pogroms, and the Nazi Holocaust to buttress its claims that Israel is a necessary bulwark against anti-semitism.
Let’s start with the Bible. Malinowitz writes that
David Ben-Gurion,
Israel’s first prime minister, frequently said that “the bible is our mandate.” Similarly, in 1947
Chaim Weitzman
, Israel’s first president, told the United Nations that, “God made a promise: Palestine to the Jews.” Seventy-two years later, in 2019, Danny Danon, Israel’s then-ambassador to the UN, reiterated this message, holding up a Bible and telling his audience that the book is Israel’s “deed” to the land, evidence of an “everlasting covenant” with God.
Logic assumes that this claim has been questioned, but for the most part, it has not been.
For years, most Zionists denied Palestinian’s existence and repeatedly stated that the land was both empty of people and nonarable.
And those pesky Palestinians who’d been living on that land? For years, most Zionists denied their existence and repeatedly stated that the land was both empty of people and nonarable, claims that were easily refuted since European nations that had long imported Palestinian barley, sesame, wheat, and Jaffa oranges. As Lebanese historian
Marwan R. Buheiry
wrote, “Palestine had always been an important producer of key agricultural commodities and was experiencing a significant expansion of agriculture and allied manufactures at least two generations before the arrival of the first colons from East Europe.”
Malinowitz argues that Zionists knew this, but consciously chose to tell a tall tale about “a land without people for a people without land.” This idea was supported by two additional, if contradictory, notions: That the indigenous population of Palestine could be “transferred” to a host of Arab nations and/or that those who remained in Palestine would benefit from “Zionist accomplishments.”
The fact that many Palestinians saw the situation differently was unsurprising. Indeed, several Jewish writers and theorists had foreseen Palestinian resistance, but they were ignored or condemned as wrongheaded.
Instead, the Jewish National Fund, a group created in 1901 when Zionism was little more than the fledgling dream of a small percentage of world Jewry, began to raise funds to “transfer Palestinian Arabs to Iraq, Syria, and even Transjordan.” Their mission has never wavered, although it has expanded. Among its many efforts, the JNF has solicited donations for community development, with projects to “green the desert” and “drain festering swamps” to make more of the country suitable for farming.
Malinowitz reports that neither effort has panned out as planned. The 1950 draining of Hula Lake, she writes, is just one example. Although drainage was meant to redeem the land for agriculture and turn “water that had been a menace to health into a blessing,” it has instead caused environmental calamity. “Among the casualties in the Hula Valley,” she explains, “were the disappearance of 119 animal species, the extinction of numerous fresh water plant species, the rerouting of migratory birds flying between Europe and Africa, and the unanticipated population explosion of the vole, which rampantly destroyed crops and led many to cease farming there – in turn accelerating soil deterioration.” And the greening of the desert? The desire to create a “garden oasis” led Israelis to plant Aleppo pine and cypress trees, rather than trees native to the area. These trees have obliterated grasslands. Moreover, since the natural habitat of the Aleppo pine is wetter, “when planted in arid areas they require intense irrigation to facilitate sapling growth and suffer major, irreparable losses during droughts. Yet neither blunder has dissuaded the Jewish National Fund from working to garner support for these projects. What’s more, they continue to raise money to “strengthen Israel’s peripheral regions.”
Educational efforts are, of course, key to these initiatives, both within and outside of Israel. The use and misuse of the Holocaust come into play here. For several decades after World War II, Malinowitz writes that Nazi death camps were rarely invoked because Israeli leaders felt that the annihilation of six million Jews could be read as a sign of weakness, as if the victims had somehow allowed themselves to be led to their deaths, like “lambs to the slaughter.” This began to change during the 1961 trial of high-ranking Nazi, Adolf Eichmann. As world revulsion to Nazi ideology mounted, Israel’s leaders began an ideological pivot, now arguing that “an armed Jewish state was both essential for preventing an ever-looming second Holocaust and would have mitigated the first one.” Images of gun-toting Israeli soldiers became a source of national – and international – pride that dovetailed with constant rhetoric to dehumanize Palestinians as an unprovoked, menacing threat.
This is where hasbara has been most effective.
Since the 1970s, hasbara has presented Israel as a rugged, innovative, and pioneering nation where queer identity is respected, environmental sustainability is championed, and Jews are able to live in safety, security, and prosperity. That these claims are specious has not mattered to Israel’s supporters, Malinowitz writes. As Gaza is pummeled into oblivion and the West Bank is increasingly occupied by people eager to eliminate every Palestinian person, peace seems less and less possible. It’s both sad and appalling.
Gaza is pummeled into oblivion and the West Bank is increasingly occupied by people eager to eliminate every Palestinian person, peace seems less and less possible.
Worse, in the more than two years since October 7th, the conflation between Judaism and Zionism has ramped up. Efforts to decouple them remain ongoing, but
Selling Israel
provides both the hard facts and historical background necessary to contest Zionism as the best and only way to combat hatred of Jews. In parsing Zionism’s 19th-century roots and the heinous pogroms the movement was responding to, Malinowitz has given readers ammunition for fighting anti-Semitic bigotry. It’s a vitally important book.
A Technical Tour of the DeepSeek Models from V3 to V3.2
Lobsters
magazine.sebastianraschka.com
2025-12-04 04:46:49
Note that this does assume some prior transformer architecture knowledge, but if you know how attention works then you should at least be able to get the overall idea.
Comments...
Similar to DeepSeek V3, the team released their new flagship model over a major US holiday weekend. Given DeepSeek V3.2’s really good performance (on GPT-5 and Gemini 3.0 Pro) level, and the fact that it’s also available as an open-weight model, it’s definitely worth a closer look.
Figure 1: Benchmark comparison between DeepSeek V3.2 and proprietary flagship models. This is an annotated figure from the
DeepSeek V3.2 report
.
I covered the predecessor, DeepSeek V3, at the very beginning of my
The Big LLM Architecture Comparison
article, which I kept extending over the months as new architectures got released. Originally, as I just got back from Thanksgiving holidays with my family, I planned to “just” extend the article with this new DeepSeek V3.2 release by adding another section, but I then realized that there’s just too much interesting information to cover, so I decided to make this a longer, standalone article.
There’s a lot of interesting ground to cover and a lot to learn from their technical reports, so let’s get started!
While DeepSeek V3 wasn’t popular immediately upon release in December 2024, the DeepSeek R1 reasoning model (based on the identical architecture, using DeepSeek V3 as a base model) helped DeepSeek become one of the most popular open-weight models and a legit alternative to proprietary models such as the ones by OpenAI, Google, xAI, and Anthropic.
Figure 2: DeepSeek V3/R1 architecture from December 2024. We will revisit and discuss architectural details in a later section.
So, what’s new since V3/R1? I am sure that the DeepSeek team has been super busy this year. However, there hasn’t been a major release in the last 10-11 months since DeepSeek R1.
Personally, I think it’s reasonable to go ~1 year for a major LLM release since it’s A LOT of work. However, I saw on various social media platforms that people were pronouncing the team “dead” (as a one-hit wonder).
I am sure the DeepSeek team has also been busy navigating the switch from NVIDIA to Huawei chips.
By the way, I am not affiliated with them or have spoken with them; everything here is based on public information
. As far as I know, they are back to using NVIDIA chips.
Finally, it’s also not that they haven’t released anything. There have been a couple of smaller releases that trickled in this year, for instance, DeepSeek V3.1 and V3.2-Exp.
Figure 3: DeepSeek releases since last year. The main models are shown in red.
As I
predicted
back in September, the DeepSeek V3.2-Exp release was intended to get the ecosystem and inference infrastructure ready to host the just-released V3.2 model.
V3.2-Exp and V3.2 use a non-standard sparse attention variant that requires custom code, but more on this mechanism later. (I was tempted to cover it in my previous Beyond Standard LLMs article, but Kimi Linear was released around then, which I prioritized for this article section on new attention variants.)
Before discussing further model details, it might be worthwhile to discuss the overall model types. Originally, DeepSeek V3 was released as a base model, and DeepSeek R1 added additional post-training to develop a dedicated reasoning model. This procedure is summarized in the figure below.
What’s worthwhile noting here is that DeepSeek V3 is a base model, and DeepSeek R1 is a dedicated reasoning model.
In parallel with DeepSeek, other teams have also released many really strong open-weight reasoning models. One of the strongest open-weight models this year was Qwen3. Originally, it was released as a hybrid reasoning model, which means that users were able to toggle between reasoning and non-reasoning modes within the same model. (In the case of Qwen3, this toggling was enabled via the tokenizer by adding/omitting
<think></think>
tags.)
Since then, LLM teams have released (and in some cases gone back and forth between) both dedicated reasoning models and Instruct/Reasoning hybrid models, as shown in the timeline below.
Figure 5: The timeline of some of the reasoning and hybrid models released this year.
For instance, Qwen3 started out as a hybrid model, but the Qwen team then later released separate instruct and reasoning models as they were easier to develop and yielded better performance in each respective use case.
Some models like OpenAI’s gpt-oss only come in a hybrid variant where users can choose the reasoning effort via a system prompt (I suspect this is handled similarly in GPT-5 and GPT-5.1).
And in the case of DeepSeek, it looks like they moved in the opposite direction from a dedicated reasoning model (R1) to a hybrid model (V3.1 and V3.2). However, I suspect that R1 was mainly a research project to develop reasoning methods and the best reasoning model at the time. The V3.2 release may be more about developing the best overall model for different use cases. (Here, R1 was more like a testbed or prototype model.)
And I also suspect that, while the DeepSeek team developed V3.1 and V3.2 with reasoning capabilities, they might still be working on a dedicated R2 model.
Before discussing the new DeepSeek V3.2 release in more detail, I thought it would be helpful to start with an overview of the main changes going from V3 to V3.1.
I already discussed DeepSeek V3 and R1 in great detail in several other articles. To summarize the main points, DeepSeek V3 is a base model that uses two noteworthy architecture aspects: Mixture-of-Experts (MoE) and Multi-Head Latent Attention (MLA).
I think you are probably well familiar with MoE at this point, so I am skipping the introduction here. However, if you want to read more, I recommend the short overview in my
The Big Architecture Comparison
article for more context.
The other noteworthy highlight is the use of MLA. MLA, which is used in
DeepSeek V2, V3, and R1
, offers a memory-saving strategy that pairs particularly well with KV caching. The idea in MLA is that it compresses the key and value tensors into a lower-dimensional space before storing them in the KV cache.
At inference time, these compressed tensors are projected back to their original size before being used, as shown in the figure below. This adds an extra matrix multiplication but reduces memory usage.
(As a side note, the queries are also compressed, but only during training, not inference.)
Figure 6: Multi-Head Latent Attention (MLA) in DeepSeek V3/R1. (The compressed space of the query vector is not shown for simplicity.)
The figure above illustrates the main idea behind MLA, where the keys and values are first projected into a latent vector, which can then be stored in the KV cache to reduce memory requirements. This requires a later up-projection back into the original key-value space, but overall it improves efficiency (as an analogy, you can think of the down- and up-projections in LoRA).
Note that the query is also projected into a separate compressed space, similar to what’s shown for the keys and values. However, I omitted it in the figure above for simplicity.
By the way, as mentioned earlier, MLA is not new in DeepSeek V3, as its
DeepSeek V2 predecessor
also used (and even introduced) it.
DeepSeek R1 uses the same architecture as DeepSeek V3 above. The difference is the training recipe. I.e., using DeepSeek V3 as the base model, DeepSeek R1 was focused on the Reinforcement Learning with Verifiable Rewards (RLVR) method to improve the reasoning capabilities of the model.
The core idea in RLVR is to have the model learn from responses that can be verified symbolically or programmatically, such as math and code (but this can, of course, also be extended beyond these two domains).
Figure 7: An example of a verifiable task.
The GRPO algorithm, which is short for Group Relative Policy Optimization, is essentially a simpler variant of the Proximal Policy Optimization (PPO) algorithm that is popular in Reinforcement Learning with Human Feedback (RLHF), which is used for LLM alignment.
Figure 8:
Comparison of reinforcement learning setups in LLM training. Traditional RLHF with PPO uses both a reward model (trained on human preferences) and a critic (value model) to guide learning. GRPO eliminates the critic model. RLVR with GRPO goes a step further by removing the reward model, relying instead on verifiable rewards from symbolic tools such as calculators or compilers.
As the
DeepSeek team stated
themselves, DeepSeek R1-0528 is basically a “minor version upgrade.”
The architecture remains the same as in DeepSeek V3/R1, and the improvements are on the training side to bring it up to par with OpenAI o3 and Gemini 2.5 Pro at the time.
Unfortunately, the DeepSeek team didn’t release any specific information describing how this was achieved; however, they stated that it partly comes from optimizations in their post-training pipeline. Also, based on what’s been shared, I think it’s likely that the hosted version of the model uses more computational resources at inference time (longer reasoning).
DeepSeek V3.1 is a hybrid model with both general chat (instruct) and reasoning capabilities. I.e., instead of developing two separate models, there is now one model in which users can switch modes via the chat prompt template (similar to the initial Qwen3 model).
DeepSeek V3.1 is based on DeepSeek V3.1-Base, which is in turn based on DeepSeek V3. They all share the same architecture.
DeepSeek V3.2-Exp (Sep 2025) is where it gets more interesting.
Originally, the DeepSeek V3.2-Exp didn’t top the benchmarks, which is why there wasn’t as much excitement around this model upon release. However,
as I speculated
back in September, this was likely an early, experimental release to get the infrastructure (especially the inference and deployment tools) ready for a larger release, since there are a few architectural changes in DeepSeek V3.2-Exp. The bigger release is DeepSeek V3.2 (not V4), but more on that later.
So, what’s new in DeepSeek V3.2-Exp? First, DeepSeek V3.2-Exp was trained based on DeepSeek V3.1-Terminus as a base model. What’s DeepSeek V3.1-Terminus? It’s just a small improvement over the DeepSeek V3.1 checkpoint mentioned in the previous section.
DeepSeek-V3.2-Exp, an experimental sparse-attention model, which equips
DeepSeek-V3.1-Terminus with DeepSeek Sparse Attention (DSA) through continued training. With DSA, a fine-grained sparse attention mechanism powered by a lightning indexer, DeepSeek-V3.2-Exp achieves significant efficiency improvements in both training and inference, especially in long-context scenarios.
As the paragraph above states, the main innovation here is the DeepSeek Sparse Attention (DSA) mechanism that they add to DeepSeek V3.1-Terminus before doing further training on that checkpoint.
This DSA consists of (1) a lightning indexer and (2) a token-selector, and the goal is to selectively reduce the context to improve efficiency.
To explain how it works, let’s start with sliding-window attention. For instance, sliding window attention is a technique (recently used by Gemma 3 and Olmo 3) that limits the attention window to a fixed size, as illustrated in the figure below.
Figure 9: In sliding window attention, the current query token doesn’t attend to all previous tokens but just a subset.
DSA is based on the same idea as sliding-window attention: only a subset of past tokens can be attended to. However, instead of selecting the tokens that can be attended via a fixed-width sliding window, DSA has an indexer and token selector to decide which past tokens can be attended. In other words, the tokens that can be attended are more random, as illustrated in the figure below.
Figure 10: In DSA, the current token can attend a select number of tokens in the past (instead of all tokens like in regular causal attention).
However, while I said “random” above, the pattern of which past tokens are selected is not actually random but learned.
In practice, DSA uses its so-called lightning indexer to compute relevance scores for each new query token based on all previous tokens. For this computation, the lightning indexer uses the compressed token representations in DeepSeek’s Multi-Head Latent Attention (MLA) and computes the token similarity towards other tokens. The similarity score is basically a scaled dot product between query and key vectors passed through a ReLU function.
If you are interested in the mathematical details, the equation (taken from the paper) for this lightning indexer similarity score is shown below:
Here, w is a learned per-head weighting coefficient that determines how much each indexer head should contribute to the final similarity score. The
q
refers to the query, and the
k
refers to the key vector. And below is a list of the different subscripts:
t
: position of the current query token;
s
: position of a previous token in the sequence (0 ≤ s < t);
j
: the index over the different indexer heads (Figure 10 above only showed one head for simplicity), so
q
t, j
means “query vector for current token
t
in indexer head
j
“.
You may notice that the indexer is only over the queries, not the keys. That’s because the model only needs to decide which past tokens each new query should consider. The keys are already compressed and stored in the KV cache, so the indexer does not need to score or compress them again over the different heads.
The ReLU function here, since it’s
f(x) = max(x, 0)
, zeroes negative dot-product positions, which could theoretically enable sparsity, but since there is a summation over the different heads, it’s unlikely that the indexer score is actually 0. The sparsity rather comes from the separate token selector.
The separate token selector keeps only a small number of high-scoring tokens (for example, the top-
k
positions) and constructs a sparse attention mask that masks out the other tokens that are not contained in the selected subset. (The
k
in top-
k
, not to be confused with the
k
that is used for the keys in the equation above, is a hyperparameter that is set to 2048 in the
model code
that the DeepSeek team shared.)
The figure below illustrates the whole process in a flowchart.
Figure 11: A visual summary of DeepSeek V3.2’s Sparse Attention mechanism.
To sum it up, the indexer and token selector result in each token attending to a few past tokens that the model has learned to consider most relevant, rather than all tokens or a fixed local window.
The goal here was not to improve the performance over DeepSeek V3.1-Terminus but to reduce the performance degradation (due to the sparse attention mechanism) while benefiting from improved efficiency.
Overall, the DSA reduces the computational complexity of the attention mechanism from quadratic O(𝐿
2
), where L is the sequence length, to a linear O(𝐿𝑘), where 𝑘 (≪𝐿) is the number of selected tokens.
Having discussed DeepSeek V3.2-Exp, we are getting closer to the main topic of this article: DeepSeek V3.2. However, there is one more puzzle piece to discuss first.
On November 27, 2025 (Thanksgiving in the US), and just 4 days before the DeepSeek V3.2 release, the DeepSeek team released
DeepSeekMath V2
, based on DeepSeek V3.2-Exp-Base.
This model was specifically developed for math and achieved gold-level scores in several math competitions. Essentially, we can think of it as a proof (of concept) model for DeepSeek V3.2, introducing one more technique.
The key aspect here is that reasoning models (like DeepSeek R1 and others) are trained with an external verifier, and the model learns, by itself, to write explanations before arriving at the final answer. However, the explanations may be incorrect.
As the DeepSeek team succinctly states, the shortcomings of regular RLVR:
[...] a model can arrive at the correct answer through flawed logic or fortunate errors.
The other limitation of the DeepSeek R1 RLVR approach they aim to address is that:
[...] many mathematical tasks like theorem proving require rigorous step-by-step derivation rather than numerical answers, making final answer rewards inapplicable.
So, to improve upon these two shortcomings mentioned above, in this paper, they train two models:
An LLM-based verifier for theorem proving.
The main model, a proof-generator, uses the LLM-based verifier as a reward model (instead of a symbolic verifier).
In addition to this self-verification via an LLM as described above, they also use self-refinement (covered in the upcoming Chapter 5 of my Build a Reasoning Model (From Scratch) book) to have the LLM iteratively improve its own answers.
The challenges with process reward models are that it’s not easy to check whether intermediate rewards are correct, and it can also lead to reward hacking.
In the DeepSeek R1 paper in Jan 2025, they didn’t use process reward models as they found that:
its advantages are limited compared to the additional computational overhead it introduces during the large-scale reinforcement learning process in our experiments.
In this paper, they successfully revisit this in the form of self-verification. The motivation is that, even if no reference solution exists, humans can self-correct when reading proofs and identifying issues.
So, in order to develop a better model for writing mathematical proofs (LLM 1 in the figure below), they developed a proof verifier (LLM 2) in the figure below, which can be used as an LLM-as-a-judge to score the prover (LLM 1) outputs.
Figure 12: The general math proof generator (LLM 1) and verifier (LLM 2) setup.
The verifier LLM (LLM 2) takes in a rubric to score the generated proof, where the score is
“1 for complete and rigorous proofs with all logical steps clearly justified;”
“0.5 for proofs with sound overall logic but minor errors or omitted details;”
“and 0 for fundamentally flawed proofs containing fatal logical errors or critical gaps.”
For the proof verifier model, they start with DeepSeek V3.2-Exp-SFT, a model they created based on DeepSeek V3.2-Exp by supervised fine-tuning on reasoning data (both math and code). They then further train the model with reinforcement learning using a format reward (a check whether the solution is in the expected format) and a score reward based on how close the predicted score is to the actual score (annotated by human math experts).
The goal of the proof verifier (LLM 2) is to check the generated proofs (LLM 1), but who checks the proof verifier? To make the proof verifier more robust and prevent it from hallucinating issues, they developed a third LLM, a meta-verifier.
Figure 13: The meta-verifier (LLM 3) checks whether the verifier (LLM 2) is verifying the generator (LLM 1) correctly.
The meta-verifier (LLM 3) is also developed with reinforcement learning, similar to LLM 2. While the use of a meta-verifier is not required, the DeepSeek team reported that:
the average quality score of the verifier’s proof analyses – as evaluated by the meta-verifier – improved from 0.85 to 0.96, while maintaining the same accuracy in proof score prediction.
This is actually quite an interesting setup. If you are familiar with generative adversarial networks (GANs), you may see the analogy here. For instance, the proof verifier (think of it as a GAN discriminator) improves the proof generator, and the proof generator generates better proofs, further pushing the proof verifier.
The meta score is used during training of the verifier (LLM 2) and the generator (LLM 1). It is not used at inference time in the self‑refinement loop, which we will discuss in the next section.
In the previous section, we talked about self-verification, i.e., analyzing the quality of the solution. The purpose of this is to implement self-refinement, which means that the LLM can act upon the feedback and revise its answer.
Traditionally, in self-refinement, which is an established and popular inference-scaling technique, we would use the same LLM for generating the solution and verifying it, before refining it. In other words, in the previous figures 12 and 13, LLM 1 and LLM 2 would be the same LLM. So, a traditional self-refinement process would look as follows:
Figure 14: A classic self-refinement iteration where we use the same LLM for generating the initial response (Output 1), the evaluation (Eval), and the refined answer (Output 2).
However, the DeepSeek team observed a crucial issue with using the same LLM for both the generation and verification in practice:
when prompted to both generate and analyze its own proof in one shot, the generator tends to claim correctness even when the external verifier easily identify flaws. In other words, while the generator can refine proofs based on external feedback, it fails to evaluate its own work with the same rigor as the dedicated verifier.
As a logical consequence, one would assume they use a separate proof generator (LLM 1) and proof verifier (LLM 2). So, the self-refinement loop used here becomes similar to the one shown in the figure below. Note that we omit LLM 3, which is only used during the development of the verifier (LLM 2).
Figure 15: Self-refinement with a separate verifier LLM (LLM 2).
However, in practice, and different from Figure 15, the DeepSeek team uses the same generator and verifier LLM as in a classic self-refinement loop in Figure 14:
“All experiments used a single model, our final proof generator, which performs both proof generation and verification.”
In other words the separate verifier is essential for training, to improve the generator, but it is not used (/needed) later during inference once the generator is strong enough. And the key difference from naive single‑model self‑refinement is that the final prover has been trained under the guidance of a stronger verifier and meta‑verifier, so it has learned to apply those rubrics to its own outputs.
Also, using this 2-in-1 DeepSeekMath V2 verifier during inference is also beneficial in terms of resource and cost, as it add less complexity and compute requirements than running a second LLM for proof verification.
Coming back to the general self-refinement concept shown in Figures 14 and 15, both figures show self-refinement with 2 iterations (the initial one and a refined answer). Of course, we can add more iterations to this process. It’s a classic inference-scaling trade-off: the more iterations we add, the more expensive it becomes to generate the answer, but the higher the overall accuracy.
In the paper, the DeepSeek team used up to 8 iterations, and it looks like the accuracy didn’t saturate yet.
The reason why we spent so much time on DeepSeekMath V2 in the previous section is that a) it’s a very interesting proof of concept that pushes the idea of Reinforcement Learning with Verifiable Rewards (RLVR) further with self-verification and self-refinement techniques, and b) the self-verification and self-refinement techniques are used in DeepSeek V3.2 as well.
But before we get to this part, let’s start with a general overview of DeepSeek V3.2. This model is a big deal because it performs really well compared to current flagship models.
Figure 17: Benchmark comparison between DeepSeek V3.2 and proprietary flagship models. This is an annotated figure from the
DeepSeek V3.2 report
.
Similar to several other DeepSeek models, V3.2 comes with a nice
technical report
, which I will discuss in the next sections.
The main motivation for this model is, of course, to improve overall model performance. For instance, like DeepSeekMath V2, it achieves gold-level performance on math benchmarks. However, the model is also trained with tool-use in mind and also performs well on other tasks, for instance, code and agentic tasks.
At the same time, the DeepSeek team writes about computational efficiency as a big, motivating factor. That’s why they use the Multi-Head Latent Attention (MLA) mechanism from V2 and V3 together with the DeepSeek Sparse Attention (DSA) mechanism, which they added in V3.2. In fact, the paper says that “DeepSeek-V3.2 uses exactly the same architecture as DeepSeek-V3.2-Exp,” which we discussed in an earlier section.
Figure 18: The DeepSeek V3.2 architecture.
As I mentioned earlier the DeepSeek V3.2-Exp release was likely intended to get the ecosystem and inference infrastructure ready to host the just-released V3.2 model.
Figure 19: Inference cost savings thanks to DeepSeek Sparse Attention (DSA). Annotated figure from the
DeepSeek V3.2 report
.
Interestingly, as the screenshot from the paper above shows, the DeepSeek team reverted to using NVIDIA chips (after they allegedly experimented with model training on chips from Huawei).
Since the architecture is the same as that of DeepSeek V3.2-Exp, the interesting details lie in the training methods, which we will discuss in the next sections.
Overall, the DeepSeek team adopts the Reinforcement Learning with Verifiable Rewards (RLVR) procedure using the Group Relative Policy Optimization (GRPO) algorithm similar to DeepSeek R1. However, there are some interesting updates to discuss.
Originally, DeepSeek R1 used
a format reward (to make sure the answer is properly formatted);
a language consistency reward (so that the model doesn’t alternate between different languages when writing its response);
and the main verifier reward (whether the answer, in a math or code problem, is correct or not)
For DeepSeek V3.2, they changed the rewards:
For reasoning and agent tasks, we employ rule-based outcome reward, length penalty, and language consistency reward. For general tasks, we employ a generative reward model where each prompt has its own rubrics for evaluation.
For instance, they removed the format reward but added a length penalty for agentic tasks. Then, for general tasks where there is no symbolic verifier (math) or code interpreter to verify the answer, they use a reward model (another LLM trained to output a reward score).
So, it sounds like the pipeline is no longer purely verifier‑based RLVR like in DeepSeek R1, but a hybrid of RLVR (for verifiable domains) and more standard LLM‑as‑a‑judge reward modeling for everything else.
For the math domain, they state that they additionally “incorporated the dataset and reward method from DeepSeekMath-V2,” which we discussed earlier in this article.
Regarding GRPO itself, the learning algorithm inside the RLVR pipeline, they made a few changes since the original version in the DeepSeek R1 paper, too.
Over the last few months, dozens of papers have proposed modifications to GRPO to improve its stability and efficiency. I wrote about two popular ones, DAPO and Dr. GRPO, earlier this year in my
The State of Reinforcement Learning for LLM Reasoning
article .
Without getting into the mathematical details of GRPO, in short,
DAPO
modifies
GRPO
with asymmetric clipping, dynamic sampling, token-level loss, and explicit length-based reward shaping. Dr. GRPO changes the GRPO objective itself to remove the length and std normalizations.
The recent
Olmo 3 paper
also adopted similar changes, which I am quoting below:
Zero Gradient Signal Filtering:
We remove groups of instances whose rewards are all identical (that is, a batch with zero standard deviation in their advantage) to avoid training on samples that provide zero gradient, similar to DAPO (Yu et al., 2025). [DAPO]
Active Sampling:
We maintain a consistent batch size in spite of zero gradient filtering with a novel, more efficient version of dynamic sampling (Yu et al., 2025). See OlmoRL Infra for details. [DAPO]
Token-level loss:
We use a token-level loss to normalize the loss by the total number of tokens across the batch (Yu et al., 2025), rather than per-sample to avoid a length bias. [DAPO]
No KL Loss:
We remove the KL loss as a common practice (GLM-4.5 Team et al., 2025; Yu et al., 2025; Liu et al., 2025b) as it allows less restricted policy updates, and removing it does not lead to over-optimization or destabilized training. [DAPO and Dr. GRPO]
Clip Higher:
We set the upper-bound clipping term in the loss to a slightly higher value than the lower bound to enable larger updates on tokens, as proposed by Yu et al. (2025). [DAPO]
Truncated Importance Sampling:
To adjust for differences between log probabilities from the inference and training engines, we multiply the loss by the truncated importance sampling ratio, following Yao et al. (2025).
No standard deviation normalization:
When calculating advantage, we do not normalize by the standard deviation of the group, following Liu et al. (2025b). This removes a difficulty bias, where questions with low standard deviation in their rewards (for example, too hard or too easy) have their advantages significantly increased by the normalization term. [Dr. GRPO]
The GRPO modifications in DeepSeek V3.2 are a bit less aggressive, which I summarized in a similar style as Olmo 3 did:
Domain‑specific KL strengths (including zero for math):
Instead of always dropping KL like DAPO and Dr. GRPO do for math‑style RL, DeepSeek V3.2 keeps a KL term in the objective but tunes its weight per domain. However, they also note that very weak or even zero KL often works best for mathematics. (But instead of removing it completely, it becomes a hyperparameter.)
Unbiased KL estimate:
As mentioned above, DeepSeek V3.2 doesn’t remove the KL penalty. And in addition to treating it as a tuning knob, they propose a fix to how the KL penalty is estimated in GRPO by reweighting the KL term with the same importance ratio used for the main loss, so the KL gradient actually matches the fact that samples come from the old policy rather than the current one.
Off‑policy sequence masking:
When they reuse rollout data (rollout is simply jargon for the full sequence the model generates) across many gradient steps, DeepSeek V3.2 measures how far the current policy has drifted from the rollout policy on each full answer and simply drops those sequences that both have negative advantage and are “too off‑policy”. So, this prevents the model from learning from overly off‑policy or stale data.
Keep routing for MoE models:
For the Mixture‑of‑Experts backbone, they log which experts were activated during rollout and force the same routing pattern during training, so gradient updates are for those experts that produced the sampled answers.
Keep sampling mask for top‑p / top‑k:
When rollouts use top‑p or top‑k sampling, DeepSeek V3.2 stores the selection mask and reapplies it when computing the GRPO loss and KL, so the action space at training time matches what was actually available during sampling.
Keep original GRPO advantage normalization:
Dr. GRPO shows that GRPO’s length and per‑group standard‑deviation normalization terms bias optimization toward overly long incorrect answers and over‑weight very easy or very hard questions. Dr. GRPO fixes this by removing both terms and going back to an unbiased PPO‑style objective. In contrast, DAPO moves to a token‑level loss that also changes how long vs short answers are weighted. DeepSeek V3.2, however, keeps the original GRPO normalization and instead focuses on other fixes, such as those above.
So, overall, DeepSeek V3.2 is closer to the original GRPO algorithms than some other recent models but adds some logical tweaks.
DeepSeek V3.2 also comes in an extreme, extended-thinking variant called DeepSeek V3.2-Speciale, which was trained only on reasoning data during the RL stage (more akin to DeepSeek R1). Besides training only on reasoning data, they also reduced the length penalty during RL, allowing the model to output longer responses.
Generating longer responses is a form of inference scaling, where responses become more expensive due to the increased length, in return for better results.
Figure 20: The “extended-thinking” Speciale model achieves higher accuracy but also generates more tokens.
In this article, I didn’t cover all the nitty-gritty details of the DeepSeek V3.2 training approach, but I hope the comparison with previous DeepSeek models helps clarify the main points and innovations.
In short, the interesting takeaways are:
DeepSeek V3.2 uses a similar architecture to all its predecessors since DeepSeek V3;
The main architecture tweak is that they added the sparse attention mechanism from DeepSeek V3.2-Exp to improve efficiency;
To improve math performance, they adopted the self-verification approach from DeepSeekMath V2;
There are several improvements to the training pipeline, for example, GRPO stability updates (note the paper goes into several other aspects around distillation, long-context training, integration of tool-use similar to gpt-oss, which we did not cover in this article).
Irrespective of the relative market share of DeepSeek models compared to other smaller open-weight models or proprietary models like GPT-5.1 or Gemini 3.0 Pro, one thing is for sure: DeepSeek releases are always interesting, and there’s always a lot to learn from the technical reports that come with the open-weight model checkpoints.
I hope you found this overview useful!
This magazine is a personal passion project, and your support helps keep it alive.
In the past three years, I've been using LLMs for assisted coding. If you read this, you probably went through the same evolution: from copying and pasting code into
ChatGPT
, to
Copilot
auto-completions (which never worked for me), to
Cursor
, and finally the new breed of coding agent harnesses like
Claude Code
,
Codex
,
Amp
,
Droid
, and
opencode
that became our daily drivers in 2025.
I preferred Claude Code for most of my work. It was the first thing I tried back in April after using Cursor for a year and a half. Back then, it was much more basic. That fit my workflow perfectly, because I'm a simple boy who likes simple, predictable tools. Over the past few months, Claude Code has turned into a spaceship with 80% of functionality I have no use for. The
system prompt and tools also change
on every release, which breaks my workflows and changes model behavior. I hate that. Also, it flickers.
I've also built a bunch of agents over the years, of various complexity. For example,
Sitegeist
, my little browser-use agent, is essentially a coding agent that lives inside the browser. In all that work, I learned that context engineering is paramount. Exactly controlling what goes into the model's context yields better outputs, especially when it's writing code. Existing harnesses make this extremely hard or impossible by injecting stuff behind your back that isn't even surfaced in the UI.
Speaking of surfacing things, I want to inspect every aspect of my interactions with the model. Basically no harness allows that. I also want a cleanly documented session format I can post-process automatically, and a simple way to build alternative UIs on top of the agent core. While some of this is possible with existing harnesses, the APIs smell like organic evolution. These solutions accumulated baggage along the way, which shows in the developer experience. I'm not blaming anyone for this. If tons of people use your shit and you need some sort of backwards compatibility, that's the price you pay.
I've also dabbled in self-hosting, both locally and on
DataCrunch
. While some harnesses like opencode support self-hosted models, it usually doesn't work well. Mostly because they rely on libraries like the
Vercel AI SDK
, which doesn't play nice with self-hosted models for some reason, specifically when it comes to tool calling.
So what's an old guy yelling at Claudes going to do? He's going to write his own coding agent harness and give it a name that's entirely un-Google-able, so there will never be any users. Which means there will also never be any issues on the GitHub issue tracker. How hard can it be?
To make this work, I needed to build:
pi-ai
: A unified LLM API with multi-provider support (Anthropic, OpenAI, Google, xAI, Groq, Cerebras, OpenRouter, and any OpenAI-compatible endpoint), streaming, tool calling with TypeBox schemas, thinking/reasoning support, seamless cross-provider context handoffs, and token and cost tracking.
pi-agent-core
: An agent loop that handles tool execution, validation, and event streaming.
pi-tui
: A minimal terminal UI framework with differential rendering, synchronized output for (almost) flicker-free updates, and components like editors with autocomplete and markdown rendering.
pi-coding-agent
: The actual CLI that wires it all together with session management, custom tools, themes, and project context files.
My philosophy in all of this was: if I don't need it, it won't be built. And I don't need a lot of things.
pi-ai and pi-agent-core
I'm not going to bore you with the API specifics of this package. You can read it all in the
README.md
. Instead, I want to document the problems I ran into while creating a unified LLM API and how I resolved them. I'm not claiming my solutions are the best, but they've been working pretty well throughout various agentic and non-agentic LLM projects.
They're all pretty similar in features, so building an abstraction on top of them isn't rocket science. There are, of course, provider-specific peculiarities you have to care for. That's especially true for the Completions API, which is spoken by pretty much all providers, but each of them has a different understanding of what this API should do. For example, while OpenAI doesn't support reasoning traces in their Completions API, other providers do in their version of the Completions API. This is also true for inference engines like
llama.cpp
,
Ollama
,
vLLM
, and
LM Studio
.
Cerebras, xAI, Mistral, and Chutes don't like the
store
field
Mistral and Chutes use
max_tokens
instead of
max_completion_tokens
Cerebras, xAI, Mistral, and Chutes don't support the
developer
role for system prompts
Grok models don't like
reasoning_effort
Different providers return reasoning content in different fields (
reasoning_content
vs
reasoning
)
To ensure all features actually work across the gazillion of providers, pi-ai has a pretty extensive test suite covering image inputs, reasoning traces, tool calling, and other features you'd expect from an LLM API. Tests run across all supported providers and popular models. While this is a good effort, it still won't guarantee that new models and providers will just work out of the box.
Another big difference is how providers report tokens and cache reads/writes. Anthropic has the sanest approach, but generally it's the Wild West. Some report token counts at the start of the SSE stream, others only at the end, making accurate cost tracking impossible if a request is aborted. To add insult to injury, you can't provide a unique ID to later correlate with their billing APIs and figure out which of your users consumed how many tokens. So pi-ai does token and cache tracking on a best-effort basis. Good enough for personal use, but not for accurate billing if you have end users consuming tokens through your service.
Special shout out to Google who to this date seem to not support tool call streaming which is extremely Google.
pi-ai also works in the browser, which is useful for building web-based interfaces. Some providers make this especially easy by supporting CORS, specifically Anthropic and xAI.
Context handoff
Context handoff between providers was a feature pi-ai was designed for from the start. Since each provider has their own way of tracking tool calls and thinking traces, this can only be a best-effort thing. For example, if you switch from Anthropic to OpenAI mid-session, Anthropic thinking traces are converted to content blocks inside assistant messages, delimited by
<thinking></thinking>
tags. This may or may not be sensible, because the thinking traces returned by Anthropic and OpenAI don't actually represent what's happening behind the scenes.
These providers also insert signed blobs into the event stream that you have to replay on subsequent requests containing the same messages. This also applies when switching models within a provider. It makes for a cumbersome abstraction and transformation pipeline in the background.
I'm happy to report that cross-provider context handoff and context serialization/deserialization work pretty well in pi-ai:
import { getModel, complete, Context } from'@mariozechner/pi-ai';
// Start with Claudeconst claude = getModel('anthropic', 'claude-sonnet-4-5');
constcontext: Context = {
messages: []
};
context.messages.push({ role: 'user', content: 'What is 25 * 18?' });
const claudeResponse = awaitcomplete(claude, context, {
thinkingEnabled: true
});
context.messages.push(claudeResponse);
// Switch to GPT - it will see Claude's thinking as <thinking> tagged textconst gpt = getModel('openai', 'gpt-5.1-codex');
context.messages.push({ role: 'user', content: 'Is that correct?' });
const gptResponse = awaitcomplete(gpt, context);
context.messages.push(gptResponse);
// Switch to Geminiconst gemini = getModel('google', 'gemini-2.5-flash');
context.messages.push({ role: 'user', content: 'What was the question?' });
const geminiResponse = awaitcomplete(gemini, context);
// Serialize context to JSON (for storage, transfer, etc.)const serialized = JSON.stringify(context);
// Later: deserialize and continue with any modelconstrestored: Context = JSON.parse(serialized);
restored.messages.push({ role: 'user', content: 'Summarize our conversation' });
const continuation = awaitcomplete(claude, restored);
We live in a multi-model world
Speaking of models, I wanted a typesafe way of specifying them in the
getModel
call. For that I needed a model registry that I could turn into TypeScript types. I'm parsing data from both
OpenRouter
and
models.dev
(created by the opencode folks, thanks for that, it's super useful) into
models.generated.ts
. This includes token costs and capabilities like image inputs and thinking support.
And if I ever need to add a model that's not in the registry, I wanted a type system that makes it easy to create new ones. This is especially useful when working with self-hosted models, new releases that aren't yet on models.dev or OpenRouter, or trying out one of the more obscure LLM providers:
Many unified LLM APIs completely ignore providing a way to abort requests. This is entirely unacceptable if you want to integrate your LLM into any kind of production system. Many unified LLM APIs also don't return partial results to you, which is kind of ridiculous. pi-ai was designed from the beginning to support aborts throughout the entire pipeline, including tool calls. Here's how it works:
import { getModel, stream } from'@mariozechner/pi-ai';
const model = getModel('openai', 'gpt-5.1-codex');
const controller = newAbortController();
// Abort after 2 secondssetTimeout(() => controller.abort(), 2000);
const s = stream(model, {
messages: [{ role: 'user', content: 'Write a long story' }]
}, {
signal: controller.signal
});
forawait (const event of s) {
if (event.type === 'text_delta') {
process.stdout.write(event.delta);
} elseif (event.type === 'error') {
console.log(`${event.reason === 'aborted' ? 'Aborted' : 'Error'}:`, event.error.errorMessage);
}
}
// Get results (may be partial if aborted)const response = await s.result();
if (response.stopReason === 'aborted') {
console.log('Partial content:', response.content);
}
Structured split tool results
Another abstraction I haven't seen in any unified LLM API is splitting tool results into a portion handed to the LLM and a portion for UI display. The LLM portion is generally just text or JSON, which doesn't necessarily contain all the information you'd want to show in a UI. It also sucks hard to parse textual tool outputs and restructure them for display in a UI. pi-ai's tool implementation allows returning both content blocks for the LLM and separate content blocks for UI rendering. Tools can also return attachments like images that get attached in the native format of the respective provider. Tool arguments are automatically validated using
TypeBox
schemas and
AJV
, with detailed error messages when validation fails:
import { Type, AgentTool } from'@mariozechner/pi-ai';
const weatherSchema = Type.Object({
city: Type.String({ minLength: 1 }),
});
constweatherTool: AgentTool<typeof weatherSchema, { temp: number }> = {
name: 'get_weather',
description: 'Get current weather for a city',
parameters: weatherSchema,
execute: async (toolCallId, args) => {
const temp = Math.round(Math.random() * 30);
return {
// Text for the LLMoutput: `Temperature in ${args.city}: ${temp}°C`,
// Structured data for the UIdetails: { temp }
};
}
};
// Tools can also return imagesconstchartTool: AgentTool = {
name: 'generate_chart',
description: 'Generate a chart from data',
parameters: Type.Object({ data: Type.Array(Type.Number()) }),
execute: async (toolCallId, args) => {
const chartImage = awaitgenerateChartImage(args.data);
return {
content: [
{ type: 'text', text: `Generated chart with ${args.data.length} data points` },
{ type: 'image', data: chartImage.toString('base64'), mimeType: 'image/png' }
]
};
}
};
What's still lacking is tool result streaming. Imagine a bash tool where you want to display ANSI sequences as they come in. That's currently not possible, but it's a simple fix that will eventually make it into the package.
Partial JSON parsing during tool call streaming is essential for good UX. As the LLM streams tool call arguments, pi-ai progressively parses them so you can show partial results in the UI before the call completes. For example, you can display a diff streaming in as the agent rewrites a file.
Minimal agent scaffold
Finally, pi-ai provides an
agent loop
that handles the full orchestration: processing user messages, executing tool calls, feeding results back to the LLM, and repeating until the model produces a response without tool calls. The loop also supports message queuing via a callback: after each turn, it asks for queued messages and injects them before the next assistant response. The loop emits events for everything, making it easy to build reactive UIs.
The agent loop doesn't let you specify max steps or similar knobs you'd find in other unified LLM APIs. I never found a use case for that, so why add it? The loop just loops until the agent says it's done. On top of the loop, however,
pi-agent-core
provides an
Agent
class with actually useful stuff: state management, simplified event subscriptions, message queuing with two modes (one-at-a-time or all-at-once), attachment handling (images, documents), and a transport abstraction that lets you run the agent either directly or through a proxy.
Am I happy with pi-ai? For the most part, yes. Like any unifying API, it can never be perfect due to leaky abstractions. But it's been used in seven different production projects and has served me extremely well.
Why build this instead of using the Vercel AI SDK?
Armin's blog post
mirrors my experience. Building on top of the provider SDKs directly gives me full control and lets me design the APIs exactly as I want, with a much smaller surface area. Armin's blog gives you a more in-depth treatise on the reasons for building your own. Go read that.
pi-tui
I grew up in the DOS era, so terminal user interfaces are what I grew up with. From the fancy setup programs for Doom to Borland products, TUIs were with me until the end of the 90s. And boy was I fucking happy when I eventually switched to a GUI operating system. While TUIs are mostly portable and easily streamable, they also suck at information density. Having said all that, I thought starting with a terminal user interface for pi makes the most sense. I could strap on a GUI later whenever I felt like I needed to.
So why build my own TUI framework? I've looked into the alternatives like
Ink
,
Blessed
,
OpenTUI
, and so on. I'm sure they're all fine in their own way, but I definitely don't want to write my TUI like a React app. Blessed seems to be mostly unmaintained, and OpenTUI is explicitly not production ready. Also, writing my own TUI framework on top of Node.js seemed like a fun little challenge.
Two kinds of TUIs
Writing a terminal user interface is not rocket science per se. You just have to pick your poison. There's basically two ways to do it. One is to take ownership of the terminal viewport (the portion of the terminal contents you can actually see) and treat it like a pixel buffer. Instead of pixels you have cells that contain characters with background color, foreground color, and styling like italic and bold. I call these full screen TUIs. Amp and opencode use this approach.
The drawback is that you lose the scrollback buffer, which means you have to implement custom search. You also lose scrolling, which means you have to simulate scrolling within the viewport yourself. While this is not hard to implement, it means you have to re-implement all the functionality your terminal emulator already provides. Mouse scrolling specifically always feels kind of off in such TUIs.
The second approach is to just write to the terminal like any CLI program, appending content to the scrollback buffer, only occasionally moving the "rendering cursor" back up a little within the visible viewport to redraw things like animated spinners or a text edit field. It's not exactly that simple, but you get the idea. This is what Claude Code, Codex, and Droid do.
Coding agents have this nice property that they're basically a chat interface. The user writes a prompt, followed by replies from the agent and tool calls and their results. Everything is nicely linear, which lends itself well to working with the "native" terminal emulator. You get to use all the built-in functionality like natural scrolling and search within the scrollback buffer. It also limits what your TUI can do to some degree, which I find charming because constraints make for minimal programs that just do what they're supposed to do without superfluous fluff. This is the direction I picked for pi-tui.
Retained mode UI
If you've done any GUI programming, you've probably heard of retained mode vs immediate mode. In a retained mode UI, you build up a tree of components that persist across frames. Each component knows how to render itself and can cache its output if nothing changed. In an immediate mode UI, you redraw everything from scratch each frame (though in practice, immediate mode UIs also do caching, otherwise they'd fall apart).
pi-tui uses a simple retained mode approach. A
Component
is just an object with a
render(width)
method that returns an array of strings (lines that fit the viewport horizontally, with ANSI escape codes for colors and styling) and an optional
handleInput(data)
method for keyboard input. A
Container
holds a list of components arranged vertically and collects all their rendered lines. The
TUI
class is itself a container that orchestrates everything.
When the TUI needs to update the screen, it asks each component to render. Components can cache their output: an assistant message that's fully streamed doesn't need to re-parse markdown and re-render ANSI sequences every time. It just returns the cached lines. Containers collect lines from all children. The TUI gathers all these lines and compares them to the lines it previously rendered for the previous component tree. It keeps a backbuffer of sorts, remembering what was written to the scrollback buffer.
Then it only redraws what changed, using a method I call differential rendering. I'm very bad with names, and this likely has an official name.
Differential rendering
Here's a simplified demo that illustrates what exactly gets redrawn.
The algorithm is simple:
First render
: Just output all lines to the terminal
Normal update
: Find the first line that differs from what's on screen, move the cursor to that line, and re-render from there to the end
There's one catch: if the first changed line is above the visible viewport (the user scrolled up), we have to do a full clear and re-render. The terminal doesn't let you write to the scrollback buffer above the viewport.
To prevent flicker during updates, pi-tui wraps all rendering in synchronized output escape sequences (
CSI ?2026h
and
CSI ?2026l
). This tells the terminal to buffer all the output and display it atomically. Most modern terminals support this.
How well does it work and how much does it flicker? In any capable terminal like Ghostty or iTerm2, this works brilliantly and you never see any flicker. In less fortunate terminal implementations like VS Code's built-in terminal, you will get some flicker depending on the time of day, your display size, your window size, and so on. Given that I'm very accustomed to Claude Code, I haven't spent any more time optimizing this. I'm happy with the little flicker I get in VS Code. I wouldn't feel at home otherwise. And it still flickers less than Claude Code.
How wasteful is this approach? We store an entire scrollback buffer worth of previously rendered lines, and we re-render lines every time the TUI is asked to render itself. That's alleviated with the caching I described above, so the re-rendering isn't a big deal. We still have to compare a lot of lines with each other. Realistically, on computers younger than 25 years, this is not a big deal, both in terms of performance and memory use (a few hundred kilobytes for very large sessions). Thanks V8. What I get in return is a dead simple programming model that lets me iterate quickly.
pi-coding-agent
I don't need to explain what features you should expect from a coding agent harness. pi comes with most creature comforts you're used to from other tools:
Runs on Windows, Linux, and macOS (or anything with a Node.js runtime and a terminal)
Multi-provider support with mid-session model switching
Session management with continue, resume, and branching
Project context files (AGENTS.md) loaded hierarchically from global to project-specific
Slash commands for common operations
Custom slash commands as markdown templates with argument support
OAuth authentication for Claude Pro/Max subscriptions
Custom model and provider configuration via JSON
Customizable themes with live reload
Editor with fuzzy file search, path completion, drag & drop, and multi-line paste
Message queuing while the agent is working
Image support for vision-capable models
HTML export of sessions
Headless operation via JSON streaming and RPC mode
Full cost and token tracking
If you want the full rundown, read the
README
. What's more interesting is where pi deviates from other harnesses in philosophy and implementation.
Minimal system prompt
Here's the system prompt:
You are an expert coding assistant. You help users with coding tasks by reading files, executing commands, editing code, and writing new files.
Available tools:
- read: Read file contents
- bash: Execute bash commands
- edit: Make surgical edits to files
- write: Create or overwrite files
Guidelines:
- Use bash for file operations like ls, grep, find
- Use read to examine files before editing
- Use edit for precise changes (old text must match exactly)
- Use write only for new files or complete rewrites
- When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did
- Be concise in your responses
- Show file paths clearly when working with files
Documentation:
- Your own documentation (including custom model setup and theme creation) is at: /path/to/README.md
- Read it when users ask about features, configuration, or setup, and especially if the user asks you to add a custom model or provider, or create a custom theme.
You might think this is crazy. In all likelihood, the models have some training on their native coding harness. So using the native system prompt or something close to it like opencode would be most ideal. But it turns out that all the frontier models have been RL-trained up the wazoo, so they inherently understand what a coding agent is. There does not appear to be a need for 10,000 tokens of system prompt, as we'll find out later in the benchmark section, and as I've anecdotally found out by exclusively using pi for the past few weeks. Amp, while copying some parts of the native system prompts, seems to also do just fine with their own prompt.
Minimal toolset
Here are the tool definitions:
read
Read the contents of a file. Supports text files and images (jpg, png,
gif, webp). Images are sent as attachments. For text files, defaults to
first 2000 lines. Use offset/limit for large files.
- path: Path to the file to read (relative or absolute)
- offset: Line number to start reading from (1-indexed)
- limit: Maximum number of lines to read
write
Write content to a file. Creates the file if it doesn't exist, overwrites
if it does. Automatically creates parent directories.
- path: Path to the file to write (relative or absolute)
- content: Content to write to the file
edit
Edit a file by replacing exact text. The oldText must match exactly
(including whitespace). Use this for precise, surgical edits.
- path: Path to the file to edit (relative or absolute)
- oldText: Exact text to find and replace (must match exactly)
- newText: New text to replace the old text with
bash
Execute a bash command in the current working directory. Returns stdout
and stderr. Optionally provide a timeout in seconds.
- command: Bash command to execute
- timeout: Timeout in seconds (optional, no default timeout)
There are additional read-only tools (grep, find, ls) if you want to restrict the agent from modifying files or running arbitrary commands. By default these are disabled, so the agent only gets the four tools above.
As it turns out, these four tools are all you need for an effective coding agent. Models know how to use bash and have been trained on the read, write, and edit tools with similar input schemas. Compare this to
Claude Code's tool definitions
or
opencode's tool definitions
(which are clearly derived from Claude Code's, same structure, same examples, same git commit flow). Notably,
Codex's tool definitions
are similarly minimal to pi's.
pi's system prompt and tool definitions together come in below 1000 tokens.
YOLO by default
pi runs in full YOLO mode and assumes you know what you're doing. It has unrestricted access to your filesystem and can execute any command without permission checks or safety rails. No permission prompts for file operations or commands. No
pre-checking of bash commands by Haiku
for malicious content. Full filesystem access. Can execute any command with your user privileges.
If you look at the security measures in other coding agents, they're mostly security theater. As soon as your agent can write code and run code, it's pretty much game over. The only way you could prevent exfiltration of data would be to cut off all network access for the execution environment the agent runs in, which makes the agent mostly useless. An alternative is allow-listing domains, but this can also be worked around through other means.
Simon Willison has
written extensively
about this problem. His "dual LLM" pattern attempts to address confused deputy attacks and data exfiltration, but even he admits "this solution is pretty bad" and introduces enormous implementation complexity. The core issue remains: if an LLM has access to tools that can read private data and make network requests, you're playing whack-a-mole with attack vectors.
Since we cannot solve this trifecta of capabilities (read data, execute code, network access), pi just gives in. Everybody is running in YOLO mode anyways to get any productive work done, so why not make it the default and only option?
By default, pi has no web search or fetch tool. However, it can use
curl
or read files from disk, both of which provide ample surface area for prompt injection attacks. Malicious content in files or command outputs can influence behavior. If you're uncomfortable with full access, run pi inside a container or use a different tool if you need (faux) guardrails.
No built-in to-dos
pi does not and will not support built-in to-dos. In my experience, to-do lists generally confuse models more than they help. They add state that the model has to track and update, which introduces more opportunities for things to go wrong.
If you need task tracking, make it externally stateful by writing to a file:
The agent can read and update this file as needed. Using checkboxes keeps track of what's done and what remains. Simple, visible, and under your control.
No plan mode
pi does not and will not have a built-in plan mode. Telling the agent to think through a problem together with you, without modifying files or executing commands, is generally sufficient.
If you need persistent planning across sessions, write it to a file:
# PLAN.md## Goal
Refactor authentication system to support OAuth
## Approach1. Research OAuth 2.0 flows
2. Design token storage schema
3. Implement authorization server endpoints
4. Update client-side login flow
5. Add tests
## Current Step
Working on step 3 - authorization endpoints
The agent can read, update, and reference the plan as it works. Unlike ephemeral planning modes that only exist within a session, file-based plans can be shared across sessions, and can be versioned with your code.
Funnily enough, Claude Code now has a
Plan Mode
that's essentially read-only analysis, and it will eventually write a markdown file to disk. And you can basically not use plan mode without approving a shit ton of command invocations, because without that, planning is basically impossible.
The difference with pi is that I have full observability of everything. I get to see which sources the agent actually looked at and which ones it totally missed. In Claude Code, the orchestrating Claude instance usually spawns a sub-agent and you have zero visibility into what that sub-agent does. I get to see the markdown file immediately. I can edit it collaboratively with the agent. In short, I need observability for planning and I don't get that with Claude Code's plan mode.
If you must restrict the agent during planning, you can specify which tools it has access to via the CLI:
pi --tools read,grep,find,ls
This gives you read-only mode for exploration and planning without the agent modifying anything or being able to run bash commands. You won't be happy with that though.
No MCP support
pi does not and will not support MCP. I've
written about this extensively
, but the TL;DR is: MCP servers are overkill for most use cases, and they come with significant context overhead.
Popular MCP servers like Playwright MCP (21 tools, 13.7k tokens) or Chrome DevTools MCP (26 tools, 18k tokens) dump their entire tool descriptions into your context on every session. That's 7-9% of your context window gone before you even start working. Many of these tools you'll never use in a given session.
The alternative is simple: build CLI tools with README files. The agent reads the README when it needs the tool, pays the token cost only when necessary (progressive disclosure), and can use bash to invoke the tool. This approach is composable (pipe outputs, chain commands), easy to extend (just add another script), and token-efficient.
Here's how I add web search to pi:
I maintain a collection of these tools at
github.com/badlogic/agent-tools
. Each tool is a simple CLI with a README that the agent reads on demand.
pi's bash tool runs commands synchronously. There's no built-in way to start a dev server, run tests in the background, or interact with a REPL while the command is still running.
This is intentional. Background process management adds complexity: you need process tracking, output buffering, cleanup on exit, and ways to send input to running processes. Claude Code handles some of this with their background bash feature, but it has poor observability (a common theme with Claude Code) and forces the agent to track running instances without providing a tool to query them. In earlier Claude Code versions, the agent forgot about all its background processes after context compaction and had no way to query them, so you had to manually kill them. This has since been fixed.
Use
tmux
instead. Here's pi debugging a crashing C program in LLDB:
How's that for observability? The same approach works for long-running dev servers, watching log output, and similar use cases. And if you wanted to, you could hop into that LLDB session above via tmux and co-debug with the agent. Tmux also gives you a CLI argument to list all active sessions. How nice.
There's simply no need for background bash. Claude Code can use tmux too, you know. Bash is all you need.
No sub-agents
pi does not have a dedicated sub-agent tool. When Claude Code needs to do something complex, it often spawns a sub-agent to handle part of the task. You have zero visibility into what that sub-agent does. It's a black box within a black box. Context transfer between agents is also poor. The orchestrating agent decides what initial context to pass to the sub-agent, and you generally have little control over that. If the sub-agent makes a mistake, debugging is painful because you can't see the full conversation.
If you need pi to spawn itself, just ask it to run itself via bash. You could even have it spawn itself inside a tmux session for full observability and the ability to interact with that sub-agent directly.
But more importantly: fix your workflow, at least the ones that are all about context gathering. People use sub-agents within a session thinking they're saving context space, which is true. But that's the wrong way to think about sub-agents. Using a sub-agent mid-session for context gathering is a sign you didn't plan ahead. If you need to gather context, do that first in its own session. Create an artifact that you can later use in a fresh session to give your agent all the context it needs without polluting its context window with tool outputs. That artifact can be useful for the next feature too, and you get full observability and steerability, which is important during context gathering.
Because despite popular belief, models are still poor at finding all the context needed for implementing a new feature or fixing a bug. I attribute this to models being trained to only read parts of files rather than full files, so they're hesitant to read everything. Which means they miss important context and can't see what they need to properly complete the task.
Just look at the
pi-mono issue tracker
and the pull requests. Many get closed or revised because the agents couldn't fully grasp what's needed. That's not the fault of the contributors, which I truly appreciate because even incomplete PRs help me move faster. It just means we trust our agents too much.
I'm not dismissing sub-agents entirely. There are valid use cases. My most common one is code review: I tell pi to spawn itself with a code review prompt (via a custom slash command) and it gets the outputs.
---
description: Run a code review sub-agent
---
Spawn yourself as a sub-agent via bash to do a code review: $@
Use `pi --print` with appropriate arguments. If the user specifies a model,
use `--provider` and `--model` accordingly.
Pass a prompt to the sub-agent asking it to review the code for:
- Bugs and logic errors
- Security issues
- Error handling gaps
Do not read the code yourself. Let the sub-agent do that.
Report the sub-agent's findings.
And here's how I use this to review a pull request on GitHub:
With a simple prompt, I can select what specific thing I want to review and what model to use. I could even set thinking levels if I wanted to. I can also save out the full review session to a file and hop into that in another pi session if I wanted. Or I can say this is an ephemeral session and it shouldn't be saved to disk. All of that gets translated into a prompt that the main agent reads and based on which it executes itself again via bash. And while I don't get full observability into the inner workings of the sub-agent, I get full observability on its output. Something other harnesses don't really provide, which makes no sense to me.
Of course, this is a bit of a simulated use case. In reality, I would just spawn a new pi session and ask it to review the pull request, possibly pull it into a branch locally. After I see its initial review, I give my own review and then we work on it together until it's good. That's the workflow I use to not merge garbage code.
Spawning multiple sub-agents to implement various features in parallel is an anti-pattern in my book and doesn't work, unless you don't care if your codebase devolves into a pile of garbage.
Benchmarks
I make a lot of grandiose claims, but do I have numerical proof that all the contrarian things I say above actually work? I have my lived experience, but that's hard to transport in a blog post and you'd just have to believe me. So I created a
Terminal-Bench 2.0
test run for pi with Claude Opus 4.5 and let it compete against Codex, Cursor, Windsurf, and other coding harnesses with their respective native models. Obviously, we all know benchmarks aren't representative of real-world performance, but it's the best I can provide you as a sort of proof that not everything I say is complete bullshit.
I performed a complete run with five trials per task, which makes the results eligible for submission to the leaderboard. I also started a second run that only runs during CET because I found that error rates (and consequently benchmark results) get worse once PST goes online. Here are the results for the first run:
And here's pi's placement on the current leaderboard as of December 2nd, 2025:
And here's the
results.json
file I've submitted to the Terminal-Bench folks for inclusion in the leaderboard. The bench runner for pi can be found in
this repository
if you want to reproduce the results. I suggest you use your Claude plan instead of pay-as-you-go.
Finally, here's a little glimpse into the CET-only run:
This is going to take another day or so to complete. I will update this blog post once that is done.
Also note the ranking of
Terminus 2
on the leaderboard. Terminus 2 is the Terminal-Bench team's own minimal agent that just gives the model a tmux session. The model sends commands as text to tmux and parses the terminal output itself. No fancy tools, no file operations, just raw terminal interaction. And it's holding its own against agents with far more sophisticated tooling and works with a diverse set of models. More evidence that a minimal approach can do just as well.
In summary
Benchmark results are hilarious, but the real proof is in the pudding. And my pudding is my day-to-day work, where pi has been performing admirably. Twitter is full of context engineering posts and blogs, but I feel like none of the harnesses we currently have actually let you do context engineering. pi is my attempt to build myself a tool where I'm in control as much as possible.
I'm pretty happy with where pi is. There are a few more features I'd like to add, like
compaction
or
tool result streaming
, but I don't think there's much more I'll personally need. Missing compaction hasn't been a problem for me personally. For some reason, I'm able to cram
hundreds of exchanges
between me and the agent into a single session, which I couldn't do with Claude Code without compaction.
That said, I welcome contributions. But as with all my open source projects, I tend to be dictatorial. A lesson I've learned the hard way over the years with my bigger projects. If I close an issue or PR you've sent in, I hope there are no hard feelings. I will also do my best to give you reasons why. I just want to keep this focused and maintainable. If pi doesn't fit your needs, I implore you to fork it. I truly mean it. And if you create something that even better fits my needs, I'll happily join your efforts.
I think some of the learnings above transfer to other harnesses as well. Let me know how that goes for you.
This page respects your privacy by not using cookies or similar technologies and by not collecting any personally identifiable information.
★ Bad Dye Job
Daring Fireball
daringfireball.net
2025-12-04 03:16:33
It might have made some sense to bring someone from the fashion/brand world to lead software design for Apple Watch, but it sure didn’t seem to make sense for the rest of Apple’s platforms. And the decade of Dye’s HI leadership has proven it....
It sounds like Dye chose to jump ship, and wasn’t squeezed out (as
it seems
with former AI chief John Giannandrea
earlier this
week). Gurman/Bloomberg are spinning this like a coup for Meta
(headline: “
Apple Design Executive Alan Dye Poached by Meta in
Major Coup
”), but I think this is the best personnel news at Apple
in decades. Dye’s decade-long stint running Apple’s software
design team has been, on the whole, terrible — and rather than
getting better, the problems have been getting worse.
Dye’s replacement at Apple is longtime Apple designer Stephen Lemay. I’ve never met Lemay (or at least can’t recall meeting him), and prior to today never heard much about him. But that’s typical for Apple employees. Part of the job working for Apple is remaining under the radar and out of the public eye. What I’ve learned today is that Lemay, very much unlike Dye, is a career interface/interaction designer. Sources I’ve spoken to who’ve worked with Lemay at Apple speak highly of him, particularly his attention to detail and craftsmanship. Those things have been sorely lacking in the Dye era. Not everyone loves everything Lemay has worked on, but nobody bats 1.000 and designers love to critique each other’s work. I’ve chatted with people with criticisms of specific things Lemay has worked on or led at Apple (e.g. aspects of iPadOS multitasking that struck many of us as deliberately limiting, rather than empowering), but
everyone
I’ve spoken to is happy — if not downright giddy — at the news that Lemay is replacing Dye. Lemay is well-liked personally and deeply respected talent-wise. Said one source, in a position to know the choices, “I don’t think there was a better choice than Lemay.”
The sentiment within the ranks at Apple is that today’s news is almost too good to be true. People had given up hope that Dye would ever get squeezed out, and no one expected that he’d just up and leave on his own. (If you care about design, there’s nowhere to go but down after leaving Apple. What people overlooked is the obvious: Alan Dye doesn’t actually care about design.)
What I struggled with in the wake of today’s news is how to square the following contradiction:
Dye apparently left for Meta on his own; he wasn’t squeezed out.
Apple replacing Dye with Lemay seemingly signals a significant shift in direction, replacing a guy whose approach was almost entirely superficial/visual with a guy who’s spent his entire career sweating actual interaction details.
If Apple’s senior leadership would have been happy to have Dye remain as leader of Apple’s software design teams, why didn’t they replace him with a Dye acolyte? Conversely, if the decision makers at Apple saw the need for a directional change, why wasn’t Dye pushed out?
2
The answer, I think, is that the decision to elevate Lemay wasn’t about direction, but loyalty. Why risk putting in a Dye-aligned replacement when that person might immediately get poached too? We know, from this year’s AI recruitment battles, that Zuckerberg is willing to throw
almost unfathomable sums of money
to poach talent he wants to hire from competitors. Gurman reported that Billy Sorrentino, a Dye deputy who has served as a senior director of design at Apple since 2016, is leaving for Meta with Dye.
3
I don’t have any other names, but word on the street is that other members of Dye’s inner circle are leaving Apple for Meta with him. But those who remain — or who might remain, if they’d have been offered the promotion to replace Dye — simply can’t be trusted from the perspective of senior leadership, who were apparently blindsided by Dye’s departure for Meta. They wouldn’t have given Dye a prime spot in the WWDC keynote if they thought he might be leaving within months.
So the change in direction we may see — that many of us desperately
hope
to see — under Lemay’s leadership might be happenstance. More a factor of Lemay being politically safe, as someone predating Dye and outside Dye’s inner circle at Apple, than from Tim Cook or anyone else in senior leadership seeing a
need
for a directional change in UI design. But happenstance or not, it could be the best thing to happen to Apple’s HI design in the entire stretch since Steve Jobs’s passing and Scott Forstall’s ouster.
Putting Alan Dye in charge of user interface design was the one big mistake Jony Ive made as Apple’s Chief Design Officer.
4
Dye had no background in user interface design — he came from a brand and print advertising background. Before joining Apple,
he was design director for the fashion brand Kate Spade
, and before that worked on branding for the ad agency Ogilvy. His promotion to lead Apple’s software interface design team under Ive happened in 2015, when Apple was launching Apple Watch, their closest foray into the world of fashion. It might have made some sense to bring someone from the fashion/brand world to lead software design for Apple Watch, but it sure didn’t seem to make sense for the rest of Apple’s platforms. And the decade of Dye’s HI leadership has proven it.
The most galling moment in Dye’s entire tenure was
the opening of this year’s iPhone event keynote in September
, which began with a title card showing the
oft-cited Jobs quote
“Design is not just what it looks like and feels like. Design is how it works.” The whole problem with the Dye era of HI design at Apple is that it has so largely — not entirely, but largely — been driven purely by how things look. There are a lot of things in Apple’s software —
like app icons
— that don’t even look good any more. But it’s the “how it works” part that has gone so horribly off the rails. Alan Dye seems like
exactly
the sort of person Jobs was describing in the first part of that quote: “People think it’s this veneer — that the designers are handed this box and told, ‘Make it look good!’”
I am not a Liquid Glass hater. I actually think, on the whole, iOS 26 is a better and more usable UI than iOS 18. But MacOS 26 Tahoe is a mess, visually, and I’m not sure there’s a single thing about its UI that is better than MacOS 15 Sequoia. There are
new software features in Tahoe
that are excellent and serve as legitimate enticements to upgrade. But I’m talking about the user interface — the work from Alan Dye’s HI team, not Craig Federighi’s teams. I think the fact that Liquid Glass is worse on MacOS than it is on iOS is not just a factor of iOS being Apple’s most popular, most profitable, most important platform — and thus garnering more of Apple’s internal attention. I think it’s also about the fact that the Mac interface, with multiple windows, bigger displays, and more complexity, demands more nuanced, more expert, interaction design skills. Things like depth, layering, and unambiguous indications of input focus are important aspects of any platform. But they’re more important on the platform which, by design, shoulders more complexity. Back in 2010, predicting a bright future for the Mac at a time when many pundits were thinking Apple would soon put the entire platform out to pasture, I wrote, “
It’s the heaviness of the Mac that allows iOS to remain light
.” That remains as true today as it was 15 years ago. But Liquid Glass, especially as expressed on MacOS, is a lightweight poorly considered design system as a whole, and its conceptual thinness is not sufficient to properly allow the Mac to carry the weight it needs to bear.
Perhaps more tellingly, there should have been no need for the “
clear/tinted
” Liquid Glass preference setting that Apple added in the 26.1 OS releases. Alan Dye wasn’t fired, by all accounts, but that preference setting was as good a sign as any that he should have been. And it’s very much a sign that inside Apple, there’s a strong enough contingent of people who prioritize how things work — like, you know,
whether you can read text against the background of an alert
— to get a setting like this shipped, outside the Accessibility section of Settings.
It remains worrisome that Apple needed to luck into Dye leaving the company. But fortune favors the prepared, and Apple remains prepared by having an inordinate number of longtime talented HI designers at the company. The oddest thing about Alan Dye’s stint leading software design is that there are, effectively, zero design critics who’ve been on his side. The debate regarding Apple’s software design over the last decade isn’t between those on Dye’s side and those against. It’s only a matter of debating how bad it’s been, and how far it’s fallen from its previous remarkable heights. It’s rather extraordinary in today’s hyper-partisan world that there’s nearly universal agreement amongst actual practitioners of user-interface design that Alan Dye is a fraud who led the company deeply astray. It was a big problem inside the company too. I’m aware of dozens of designers who’ve left Apple, out of frustration over the company’s direction, to work at places like LoveFrom, OpenAI, and their secretive joint venture
io
. I’m not sure there are any experience designers at io who aren’t ex-Apple, and if there are, it’s only a handful. From the stories I’m aware of, the theme is identical: these are designers driven to do great work, and under Alan Dye, “doing great work” was no longer the guiding principle at Apple. If reaching the most users is your goal, go work on design at Google, or Microsoft, or Meta. (Design, of course, isn’t even a thing at Amazon.) Designers choose to work at Apple to do the best work in the industry. That has stopped being true under Alan Dye. The most talented designers I know are the harshest critics of Dye’s body of work, and the direction in which it’s been heading.
Back in June, after WWDC, I quoted from Alan Dye’s introduction of Liquid Glass during the keynote, and then quoted from
Steve Jobs’s introduction of Aqua
when he unveiled the Mac OS X Public Beta in January 2000.
I wrote
:
Re-watching Jobs’s introduction of Aqua for the umpteenth time, I
still find it enthralling. I found Alan Dye’s introduction of
Liquid Glass to be soporific, if not downright horseshitty.
One of the bits from Jobs’s Aqua introduction I quoted was this:
This is what the top of windows look like. These three buttons
look like a traffic signal, don’t they? Red means close the
window. Yellow means minimize the window. And green means maximize
the window. Pretty simple. And tremendous fit and finish in this
operating system. When you roll over these things, you get those.
You see them? And when you are no longer the key window, they go
transparent. So a lot of fit and finish in this.
After I published that post, I got a note from a designer friend who left Apple, in frustration, a few years ago. After watching Jobs’s Aqua introduction for the first time in years, he told me, “I’m really struck by Steve directly speaking to ‘radio buttons’ and ‘the key window’.” He had the feeling that Dye and his team looked down on interface designers who used terms like Jobs himself once used — in a public keynote, no less. That to Dye’s circle, such terms felt too much like “programmer talk”. But the history of Apple (and NeXT) user interface design is the opposite. Designers and programmers used to — and still should — speak the exact same language about such concepts. Steve Jobs certainly did, and something feels profoundly broken about that disconnect under Alan Dye’s leadership. It’s like the head of cinematography for a movie telling the camera team to stop talking about nerdy shit like “f-stops”. The head of cinematography shouldn’t just abide talking about f-stops and focal lengths, but love it. Said my friend to me, regarding his interactions with Dye and his team at Apple, “I swear I had conversations in which I mentioned ‘key window’ and no one knew what I meant.”
That won’t be a problem with Stephen Lemay. Understanding of fundamental principles will no longer be lacking. Lemay has been at Apple spanning the gamut between the
Greg Christie
/
Bas Ording
glory days and the current era. At the very least, Lemay running HI should stop the bleeding — both in terms of work quality and talent retention. I sincerely believe things might measurably improve, but I’m more sure that things will stop getting worse. That alone will be a win for everyone — even though the change was seemingly driven by Mark Zuckerberg’s desire to poach Dye, not Tim Cook and Apple’s senior leadership realizing they should have shitcanned him long ago.
Alan Dye is not untalented. But his talents at Apple were in politics. His political skill was so profound that it was
his
decision to leave, despite the fact that his tenure is considered a disaster by actual designers inside and outside the company. He obviously figured out how to please Apple’s senior leadership. His departure today landed as a total surprise because his stature within the company seemed so secure. And so I think he might do very well at Meta. Not because he can bring world-class interaction design expertise — because he obviously can’t — but because the path to success at Meta has never been driven by design. It’s about getting done what Zuck wants done. Dye might excel at that. Dye was an anchor holding Apple back, but might elevate design at Meta.
5
Cro provides commentary on LWN's Zig asynchronicity article
Linux Weekly News
lwn.net
2025-12-04 00:44:38
Loris Cro has published
a detailed YouTube video talking about the terminology used to discuss asynchronicity, concurrency, and parallelism in our recent article about Zig's new Io interface. Our article is not completely clear because it uses the term "asynchronous I/O" to refer to what should re...
Loris Cro has published
a detailed YouTube video
talking about the terminology used to discuss asynchronicity, concurrency, and parallelism in
our recent article
about Zig's new
Io
interface. Our article is not completely clear because it uses the term "asynchronous
I/O
" to refer to what should really be called "non-blocking
I/O
", and sometimes confuses asynchronicity for concurrency, among other errors of terminology, he says. Readers interested in precise details about Zig's approach and some of the motivation behind the design may find Cro's video interesting.
Show HN: A Minimal Monthly Task Planner (printable, offline, no signup)
The
Mississippi flood of 1927
was one of America’s greatest natural disasters. Some 27,000 square miles were inundated, in some cases by 30 feet of water. Hundreds, maybe thousands, died — many of the victims were poor and Black, and their deaths went unrecorded. Around 700,000 people were displaced — equivalent to about 2 million people today, adjusting for population growth.
How did America respond? Initially, President Calvin Coolidge was
adamantly opposed
to any federal role in disaster relief, declaring that “The Government is not an insurer of its citizens against the hazard of the elements.” His refusal to provide aid was, however, deeply unpopular, and he eventually gave in to demands from Congress to deliver government aid.
Ever since that catastrophic flood, providing government aid to the victims of natural disasters has been an integral part of the American Way: federal aid to disaster victims became the norm after the Mississippi flood. Yet it was often a haphazard, uncoordinated process until 1979, when the federal response to natural disasters was consolidated under the
Federal Emergency Management Agency
.
Since then FEMA has become a well-established part of the American social safety net, especially in the face of worsening climate catastrophes. Americans have come to rely on FEMA as a first line of support after disasters. And when FEMA was seen to be falling down on the job, as it did after Hurricane Katrina virtually destroyed New Orleans in 2005, Americans were angry. The fact is, they want FEMA to be better, not smaller. In a
July poll
, only 9 percent of Americans wanted to see FEMA eliminated, and only another 10 percent wanted to see its budget cut.
Donald Trump, however, believes that he knows better than the majority of Americans. In June he announced his intention to
dismantle FEMA
and force the states to assume responsibility for disaster relief. While Trump publicly backed down after an intense public backlash, in practice he is gutting FEMA nonetheless. He is drastically scaling back federal emergency aid, even for communities in which the need for federal assistance is overwhelming.
The latest example of Trump’s stiffing those in need is in
rural northern Michigan
, where the power grid suffered severe damage from an ice storm last March. Rebuilding the power lines will cost thousands of dollars for each household served by the region’s power cooperatives. Without outside help, that cost will have to be paid by the cooperatives’ customers, a huge burden on a relatively poor part of the state. Yet FEMA has turned down the state’s request for aid, in an unprecedented break with past policies.
Adding further injury to Michiganders, who – by the way – voted to deliver the presidency to Donald Trump in 2024, the Trump administration has ordered another Michigan utility to keep an aging, unneeded, highly polluting coal-fired power plant operating, at a cost to ratepayers of
$113 million so far
, and ongoing at $615,000 per day.
Trump tried, unsuccessfully, to withhold wildfire aid from California unless it
adopted voter ID
. He has also tried to divert aid away from states that, in his view, aren’t cooperating with his immigration policies, although
the courts stopped him
. But the storm-hit areas that he is currently refusing to help are, or plausibly “were”, Trump country. The map on the left shows the areas covered by different Michigan electricity utilities; #3 and #7 are the utilities seeking FEMA aid. The map on the right shows the 2024 presidential vote by county, with deeper red corresponding to a higher Trump share:
Since this is not another case of Trump’s political retribution, what lies behind the denial of aid? I believe that it is a knee-jerk dominance display on Trump’s part. Whenever someone comes to him in need, whether its Volodomyr Zelensky, helpless African children dependent on USAID, or rural Michiganers, his cruelty is activated. And he likes surrounding himself with those of the same ilk: Stephen Miller, Pete Hegseth, and Kristi Noem, the secretary of homeland security, who
impeded and slow-walked
the emergency response to deadly Texas flooding back in July.
But that’s not all: there’s also an ideological component. The pre-Trump typical conservative argument against government aid restricted itself to programs like food stamps. The usual suspects fulminate against those who need help putting food on the table, asserting that it’s because they have
chosen to be poor
. In the conservative ideology of Ronald Reagan, helping the poor relieves them of individual responsibility and only makes them lazy.
But those old-time conservatives also recognized a difference between being the victim of a natural disaster and being impoverished. In their view, nobody chooses to have an ice storm or a hurricane. And helping to re-build entire communities didn’t, in their view, encourage sloth.
But that was conservatism then and this is Trumpism now. The fact is that disaster relief runs counter to the
libertarian ideology
embraced by tech bros like Peter Thiel. In the world of the libertarian tech broligarchy, who believe that they should be running things rather than be constrained by democracy, selfishness is a virtue. Hence they don’t believe that their tax dollars should be used to help others, even when those others are victims of circumstances beyond their control. Oh, that is, unless you are a wealthy Silicon Valley type with deposits at the failed
Silicon Valley Bank
. They apparently had no problem with a federal bailout of SVB.
In fact, the libertarian tech broligarchy is opposed to the very impulse to care about other people. “The fundamental weakness of Western civilization,” declared
Elon Musk
last March, “is empathy.”
And let’s not forget — because conservatives never do — that there’s a deeper strategy at play: if you want people to despise and hate government, you don’t want them to see the government doing anything that clearly helps people.
So American victims of natural disasters are being abandoned by Trump. That abandonment reflects his personal cruelty and that of those around him, as well as the ideological allegiance to cruelty among the libertarian tech broligarchy. And the resulting message is clear. Trump to disaster victims, wherever they live and whoever they voted for: Drop dead.
Russia has blocked access to popular gaming platform Roblox due to concerns over child safety and extremism, including the spread of LGBT-related content.
The country's media regulator said Roblox had become rife with "inappropriate content that can negatively impact the spiritual and moral development of children", according to local news outlets.
The multiplayer online platform ranks among the world's most popular, but has been heavily criticised over its lack of features to protect children.
A spokesperson from Roblox said the company respects the laws where it operates, adding that the platform provides a "positive space" for learning.
"We have a deep commitment to safety and we have a robust set of proactive and preventative safety measures designed to catch and prevent harmful content on our platform," the spokesperson said in response to the BBC.
Russian media reported
that Roskomnadzor, the country's media regulator, blocked the US platform over concerns about terrorism-related content and information on LGBT issues, which are classified as extremist and banned in Russia.
Such activity is often found in Roblox's servers, where scenarios simulating terrorist attacks, as well as gambling, have surfaced, the agency is quoted as saying.
Roblox, which ranks among Russia's most downloaded mobile games in recent years, allows players to create and share their own games - a model that has made regulation challenging.
The Roskomnadzor also flagged reports of sexual harassment of children and the sharing of intimate images on the platform. Other countries have raised similar issues, and the platform is already banned in certain countries,
including Turkey
, over concerns about child safety.
Roblox also came under scrutiny in Singapore in 2023 after the government there said a self-radicalised teenager had joined ISIS-themed servers on the platform.
The rapid growth of "energy-hungry" data centres is delaying new homes in London, just as its housing crisis is "at its worst", a new report has warned.
Data centres are giant warehouses full of powerful computers used to run digital services, such as streaming and artificial intelligence.
However, they require masses of electricity from the National Grid to keep running.
The committee's chair James Small-Edwards said energy capacity had become a "real constraint" on housing and economic growth in the city.
In 2022, the General London Assembly (GLA) began to investigate delays to housing developments in the boroughs of Ealing, Hillingdon and Hounslow - after it received reports that completed projects were being told they would have to "wait until 2037" to get a connection to the electricity grid.
There were fears the boroughs may have to "pause new housing altogether" until the issue was resolved.
But the GLA found short-term fixes with the National Grid and energy regulator Ofgem to ensure the "worst-case scenario" did not happen - though several projects were still set back.
The strains on parts of London's housing highlighted the need for "longer term planning" around grid capacity in the future, said the report.
It added that while data centres made up fewer than 10% of the UK's total electricity demand last year, that was expected to rise up by to 600% between 2025 and 2050.
It estimated the energy usage of one typical data centre was similar to that of roughly 100,000 households.
Figures shared with BBC News
in August showed an estimated 447 data centres currently in the UK, with that number set to rise by about 100 in the next few years.
More than half of new data centres are planned in and around London.
Andrew Dakers, chief executive of industry body West London Business, told BBC News the area was proud to host "so much digital tech and investment", but that it came with challenges.
"At the moment National Grid are looking to try and get 7 GW of additional power into west London by 2037," he said. "Our ask is that needs to happen faster... 12 years is just too far. The demand is here and now".
Rhodri Williams, technical director of the Home Builders Federation, told BBC News it was "essential" the government made sure there was "adequate investment" into the supply network to support housing developments.
Among a list of recommendations, the committee suggested introducing a separate planning category for data centres, to ensure better energy coordination.
A government spokesperson told BBC News it was exploring "bespoke options", including through the AI Energy Council, to support data centres and the housing sector.
The report also called on Mayor of London Sir Sadiq Khan to include a dedicated data centre policy in the next London Plan.
A spokesperson for the mayor told BBC News they are working to include "how to best address the need for data centres in London" in the next London Plan, and would "carefully consider" the recommendations of the report.
"Under Sadiq, we have seen more new council home starts in London than at any time since the 1970s and, prior to the pandemic, more new homes completed in London than any time since the 1930s," they added.
In 1966, Lander and Parkin published
a paper containing exactly two sentences
. They reported that they had used a program that used direct search on a CDC 6600 to obtain one counterexample to Euler’s Sum Of Powers Conjecture. The result:
27^4 + 84^4 +110^4 +133^4 =144^4
A small program, written in Fortran and using OpenMP, reproduces this result (and others of a similar nature) in two minutes on a current PC (OpenMP, 8 threads).
I am curious to know if anyone can estimate how long the CDC 6600 (which cost about $10 million in 1966, equivalent to about $ 70 million this year) would have run before it produced the reported result.
I am grateful to John Nichols for bringing the paper to my attention in the Intel Fortran Forum a few years ago.
Some details on the search algorithm used in the 1966 paper may be seen in another paper, https://www.ams.org/journals/mcom/1967-21-097/S0025-5718-1967-0220669-3/S0025-5718-1967-0220669-3.pdf.
4 minutes seems like a lot of time.
I just tested this with:
! sum_conjecture.f90
!
! 27^5 + 84^5 + 110^5 + 133^5 = 144^5
!
use, intrinsic :: iso_fortran_env, only: int64
implicit none
integer(int64) :: i,j,k,l,n
outer: do i = 1_int64, 10000_int64
do j = 1_int64, i
do k = 1_int64, i
do l = 1_int64, i
do n = 1_int64, i
if (j**5 + k**5 + l**5 + n**5 == i**5) then
print *, "i^5 = ", i**5
print *, "j^5 + k^5 + l^5 + n^5 = ", j**5 + k**5 + l**5 + n**5
print *, j,k,l,n,i
exit outer
end if
end do
end do
end do
end do
end do outer
end
On an Apple M2 Pro I get:
$ time ./a.out
i^5 = 61917364224
j^5 + k^5 + l^5 + n^5 = 61917364224
27 84 110 133 144
real 0m5.956s
user 0m5.873s
sys 0m0.060s
It is an interesting example since it is dominated by floating point multiplication, with a relatively small number of integer adds, and minimal memory references.
A floating point multiply functional unit on the 6600 took 10 cycles to complete. It wasn’t pipelined. There were actually two FP multiply units. So with a clock speed of 10 mhz, it could theoretically do ~2 million multiplies/sec. The 6600 allowed other instructions in other functional units to operate in parallel via its “scoreboard” logic. The compiler could schedule integer adds and memory loads/stores into the gaps while the multiplier units were busy.
In the later CDC 7600, Mr Cray ditched the scoreboard and went to fully pipelined functional units (except floating divide). So a new floating point multiply could be issued every clock period. Clock speed was faster (36 mhz) as well. But I’d guess the bottleneck would have moved to the eight 60-bit X registers.
Both the 6600 and 7600 did integer multiplies via floating point multiply units. Integer adds were done via a separate integer add functional unit.
Do you know how many clock cycles an integer multiply and a floating point multiply took on these machines? The integer instruction supposedly could skip the exponent operations.
BTW, the inner loop in that code really only does one
n**5
operation, one addition, and one comparison. The compiler should move all the other stuff to the outside of the inner loop.
Timing is identical for integer vs FP multiply. It is really a FP instruction either way - just unrounded vs rounded variants, and of course, zeros in the upper 12 bits for sign and exponent. There is also a ‘double precision’ version of the multiply - which gives the upper 48 bits of a 96 bit result.
It’s been a long time since I’ve thought about these details… The DP product instruction returns the lower 48 bits…
Perusing my copy of the classic “Assembly Language Programming”, by Ralph Grishman, he reminds us that the earliest versions of the 6600 did not have the ability to directly multiply integers via the FP multiply. So one had to use the Pack instruction to convert the integers to FP, then the DP product instruction, then convert back to integer via the Unpack instruction.
CDC fairly quickly realized they could make a small change to the DP product instruction to recognize that “if an integer smaller than 2^48 is used as an operand to a floating point instruction, it is treated as a floating point number with a biased exponent of 0 and hence a true exponent of -1777(base 8).” Before the change was made, “if both operands were integers less than 2^48 the machine compute a true exponent of -3776(base 8). Since a floating point number that small can not be represented in 6600 floating point format, the instruction would return a zero result.”
The hardware change was made to most of the earliest machines.
using the Linux time function. Since both AMD and Nvidia are based on classic flang I’m not surprised they give the same times. ifx though is a puzzle. All runs were compiled with -O3 optimization
Good idea. That gets the inner loop down to an integer addition and a couple of array lookups.
program sum_conjecture
! sum_conjecture.f90
!
! 27^5 + 84^5 + 110^5 + 133^5 = 144^5
!
use, intrinsic :: iso_fortran_env, only: int64
implicit none
integer(int64), parameter :: nmax = 10000_int64
integer(int64) :: i, j, k, l, n, sk, sl, sn
integer(int64) :: n5(nmax)
character(*), parameter :: cfmt = '(*(g0,1x))'
outer: do i = 1_int64, nmax
n5(i) = i**5
do j = 1_int64, i
do k = 1_int64, j
sk = n5(j) + n5(k)
do l = 1_int64, k
sl = sk + n5(l)
do n = 1_int64, l
sn = sl + n5(n)
if ( sn == n5(i) ) then
print cfmt, "i^5 = ", n5(i)
print cfmt, j, k, l, n, i
exit outer
end if
end do
end do
end do
end do
end do outer
end program sum_conjecture
$ gfortran -O3 sum_conjecture.f90
$ time a.out
i^5 = 61917364224
133 110 84 27 144
real 0m0.188s
user 0m0.174s
sys 0m0.007s
it’s not faster for me, but it gives overflow warnings upfront.
I don’t think this warning applies here because the array is small, but the danger of this approach in general is that the executable file must contain that compile-time computed data, which means that it takes time to load that memory from disk (SSD, etc.) into the process memory on startup. In contrast, if that memory is allocated at run time directly by the executable, and then filled by cpu instructions, then it can be some 10^3 to 10^5 times faster. You can also see the difference by looking at the size of the executable image (
ls -l a.out
). With most modern compilers, you do not see the size of the declared array reflected in the executable size unless they are parameter arrays, arrays initialized at compile time, or arrays in common blocks.
Also, if done at compile time, the whole array would need to be computed. That is 10^4 elements in this case (and integer overflows would be generated in doing so). The above run time code only computes 144 elements of the array before finding a solution and stopping.
A further question is where does that extra effort get charged? This extra effort is appropriate if the user is paying for that time (e.g. with money, or with elapsed time, or with total throughput through the machine), but not if that overhead is somehow not charged as user time (e.g. in a timeshare or batch environment with many other users). This is why the programmer sometimes writes code to minimize wall time and sometimes to minimize cpu time. Those two goals are not always exactly the same.
In the original code, the posix
time
command was used for the timings. That command returns three different time values for exactly this reason. If you alone own the machine you are running on, and you want to maximize throughput through that machine every 24 hour period, then it is the elapsed time that is critical. If you are one of many users sharing the machine, then it is the user time that you want to minimize, the system time is not charged to you because while your job is stalled waiting for the disk to respond, someone else’s job is swapped in and is being charged to execute its user time.
Here is the timing result of that last version of the code using gfortran -O3 with an Apple M2 cpu
on MacOS. It computes the
i**5
values at run time, so the system time is minimal.
i^5 = 61917364224
133 110 84 27 144
real 0m0.141s
user 0m0.139s
sys 0m0.002s
One other comment about timings is that they are almost never really consistent from run to run. For small segments of code like this, one can do
$ time a.out; time a.out; time a.out
The first results are usually longer than the others, which are then usually more consistent if not identical at the millisecond level. But if timings are measured at the microsecond level, they would show variations too.
I think int64 just doesn’t have enough range.
> nagfor test_int64.f90
NAG Fortran Compiler Release 7.2(Shin-Urayasu) Build 7203
Warning: test_int64.f90, line 8: Unused PARAMETER N5
Error: test_int64.f90, line 8: Integer overflow for exponentiation 6209**5
Errors in declarations, no further processing for TEST
[NAG Fortran Compiler error termination, 1 error, 1 warning]
Click for test_int64.f90
program test
use, intrinsic :: iso_fortran_env, only: int64
implicit none
integer(int64) :: i
integer(int64), parameter :: nmax = 10000_int64
integer(int64), parameter :: n5(nmax) = int([(i,i=1_int64,nmax)],int64)**5_int64
end program
Yes,
i=6208
is the largest integer for which
i**5
can be computed without overflow, so
nmax=10000
is overkill. But imagine a situation with an array say 10^8 or 10^9 in size, and the choice is to compute it at compile time or at run time. Then you can see how the differences in executable size and the differences in compile time vs. run time efficiency become relevant.
@RonShepard
, Here is a shortened version of your code, guided by the motto “calculate just in time” . This version is short enough, fast enough and the aim of the program rich enough in historical interest, that it should be made part of the TIOBE benchmark suite. Does anyone know the process for submission?
```
!
! 27^5 + 84^5 + 110^5 + 133^5 = 144^5
!
program euler
use, intrinsic :: iso_fortran_env, only: int64
implicit none
integer, parameter :: nmax = 150
integer(int64) :: i,j, k,l,m
integer(int64) :: n5(nmax)
character(*), parameter :: cfmt = '(*(g0,1x))'
outer: do i = 1, nmax
n5(i) = i**5
do j = 1, i
do k = 1, j
do l = 1, k
do m = 1, l
if ( n5(j)+n5(k)+n5(l)+n5(m) == n5(i) ) then
print cfmt, "i^5 = ", n5(i)
print cfmt, j, k, l, m, i
exit outer
end if
end do
end do
end do
end do
end do outer
end program euler
```
When compiled with the Intel Ifort compiler and run on a Windows PC with Ryzen 7-6800U, this program ran in 0.3 s.
That 0.3 s for Python is impressive. Please consider posting your code!
One would hope that the compiler would automatically move the additions outside of the inner loops. However, one additional reason for manually moving them outside is that you can exit some of the loops early.
do k = 1, j
sk = n5(j)+n5(k)
if ( sk > n5(i) ) exit
do l = 1, k
sl = sk +n5(l)
if ( sl > n5(i) ) exit
do m = 1, l
sm = sl +n5(m)
if ( sm > n5(i) ) exit
I doubt that a compiler optimizer would do those early loop exits.
I notice that if you have a solution to the equation j^5+k^5+l^5+m^5=i^5, then if you multiply the integers by some common integer factor x, then (x
j)^5+(x
k)^5+(x
l)^5+(x
m)^5=(x*i)^5 is also a solution. So when one violation of the conjecture is found, that automatically means that an infinite number have been found. I was curious if there are any other violations of the conjecture in addition to the multiples of the (133,110,85,27,144) sequence, so I removed the
exit outer
statement from the program and let it run for up to
i=1728
, and those multiples are the only ones found. I expect that mathematicians who are familiar with this problem already know if there are other violations, but it was a simple thing to do, so I did it. That code ran several hours overnight. I wonder if there are algorithms to do this search in a more efficient, maybe less brute force, way? For example, can you remove the outer
i
loop, do the other four loops, and then efficiently search the
n5(:)
array for matches to
sm
? If you could do that, it would reduce the effort from O(N^5) to O(N^4).
@mecej4
mentioned using modular arithmetic, maybe that could be used to limit some of the possibilities early in the outer loops? Of course, all these things will make the code more complicated.
TYSONS CORNER, VA and PETAH TIKVA, ISRAEL – June 5, 2025 –
Cellebrite (NASDAQ: CLBT), a global leader in premier Digital Investigative solutions for the public and private sectors, today announced its agreement to acquire Corellium, a leader in Arm-based virtualization software. This combination will set a new standard for digital investigations and the security of smart devices including iOS, Android, automotive systems and any Arm-based IoT device. Customers across public safety, defense, intelligence and private sectors will benefit from:
Accelerated identification of mobile vulnerabilities and exploits
Industry-first ability to visualize and interact with virtual devices as if they were physically present – aiding prosecutors, juries, investigators and developers
Increased efficiency, speed and efficacy of DevSecOps across all Arm-based devices
Arm-native and dynamic Mobile Pen Testing
“Leading edge technology matters,” said Thomas E. Hogan, Cellebrite’s interim chief executive officer. “Corellium has built an industry-unique virtualization platform that will ultimately help Cellebrite customers secure both their communities and institutions. The power of this combination goes beyond their technology and includes the addition of some of the brightest minds in vulnerability research, malware analysis and security testing. We could not be more excited about adding the Corellium team to the Cellebrite family. The combination of our respective talent and IP changes the game in the efficient securing and analysis of all Arm-based devices which are pervasive across a vast range of applications from cloud to edge.”
“The combination of Cellebrite’s commitment to innovation and its focus on public safety and security made this the perfect home for our people and our technology,” said Chris Wade, Corellium’s founder and chief technology officer, who will join Cellebrite as chief technology officer. “With Cellebrite’s offerings, users have ‘blueprints’ — technical schematics of what is on a device. With the addition of Corellium’s technology, users will virtually walk through the device, explore every room and open every door safely and without altering a thing in a forensically sound manner. I am personally thrilled to join Tom’s team as the new chief technology officer and to work closely with the Cellebrite research and development team.”
“Arm is the world’s most pervasive computing platform, and as AI continues to transform markets and deliver new experiences, the safety and security of our devices has never been more critical,” said Mohamed Awad, SVP and GM, Infrastructure, Arm. “Corellium’s innovative Arm-based virtualization solutions leverage the unique footprint Arm has from cloud to edge, and we value their contributions as part of the ecosystem building the future of computing on Arm.”
The acquisition of Corellium is expected to broaden Cellebrite’s TAM in both the public and private sectors. In the public sector, Corellium’s technology and talent will further enhance and broaden Cellebrite’s
Digital Investigation Platform
, while Corellium’s solution for mobile vulnerability research is expected to increase Cellebrite’s offerings for customers in the defense and intelligence sector. In the private sector, Corellium’s virtualization platform is expected to extend Cellebrite’s reach beyond eDiscovery and corporate investigation use cases through powerful virtualization solutions that enable development and security professionals to design the next generation of high-performance, secure mobile applications, IoT devices and automotive systems.
Cellebrite intends to acquire Corellium for an enterprise value of $170 million in cash with $20 million converted to equity at closing. Corellium securityholders will receive up to an additional $30 million in cash based on the achievement of certain performance milestones over the next two years. The deal is expected to close this summer, subject to approval of the Committee on Foreign Investment in the United States and other customary closing conditions. Cellebrite plans to provide additional information about Corellium’s anticipated financial contribution after the transaction closes.
J.P. Morgan Securities LLC is acting as exclusive financial advisor to Corellium.
Finally, Cellebrite is approaching the conclusion of its thorough CEO search process. The Company expects and plans to announce the appointment of the permanent CEO in conjunction with or prior to the mid-August disclosure of its second quarter 2025 results.
References to Websites and Social Media Platforms
References to information included on, or accessible through, websites and social media platforms do not constitute incorporation by reference of the information contained at or available through such websites or social media platforms, and you should not consider such information to be part of this press release.
About Cellebrite
Cellebrite’s (Nasdaq: CLBT) mission is to enable its global customers to protect and save lives by enhancing digital investigations and intelligence gathering to accelerate justice in communities around the world. Cellebrite’s AI-powered Digital Investigation Platform enables customers to lawfully access, collect, analyze and share digital evidence in legally sanctioned investigations while preserving data privacy. Thousands of public safety organizations, intelligence agencies, and businesses rely on Cellebrite’s digital forensic and investigative solutions—available via cloud, on-premises, and hybrid deployments—to close cases faster and safeguard communities. To learn more, visit us at
www.cellebrite.com
,
https://investors.cellebrite.com
, and find us on social media @Cellebrite.
About Corellium
Corellium is the leader in Arm hypervisor virtualization, helping security and developer teams build, test, and secure software for mobile, automotive, and IoT devices using the power of virtual hardware. Over 500 organizations, governments, and security practitioners worldwide rely on Corellium’s virtualization solutions to enhance security testing and streamline DevOps. By providing highly performant, scalable, and precise virtual models, Corellium enables new capabilities not possible with physical hardware. Corellium previously raised $25M in a Series A round, led by Paladin Capital Group with participation from Cisco Investments.
Caution Regarding Forward Looking Statements
This document includes “forward-looking statements” within the meaning of the “safe harbor” provisions of the United States Private Securities Litigation Reform Act of 1995. Forward looking statements may be identified by the use of words such as “forecast,” “intend,” “seek,” “target,” “anticipate,” “will,” “appear,” “approximate,” “foresee,” “might,” “possible,” “potential,” “believe,” “could,” “predict,” “should,” “could,” “continue,” “expect,” “estimate,” “may,” “plan,” “outlook,” “future” and “project” and other similar expressions that predict, project or indicate future events or trends or that are not statements of historical matters. Such forward-looking statements include, but are not limited to, this combination will set a new standard for digital investigations and the security of smart devices including iOS, Android, automotive systems and any Arm-based IoT device; with the addition of Corellium’s technology, users will virtually walk through the device, explore every room and open every door safely and without altering a thing in a forensically sound manner; Chris Wade’s prospective appointment as chief technology officer; the acquisition of Corellium is expected to broaden Cellebrite’s TAM in both the public and private sectors; in the public sector, Corellium’s technology and talent will further enhance and broaden Cellebrite’s digital forensics capabilities while Corellium’s solution for mobile vulnerability research is expected to increase Cellebrite’s offerings for customers in the defense and intelligence sector; in the private sector, Corellium’s virtualization platform is expected to extend Cellebrite’s reach beyond eDiscovery and corporate investigation use cases through powerful virtualization solutions that enable development and security professionals to design the next generation of high-performance, secure mobile applications, IoT devices and automotive systems; the timing associated with closing the transaction;
Corellium earning up to an additional $30M in cash based on the achievement of certain performance milestones over the next two years;
and the potential impact of the transaction on the Company’s 2025 revenue, annual recurring revenue (ARR), adjusted EBITDA, operating profitability and earnings are based on current expectations that are subject to risks and uncertainties. A number of factors could cause actual results or outcomes to differ materially from those indicated by such forward-looking statements. These factors include, but are not limited to: Cellebrite’s ability to keep pace with technological advances and evolving industry standards; Cellebrite’s material dependence on the purchase, acceptance and use of its solutions by law enforcement and government agencies; real or perceived errors, failures, defects or bugs in Cellebrite’s DI solutions; Cellebrite’s failure to maintain the productivity of sales and marketing personnel, including relating to hiring, integrating and retaining personnel; intense competition in all of Cellebrite’s markets; the inadvertent or deliberate misuse of Cellebrite’s solutions; failure to manage its growth effectively; Cellebrite’s ability to introduce new solutions and add-ons; its dependency on its customers renewing their subscriptions; the low volume of business Cellebrite conducts via e-commerce; risks associated with the use of artificial intelligence; the risk of requiring additional capital to support the growth of its business; risks associated with higher costs or unavailability of materials used to create its hardware product components; fluctuations in foreign currency exchange rates; lengthy sales cycle for some of Cellebrite’s solutions; near term declines in new or renewed agreements; risks associated with inability to retain qualified personnel and senior management; the security of Cellebrite’s operations and the integrity of its software solutions; risks associated with the negative publicity related to Cellebrite’s business and use of its products; risks related to Cellebrite’s intellectual property; the regulatory constraints to which Cellebrite is subject; risks associated with Cellebrite’s operations in Israel, including the ongoing Israel-Hamas war and the risk of a greater regional conflict; risks associated with different corporate governance requirements applicable to Israeli companies and risks associated with being a foreign private issuer and an emerging growth company; market volatility in the price of Cellebrite’s shares; changing tax laws and regulations; risks associated with joint, ventures, partnerships and strategic initiatives; risks associated with Cellebrite’s significant international operations; risks associated with Cellebrite’s failure to comply with anti-corruption, trade compliance, anti-money-laundering and economic sanctions laws and regulations; risks relating to the adequacy of Cellebrite’s existing systems, processes, policies, procedures, internal controls and personnel for Cellebrite’s current and future operations and reporting needs; and other factors, risks and uncertainties set forth in the section titled “Risk Factors” in Cellebrite’s annual report on Form 20-F filed with the SEC on April 27, 2023 and in other documents filed by Cellebrite with the U.S. Securities and Exchange Commission (“SEC”), which are available free of charge at
www.sec.gov
. You are cautioned not to place undue reliance upon any forward-looking statements, which speak only as of the date made, in this communication or elsewhere. Cellebrite undertakes no obligation to update its forward-looking statements, whether as a result of new information, future developments or otherwise, should circumstances change, except as otherwise required by securities and other applicable laws.
Run NVIDIA CUDA applications on AMD GPUs without recompilation
What is APEX GPU?
APEX GPU is a
lightweight CUDA→AMD translation layer
that allows unmodified CUDA applications to run on AMD GPUs using
LD_PRELOAD
. No source code changes, no recompilation required.
# Your existing CUDA application
./my_cuda_app
# Same application on AMD GPU - just add LD_PRELOAD
LD_PRELOAD=/path/to/libapex_hip_bridge.so ./my_cuda_app
It's that simple.
Why APEX GPU?
The Problem
You have CUDA applications. You want to use AMD GPUs (they're cheaper and often more powerful). But CUDA only works on NVIDIA hardware.
Traditional solutions require:
❌ Source code access
❌ Manual code porting
❌ Recompilation for each application
❌ Weeks or months of engineering time
❌ Ongoing maintenance as CUDA evolves
The APEX Solution
APEX GPU intercepts CUDA calls at runtime and translates them to AMD equivalents:
✅
Binary compatible
- works with closed-source applications
✅
Zero code changes
- use existing CUDA binaries as-is
✅
Instant deployment
- add one environment variable
// Your existing CUDA code__global__voidvectorAdd(float* a, float* b, float* c, int n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) c[i] = a[i] + b[i];
}
intmain() {
// Compile with nvcc as usualcudaMalloc(&d_a, size);
cudaMalloc(&d_b, size);
cudaMalloc(&d_c, size);
vectorAdd<<<blocks, threads>>>(d_a, d_b, d_c, n);
cudaDeviceSynchronize();
}
Compile once with nvcc:
nvcc vector_add.cu -o vector_add
Run on NVIDIA:
Run on AMD (no recompilation):
LD_PRELOAD=./libapex_hip_bridge.so ./vector_add
Performance
Operation
APEX Overhead
AMD Performance
cudaMalloc
<1μs
Native AMD speed
cudaMemcpy
<1μs
~2TB/s (HBM3)
Convolution
<5μs
95-98% of native
GEMM
<3μs
97-99% of native
Pooling
<2μs
99% of native
Bottom line:
Negligible overhead for compute-heavy workloads. Performance is limited by AMD hardware capabilities, not APEX translation.
Testing
Run the Test Suite
Expected output:
╔════════════════════════════════════════════════════════════════╗
║ TEST SUITE SUMMARY ║
╠════════════════════════════════════════════════════════════════╣
║ Total Tests: 5 ║
║ Passed: 5 ║
║ Failed: 0 ║
║ Success Rate: 100% ║
╚════════════════════════════════════════════════════════════════╝
Tests Included
test_events_timing
- Event API and timing (117 lines)
test_async_streams
- Async operations and streams (183 lines)
test_2d_memory
- 2D memory operations (202 lines)
test_host_memory
- Pinned memory (217 lines)
test_device_mgmt
- Device management (259 lines)
Coverage:
27 CUDA functions tested across 5 comprehensive test suites
Architecture
How It Works
┌─────────────────────┐
│ CUDA Application │ ← Your unmodified binary
│ (calls cudaMalloc) │
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ LD_PRELOAD │ ← Linux dynamic linker intercepts
│ libapex_hip_bridge │ the call before it reaches
│ │ the real CUDA library
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ APEX Translation │ ← Translates cudaMalloc → hipMalloc
│ (dlopen/dlsym) │ using dynamic loading
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ AMD Runtime │ ← Calls native AMD HIP library
│ (libamdhip64.so) │
└──────────┬──────────┘
│
↓
┌─────────────────────┐
│ AMD GPU │ ← Executes on AMD hardware
│ (MI300X, etc) │
└─────────────────────┘
Design Principles
Dynamic Loading:
Uses
dlopen
/
dlsym
to load AMD libraries at runtime
No compile-time dependencies on AMD headers
Compiles on any Linux system
Portable across distributions
Minimal Overhead:
Direct function call translation
No complex state management
No unnecessary abstractions
<1% overhead for typical workloads
Binary Compatibility:
Exports exact CUDA function signatures
Works with any CUDA binary
No ABI issues
Drop-in replacement
Supported Applications
Tested & Working
✅
PyTorch
- Full training and inference
✅
TensorFlow
- GPU operations
✅
NVIDIA CUDA Samples
- 95%+ compatibility
✅
Custom CUDA kernels
- Binary compatible
✅
cuBLAS applications
- Linear algebra workloads
✅
cuDNN applications
- Deep learning workloads
Use Cases
🧠
Machine Learning:
Train models on AMD GPUs
🔬
Scientific Computing:
Run simulations and analysis
📊
Data Processing:
GPU-accelerated analytics
🎮
Compute Workloads:
Any CUDA application
💰
Cost Savings:
Use cheaper AMD hardware for CUDA workloads
Compatibility
AMD GPU Support
RDNA (Gaming):
RX 6000 series (RDNA2)
RX 7000 series (RDNA3)
CDNA (Compute):
MI100, MI200 series (CDNA1/2)
MI300 series (CDNA3) ⭐
Recommended
CUDA Version Support
CUDA 11.x ✅
CUDA 12.x ✅
OS Support
Ubuntu 20.04+ ✅
RHEL 8+ ✅
Other Linux distributions (should work, not extensively tested)
Limitations & Known Issues
Current Limitations
CUDA Driver API:
Not yet implemented (only Runtime API)
Unified Memory:
cudaMallocManaged
not supported yet
Texture Memory:
Limited texture support
Multi-GPU:
Basic support (tested with single GPU primarily)
Dynamic Parallelism:
Not supported (rare use case)
Workarounds
Most applications use CUDA Runtime API exclusively, so these limitations affect <5% of real-world use cases.
Roadmap
✅ Phase 1: Core Translation (Complete)
CUDA Runtime API (38 functions)
cuBLAS operations (15+ functions)
cuDNN operations (8+ operations)
Test suite (100% pass rate)
Documentation
🚧 Phase 2: Extended Coverage (In Progress)
Additional cuDNN operations (backward passes)
More cuBLAS functions (batched operations)
CUDA Driver API support
Unified memory support
🔮 Phase 3: Optimization (Future)
Performance profiling tools
Automatic kernel optimization
Multi-GPU orchestration
Cloud deployment automation
Contributing
We welcome contributions! Here's how you can help:
Ways to Contribute
Test on Your Hardware
Try APEX with your CUDA applications
Report compatibility issues
Share performance results
Add Missing Functions
Check
COMPLETE_CUDA_API_MAP.txt
for unimplemented functions
Implement missing CUDA calls
Submit a PR with tests
Improve Documentation
Add examples
Improve tutorials
Fix typos and clarify explanations
Performance Optimization
Profile bottlenecks
Optimize hot paths
Submit benchmarks
Development Setup
# Clone and build
git clone https://github.com/yourusername/APEX-GPU.git
cd APEX-GPU
# Build all bridges
./build_hip_bridge.sh
./build_cublas_bridge.sh
./build_cudnn_bridge.sh
# Run tests
./run_all_tests.sh
# Make your changes to apex_hip_bridge.c (or other bridges)# Rebuild
./build_hip_bridge.sh
# Test your changes
LD_PRELOAD=./libapex_hip_bridge.so ./test_your_change
Contribution Guidelines
Follow existing code style (K&R C style)
Add tests for new functionality
Update documentation
Keep commits focused and atomic
Write clear commit messages
FAQ
Q: Does this really work?
A:
Yes! APEX has a 100% test pass rate on our test suite covering 27 CUDA functions. It's been validated with PyTorch CNNs and various CUDA applications.
Q: What's the performance impact?
A:
Minimal (<1% for typical workloads). The translation overhead is microseconds per call, which is negligible for compute-heavy GPU operations that take milliseconds.
Q: Do I need NVIDIA hardware?
A:
No! That's the whole point. You only need AMD GPUs with ROCm installed.
Q: Can I use this commercially?
A:
No. APEX is licensed under CC BY-NC-SA 4.0 (Non-Commercial). You can use it for research, education, and personal projects, but not for commercial purposes. For commercial licensing, please contact the maintainers.
Q: Will this break with CUDA updates?
A:
CUDA's ABI is stable. APEX should continue working across CUDA versions. If new functions are added, we may need to implement them.
Q: How is this different from hipify?
A:
hipify requires source code and recompilation. APEX works with binaries using LD_PRELOAD. No source or recompilation needed.
Q: What about ZLUDA?
A:
ZLUDA is similar but less actively maintained. APEX is lighter (93KB vs several MB), open source, and uses a cleaner dynamic loading architecture.
Q: Can I contribute?
A:
Absolutely! See the Contributing section above.
License
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
APEX GPU solves a multi-billion dollar industry problem. While we want the community to benefit and contribute, we've chosen to reserve commercial rights. This ensures:
Fair compensation for the value created
Sustainable development and support
Prevention of exploitation by large corporations
Commercial Licensing
If you need to use APEX GPU commercially, we offer commercial licenses with:
Full commercial usage rights
Priority support
Custom feature development
Service level agreements
Contact:
[Add your contact email here]
For Contributors
By contributing to APEX GPU, you agree that your contributions will be licensed under the same CC BY-NC-SA 4.0 license.
Acknowledgments
AMD ROCm Team
- For HIP, rocBLAS, and MIOpen
CUDA Community
- For comprehensive documentation
Open Source Contributors
- For testing and feedback
Citation
If you use APEX GPU in research or publications, please cite:
@software{apex_gpu,
title = {APEX GPU: CUDA to AMD Translation Layer},
author = {Your Name},
year = {2024},
url = {https://github.com/yourusername/APEX-GPU}
}
When I took a look at the history of Quake binaries, they all made sense to me.
quake.exe
was the original release, able to run on DOS and Windows 95. Then came
vquake.exe
to support the hardware accelerated chip Vérité 1000. Later,
glquake.exe
generalized hardware acceleration to any vendor providing OpenGL drivers. And to revolutionize Internet deathmatch, id Software released QuakeWorld server and client (
qwsv.exe
and
qwcl.exe
).
However, I could not figure out the point of
winquake.exe
. Until now. Here is what I understood and a little bit of a dive into how it works.
Quake.exe performance
quake.exe
runs on both DOS and Windows 95 but how well does it perform? A quick benchmark on my Pentium MMX 233MHz, Matrox Mystique PC (320x200 with 101 screen size) and sound on, showed the following numbers.
Configuration
Framerate
quake.exe
started from DOS
48 fps
quake.exe
started from Windows 95
38 fps
So "framerate" is the beginning of an answer to justify the existence of WinQuake.
quake.exe
running from
Windows 95
is roughly 25% slower than the same binary started from DOS. And that is to be expected.
Windows 95
runs DOS applications in a virtual machine ("DOS BOX"), where memory access, interrupts, and signals are virtualized, which incurs overhead.
Another element of the answer comes from
Quake Chunnel
.
quake.exe
can access Windows 95 TCP/IP stack, but only via a convoluted tech from Mpath to bridge a "DOS BOX" to win32 dlls. By having a win32-only application, id Software had guaranteed direct access to
winsock.dll
.
Last but not least, id Software really wanted Quake to work on Windows NT. Despite their best efforts, the people at DJGPP could not make their DPMI client in
quake.exe
compatible with the NT Virtual DOS Machine (NTVDM).
Near pointers don't work under NT - which was a huge disappointment to iD and generated some conference calls to Microsoft.
A fun way to start exploring is to first read
WQREADME.TXT
and then take a look at all the modes available in
winquake.exe
. They are configured with the script
wq.bat
.
Options for running WinQuake:
wq max: all features on, but doesn't work on all systems
wq fast: maximum speed, but doesn't work on all systems
wq fastvid: maximum video speed, but safer, probably slower sound
wq fastsnd: maximum sound speed, but safer, probably slower video
wq safe: very likely to run, but may be slower
wq verysafe: almost sure to run, but probably slower, and no sound
Here are the numbers I got for each mode, still with the same Pentium MMX 233MHz machine and same configuration.
Configuration
Framerate
wq max
42.4 fps
wq fast
41.8 fps
wq fastvid
45.0 fps
wq fastsnd
41.8 fps
wq safe
45.0 fps
wq verysafe
40.0 fps*
Impressive.
winquake.exe
managed to bring up the framerate within 6% of
quake.exe
running on DOS. Mission accomplished. But how does it works?
winQuake.exe backends
Each "mode" is configured via command-line flags. This part reveals there are three types of backend for input controls, audio, and video.
Amusingly, the mode that provides the highest framerate,
fastvid
keeps everything default but disables an audio backend!
"fastvid" was also the name of a
tool
to fix the Pentium Pro abysmal video write speed on chipset that shipped with buggy "Write Posting". The option in
qw.bat
has nothing to do with it.
Audio backends
WinQuake can send its sound effects (the music comes from CD tracks) using two audio backends (with
-nosound
disables sound effects altogether).
The two backends are
DirectSound
(
dsound.h
from DirectX) and what id calls
wave sound
which is in fact
winmm.h
, the Windows MultiMedia audio API, dating back to Windows 3.1.
If DirectSound is available, WinQuake uses it to provide the lowest latency. However this backend has a higher impact on the CPU and results in 10% lower framerate. With
-wavonly
, users can force usage of
WinMM
which results in higher latency but higher framerate.
Input Control backends
To read user inputs, WinQuake uses either
DirectInput
(
dinput.h
from DirectX) or the legacy Windows API
winuser.h
.
By default WinQuake uses
winuser.h
but usage of DirectInput can be requested via
-dinput
for slightly smoother motion and responsiveness to fast spinning motions. I suspect it was not enabled by default for cases where DirectX was not installed or perhaps fear of driver problems.
Joystick inputs are handled with
joystickapi.h
. Likewise, it seems drivers may not have been stable since id provided a way to disable it with
-nojoy
.
Video backends
The part that was the most interesting to me was the video backends. WinQuake can operate in five modes using GDI, VGA, VESA, Accelerated VESA, or DirectDraw.
DIB video backend
The Graphics Device Interface (GDI) (
wingdi.h
) is the foundation to render anything on the desktop in Windows 95. Applications usually did not use it directly but instead called
winuser.h
(which in turns used low-level
wingdi.h
).
WinQuake can render to a Device-Independent Bitmaps (DIB) which is a surface to be blitted towards a window though GDI. The surface can be of any dimension so there are no "display mode" to detect here, WinQuake hardcodes its DIB modes to square-pixel resolutions 320x240, 640x480, and 800x600.
Because it is using Windows "by the book", DIB mode is the safest mode that should always work. It is also the slowest way to render to the screen because WinQuake first renders to a DIB that is then sent to the GDI and then sent to the video card.
While slower, it is not devoid of hardware acceleration. Many graphic cards wanting to perform well under Windows 95 had hardware acceleration implementation of crucial functions such as
bitBlt
.
Finally, DIB mode is the only one able to render in "windowed" mode. Every other mode takes over and renders in "fullscreen" mode. Note that DIB can also render in pseudo-full screen if WinQuake is started with
dibonly
but this is "faked" with a borderless window covering the whole screen.
SciTech's Multi-platform Graphics Library (MGL)
For everything not DIB, WinQuake uses SciTech's
MegaGraph Graphics Library
. It was a rather expensive lib ($499 in 1997, $1,000 in 2025)
[2]
but well worth its price because it brought order into the chaos that was the world of video systems in 1997 if a game operated outside GDI.
WinQuake could find itself having to deal with the following types of video systems.
1. VBEAF : VESA Accelerator Function
2. VBE2 : VESA Linear Frame Buffer for direct to VRAM write/read.
3. DirectDraw : Only available if DirectX is installed.
4. StandardVGA : That good ol' VGA video mode.
When it starts, WinQuake registers the drivers it wants MGL to load (see
registerAllDispDrivers
). MGL then lists all supported resolutions and pick the highest performance drivers to access each of them (in the order list above).
void registerAllDispDrivers(void) {
/* Even though these driver require WinDirect, we register
* them so that they will still be available even if DirectDraw
* is present and the user has disabled the high performance
* WinDirect modes.
*/
MGL_registerDriver(MGL_VGA8NAME,VGA8_driver);
if (useWinDirect){
MGL_registerDriver(MGL_LINEAR8NAME,LINEAR8_driver);
if (!COM_CheckParm ("-novbeaf"))
MGL_registerDriver(MGL_ACCEL8NAME,ACCEL8_driver);
}
if (useDirectDraw) {
MGL_registerDriver(MGL_DDRAW8NAME,DDRAW8_driver);
}
}
The list of modes and which driver was selected by MGL is available via the command
vid_describemodes
in Quake console. In the screenshot below, we can see almost the full house of drivers
VGA8.DRV
,
DDRAW.DRV
,
LINEAR8.DRV
, and the windowed DIB modes.
Quake fast mode.
Quake dibonly mode.
Quake nowindirect mode.
Quake nodirectdraw mode.
I had never heard of VBE/AF before reading MGL source code. As far as I understand, it never gained much traction and few vendors wrote drivers to support it.
Many games used MGL: WinQuake, Hexen II, Grand Theft Auto, Maui Mallard in Cold Shadow, Total Mayhem, Balls of Steel.
DirectDraw video system
Microsoft was very much aware that GDI was fine for applications but not enough for video games. Already in Windows 3.1 they had released a game developer SDK called WinG to give a more direct fullscreen access to the screen. The second version of WinG was renamed DirectX and contained the 2D fullscreen API which they called DirectDraw.
Although safer and more reliable, Microsoft Windows imposed many restrictions on applications.
One result of this situation was that games, and other high-performance graphics applications,
could no longer access the hardware resources directly in order to maximize performance and
expand functionalities. For several years game programmers continued to exercise the craft in
DOS, and Windows users had to switch to the DOS mode to run games, simulations, and other
graphics programs. The resulting situation implied a major contradiction: a graphical operating
system in which graphics applications would execute with marginal performance
The first effort in this direction was a product named WinG,
in reference to Windows for Games. WinG was first made available in 1994 and it required Win32
in Windows 3.1. Its main feature is that WinG enabled the game programmer to rapidly transfer
bitmaps from system memory into video memory. This made possible the creation of Windows
games that executed with much better performance.
Microsoft renamed the new version of the Game SDK, calling it DirectX 2. Other versions later
released were named DirectX 3, DirectX 5, DirectX 6, and currently, DirectX 7.
- Feng Yuan, "Windows Graphics Programming Win32 GDI and DirectDraw"
In terms of performance, DirectDraw was a step up from GDI but it was also not guaranteed to work due to driver bugs or if the user had not installed DirectX. It can be disabled with
nodirectdraw
.
WinDirect video system
Readers may have picked up on something written earlier that was blatantly wrong. Direct access to the hardware is forbidden to Win32 applications. So how is MGL able to bypass GDI/DirectDraw and directly hit VBEAF, VBE, and VGA?
A key component of the SciTech MGL, WinDirect is a runtime package for
DOS and Windows 95 that provides direct access to the display hardware
for both 16 and 32-bit applications. Traditionally Windows applications
have had to perform all graphics output using the standard Graphics Device
Interface (GDI). Although the GDI is very extensive and powerful, it is also
not particularly fast for the sort of graphics that real time applications like
interactive video games require.
WinDirect breaks this barrier by allowing high performance applications to
shut down the normal GDI interface, and to take over the entire graphics
display hardware just like you would normally do under DOS. Once GDI
has been shut down, interactive graphics applications can re-program the
display controller and write directly to video memory. A WinDirect
application can program any standard VGA graphics mode such as
320x200x256, it can re-program the controller and run standard VGA ModeX
style graphics, or it can call the standard VESA BIOS services to run high
resolution SuperVGA graphics.
MGL v4
programmer guide
, is a treasure strove of information. If, like me, you wondered what were these
WDIR32.DLL
and
WDIR16.DLL
libraries that came with WinQuake, the doc mentions them (
W
in
DIR
ect). Likewise, the doc describes
PMPRO16.DLL
and
PMPRO32.DLL
as
DOS extender independent API for protected mode services
. Michael Abrash's Zen Timer is also mentioned in there :)!
WinQuake source code does not include MGL. Only the headers and a pre-compiled 32-bit
MGLLT.LIB
(MGL Lite) are provided to allow compilation. SciTech did eventually publish the source in 2000
[4]
but it is
no longer available
. What was uploaded on GitHub
[5]
is v5 which by then had dramatically changed (e.g: WinDirect was gone).
Luckily a kind soul has mirrored
MGL v4
. If you want to do your own digging, install mglb405.exe and mgls405.exe. Or just download my installation,
src.rar
.
Putting it all together
Overall,
winquake.exe
was often able to find a fast rendering path, either through DirectDraw or WinDirect. The fallback to DIB mode was not ideal but still a win compared to
quake.exe
. Add to that the ability to select a sound backend to optimize for framerate or audio latency and the result was a damn good experience that completely justified the effort.
More than 30 years later, you can still run
winquake.exe
on Windows 11. Fullscreen does not support widescreen but the windowed mode still works flawlessly. As much as Microsoft has been questionable lately, their commitment to backward compatibility is impressive.
File: Then Apple Creative Director Alan Dye celebrates the launch of the July Issue at the new WIRED office on June 24, 2015 in San Francisco, California.
Kimberly White | Getty Images
Apple
's head of user interface design, Alan Dye, will join
Meta
, in a notable shift of executive talent in Silicon Valley.
The iPhone maker confirmed Dye's departure on Wednesday and Apple CEO Tim Cook said in a statement that the company prioritizes design and has a strong team. The statement said that veteran designer Stephen Lemay will succeed Dye.
"Steve Lemay has played a key role in the design of every major Apple interface since 1999," Cook said in a statement.
Meta CEO Mark Zuckerberg in a Wednesday social media
post
said that Dye would lead up a new creative studio that brings together design, fashion and technology.
"We plan to elevate design within Meta,"
wrote
Zuckerberg, who did not say what specific products Dye will work on.
Compared to other Silicon Valley companies, Apple has always emphasized design to customers and investors as one of its strengths. Apple prominently features its design executives to discuss interface changes at the company's launch events.
In June, Dye revealed a redesign of Apple's software interface for iPhones, Macs and the Apple Watch called Liquid Glass. The company described it as an "elegant" new design with translucent buttons, updated app icons and fluid animations.
Dye said it was the "next chapter" of the company's software and said it "sets the stage" for the next era of Apple products.
"Our new design blurs the lines between hardware and software to create an experience that's more delightful than ever while still familiar and easy to use," Dye said at the launch.
Apple announces liquid glass during the Apple Worldwide Developers Conference (WWDC) on June 9, 2025 in Cupertino, California.
Justin Sullivan | Getty Images
For years, Apple design was embodied by executive Jony Ive, who left Apple in 2019 and is now working
with OpenAI
on artificial intelligence hardware alongside Sam Altman.
Dye took over user interface design and became one of the design studio's leads
in 2015
when Ive stepped back from a day-to-day role. Dye started at Apple in 2006 and worked on software for the iPhone, iPad, Mac, Apple Watch, Apple TV and Vision Pro, according to his LinkedIn
profile
.
He was also partly responsible for the first iPhone in 2017 that did away with the home screen button at the bottom of the device and replaced it with a software-based swipe-up motion.
Meta has said in recent years that it wants to be a major developer of hardware and
Zuckerberg
has
said
Apple is one of his company's biggest competitors.
The social media company currently makes several virtual reality headsets under its Quest brand, and recently scored its first hardware hit with Ray-Ban Meta smart glasses, which are stylish sunglasses equipped with cameras and the ability to run an AI model that can answer questions. Sales of the device tripled over the past year, Ray-Ban parent company EssilorLuxottica
said in July
.
"We're entering a new era where AI glasses and other devices will change how we connect with technology and each other," Zuckerberg wrote.
The minimum wage for workers will increase in 19 states and 49 cities and counties next month, with the wage floor reaching $15 per hour in dozens of localities, a new report found.
Though the federal minimum wage of $7.25 per hour has not increased since 2009, many state and local governments continue to increase minimums through legislation or scheduled increases tied to inflation.
An
annual report
from the National Employment Law Project, a nonprofit advocating for workers’ rights, found that 88 jurisdictions will raise their minimum wages by the end of 2026.
In January, Nebraska’s minimum wage will increase from $13.50 to $15 per hour, while Rhode Island will see an increase from $15 to $16 per hour.
All workers in Denver will see the minimum wage increase from $18.81 to $19.29 in January and the minimum in Flagstaff, Arizona, will increase from $17.85 to $18.35 per hour.
The increases come as rising costs of housing, food and utilities are pinching more workers across the country, said Yannet Lathrop, senior researcher and policy analyst at the National Employment Law Project who authored the report. Those costs are particularly challenging for lower-income workers, who are most likely to be affected by minimum wage changes.
“They are really struggling right now,” she said. “These wages basically mitigate the effects of inflation, the effects of the rising cost of living, and the difficulties so many people are having paying for basics, for food, for housing, for medicine — just the basics of life.”
The minimum wage for workers will increase in 19 states and 49 cities and counties next month, with the wage floor reaching $15 per hour in dozens of localities, a new report found.
Though the federal minimum wage of $7.25 per hour has not increased since 2009, many state and local governments continue to increase minimums through legislation or scheduled increases tied to inflation.
An
annual report
from the National Employment Law Project, a nonprofit advocating for workers’ rights, found that 88 jurisdictions will raise their minimum wages by the end of 2026.
In January, Nebraska’s minimum wage will increase from $13.50 to $15 per hour, while Rhode Island will see an increase from $15 to $16 per hour.
All workers in Denver will see the minimum wage increase from $18.81 to $19.29 in January and the minimum in Flagstaff, Arizona, will increase from $17.85 to $18.35 per hour.
The increases come as rising costs of housing, food and utilities are pinching more workers across the country, said Yannet Lathrop, senior researcher and policy analyst at the National Employment Law Project who authored the report. Those costs are particularly challenging for lower-income workers, who are most likely to be affected by minimum wage changes.
“They are really struggling right now,” she said. “These wages basically mitigate the effects of inflation, the effects of the rising cost of living, and the difficulties so many people are having paying for basics, for food, for housing, for medicine — just the basics of life.”
Lathrop said research increasingly finds that higher wages improve educational outcomes, mental and physical health.
“Things that are going to benefit not just the workers, but also the communities and society as a whole,” she said. “I think that’s an important thing to keep in mind.”
Inflation has significantly eroded the buying power of the federal minimum wage since 2009.
Advocates say raising the wage floor helps low-wage workers cover the rising cost of essentials and boosts the economy by putting more money into the pockets of people who are likely to spend it. But many employers, especially small businesses, argue that raising the minimum wage forces them to cut workers or raise prices.
In Rhode Island, lawmakers this year proposed
legislation
that would raise the minimum wage $1 per year, culminating with a $20 per hour minimum in 2030. But
backlash
from business groups and economic uncertainty led the Democratic sponsor to successfully push for a “more measured approach” that includes hikes for the next two years rather than five, reaching a $17 hourly minimum by 2027.
That compromise didn’t persuade Republican opponents, who said even the more modest minimum wage change will force layoffs and consumer price hikes.
“The real minimum wage is $0,” said state House Minority Whip David Place, according to the
Rhode Island Current.
“That’s what they make when they get fired because business can’t afford to keep them.”
The minimum wage remains stagnant at $7.25 in 20 states, according to the National Employment Law Project. Those are primarily conservative-led states including Alabama, Iowa, Texas and Wyoming.
Last year, Missouri voters
approved
a ballot measure ensuring paid sick leave and boosting the minimum wage to $15 per hour, with future increases tied to inflation.
But Republican lawmakers took aim at the changes in Jefferson City this year.
In a move Democrats called “absolute disdain” for workers, GOP lawmakers
passed a bill
repealing the paid sick leave provision and nixing the annual minimum wage increases tied to inflation. Missouri’s current $13.75 minimum wage will still rise to $15 next month but is no longer subject to future increases.
“Today, we are protecting the people who make Missouri work—families, job creators, and small business owners—by cutting taxes, rolling back overreach, and eliminating costly mandates,” Republican Gov. Mike Kehoe said in a July statement when he signed the bill into law.
an exposition on the great utility of using the parent-index method of representing trees in J.
This is similar, but with K and less comprehensive: https://github.com/JohnEarnest/ok/blob/gh-pages/docs/Trees.md
Comments...
Here's an exposition of one method for representing trees in J.
Parent Index Vector
The tree is a vector of integers where each element is the index of the parent node but with _1 for the root's parent. This is handy for modeling directory trees and it works well for that.
Whichever we choose is not functionally significant: all the following code works the same on either version. This representation of trees is good for looking up an item and locating its parent, or joining sub-trees into a larger tree. By "good", we mean simple to code and fast-running.
Basic Navigation
Here are some basic verbs with examples of using them.
whChild=: [: I. [ e. ] NB.* whChild: where are children of x in tree y
whRoot=: [:I. _1=] NB.* whRoot: where roots are in tree y
nextLevel=: [ whChild ] NB.* nextLevel: indexes of descendants of x in tree y
whParent=: _1-.~]{[ NB.* whParent: index of parent; _1 means no parent.
whLeaves=: [: I. [: -. ] e.~ [: i.# NB.* whLeaves: indexes into tree of leaf nodes.
trb whChild whRoot trb NB. Indexes of children of root
1 2
trd whChild whRoot trd NB. Indexes of children of root on depth-first version
1 4
Though the numbers differ, they refer to the same items in each case:
nmsb{~trb whChild whRoot trb NB. Children of root using breadth-first
+--+--+
|n0|n1|
+--+--+
nmsd{~trd whChild whRoot trd NB. Depth-first
+--+--+
|n0|n1|
+--+--+
Here we see how to generalize moving to levels further down the tree using the power conjunction " ^: ".
trb nextLevel whRoot trb NB. Next level down from root
1 2
trb nextLevel trb nextLevel whRoot trb NB. two levels down
3 4 5 6 7
trb nextLevel^:2 ] whRoot trb NB. two levels down
3 4 5 6 7
This representation of trees allows multiple root nodes - which is called a "forest" - and can represent closed loops, which are invalid as trees. Since invalid representations are possible, it may behoove us to check if a given tree is valid. We can do this in the following way.
NB.* verifyTree: check that tree has >:1 root, no cycles, no unreachable nodes:
NB. 0 for bad node, 1 for good one.
verifyTree=: 3 : 0
cc=. 0$~#y NB. Cycle counter
nli=. whRoot y NB. Next level indexes: start at root(s)
cc=. (1+nli{cc) nli}cc NB. Count # visits to node
while. (0<#nli) *. 1=>./cc do.
nli=. y nextLevel nli
cc=. (1+nli{cc) nli}cc NB. Count # visits to node
end.
cc *. -.(_1=y) *. 1<+/_1= y NB. Dis-allow forests
NB.EG (1 0;0 0;1 0 0) -: verifyTree &.> _1 1; 0 1; _1 2 1 NB. bad ones
NB.EG (1 1 1;1 1 1 1) -: verifyTree &.> _1 0 1; _1 0 _1 2 NB. good ones
NB.EG (0 0;0 1 0 1) -: verifyTree &.> _1 _1; _1 0 _1 2 NB. bad ones, multi-root
)
The "EG" comments at the end give examples of use and the results expected: each phrase should return a single "1" indicating the results match the outputs. The three initial bad trees fail verification (indicated by zeros corresponding to bad nodes) because the first one is cyclical because it has a node that is its own parent, the second one is cyclical and has no root, and the third has a cycle between 1 and 2.
Notice that we choose to treat multi-rooted trees as invalid only with the final condition. This is an arbitrary choice. Similarly, our exclusion of cycles is arbitrary as well. We could just as well allow forests or cycles if we wanted to generalize beyond trees to graphs of some kind. However, this "parent index" scheme would allow for only a limited subset of graphs, so is not a good choice to represent more general graphs.
Grafting, Pruning, and Displaying
We use the
"tree" routine
to display trees to illustrate the changes we can make by pruning and grafting nodes from one part of a tree to another.
To convert initial tree to the following, first split off the "n0" branch:
'trb0 nms0 trb1 nms1'=. ;0 1 pruneB &.><(nmsb i. <'n0');trb;<nmsb
trb0 NB. Tree without pruned branch
_1 0 1 1 1 2 3 3 3
nms0
+--+--+---+---+---+----+----+----+----+
|C:|n1|n10|n11|n12|n100|n110|n111|n112|
+--+--+---+---+---+----+----+----+----+
trb1 NB. Pruned branch
_1 0 0
nms1
+--+---+---+
|n0|n00|n01|
+--+---+---+
nms=. nms0,nms1 NB. All names for new combined tree
tr=. graftRoot trb0;(nms0 i. <'n100');trb1 NB. Tree with pruned branch grafted to "n100" node.
Pruned branch re-attached in different place:
tree }.nms,.~tr{nms
+-------------------------------------------+
| ┌─ n00| NB. We see the "n0" node moved from
| ┌─ n10 ─── n100 ─── n0 ─┴─ n01| NB. the root to node "n100".
| │ ┌─ n110 |
|─ C: ─── n1 ─┼─ n11 ─┼─ n111 |
| │ └─ n112 |
| └─ n12 |
+-------------------------------------------+
There's
an existing utility to display a tree
in a printable format. To use this utility, we must convert our "parent index" form to one in which edges are listed as point pairs. One way to do this is shown as follows.
That this latter representation, based on the depth-first ordered tree, is the same as the former one can be see if we insert the node names to illuminate the correspondence between the indexes of the two tree representations.
Reader subscriptions are a necessary way
to fund the continued existence of LWN and the quality of its content.
If you are already an LWN.net subscriber, please log in
with the form below to read this content.
Please consider
subscribing to LWN
. An LWN
subscription provides numerous benefits, including access to restricted
content and the warm feeling of knowing that you are helping to keep LWN
alive.
(Alternatively, this item will become freely
available on December 11, 2025)
Axon Enterprise Inc. is working with a Canadian police department to test the addition of face recognition technology (FRT) to its body-worn cameras (BWCs). This is an alarming development in government surveillance that should put communities everywhere on alert.
As many as 50 officers from the E...
Axon Enterprise Inc. is working with a Canadian police department to test the addition of
face recognition technology (FRT)
to its body-worn cameras (
BWCs
). This is an alarming development in government surveillance that should put communities everywhere on alert.
As many as 50 officers from the Edmonton Police Department (EPD) will begin using these FRT-enabled BWCs today as part of a proof-of-concept experiment. EPD is the first police department in the world to use these Axon devices, according to
a report from the Edmonton Journal
.
This kind of technology could give officers instant identification of any person that crosses their path. During the current trial period, the Edmonton officers will not be notified in the field of an individual’s identity but will review identifications generated by the BWCs later on.
“This Proof of Concept will test the technology’s ability to work with our database to make officers aware of individuals with safety flags and cautions from previous interactions,” as well as “individuals who have outstanding warrants for serious crime,” Edmonton Police described in a
press release
, suggesting that individuals will be placed on a watchlist of sorts.
FRT brings a rash of problems. It relies on extensive surveillance and collecting images on individuals, law-abiding or otherwise. Misidentifications can cause horrendous consequences for individuals, including prolonged and difficult fights for innocence and unfair incarceration for crimes never committed. In a world where police are using real-time face recognition, law-abiding individuals or those participating in legal, protected activity that police may find objectionable — like protest — could be quickly identified.
With the increasing connections being made between disparate data sources about nearly every person, BWCs enabled with FRT can easily connect a person minding their own business, who happens to come within view of a police officer, with a whole slew of other personal information.
Axon had
previously claimed
it would pause the addition of face recognition to its tools due to concerns raised in 2019 by the company’s AI and Policing Technology Ethics Board. However, since then, the company
has continued to research
and consider the addition of FRT to its products.
This BWC-FRT integration signals possible other FRT integrations in the future. Axon is building an entire arsenal of cameras and surveillance devices for law enforcement, and the company grows the reach of its police surveillance apparatus, in part, by leveraging relationships with its thousands of customers, including those using its flagship product,
the Taser
. This so-called
“ecosystem” of surveillance technology
q includes the
Fusus system
, a platform for connecting surveillance cameras to facilitate real-time viewing of video footage. It also involves expanding the use of surveillance tools like BWCs and the
flying cameras
of
“drone as first responder” (DFR) programs
.
Face recognition undermines individual privacy, and it is
too dangerous
when deployed by police. Communities everywhere must move to protect themselves and safeguard their civil liberties, insisting on transparency, clear policies, public accountability, and audit mechanisms. Ideally, communities should
ban
police use of the technology altogether. At a minimum,
police must not add FRT to BWCs
.
Kea DHCP: Modern, open source DHCPv4 and DHCPv6 server
ISC distributes TWO full-featured, open source, standards-based DHCP server distributions: Kea DHCP and ISC DHCP. Kea includes all the most-requested features, is far newer, and is designed for a more modern network environment. ISC announced the
End of Life
for the older ISC DHCP system in 2022. Users of ISC DHCP may find
these resources
helpful in migrating their DHCP server deployments to the Kea server.
How is the Kea DHCP server different from the older ISC DHCP?
Modular Component Design, Extensible with Hooks Modules.
The Kea distribution includes separate daemons for a DHCPv4 server, a DHCPv6 server, and a dynamic DNS (DDNS) module. Many optional features are enabled with dynamically-loaded “Hooks Modules,” which you need run only if you are using them. You can write your own hooks modules (in C++) or try some of the hooks we offer.
On-line Re-configuration with REST API.
Kea uses a JSON configuration file that can be modified remotely via
set
commands and reloaded without stopping and restarting the server, an operation that could take quite a while with ISC DHCP.
Designed to Integrate with Your Existing Systems.
Kea allows you to separate the data from the execution environment, enabling new deployment options. Your network data - leases, host reservation definitions, and most configuration data - can be located separately from the DHCP server itself, using a Kea “backend.”
Kea supports two database backends; MySQL and PostgreSQL. Besides the obvious benefits (you avoid JSON formatting errors, you can quickly and easily mine the data for other purposes) using a database backend enables multiple Kea servers to share the data.
Potential benefits:
A host reservation database, used with the Host_Cmds hook, allows remote management of host reservations via Stork, and permits multiple Kea servers to use a shared host reservations database.
A configuration database, while currently not supported by Stork, allows use of some configuration elements, such as subnets, across multiple Kea servers. This can make it much easier to add new Kea servers.
Web-based graphical dashboard.
Kea now has a graphical dashboard for monitoring multiple Kea servers. This system, called Stork, uses agents deployed on the Kea servers to relay information to a centralized management platform, providing the administrator with an easy-to-use quick view of system status and activity.
Modern, higher performance implementation.
Kea is multi-threaded, and when
configured for efficient operation
, it can be performant enough for a large-scale, short-lease duration environment, which is the most demanding scenario.
The core Kea daemons are open source, shared under MPL2.0 licensing. Kea is developed in the open on ISC’s
GitLab
; we welcome you to open issues and submit patches there. Kea runs on most Linux and Unix platforms, as well as MacOS. If you don’t want to build from our source distribution, we also provide a
repository of pre-built packages
for most popular operating systems.
Your major design decisions are whether to deploy in pairs for
High Availability
and use the default csv file for host and lease data, or to
install a separate database
for a Kea data “backend.” Some of these decisions can limit your performance. See our Knowledgebase for advice on designing for
optimal performance
.
3.
Configuration
The Kea
Administrator Reference Manual (ARM)
is the primary reference for Kea configuration. The extensive set of example configuration files in the
project repo
and our
knowledgebase
may help you get started. If you are migrating from an existing ISC DHCP deployment, try the Kea
Migration Assistant
(a special feature of the ISC DHCP distribution). This will enable you to save your current ISC DHCP server configuration as a Kea configuration file. It will still need some manual adjustment, but this tool should translate the bulk of your configuration.
4.
Maintenance
Most users will benefit from joining the
kea-users
mailing list. Consider joining our Kea project
GitLab
to log issues, see what we’re working on, submit patches, and participate in development. Consider deploying
Stork
for a graphical management dashboard. If your DHCP is critical to your business, we recommend you
subscribe for technical support from ISC
.
Stork Dashboard for Kea
Monitor both the machine and the application
Stork aggregates data about the health of the system hosting Kea, as well as the status and activity level of Kea itself. Parameters reported include memory, CPU utilization, software versions, and uptime.
Monitor Pool Utilization and High Availability
Stork displays configured pools, with # of addresses provisioned and assigned and even tracks pool utilization across shared networks. Graphical elements highlight areas of high utilization to alert the operator to take actionHigh Availability pairs are monitored and their configured role and status are shown, making it easy to see which servers don’t have a backup established, and when a failover event has occurred.
Manage Host Reservations
Add, update and view DHCPv4 and DHCPv6 host reservations, using a graphical interface to select a host identifier, assign a hostname, reserve an IP address, associate a client class, and configure boot file information and DHCP options.
Ability to attach and detach from a shell session without killing it
Native terminal scrollback
Multiple clients can connect to the same session
Re-attaching to a session restores previous terminal state and output
Works on mac and linux
This project does
NOT
provide windows, tabs, or splits
install
Requires zig
v0.15
Clone the repo
Run build cmd
zig build -Doptimize=ReleaseSafe --prefix ~/.local
# be sure to add ~/.local/bin to your PATH
usage
Important
Press
ctrl+\
to detach from the session.
Usage: zmx <command> [args]
Commands:
[a]ttach <name> [command...] Create or attach to a session
[d]etach Detach all clients from current session (ctrl+\ for current client)
[l]ist List active sessions
[k]ill <name> Kill a session and all attached clients
[h]elp Show this help message
examples
zmx attach dev # start a shell session
zmx attach dev nvim .# start nvim in a persistent session
zmx attach build make -j8 # run a build, reattach to check progress
zmx attach mux dvtm # run a multiplexer inside zmx
shell prompt
When you attach to a zmx session, we don't provide any indication that you are inside
zmx
. We do provide an environment variable
ZMX_SESSION
which contains the session name.
We recommend checking for that env var inside your prompt and displaying some indication there.
fish
functions-cfish_prompt _original_fish_prompt 2>/dev/null
functionfish_prompt--description'Write out the prompt'ifset-q ZMX_SESSION
echo-n"[$ZMX_SESSION] "end
_original_fish_prompt
end
bash
todo.
zsh
todo.
philosophy
The entire argument for
zmx
instead of something like
tmux
that has windows, panes, splits, etc. is that job should be handled by your os window manager. By using something like
tmux
you now have redundent functionality in your dev stack: a window manager for your os and a window manager for your terminal. Further, in order to use modern terminal features, your terminal emulator
and
tmux
need to have support for them. This holds back the terminal enthusiast community and feature development.
Instead, this tool specifically focuses on session persistence and defers window management to your os wm.
ssh workflow
Using
zmx
with
ssh
is a first-class citizen. Instead of
ssh
ing into your remote system with a single terminal and
n
tmux panes, you open
n
terminals and run
ssh
for all of them. This might sound tedious, but there are tools to make this a delightful workflow.
First, create an
ssh
config entry for your remote dev server:
Now you can spawn as many terminal sessions as you'd like:
ssh d.term
ssh d.irc
ssh d.pico
ssh d.dotfiles
This will create or attach to each session and since we are using
ControlMaster
the same
ssh
connection is reused for every call to
ssh
for near-instant connection times.
Now you can use the
autossh
tool to make your ssh connections auto-reconnect. For example, if you have a laptop and close/open your laptop lid it will automatically reconnect all your ssh connections:
Or create an
alias
/
abbr
:
abbr -a ash "autossh -M 0 -q"
ash d.term
ash d.irc
ash d.pico
ash d.dotifles
Wow! Now you can setup all your os tiling windows how you like them for your project and have as many windows as you'd like, almost replicating exactly what
tmux
does but with native windows, tabs, splits, and scrollback! It also has the added benefit of supporting all the terminal features your emulator supports, no longer restricted by what
tmux
supports.
socket file location
Each session gets its own unix socket file. Right now, the default location is
/tmp/zmx
. At the moment this is not configurable.
debugging
We store global logs for cli commands in
/tmp/zmx/logs/zmx.log
. We store session-specific logs in
/tmp/zmx/logs/{session_name}.log
. These logs rotate to
.old
after 5MB. At the moment this is not configurable.
a note on configuration
At this point, nothing is configurable. We are evaluating what should be configurable and what should not. Every configuration option is a burden for us maintainers. For example, being able to change the default detach shortcut is difficult in a terminal environment.
a smol contract
Write programs that solve a well defined problem.
Write programs that behave the way most users expect them to behave.
Write programs that a single person can maintain.
Write programs that compose with other smol tools.
Write programs that can be finished.
todo
bug
: unix socket files not always getting removed properly
bug
: remove log files when closing session
bug
: send resize event when a client first sends stdin
The
daemon
and client processes communicate via a unix socket
Both
daemon
and
client
loops leverage
poll()
Each session creates its own unix socket file
/tmp/zmx/*
We restore terminal state and output using
libghostty-vt
libghostty-vt
We use
libghostty-vt
to restore the previous state of the terminal when a client re-attaches to a session.
How it works:
user creates session
zmx attach term
user interacts with terminal stdin
stdin gets sent to pty via daemon
daemon sends pty output to client
and
ghostty-vt
ghostty-vt
holds terminal state and scrollback
user disconnects
user re-attaches to session
ghostty-vt
sends terminal snapshot to client stdout
In this way,
ghostty-vt
doesn't sit in the middle of an active terminal session, it simply receives all the same data the client receives so it can re-hydrate clients that connect to the session. This enables users to pick up where they left off as if they didn't disconnect from the terminal session at all. It also has the added benefit of being very fast, the only thing sitting in-between you and your PTY is a unix socket.
prior art
Below is a list of projects that inspired me to build this project.
shpool
is a service that enables session persistence by allowing the creation of named shell sessions owned by
shpool
so that the session is not lost if the connection drops.
shpool
can be thought of as a lighter weight alternative to tmux or GNU screen. While tmux and screen take over the whole terminal and provide window splitting and tiling features,
shpool
only provides persistent sessions.
The biggest advantage of this approach is that
shpool
does not break native scrollback or copy-paste.
abduco provides session management i.e. it allows programs to be run independently from its controlling terminal. That is programs can be detached - run in the background - and then later reattached. Together with dvtm it provides a simpler and cleaner alternative to tmux or screen.
Acme, a brief history of one of the protocols which has changed the Internet
I would like to share with you this article I wrote about the ACME protocol, which I “fell in love with” about ten years ago. It is for me a way to give back to this fantastic Free Software and Open Protocols developers community.
This article is about the roots, the conception, the standardization, the relation with its ecosystem and the evolution challenges faced by the ACME protocol.
Thank you so much to all of you for your time and support! 💚
Internet and Network Protocols
Open and Standardized Protocols at the Heart of the Internet’s Success
During the 1990s, computing underwent a true revolution driven by the rise and global spread of the Internet. The Internet fulfilled the promise embodied in Sun Microsystems’ slogan
“The Network is the Computer”
.
By interconnecting individual computers, the Internet enabled its users to communicate without limits and without worrying about borders.
This unrestricted interconnection emerged at a pivotal moment in modern history: the opposition between the West and the Eastern Bloc led by the USSR had—albeit temporarily, as we now know—faded away, China was becoming the world’s factory, and the movement and collaboration between people were much freer and open than ever.
The Internet supported a kind of utopia of instant communication and sharing, previously unknown. This utopia was made possible by a set of open and standardized protocols. This was the key to enabling all kinds of different systems to cooperate and communicate seamlessly.
There were, of course, isolationist or monopolistic temptations from certain manufacturers or software editors. But open and standardized protocols ultimately prevailed, enabling unprecedented expansion. Built on top of IP, TCP, UDP, and DNS, among others, the HTTP and HTML duo would propel the Web as the Internet’s preferred communication platform for the next 30 years.
Limited Use of Encryption
The success of this communication utopia was achieved without much concern for ensuring authentication, integrity, and confidentiality of exchanges.
In 2015, only
~40%
of websites used encryption. The consequences of this negligence in addressing security risks were confirmed by Edward Snowden’s revelations in 2013: our data was exposed to anyone who wanted and could intercept and collect it.
Let’s Encrypt is coming
When asked about the main obstacles to the widespread adoption of encryption,
J.C. Jones
, one of the architects of Let’s Encrypt and now one of its site reliability engineers after leading Firefox’s cryptographic team, responds:
“More and more information was flowing across the Web, and most data being transferred did not have integrity or confidential protections from TLS. The biggest stumbling block to using TLS everywhere was obtaining and managing server-side certificates, and so: Let’s Encrypt” –
J.C. Jones
Obtaining a certificate was the main obstacle, and this was the priority to address.
This view was shared by a group of partners who, starting in 2013, pooled resources to establish Let’s Encrypt, an automated and free certificate authority.
Sarah Gran
, VP of Advancement at Let’s Encrypt, shares:
“Early collaborators included people from Mozilla, Electronic Frontier Foundation, Akamai, Cisco, and the University of Michigan” –
Sarah Gran
And that’s how Let’s Encrypt was born.
In the Web ecosystem, certificate authorities are organizations from which you can obtain a certificate for a domain after proving you control it.
And so, Let’s Encrypt is since 2015 a certificate authority that delivers for free (as in free beer) TLS Server certificates.
On the legal/administrative side, Let’s Encrypt certificate authority operates for the public’s benefit and is a service provided by the Internet Security Research Group (ISRG), a California public benefit corporation.
Regarding
Let’s Encrypt results
ten years after its birth, they are really impressive (over 700M active certificates, over
60%
of all the public TLS server certificates) and as Sarah Gran points out, so is the global HTTPS usage:
“When we started issuance, only about 39% of website visits were HTTPS. Today, it’s nearly 95% in the United States, and over 83% globally. We still have work to do, but we are proud of the progress we’ve made over the last ten years” –
Sarah Gran
Let’s Encrypt delivers certificates in a automated manner using the ACME protocol which implies no manual action from the site owner nor the certificate authority. So, let’s speak now a little about the automation aspect!
Automation: The Core of the Operation
From the mid-2020s perspective, the automation at the heart of Let’s Encrypt might seem obvious, but in the first half of the 2010s, it was far from the norm. The ecosystem of public certificate authorities issuing server certificates was no exception.
At first glance, automation appears to be there to help website managers reliably deploy the TLS protocol on their sites, but it was first and foremost an absolute prerequisite for the very viability of the Let’s Encrypt project.
As
Aaron Gable
, tech lead of Boulder—the software at the core of Let’s Encrypt—, confirms:
“Automation was always going to be critical to Let’s Encrypt’s success. From the very beginning, we knew that there was no way we could scale manual validation on a non-profit’s budget” –
Aaron Gable
Indeed, it is worth noting that Let’s Encrypt has operated on an Internet scale from the start with a small team of about fifteen engineers, or even fewer at launch. For this team, automation was the only viable way to fulfill the immense mission they had set for themselves.
ACME
The Open and automated Protocol That Powers Let’s Encrypt
When we talk about automation in relation to Let’s Encrypt, we are talking about
ACME
(Automated Certificate Management Environment).
This protocol allows client software to prove to an ACME-compatible certificate authority that it controls the domain for which it is requesting a certificate.
Sarah Gran clarifies an important point:
“An important aspect of how Let’s Encrypt works is that we verify control over a domain, not ownership” –
Sarah Gran
Control vs. ownership of a domain—a nuance everyone should keep in mind.
This proof of control involves the client responding to a challenge issued by the ACME-compatible certificate authority. The challenge can be an HTTP, DNS, or TLS challenge, depending on the client’s choice and certificate authority support. Completing the challenge requires the ACME client to place a value provided by the ACME server—in a standardized HTTP path, a DNS zone, or a TLS response, respectively. All of these operations involve cryptography, of course.
The key point with ACME is that this entire dialogue between the client and the ACME server is executed without any human intervention, enabling the automatic issuance of certificates. Their deployment and integration into the web service can also generally be automated using scripts triggered after issuance.
One might wonder whether ACME was part of Let’s Encrypt’s design from the beginning.
J.C. Jones confirms:
“By late 2014, the idea of an HTTP REST API with “/challenge” and “/certificate” existed, but we hadn’t defined much beyond that. We had a series of in-person meetings, in the Mozilla San Francisco office on Embarcadero and the EFF office in the Tenderloin through the spring of 2015 where we worked out the details” –
J.C. Jones
ACME was indeed at the core of Let’s Encrypt from the start and underwent a refinement process to cover all use cases as thoroughly as possible.
To learn more about the roots of ACME and Let’s Encrypt, there is a very informative document to read: the
Let’s Encrypt paper
for
ACM CCS 2019
in London. It mentions the previous work of two teams:
“A group led by Alex Halderman at the University of Michigan and Peter Eckersley at EFF was developing a protocol for automatically issuing and renewing certificates. Simultaneously, a team at Mozilla led by Josh Aas and Eric Rescorla was working on creating a free and automated certificate authority”
.
When these two teams discovered each other’s work, they joined forces. ACME and its implementation in Let’s Encrypt were the result of this joint effort supported by the initial partners mentioned above.
Securing the Web or the Internet?
Speaking of use cases, one might wonder whether the Web was Let’s Encrypt’s primary target, or if securing the Internet with its multiple protocols was also part of the objectives.
Sarah Gran provides an unambiguous first-level answer:
“From Day One, we have sought to get the web to 100% encryption” –
Sarah Gran
But when asked about the various types of challenges in the protocol, J.C. Jones offers a nuance:
“DNS, TLS-SNI, and HTTP were all in planning in spring 2015, but many of us were less confident in the procedure around the DNS validation. Which is ironic, as it turned out TLS-SNI had a vulnerability so we had to stop using it and our DNS validation was ultimately fine. In general, the collection of us were simply respectful of the great complexity within the DNS” –
J.C. Jones
This is a perspective not often publicly expressed by engineers primarily from the Web: their lack of confidence in implementing a DNS challenge stemmed from their humility regarding the complexity of the DNS ecosystem and the level of expertise required to master it.
The challenge was ultimately met, and this DNS challenge—though not its primary purpose—enabled multiple protocols outside HTTP like SMTP to be secured by ACME.
Standardization and Open Source
Developed in the Open
ACME was documented openly from the start, and
Certbot
, the first open-source ACME client co-developed with the EFF, served as the client side reference implementation.
Similarly, a standardization process through the IETF resulted in
RFC 8555
in March, 2019.
“This is what we foresaw, or at least hoped for. The initial client development often had conversations like, ‘oh, if someone wants that, then they’ll write their own client.’ It was a key part of why the REST API needed to be an IETF standard, and was part of the argument at the IETF BoF that resulted in the formation of the ACME Working Group in Q3 2015” –
J.C. Jones
Let’s Encrypt has also always provided constant support to developers by responding in its forum or on its GitHub issue tracker, and all this work has truly paid off.
An interesting post
has been recently written about support on the Let’s Encrypt blog.
Standardization for what benefits?
The other question that can be asked is whether or not the standardization process within the IETF has led to an improvement in the ACME protocol thanks to the cooperation that guides this process.
Jacob Hoffman-Andrews
, one of the
RFC 8555
authors working for EFF & Let’s Encrypt, confirms an initial benefit that the ACME protocol has been able to derive from its standardization process:
“One of the big changes was from a validation-first flow to a certificate-request-first flow. In other words, earlier drafts had subscribers requesting validation for domain names and then requesting a certificate once those validations were successful. The final RFC has subscribers request a certificate, and then the CA tells the subscriber what validations are needed. This change originated from within the IETF discussion process, and was intended to make handling of wildcard certificates more natural.” –
Jacob Hoffman-Andrews
Aside this first design improvement, Jacob details a second major improvement of the security of the protocol, improvement that also landed during the IETF standardization process:
“Another big change, also originated from within the IETF, was to make all requests authenticated, including GET requests. Since ACME is authenticated with signed POSTs, this necessitated the POST-as-GET concept that’s in ACME today” –
Jacob Hoffman-Andrews
We can see there how IETF iterations can challenge the security of a protocol and leads its development to innovative solutions to tackle the challenges it faces!
Last, Jacob adds another information that illustrates the benefits of developing a protocol into the open: it allows the community to evaluate (and sometimes, fix) its security level due to the availability of all materials and often, of the reference implementation:
“Another very important evolution was the deprecation of the tls-sni-01 challenge method. This was found to be flawed by Frans Rosen, a security researcher. It was replaced with TLS-ALPN-01, developed at IETF with significant input from Google” –
Jacob Hoffman-Andrews
Let’s Encrypt, ACME, and the Public Certificate Authorities Ecosystem
In 2015, the arrival of Let’s Encrypt in the public certificate authorities ecosystem raised a number of questions.
What level of cooperation or hostility? What impact on the viability of existing certificate authorities?
Here again, the fact that Let’s Encrypt was based on an open protocol, immediately subject to an IETF standardization initiative, enabled collaboration and adoption by the most innovative certificate authorities.
I spoke about the
External Account Binding
(EAB) option of the protocol with J.C. Jones. EAB is a way for an ACME client to authenticate to an ACME server using an identifier and a key value which are verifiable by the server in a repository it maintains. With EAB, an ACME server can filter who can uses its service which is useful for commercial certificate authorities for example; it is an alternative model to Let’s Encrypt one where anybody can ask for a certificate.
Using the example of EAB, J.C. Jones confirms the collaboration with certificate authorities that happens during the IETF standardization process:
“EAB was an early addition at the IETF ACME Working Group. Many in the room were worried that without a means to bind to a payment method, ACME would not get adoption. In fact, some of the counterarguments to forming ACME were blunted by EAB, as such a mechanism wasn’t in the theoretically-competing, already-existent standard: SCEP. SCEP, it was argued, already handled ‘free’ certificate issuance, for private certificate authorities. Anything else needed a feasible path for usage payment.” –
J.C. Jones
Beyond billing, the addition of EAB enabled also some commercial certificate authorities to integrate their existing domain control validation systems with ACME, allowing some of them to skip the challenge step of the ACME protocol.
The IETF standardization process, based on an open process, created the necessary discussion space for cooperation among entities that did not necessarily share the same objectives.
The result, ten years after the introduction of ACME and the completion of its standardization process in 2019, is that ACME has become the primary means by which all public certificate authorities—both free and commercial—rely on for their transition to an automated future of issuing short-lived certificates.
Effectively, until early this year, the maximum lifespan of a public TLS server certificate was set to 398 days by the CA/B Forum, the organization that set the rules for public certificate authorities. With the vote of the
ballot SC081
at the CA/B Forum in April 2025, it has been decided that the certificate lifespan will decrease gradually starting March 2026 to reach 47 days in March 2029. The automation provided by ACME seems to be one of the main identified levers to help organizations to adapt to this drastic reduction in the lifespan of public TLS server certificates.
Created at Let’s Encrypt, adopted everywhere
It is important to note that although ACME was developed by the team managing Let’s Encrypt, this protocol is now one of the main protocols for automated certificate acquisition adopted by all public certificate authorities.
And outside the public certificate authorities ecosystem, I think it’s fair to say that this protocol is also becoming increasingly popular with technical architects in companies with private certificate authorities.
This has been the case in my company for several years now, where we have deployed an ACME endpoint in front of our internal certificate authority. Among the benefits we have seen, we have been able to rely on the vast ACME clients ecosystem in order to provide an ACME client to each OS or middleware that powers our infrastructure. We can see there how certificate obtention agility powered by ACME helps organizations in their journey to global IT agility.
Innovation and the adoption challenge
The ARI episode
We may fear that the development of a protocol supported primarily by a team as small as Let’s Encrypt’s will be fairly limited in terms of evolution and innovation.
But the history of ACME shows that its evolution continues after its initial standardization.
In 2025, we saw with the ARI (ACME Renewal Information –
RFC 9773
) extension that the ACME protocol continues to evolve. ARI is a way for a certificate authority to suggest a renewal period to its clients, often earlier than they would have determined themselves. This use case is particularly relevant when the certificate authority needs to mass-revoke certificates that, for example, did not comply with the rules the certificate authority must follow when issuing certificates.
More specifically, J.C. Jones and Aaron Gable point two incidents that had to be handled by the Let’s Encrypt team and that were the start for the ARI initiative:
Aaron Gable leads the effort of designing and implementing ARI. But even if a new extension to the protocol has been produced, it can only reach its potential users after ACME clients have implemented it into their code base. As previously said, the team and some community members invest a lot on providing support to the community. In the case of ARI, this support is oriented to the ACME clients developers in order to make these clients ARI aware.
Providing an efficient support and effective resources to the client side ACME actors is a huge part of the challenge in order to keep ACME ecosystem healthy and agile.
As illustrates by Sarah Gran, another way to give momentum to a new feature is to lift certain restrictions on access to the certificate authority:
In order to encourage ARI adoption, we’ve configured Let’s Encrypt to allow subscribers who renew via ARI to bypass our rate limits.” –
Sarah Gran
Client Side Update Challenge
But despite a good support work and incentive measures, Aaron Gable confirms ARI adoption is just at its start:
“There is still much progress to be made. Part of the appeal of the Automated Certificate Management Environment is that many users can set-and-forget their client and configuration. This means that most clients never receive software updates, and even client projects that have implemented ARI in their latest version still have massive install bases that aren’t running that version. We’ve worked closely with many clients developers to implement ARI, and contributed implementations ourselves in several cases, but for widespread adoption the whole ecosystem will need to slowly turn over”
– Aaron Gable
This situation is really shared with a lot of client side softwares that
“just work”(c)
and it raises some concerns about how to make an ecosystem keeping track with innovation on its client side.
This challenge arises not only in terms of updating the client, but also in terms of updating the configuration. Many ACME clients rely on cron tasks. To have an efficient ARI setup, your task has to run ideally on a daily basis be able to ask the certification authority every day whether the certificate needs to be reissued. This is not the classic cron task setup. So, users have to modify this cron task frequency to reach the ARI goal of certificate reissuance led by certificate authority. Client side ACME setup evolution is a really challenging task.
Evolution on server side ACME implementation
CA/B Forum has recently asked public certificate authorities to adopt
Multi-Perspective Issuance Corroboration
(MPIC) to guard against BGP attacks. We have asked Aaron Gable about the impacts that kind of measure have had on ACME server side implementation in the Let’s Encrypt infrastructure:
“We’ve had to make few if any changes to our infrastructure to accommodate recent requirements changes such as MPIC and DNSSEC validation. We innovated MPIC (then called Remote Validation) along with a research team at Princeton, and implemented it in 2020. Our experience already running such a service helped inform the requirements as they were incorporated by the CA/B Forum.”
– Aaron Gable
The lesson learnt here is that being at the edge of the innovation let you shape part of the future of your ecosystem and significantly lower the impact on your infrastructure of many regulatory measures that come into effect over time.
Future
It is really encouraging to see a lot of innovation in the ACME ecosystem.
So what evolutions can we expect to see in the future?
We have asked the question to Aaron Gable who gave us two upcoming developments:
“We’re currently working on
standardizing
profile selection for ACME
, and our deployment of the early draft of this standard has already brought some much-needed flexibility to the WebPKI, enabling us to make changes to our certificate contents with minimal disruption.”
“I’m also excited about a potential future change which would
introduce a ‘pubkey’ identifier type
, along with a set of challenges that allow the client to demonstrate control over the corresponding keypair. This would fix the gap today that presenting a CSR does not actually prove possession of the key in that CSR.” –
Araron Gable
Fastly has also recently
contributed
to ACME in order to improve the
dns-01
challenge in a multi-cloud and multi-PKI environment. An
IETF draft
describing this
dns-account-01
challenge is online. This is further proof that the public TLS ecosystem has truly embraced the ACME protocol as its primary automation tool.
Another recent development based on ACME has also shed new light on the potential of this protocol: since 2022, a
draft
is under progress at the IETF in order to write an ACME extension. The goal of this extension is to use ACME to obtain a certificate for a device in order to prove its identity. The challenge is based on device attestation and what’s new in this case is the arrival of a third party, the attestation server.
What is remarkable here is that we are no longer dealing with ACME’s initial use case, namely obtaining TLS server certificates: we can see in this IETF draft the potential of ACME as a challenge-based framework to obtain certificate in very different contexts.
Indeed, we can venture to say that ACME’s future looks bright 😊
Conclusion
It is heartening to see that, 30 years after the widespread adoption of the Internet, open and standardized protocols continue to revolutionize its use.
ACME and its Let’s Encrypt implementation at scale have enabled the widespread adoption of HTTPS, thereby raising the level of security for billions of Internet users and also of private networks.
Having been able to do it inside a non profit organization, providing the Internet with an open and standardized protocol is a great success for all people believing in FreeSoftware and an Open Internet.
As a community, I really think we can thank these organizations, teams, and engineers who continue to uphold the promise of efficiency and Freedom brought about by cooperation around open protocols. They inspire new generations (and older ones I guess 😉) demonstrating big things can still be achevied today in the open for the common good at the Internet scale!
I would like to extend a special thank you to the members of the Let’s Encrypt team,
J.C. Jones
,
Aaron Gable
,
Sarah Gran
and
Jacob Hoffman-Andrews
, for the time and effort they dedicated to answering my questions. Without them, this article would not have been possible.
After Years of Controversy, the EU’s Chat Control Nears Its Final Hurdle: What to Know
Electronic Frontier Foundation
www.eff.org
2025-12-03 23:19:03
After a years-long battle, the European Commission’s “Chat Control” plan, which would mandate mass scanning and other encryption-breaking measures, at last codifies agreement on a position within the Council of the EU, representing EU States. The good news is that the most controversial part, the fo...
After a
years-long battle
, the European Commission’s “Chat Control” plan, which would mandate mass scanning and other encryption-breaking measures, at last codifies agreement on a position within the Council of the EU, representing EU States.
The good news
is that the most controversial part, the forced requirement to scan encrypted messages, is out. The bad news is there’s more to it than that.
Chat Control has gone through several iterations since it was first introduced, with the EU Parliament backing a
position
that protects fundamental rights, while the Council of the EU spent many months pursuing an intrusive law-enforcement-focused approach. Many
proposals
earlier this year required the scanning and detection of illicit content on all services, including private messaging apps such as WhatsApp and Signal. This requirement would
fundamentally break end-to-end encryption
.
Thanks to the tireless efforts of digital rights groups, including
European Digital Rights
(EDRi),
we won a significant improvement
: the Council agreed on its
position
, which removed the requirement that forces providers to scan messages on their services. It also comes with strong language to protect encryption, which is good news for users.
But here comes the rub: first, the Council’s position allows for “voluntary” detection, where tech platforms can scan personal messages that aren’t end-to-end encrypted. Unlike in the U.S., where there is no comprehensive federal privacy law, voluntary scanning is not technically legal in the EU, though it’s been possible through a
derogation
set to expire in 2026. It is unclear how this will play out over time, though we are concerned that this approach to voluntary scanning will lead to private mass-scanning of non-encrypted services and might limit the sorts of secure communication and storage services big providers offer. With limited transparency and oversight, it will be difficult to know how services approach this sort of detection.
With mandatory detection orders being off the table, the Council has embraced
another worrying system to protect children online: risk mitigation
. Providers will have to take
“
all
reasonable mitigation measures” to reduce risks on their services. This includes
age verification and age assessment measures
. We have written about the perils of
age verification
schemes and
recent developments in the EU
,
where regulators are increasingly focusing on AV to reduce online harms.
If secure messaging platforms like Signal or WhatsApp are required to implement age verification methods, it would fundamentally reshape what it means to use these services privately. Encrypted communication tools should be available to everyone, everywhere, of all ages, freely and without the requirement to prove their identity. As age verification has started to creep in as a mandatory risk mitigation measure under the EU’s Digital Services Act in certain situations, it could become a de facto requirement under the Chat Control proposal if the wording is left broad enough for regulators to treat it as a baseline.
Likewise, the Council’s position lists “voluntary activities” as a potential risk mitigation measure. Pull the thread on this and you’re left with a contradictory stance, because an activity is no longer voluntary if it forms part of a formal risk management obligation. While courts might interpret its mention in a risk assessment as an optional measure available to providers that do not use encrypted communication channels, this reading is far from certain, and the current language will, at a minimum, nudge non-encrypted services to perform voluntary scanning if they don’t want to invest in alternative risk mitigation options. It’s largely up to the provider to choose how to mitigate risks, but it’s up to enforcers to decide what is effective. Again, we're concerned about how this will play out in practice.
For the same reason, clear and unambiguous language is needed to prevent authorities from taking a hostile view of what is meant by “allowing encryption” if that means then expecting service providers to implement client-side scanning. We welcome the clear assurance in the text that encryption cannot be weakened or bypassed, including through any requirement to grant access to protected data, but even greater clarity would come from an explicit statement that
client-side scanning cannot coexist with encryption.
As we approach the final “trilogue” negotiations of this regulation, we urge EU lawmakers to work on a final text that fully protects users’ right to private communication and avoids intrusive age-verification mandates and risk benchmark systems that lead to surveillance in practice.
I finally upgraded to a mechanical keyboard. But because Apple's so protective of their Touch ID hardware, there aren't any mechanical keyboards with that feature built in.
But there
is
a way to hack it. It's incredibly wasteful, and takes a bit more patience than I think
most
people have, but you basically take an Apple Magic Keyboard with Touch ID, rip out the Touch ID, and install it in a 3D printed box, along with the keyboard's logic board.
I'm far from the first person to do this—the first time I saw it done was
this Snazzy Labs video
. But I thought I'd share my own experience.
If you don't know what Touch ID is, it's basically Apple's version of a fingerprint sensor. But it's integrated really well into macOS, for login, unlocking password managers, and Apple Pay.
Video
I published a video covering the full process on my 2nd channel, Level 2 Jeff:
Teardown
Tearing apart the Magic Keyboard is much more daunting than it should be. Apple puts layers and layers of adhesives, from the back cover (which needs to be heated up a bit before it will start coming apart—somewhat destructively), to the battery (good luck getting it out without bending it at all), and even the tiny flexible PCB ribbon cables—one of which I was almost certain I'd rip (thankfully it didn't rip).
But Apple and repairability are not usually on good terms, so that's to be expected.
Use some heat to get the back of the keyboard off. It's literally just
tons of adhesive
. Like... see the photo above. You could set the keyboard on a 3D printer heat bed, use a heat gun or
iOpener
, or do what I did and lean the keyboard against a space heater (just... don't melt it).
Use a tiny
curved-tip tweezers
made for electronics to do things like remove the little stickers holding connectors on the logic board, or to lift the flex PCB (very, very thin ribbon cables) off the adhesive holding it in place.
Use extreme care around the flat part of the flex PCB with little surface-mount circuits on it that goes from the logic board all the way over to the Touch ID button. It was stuck
very
firmly on my keyboard, and I was 50/50 whether it would rip or not as I pulled it off.
Make sure to follow the directions for printing the Touch ID backing plate especially—I had to re-print mine at 0.1mm layer height, as the default setting I was using at 0.12mm made it just too high, and trying to sand a tiny 3D printed part is no fun.
The last part of the assembly in the 3D print was the most annoying, as you're putting M1.2-size nuts over M1.2-size screws, in a tight space. I could've used a couple more hands, but eventually I balanced the nut on the end of the screw, got my finger in position on top of it, then got the threads started with a screwdriver from below.
Why doesn't Apple make this?
Touch ID is a great feature that you grow to rely on over time. It feels like a bit of a let down if you decide to use a non-Apple keyboard, and you can't use Touch ID at all.
So
why doesn't Apple make a little Touch ID box
? They could charge $50 and I'd begrudgingly pay it. That way I (and at least hundreds if not thousands of other people) wouldn't have to sacrifice a $150 keyboard (and a few hours of time) to get one 'smart' key off it.
Louie Mantia on The Talk Show in July, Talking About Alan Dye and Liquid Glass
Daring Fireball
daringfireball.net
2025-12-03 22:30:35
Back in July, I was lucky enough to have my friend Louie Mantia on The Talk Show to talk about Liquid Glass and (as I wrote in the show notes) “the worrisome state of Apple’s UI design overall”. This was probably my favorite episode of the show all year, and I think it holds up extremely well now th...
Special guest Louie Mantia joins the show to talk about Liquid Glass, the various OS 26 updates, and the worrisome state of Apple’s UI design overall. Also: sandwiches.
Sponsored by:
Quip
: A supercharged clipboard manager and text expander for Mac, iPhone, and iPad.
Notion
: The best AI tool for work, with your notes, docs, and projects in one space.
Squarespace
: Save 10% off your first purchase of a website or domain using code
talkshow
.
This episode of The Talk Show was edited by Caleb Sexton.
Alan.app
Daring Fireball
tyler.io
2025-12-03 22:14:04
Tyler Hall, just one week ago:
Maybe it’s because my eyes are getting old or maybe it’s because
the contrast between windows on macOS keeps getting worse.
Either way, I built a tiny Mac app last night that draws a border
around the active window. I named it “Alan”.
In Alan’s preferences, you ca...
Maybe it’s because my eyes are getting old or maybe it’s because
the contrast between windows
on macOS keeps getting worse. Either way, I built a tiny Mac app last night that draws a border around the active window. I named it “Alan”.
In Alan’s preferences, you can choose a preferred border width and colors for both light and dark mode.
Marquis data breach impacts over 74 US banks, credit unions
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 22:06:07
Financial software provider Marquis Software Solutions is warning that it suffered a data breach that impacted dozens of banks and credit unions across the US. [...]...
Financial software provider Marquis Software Solutions is warning that it suffered a data breach that impacted dozens of banks and credit unions across the US.
Marquis Software Solutions provides data analytics, CRM tools, compliance reporting, and digital marketing services to over 700 banks, credit unions, and mortgage lenders.
In data breach notifications filed with US Attorney General offices, Marquis says it suffered a ransomware attack on August 14, 2025, after its network was breached through its SonicWall firewall.
This allowed the hackers to steal "certain files from its systems" during the attack.
"The review determined that the files contained personal information received from certain business customers," reads a notification filed with Maine's AG office.
"The personal information potentially involved for Maine residents includes names, addresses, phone numbers, Social Security numbers, Taxpayer Identification Numbers, financial account information without security or access codes, and dates of birth."
Marquis is now filing notifications on behalf of its customers, in some cases breaking down the number of people impacted per bank in a state. These notifications state that similar data was exposed in the attack for customers in other U.S. states.
According to notifications filed in
Maine
,
Iowa
, and Texas, over 400,000 customers have been impacted from the following 74 banks and credit unions.
1st Northern California Credit Union
Abbott Laboratories Employees Credit Union
Advantage Federal Credit Union
Agriculture Federal Credit Union
Alltrust Credit Union
BayFirst National Bank
Bellwether Community Credit Union
C&N Bank
Cape Cod Five
Capital City Bank Group
Central Virginia Federal Credit Union
Clark County Credit Union
Community 1st Credit Union
Community Bancshares of Mississippi, Inc.
Cornerstone Community Financial Credit Union
CPM Federal Credit Union
CSE Federal Credit Union
CU Hawaii Federal Credit Union
d/b/a Community Bank
Discovery Federal Credit Union
Earthmover Credit Union
Educators Credit Union
Energy Capital Credit Union
Fidelity Cooperative Bank
First Community Credit Union
First Northern Bank of Dixon
Florida Credit Union
Fort Community Credit Union
Founders Federal Credit Union
Freedom of Maryland Federal Credit Union
Gateway First Bank
Generations Federal Credit Union
Gesa Credit Union
Glendale Federal Credit Union
Hope Federal Credit Union
IBERIABANK n/k/a First Horizon Bank
Industrial Federal Credit Union
Interior Federal
Interior Federal Credit Union
Interra Credit Union
Jonestown Bank & Trust Co.
Kemba Financial Credit Union
Liberty First Credit Union
Maine State Credit Union
Market USA FCU
MemberSource Credit Union
Michigan First Credit Union
MIT Federal Credit Union
New Orleans Firemen's Federal Credit Union
New Peoples Bank
Newburyport Five Cents Savings Bank
NIH Federal Credit Union
Pasadena Federal Credit Union
Pathways Financial Credit Union
Peake Federal Credit Union
Pelican Credit Union
Pentucket Bank
PFCU Credit Union
QNB Bank
Security Credit Union
Seneca Savings
ServU Credit Union
StonehamBank Cooperative
Suncoast Credit Union
Texoma Community Credit Union
Thomaston Savings Bank
Time Bank
TowneBank
Ulster Savings Bank
University Credit Union
Valley Strong Credit Union
Westerra Credit Union
Whitefish Credit Union
Zing Credit Union
At this time, Marquis says that there is no evidence that data has been misused or published anywhere.
However, as previously reported by
Comparitech
, a now-deleted filing by Community 1st credit union claimed that Marquis paid a ransomm, which is done to prevent the leaking and abuse of stolen data.
"Marquis paid a ransomware shortly after 08/14/25. On 10/27/25 C1st was notified that nonpublic personal information related to C1st members was included in the Marquis breach," reads the deleted notification seen by Comparitech.
While the company's data breach notifications state only that it has "taken steps to reduce the risk of this type of incident," a filing by
CoVantage Credit Union
with the New Hampshire AG shares further details about how the company is increasing security.
This notification states that Marquis has now enhanced its security controls by doing the following:
Ensuring that all firewall devices are fully patched and up to date,
Rotating passwords for local accounts,
Deleting old or unused accounts,
Ensuring that multi-factor authentication is enabled for all firewall and virtual private network ("VPN") accounts,
Increasing logging retention for firewall devices, (
Applying account lock-out policies at the VPN for too many failed logins,
Applying geo-IP filtering to only allow connections from specific countries needed for business operations, and
Applying policies to automatically block connections to/from known Botnet Command and Control servers at the firewall.
These steps indicate that the threat actors likely gained access to the company network through a SonicWall VPN account, a known tactic used by some ransomware gangs, especially Akira ransomware.
Targeting SonicWall firewalls
While Marquis has not shared any further details about the ransomware attack, the Akira ransomware gang has been targeting SonicWall firewalls to gain initial access to corporate networks since at least early September 2024.
Akira started breaching SonicWall SSL VPN devices in 2024 by
exploiting the CVE-2024-40766 vulnerability
, which allowed attackers to steal VPN usernames, passwords, and seeds to generate one-time passcodes.
Even after SonicWall patched the bug, many organizations didn't properly reset their VPN credentials, allowing Akira to
continue breaching patched devices
with previously stolen credentials.
A recent report shows the group is still signing in to SonicWall VPN accounts
even when MFA is enabled
, suggesting the attackers stole OTP seeds during the earlier exploitation.
Once Akira gets in through the VPN, they move quickly to scan the network, perform reconnaissance, gain elevated privileges in the Windows Active Directory, and steal data before deploying ransomware.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
Greeting Vocalizations in Domestic Cats Are More Frequent with Male Caregivers
I have
obtained exclusive footage
of Alan Dye leaving Apple Park after overseeing visual interface design updates across Apple’s operating systems.
Critical flaw in WordPress add-on for Elementor exploited in attacks
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 21:31:20
Attackers are exploiting a critical-severity privilege escalation vulnerability (CVE-2025-8489) in the King Addons for Elementor plugin for WordPress, which lets them obtain administrative permissions during the registration process. [...]...
Attackers are exploiting a critical-severity privilege escalation vulnerability (CVE-2025–8489) in the King Addons for Elementor plugin for WordPress, which lets them obtain administrative permissions during the registration process.
The threat activity started on October 31, just a day after the issue was publicly disclosed. So far, the Wordfence security scanner from Defiant, a company that provides security services for WordPress websites, has blocked more than 48,400 exploit attempts.
King Addons is a third-party add-on for Elementor, a popular visual page builder plugin for WordPress sites. It is used on roughly 10,000 websites, providing additional widgets, templates, and features.
CVE-2025–8489, discovered by researcher Peter Thaleikis, is a flaw in the plugin’s registration handler that allows anyone signing up to specify their user role on the website, including the administrator role, without enforcing any restrictions.
According to
observations from Wordfence
, attackers send a crafted ‘
admin-ajax.php
’ request specifying ‘
user_role=administrator
,’ to create rogue admin accounts on targeted sites.
Malicious request
Source: Wordfence
The researchers noticed a peak in the exploitation activity between November 9 and 10, with two IP addresses being the most active: 45.61.157.120 (28,900 attempts) and 2602:fa59:3:424::1 (16,900 attempts).
Wordfence provides a more extensive list of offensive IP addresses and recommends that website administrators look for them in the log files. The presence of new administrator accounts is also a clear sign of compromise.
Website owners are advised to upgrade to version 51.1.35 of King Addons, which addresses CVE-2025–8489, released on September 25.
Wordfence researchers are also warning of another critical vulnerability in the Advanced Custom Fields: Extended plugin, active on more than 100,000 WordPress websites, which can be exploited by an unauthenticated attacker to execute code remotely.
The flaw affects versions 0.9.0.5 through 0.9.1.1 of the plugin and is currently tracked as CVE-2025-13486. It was discovered and reported responsibly by
Marcin Dudek
, the head of the national computer emergency response team (CERT) in Poland.
The vulnerability is "due to the function accepting user input and then passing that through call_user_func_array(),” Wordfence
explains
.
“This makes it possible for unauthenticated attackers to execute arbitrary code on the server, which can be leveraged to inject backdoors or create new administrative user accounts.”
The security issue was reported on November 18, and the plugin vendor addressed it in version 0.9.2 of Advanced Custom Fields: Extended, released a day after receiving the vulnerability report.
Given that the flaw can be leveraged without authentication only through a crafted request, the public disclosure of technical details is likely to generate malicious activity.
Website owners are advised to move to the latest version as soon as possible or disable the plugin on their sites.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
Since releasing
486Tang
, I’ve been working on recreating the 8086 with a design that stays as faithful as possible to the original chip. That exploration naturally led me deep into the original 8086 microcode —
extracted and disassembled
by Andrew Jenner in 2020.
Like all microcoded CPUs, the 8086 hides a lot of subtle behavior below the assembly layer. While studying it I kept extensive notes, and those eventually evolved into something more useful: an interactive browser for the entire 8086 microcode ROM.
So here it is: the
online 8086 microcode browser
.
Every 21-bit micro-instruction is decoded into readable fields. Hover over any field and you’ll get a tooltip explaining what it does. All jump targets are clickable — the 8086 μcode uses a surprising number of indirect jumps, calls, and short branches.
One handy feature is
Browse by Instruction
.
Click the button and you’ll get a list of ~300 documented 8086 instructions. Select any one, and the viewer jumps directly to its μcode entry point. Internally there are only about 60 unique μcode entry routines, and this feature makes navigating them effortless.
A few fun tidbits about 8086 μcode
Register IDs change meaning depending on context.
For example,
10100
refers to
SIGMA
(the ALU result) when used as a source, but to
tmpaL
(the low 8 bits of a temporary ALU register) when used as a destination.
N and R are the same physical register.
Meanwhile, SI is called
IJ
internally — naming inside the chip is extremely inconsistent and reflects its evolutionary design process.
IP (PC) does
not
point to the next instruction.
It actually points to the next
prefetch
address. The μcode uses a dedicated micro-operation called
CORR
to rewind IP back to the true next-instruction boundary when handling branches and interrupts.
Almost all arithmetic instructions share the same 4 μinstructions (
008–00B
).
The heavy lifting is done by a single micro-operation named
XI
, which performs different arithmetic behaviors depending on opcode or ModRM bits. The amount of reuse here is elegant — and very 1978 Intel.
Tandon researchers are putting the power of chip design into more hands with open-source tools, AI-assisted development, and applied teaching.
Once a week, in NYU’s engineering classrooms, an eclectic mix of chemistry, computer science, and medical students gather together to learn how to design their very own microchips.
Microchips are the brains behind everyday electronic devices such as phones, computers, and home appliances—each one containing billions of components that perform complex computations at lightning speed. Specialized microchips offer even more. These custom-made chips can accelerate scientific simulations in chemistry, biology, and materials science, and advance machine learning and AI.
Designing these custom-application chips hasn’t been for everyone though—traditionally, it’s been a task for highly trained engineers. Also, the high cost of proprietary design tools and intellectual property licenses has made the process of chip design largely inaccessible to small start-up companies and academic researchers—let alone students. And chip design is hard: It can take nearly a thousand engineers to build a complex chip like a GPU.
“The chip design cycle is also probably one of the most complicated engineering processes that exists in the world,” said Institute Professor
Siddharth Garg
(ECE). “There’s a saying that rocket science is hard, but chip design is harder.”
A few years ago, Garg, along with colleagues at NYU, recognized the need to “democratize chip design” and make it more accessible to nonspecialists. The researchers started by using AI tools to simplify each step of the process—from ideation to silicon. Simultaneously, they made these tools open-source, so anyone could access them freely. Finally, in 2025, they’ve realized the third component of the democratization process: providing courses to educate professionals and students in how to design their own microchips. One such course, Chips4All, is designed to teach microchip design to NYU’s graduate students.
“The chip design cycle is probably one of the most complicated engineering processes that exists in the world.”
“It takes insider knowledge and a deep understanding of a field to build a microchip for use in chemistry, robotics, or computer vision,” said Assistant Professor
Austin Rovinski
(ECE), who is involved in Chips4All. The objective of the program is to make it easy for nonengineering graduate students to learn the basics of chip design, so they can channel the expert knowledge of their fields into the creation of specialized microchips.
Siddharth Garg
Siddharth Garg
“Our goal is that, within the next five years, a variety of non-engineering researchers will be attracted to chip design,” Garg said. “And we’ll see a proliferation of custom chip designs across the disciplines.”
When constraints feed creativity
In 1965, Intel co-founder Gordon Moore predicted that the number of transistors on a microchip would double approximately every two years, leading to an increase in computing power and a decrease in cost. This principle, known as Moore’s Law, has played out for nearly 60 years: Chips have become more powerful at an exponential rate and have driven the development of a range of innovations from personal computers and smartphones to sophisticated AI systems.
“But what we see today is that exponential trend is substantially slowing down,” Rovinski said. In previous decades, engineers were able to continue making smaller, even atomicscale, circuits to create more powerful microchips, but now they’re brushing up against the limits of physics. “That means to make further improvements we have to look at more than just the technology advances,” he said.
To explain, Rovinski suggests considering the analogy of building a house. If the quality of bricks keeps improving year after year, construction workers can continue to build better houses, without changing anything else. But what if the quality of bricks stops improving? Does that mean houses can no longer improve? Not at all, Rovinski said. That’s when architects can step in with better designs to create more robust and energy efficient homes.
It’s a similar story with chip design. To keep increasing chip performance, instead of fitting as many transistors as possible onto a chip, the focus should be on designing better chips. It’s particularly important to have experts from different fields learn how to design chips, Rovinski said. “The most efficient and the best computer chips for these tasks come from deep knowledge of how that domain actually works,” Rovinski said.
In particular, it takes domain-level expertise to design, chips tailored for specific tasks. These custom chips have applications in research and industry in a range of fields from AI and cryptography to medicine and biophysics. Once integrated, they can achieve performance improvements of up to 1,000 times for their specific functions.
"To keep increasing chip performance, instead of fitting as many transistors as possible onto a chip, the focus should be on designing better chips.”
Consequently, the NYU researchers’ goal is to make chip design more accessible, so nonengineers, whatever their background can create their own custom-made chips. Researchers have already seen the scenario play out in the space of computer software. Once everyone had access to personal computers there followed an explosion of apps, software solutions and start-up companies. The team’s goal is to create the equivalent accessibility in the chip design space and keep the innovations rolling. “The more smart people we have designing chips, the more people we have coming up with creative solutions,” Rovinski said.
Austin Rovinski
Austin Rovinski
The challenge is that chip design has traditionally been complicated, time-consuming, and expensive. Only highly trained experts have been able to design chips, and they are in short supply. The CHIPS and Science Act, signed in August 2022, aims to address this critical talent gap. The act has provided $39 billion for manufacturing incentives and $13.2 billion for research and development and workforce development. “With the CHIPS Act signaling a real investment in this area at the same time AI enters a new phase of mass accessibility; there’s so much potential,” Garg said.
From Prompts to Prototypes
Garg completed his doctoral degree, which investigated aspects of chip design, in 2009, but he then branched out into other research areas such as cybersecurity. The U.S. research community had little interest in creating more powerful chips at the time because it was generally believed they had all the compute power they needed, Garg said.
Then came the AI revolution. Not only did it spark more interest in creating more advanced chips to power the technology, but it presented a potential solution to what had previously been seen as an intractable problem: How to enable non-hardware experts to design chips. “Right now, chip design is considered a very obscure, difficult to acquire skill,” Garg said. “The problem we wanted to explore was how to simplify this incredibly complex, painful and cumbersome process.”
Garg and his colleagues began to investigate a radical idea: designing a chip by talking to an AI chatbot.
That was the promise behind Chip Chat, a tool developed by Garg,
Hammond Pearce
, Professor
Ramesh Karri
(ECE, CATT, CCS), and other NYU researchers. It leveraged large language models (LLMs) like GPT-4 to translate natural language into Verilog—the specialized code used to describe the digital circuits and systems that define chip architectures. Rather than learning to write this complex hardware description language (HDL) from scratch, a user can type something like: “I need a chip that adds two 16-bit numbers and uses less than two watts of power.” The model then responded with functional code, which was refined through further prompts.
“You could literally speak out what you wanted your chip to do, and the tool took care of everything in the back end,” Garg said. Chip Chat drastically reduced the need for advanced technical skills typically required to write HDL, transforming a complex, months-long endeavor into something achievable in a matter of weeks. In 2023, the team used Chip Chat to design a working chip, QTcore-C1, which was fabricated via the open-source platform Efabless. It was the first microchip designed through a backand-forth conversation with a language model.
Chip Chat sparked a movement of exploring how to integrate LLMs in every stage of the chip development pipeline. While early tools focused on generating code from prompts, new research tackles turning that code into physical blueprints for manufacturing and then assessing different prototypes before the final step of turning the design into silicon.
“You could literally speak out what you wanted your chip to do, and the tool took care of everything in the back end.”
Rovinski, in collaboration with researchers at Arizona State University, has been exploring the use of AI to assist with translating Verilog language into a physical chip and speeding up the design process. Instead of getting an implementation error that a designer doesn’t necessarily understand, the researchers have created an AI tool that will help explain the problem and then interpret plain English instructions into refinements to the original design.
Brandon Reagen
Brandon Reagen
Assistant Professor
Brandon Reagen
(ECE, CSE, CCS, and CATT) has helped create a tool that can be used simultaneously with Rovinski’s. Reagen’s statistically based model takes an original design and creates thousands of possible hardware models, scoring each implementation on attributes such as how fast it runs, area, power and energy use. “The tool will explore thousands of designs automatically overnight for you,” Reagen said. “So, you just wake up and pick the one that makes best sense for your constraints and you’re good to go.”
Such tools have dramatically reduced the number of human hours and the depth of technical knowledge needed to move from concept to prototype. And the team continues to improve on each step of the pipeline. One challenge to the original Chip Chat program was the scarcity of Verilog code—the programming language that describes how to create a chip—available on the Internet. This meant there were fewer available examples to train the AI models, which proved to limit their performance.
To address this challenge, Garg and colleagues scoured Verilog code on GitHub and excerpted content from 70 Verilog textbooks to amass the largest AI training dataset of Verilog ever assembled. The team then created VeriGen, the first specialized AI model trained solely to generate Verilog code.
In tests, VeriGen has outperformed commercial state-of-the-art models and is small enough to run on a laptop. Verilog is also fully opensource allowing researchers everywhere to use, or improve on, the model. “The one thing that would advance the current chip design ecosystem would be to make everything open-source,” Garg said.
Opening up the field
Even as a graduate student at the University of Michigan, Rovinski’s mission was to solve some of the foundational issues in chip design. Early on, he identified that several key challenges were related to the closed source, proprietary nature of chip design and implementation software. If researchers wanted to design chips using commercial technology, they had to jump over various legal hurdles and sign legal agreements stating they wouldn’t share details of the software. “There were many roadblocks to trying to innovate or do anything new in the chip design space,” Rovinski said.
Recognizing the challenges in the chip design space, the Defense Advanced Research Projects Agency (DARPA), put out a call asking for proposals on potential solutions. A team of engineers, led by computer scientist Andrew Kahng, and including Rovinski, came up with an idea: providing open-source software for chip design and implementation. “We knew that it wouldn’t necessarily be as good as proprietary software, but it would be free in terms of cost and restrictions of use,” Rovinski said.
To design chips using commercial technology, researchers would have to jump over various legal hurdles.
To design chips using commercial technology, researchers would have to jump over various legal hurdles.
In 2018, with DARPA funding, the team launched OpenROAD (Foundations and Realization of Open, Accessible Design). In OpenROAD, the team created open-source electronic design automation software which helps academic researchers, industrial professionals, students and educators in the entire process of designing a digital integrated circuit down to creating the “layout masks” that are used for manufacturing the chip. The project aims to automate the entire chip design process, from design to layout, without the need for human intervention.
Developing OpenROAD proved a multi-year effort, “But fast forward to today and we have thousands of people across the world— from small companies and large companies, academics, students, and hobbyists—who use the software to implement their chip designs,” Rovinski said.
Many people download the software simply to learn more chip design and explore their designs, as there is zero cost to use it, Rovinski said. Rovinski’s early vision was to make chip design more accessible and create an ecosystem that made it easier for users to communicate and collaborate. When he joined NYU in 2023, he realized that his colleagues shared that same vision. Rovinski continues to use OpenROAD in his research and to explore how make the chip design process more accessible to researchers and students who aren’t specialists in electronic engineering. He’s also fully engaged in the third leg of the “democratizing chip design” stool: education.
Crafting a “Chips for All” world
The democratization of chip design isn’t just about advancing tools and technology; it’s about redefining who gets to use them. For decades, only a relatively small number of highly trained electrical engineers had the expertise to design chips, and the scarcity of these specialist engineers created a bottleneck as demand for chips soared.
Addressing this talent gap is crucial for maintaining the pace of innovation, Garg said. Consequently, at NYU, Garg and his colleagues have taken a multi-pronged approach to education and outreach, aiming to make chip design understandable and achievable for a much broader audience, even for those without a traditional STEM background.
One such program is BASICS (Bridge to Application Specific Integrated Circuits), a 28-week self-paced course aimed at nonSTEM professionals who want to learn the fundamentals of chip design, so that they can pivot into emerging technological careers. Participants are trained to design their own custom-made chip and test their designs to create a functional chip: The BASICs capstone project entails a two-week “chip fabrication sprint” where participants design their own ASICs and submit their designs to the platform TinyTapeout for fabrication.
A parallel effort is Chips4All, which targets graduate students in science and technology. The five-year Chips4All’s approach is collaborative: each team consists of a domain expert and a hardware designer who crosstrain by taking each other’s courses. Students work together on a capstone project to prototype ASICs to perform tasks in areas such as genome sequencing or robotics. “We have this ‘meet in the middle’ approach by pairing them up and we are hoping to get some really innovative developments out of it,” Rovinski said.
At NYU, the education component of chip design extends beyond specialized courses. NYU is also building a Silicon Makerspace where students, faculty and staff can get access to chip design tools and fabrication resources. “It will be like a one-stop shop for people who want to access the tools, fabricate chips and test them,” Garg said.
The team is also revamping traditional electronics engineering classes, such as the digital circuit design classes, to place more emphasis on the bigger picture of chip design, rather than the minutiae of chip hardware. Rovinski challenges his students to take a high-level approach to the problems they are trying to address: “What are the societal level issues? What are the constraints of your problem in terms of time, money, resources, and people?” he’ll ask.
Only after defining goals and constraints and assessing whether they have an economically and societally sound idea, do Rovinski’s students dive into design. The results have been promising: One team designed an ultra-low-power chip for glucose monitoring to enable a longer battery life and fewer replacements of glucose monitors for diabetics patients. Another created ultra-low power soil quality sensors, which could be used in agriculture to inform farmers how much fertilizer or water, a particular area of the farm needs. “It was a huge success,” Rovinski said. “The students were coming up with technologically innovative and creative solutions.”
Necessity has driven the need to democratize chip design and technological breakthroughs in AI and open-source tools have fueled the movement. Now, the researchers hope that a concerted effort to educate and inspire a new, diverse generation of designers will sustain momentum. In the coming years, the team’s vision is to create an ecosystem where chip design becomes as accessible and iterative as software development, where individuals can move from a “back of the envelope” idea to a loose prototype and then to fabrication with speed and ease.
In the coming years, the team’s vision is to create an ecosystem where chip design becomes as accessible and iterative as software development.
It’s a mission that aligns with national strategic goals, such as those outlined in the CHIPS Act, by aiming to foster a robust and diverse semiconductor talent pool. In the next five years, Garg hopes to see students from all over the campus attracted to chip design as a profession and the chip design adopted as common practice across different scientific sectors.
The democratization of chip design will help researchers and industry professions move toward a future where chips are not just commodities, but highly customized, efficient solutions that power breakthroughs in fields from medicine to robotics. Currently, it takes from a dozen to a few hundred engineers two to three years to design a new state-of-the-art chip. The ideal scenario is one where a single engineer pushes a button and the whole chip is made within a week, Rovinski said.
Obviously that’s still a stretch, Rovinski said. “But any innovations, new technologies and steps along the way, will mean the amount of problems we can solve will dramatically increase.”
Alpine Linux 3.23.0 Released: apk-tools v3, linux-stable replaces linux-edge
Version 3 of
apk
is now ready for Alpine v3.23.0. This should be a safe and
seamless upgrade from v2, but might have breaking changes if you use
libapk
.
The package manager has been upgraded to v3, but keeping the v2 index and
package format for now.
linux-stable
replaces
linux-edge
with an identical configuration as
linux-lts
while following stable instead of long-term releases. On systems
with
linux-edge
installed, apk automatically installs
linux-stable
as a
replacement.
As always, make sure to use
apk upgrade --available
when switching between
major versions.
We previously
announced
that 3.23 would be
/usr-merged, but due to technical challenges this has been postponed to a later
release. A
new
timeline
will be published later.
Users with / and /usr on separate filesystems (which is unsupported) will still
need to take special care. See the
wiki for
details
.
Thanks to everyone sending patches, bug reports, new and updated aports, and to
everyone helping with documentation, maintaining the infrastructure, or
contributing in any other way!
Achill Gilgenast
Adam Jensen
adamthiede
aerodomigue
Akihiro Suda
Alex Denes
Alex McGrath
Alex Xu (Hello71)
Alexander Edland
Alexandre Marquet
Alexey Yerin
Alice R
Alistair Francis
Amelia Clarke
André Klitzing
Andrej Kolčin
Andres Almiray
Andrew Hills
Andrew Palardy
Andrij Abyzov
Andy Hawkins
Andy Postnikov
Angelo Verlain
Angelo Verlain Shema
Anjandev Momi
Antoine Martin
Antoni Aloy Torrens
Antonio Mihăeș
Apo Apangona
Ariadne Conill
Arnav Singh
Arne de Bruijn
Arne Schoonvliet
Aster Boese
avinesh2101
ayakael
Barnabás Czémán
Bart Ribbers
bin456789
Biswapriyo Nath
bj8sk
Bradford D. Boyle
Bradley Perkins
Bryce Vandegrift
bytesharky
Callum Andrew
Carlo Landmeter
Celeste
Chad Dougherty
Channing Bellamy
Charadon
Christian Dupuis
Christiano Haesbaert
Christoph Heiss
chwoa
Clayton Craft
Coco Liliace
Conrad Hoffmann
Cormac Stephenson
CorruptedVor
cos
cow
Cow
Craig Andrews
crapStone
dabao1955
Daniel Aloor
Daniel Black
Daniel Hejduk
Daniel Markstedt
Daniel Néri
Daniel Simko
Dario Caroprese
David Florness
David Heidelberg
David Sugar
Dennis Krupenik
DerLinkman
Devin Lin
Dhruvin Gandhi
Díaz Urbaneja Víctor Diego Alejandro (Sodomon)
Dominic
Dominique Martinet
donoban
DragonTalk
Dries Schaumont
Duncan Bellamy
DWwanghao
Dylan Van Assche
Eeo Jun
Eisenbahnfan
Eivind Larsen
eu
Fabian Affolter
Fabricio Silva
famfo
Faustin Lammler
Ferass El Hafidi
Fiona Klute
Forza
Francesco Colista
François Chavant
Fredrik Foss-Indrehus
gabriel
Galen Abell
Gavin Williams
Georg Lehner
Gil Pedersen
Glenn Strauss
Guido Günther
Guy Godfroy
Gyorgy Szing
Haelwenn (lanodan) Monnier
hairyhenderson-bot[bot]
Hannes Braun
Hans de Goede
Henrik Grimler
Henrik Riomar
heurist
Hoang Nguyen
Holger Jaekel
Hristiyan Ivanov
Hugo Osvaldo Barrera
Integral
Iskren Chernev
Ivan Popovych
Iztok Fister Jr.
Iztok Fister, Jr.
J. Neuschäfer
j.r
J0WI
Jacques Boscq
jahway603
Jake Buchholz Göktürk
Jakob Hauser
Jakob Meier
Jakob Probst
Jakub Jirutka
James Chen-Smith
Jan Houstek
Jan Houštěk
Jane Rachinger
Jason Swank
jb
Jean-Francois Lessard
Jean-Louis Fuchs
Jens Reidel
Jens Zeilund
Jesse Mandel
Jingyun Hua
Jo Zzsi
Johannes Müller
John Anthony
John Vogel
Jonas
Jonathan Schleifer
Jordan Christiansen
Joren
Joshua Murphy
Jozef Mlich
juef
jvoisin
k8ie
Kaarle Ritvanen
Kaspar Schleiser
Kasper K
Kate
Kevin Daudt
kkflt
knuxify
Konstantin Goncharik
Konstantin Kulikov
kpcyrd
Krassy Boykinov
Krystian Chachuła
L. E. Segovia
Lassebq
László Várady
Laurent Bercot
Lauri Tirkkonen
lazywalker
Leon Marz
Leon White
Leonardo Arena
leso-kn
Lindsay Zhou
Linus Groh
LN Liberda
lonjil
Lovell Fuller
Luc Bijl
Luca Weiss
Lucas Ieks
Lucidiot
Luis Henriques
Lukas Wedeking
Maarten van Gompel
macmpi
Magnus Sandin
Marco
Marek Zori
Maria Lisina
Marian Buschsieweke
Markus Göllnitz
Mate Farkas
Matt Bell
Matt Hauck
Matthias Ahouansou
Matthieu Baerts (NGI0)
max1truc
Maximilian Friedersdorff
mekyt
Meng Zhuo
Michael Pirogov
Michal Jirků
Michał Polański
Mike Crute
Milan P. Stanić
Miles Alan
Mindaugas Vaitiekūnas
mini-bomba
minoplhy
Mintsuki
mio
Moritz Haase
Naomi Rennie-Waldock
Natanael Copa
NeYurii
Nick Østergaard
Nico Schottelius
Nicolas Lorin
NN708
Noel Kuntze
Oleg Titov
Oliver Smith
Olivia Thiderman
omni
Orhun Parmaksız
ovf
p_q
Pablo Correa Gómez
Paolo Barbolini
Paolo Cuffiani
para
Patrick Gansterer
Pavel Demin
Pavel Zakopaylo
pawciobiel
pcmagas
Pedro Lucas Porcellis
Pepijn de Vos
Peter Bui
Peter Mack
Peter Meiser
Peter Shkenev
Peter van Dijk
Petr Vorel
pfzetto
Philip Schlusslicht
Philipp Arras
Piero Mantovani La Terza
Prabu Anand Kalivaradhan
PRIVATE-TOKEN
prspkt
ptrcnull
Puneeth Chaganti
qaqland
Quentin Bornet
R4SAS
Rabindra Dhakal
Rafael Ávila de Espíndola
Rasmus Thomsen
RavFX XMR
Raymond Hackley
rdbo
revsuine
Rob Blanckaert
Robert Eckelmann
Robert Mader
Robin Candau
Ron Nazarov
rooyca
rubicon
Rudolf Polzer
Runxi Yu
Ryan Walklin
Sacha
Saijin-Naib
Sam Day
Sam Nystrom
Sam Thursfield
Samuel Kalbfleisch
Sascha Brawer
Sean E. Russell
Sean McAvoy
Sebastian Hamann
Sebastian Meyer
Sergey Safarov
Sertonix
Sicelo A. Mhlongo
Simon Frankenberger
Simon Rupf
Simon Zeni
Siva Mahadevan
sivasaipega
Skyler Mayfield
sodface
sodomon2
Sören Tempel
srcfn
sriharsha.arakatavemula
Stefan Hansson
Štěpán Pechman
stephen
Steve Lam
streaksu
strophy
Struan Robertson
Sylvain Prat
techknowlogick
tetsumaki
Theo Zanchi
Thiago Perrotta
Thomas Aldrian
Thomas Böhler
Thomas J Faughnan Jr
Thomas Kienlen
Thomas Liske
Tim Düsterhus
Timo Teräs
Timothy Legge
Topoetry
Travis Shivers
Tuan Anh Tran
Tyrone Buggims
Val Packett
Val V
Vasiliy Doylov
Victor Mignot
wen
Wen Heping
wener
Wesley van Tilburg
Wiktor Kwapisiewicz
Will Sinatra
William Desportes
William Floyd
Willow Barraco
Wolfgang Fischer
xrs
yabobay
Yaksh Bariya
yurenchen
Z
Zach DeCook
Zsolt Vadasz
French DIY retail giant Leroy Merlin discloses a data breach
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 20:52:36
Leroy Merlin is sending security breach notifications to customers in France, informing them that their personal data was compromised. [...]...
French home improvement and gardening retailer Leroy Merlin is notifying customers that their personal info has been compromised in a data breach.
Leroy Merlin operates in multiple European countries as well as in South Africa and Brazil, employs 165,000 people, and has an annual revenue of $9.9 billion.
The incident affects only customers in France, according to the notification published on social media by
SaxX_
, and exposed the following data types:
Full name
Phone number
Email address
Postal address
Date of birth
Loyalty program-related information
“A cyberattack recently targeted our information system, and some of your personal data may have leaked outside the company” (machine translated), reads the notification the company sent to affected customers.
“As soon as the incident was detected, we took all necessary measures to block unauthorized access and contain the incident.”
The company clarified that the exposed information does not include banking data or online account passwords.
Also, the notice mentions that the stolen information has not been used in a malicious way, suggesting that it has not been leaked online or leveraged for extortion, but cautioned customers to remain vigilant of unsolicited communications.
Customers receiving the notification are also provided with information on how to
identify phishing messages
attempting to impersonate the brand.
If any anomaly is detected in customer account activity or trouble with redeeming loyalty discounts, customers are asked to report the activity directly to the company.
BleepingComputer could confirm that the notification is genuine and has reached out to Leroy Merlin to request more details about the breach and how many customers are affected. We have not received a reply by publication time.
At the time of writing, we did not see any ransomware group claiming the attack.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
There are many possible programmer mistakes that are not caught by the
minimal checks specified by the C language; among those is passing an array
of the wrong size to a function. A recent attempt to add some safety
around array parameters within the crypto layer involved the use of some
clever tricks, but it turns out that clever tricks are unnecessary in this
case. There is an obscure C feature that can cause this checking to
happen, and it is already in use in a few places within the kernel.
A potential problem with this function is that it takes as parameters
several pointers to arrays of type
u8
. As Biesheuvel pointed out,
the size of the
nonce
and
key
arrays is not checked by
the compiler, even though it is clearly specified in the function
prototype. That makes it easy to, for example, give the parameters in the
wrong order. The resulting vulnerabilities are generally not the outcome
developers have in mind when they write cryptographic code.
Biesheuvel suggested that it was possible to write the prototype this way
instead (differences shown in bold):
The types of the last two arguments have changed; there is a new level of
pointer indirection, with the argument being a pointer to an array of a
given size. Callers must change their calls by adding an additional
&
operator to obtain the desired pointer type, but the address
that is passed is the same. In this case, though, the compiler will check
the sizes of the array passed, and will now catch a reordering of the
arguments to the function.
Jason Donenfeld
was
interested
by the idea, but he knew of an arguably more straightforward
way to address this problem. It seems that, buried deep within the C
standard, is a strange usage of the
static
keyword, making it
possible to write the prototype as:
This, too, will cause the compiler to check the sizes of the arrays, and it
does not require changes on the caller side. Unlike the pointer trick,
which requires an exact match on the array size, use of
static
will only generate a warning if the passed-in array is too small. So it
will not catch all mistakes, though it is sufficient to prevent
memory-safety and swapped-argument problems.
Eric Biggers
pointed out
that GCC can often generate "array too small" warnings even without
static
, but that the kernel currently disables those warnings;
that would suppress them when
static
is used as well. (The
warning was
disabled in
6.8
due to false positives.) But he thought that adding
static
was worthwhile to get the warnings in Clang — if Linus
Torvalds would be willing to accept use of "
this relatively obscure
feature of C
".
Torvalds, as it turns out,
has
no objection
to this usage; he likes the feature, if not the way it was
designed:
The main issue with the whole 'static' thing is just that the
syntax is such a horrible hack, where people obviously picked an
existing keyword that made absolutely no sense, but was also
guaranteed to have no backwards compatibility issues.
He pointed out that there are a number of places in the kernel that are
already using it; for a simple example, see
getconsxy()
in the virtual-terminal driver. He suggested perhaps hiding it with a
macro like
min_array_size()
just to make the usage more obvious,
but didn't seem convinced that it was necessary. Donenfeld
followed up
with
a patch to that effect a few days later, but then pivoted to
an
at_least
marker
instead.
A lot of work has gone into the kernel to make its use of C as safe as
possible, but that does not mean that the low-hanging fruit has all been
picked. The language has features, such as
static
when used to
define formal array parameters, that can improve safety, but which are not
generally known and not often used. In this particular case, though, it
would not be surprising to see this "
horrible hack
" come into wider
use in the near future.
"Real" America Is Turning Against Trump’s Mass Deportation Regime
Intercept
theintercept.com
2025-12-03 20:30:16
Everyday Americans in Appalachia and the Southeast show that resisting ICE is becoming the rule, rather than the exception.
The post “Real” America Is Turning Against Trump’s Mass Deportation Regime appeared first on The Intercept....
Homeland Security Investigations officers search for two individuals who fled the scene after being stopped while selling flowers on the side of the road on Nov. 16, 2025, in Charlotte, N.C.
Photo: Ryan Murphy/Getty Images
Alain Stephens is an investigative reporter covering gun violence, arms trafficking, and federal law enforcement.
On a chilly
evening in mid-November, about 135 people gathered along a highway in Boone, North Carolina, a small Appalachian college town not known as a hotbed of leftist protest. They held
signs reading
“Nazis were just following orders too” and “Time to melt the ICE,” and chanted profane rebukes at Immigration and Customs Enforcement agents rumored to be in the area. “They came here thinking they wouldn’t be bothered,” one Appalachian State University student told The Appalachian at the impromptu rally. “Boone is a small, southern, white, mountain town. We need to let them know they’ll be bothered anywhere they go.” In a region often stereotyped as silently conservative, this flash of defiance was a startling sign that the battle lines of American politics are shifting in unexpected ways.
For the past several weeks, the Trump administration has been rolling out a mass deportation campaign of unprecedented scope — one that is now reaching deep into Appalachia. Branded “
Operation Charlotte’s Web
,” a deployment of hundreds of Department of Homeland Security and Border Patrol agents descended on North Carolina in mid-November, making sweeping arrests in and around Charlotte and into the state’s rural mountain counties.
Officials billed the effort as targeting the “
worst of the worst
” criminal aliens, but the numbers tell a different story: More than 370 people were arrested, only 44 of whom had any prior criminal record, according to DHS. The vast majority were ordinary undocumented residents — people going to work or school, not “violent criminals” — which underscores that the crackdown is less about public safety than meeting political quotas.
Indeed, Trump campaigned on conducting the largest deportation operation in U.S. history, vowing to round up 15 to 20 million people (which is more than the estimated
14 million
undocumented people living in the U.S.) and pressuring ICE to triple its arrest rates to
3,000 per day
. The federal dragnet has already driven ICE arrests to levels not seen in years; immigrants
without criminal convictions
now make up the largest share of detainees. But the administration is also facing widespread resistance to its policy of indiscriminate arrests and mass deportations, not as the exception, but as the rule — and among everyday, fed-up Americans across the country.
Kicking the Hornets’ Nest
What officials didn’t seem to anticipate was that this crackdown would face fierce pushback not only in liberal hubs with large immigrant communities like
Los Angeles
or
Chicago
, but in predominantly white, working-class communities.
In Charlotte, a city on the edge of the Blue Ridge foothills, activists scrambled to implement a broad early-warning network to track federal agents. Thousands of local volunteers — many of them outside the city’s political establishment —
mobilized to monitor
convoys and alert vulnerable families in real time. They patrolled neighborhoods, followed unmarked vehicles, and honked their car horns to warn others when Customs and Border Protection or ICE agents were spotted: acts of quiet guerrilla resistance that Border Patrol’s local commander derided as “cult behavior.” The effort spanned from downtown Charlotte into the rural western counties, with observers checking hotels and Walmart parking lots in mountain towns for staging areas and relaying tips across the region.
By the time the sheriff announced the feds had
pulled out
— and
video showed
a convoy hightailing it down the highway — locals were already hailing it as a
“hornet’s nest”
victory, comparing the retreat to British Gen. Charles Cornwallis’s abrupt withdrawal from the area during the Revolutionary War after being met with unexpectedly fierce resistance.
Charlotte’s mostly quiet, semi-official resistance — dubbed the “
bless your heart
” approach for its polite-but-pointed Southern style — was notable. But the open rebellion brewing in coal country may be even more significant. In Harlan County, Kentucky — a storied epicenter of the Appalachian labor wars — residents recently got an alarming preview of the deportation machine’s reach. Back in May, a convoy of
black SUVs rolled into the town of Harlan
, and armed agents in tactical gear stormed two Mexican restaurants. At first, the operation was framed as a drug bust; Kentucky State Police on the scene told bystanders it was part of an “ongoing drug investigation.” But despite being carried out by DEA agents, it was an immigration raid, and local reporter Jennifer McDaniels noted that of the people arrested and jailed, their cases were listed as “immigration,” without a single drug-related offense.
Once the shock wore off, residents were livid. “We took it personal here,” McDaniels, who witnessed the raid,
told n+1 magazine
. Watching their neighbors being whisked away in an unmarked van — with no real explanation from authorities — rattled this tight-knit community. “I don’t like what [these raids] are doing to our community,” McDaniels continued. “Our local leaders don’t like what it’s doing to our community. … We just really want to know what’s happening, and nobody’s telling us.” It turned out at least 13 people from Harlan were disappeared that day, quietly transferred to a detention center 70 miles away. In Harlan –
immortalized in song
and history as “
Bloody Harlan
” for its coal miner uprisings — the sight of government agents snatching low-wage workers off the job struck a deep nerve of betrayal and anger. This is a place that knows what class war looks like, and many residents see shades of it in the federal government’s high-handed raids.
Blood in the Hills
For decades, Appalachia has lived with the same lesson carved into the hills like coal seams: When Washington shows up, it’s rarely to help. When the
mining ended
and industry dried up and when opioids ripped through these communities, the federal response was always too little, too late. When hurricanes and floods drowned eastern North Carolina — Matthew in 2016, Florence in 2018 —
thousands of homes sat unrepaired a decade later
, with families still sleeping in FEMA trailers long after the rest of the country had moved on. After Helene floods smashed the western mountains in 2024, relief trickled in like rusted pipe water — with
just
$1.3 billion delivered to address an estimated $60 billion in damage. A year later, survivors were living in tents and sheds waiting for their government to step in.
Help arrives slow; enforcement arrives fast and armored.
But the federal government’s priority is a parade of bodies — arrest numbers, detention quotas, a spectacle of force — and so suddenly, these forgotten communities are lit up with floodlights and convoys. Operation Charlotte’s Web saw hundreds of ICE and Border Patrol agents deployed overnight. Help arrives slow; enforcement arrives fast and armored. It only reinforces the oldest mountain wisdom: Never trust the government.
It’s a paradoxical arrangement that to many working Appalachians is simply untenable. “It’s a rural area with low crime,” one organizer in Boone
pointed out
, calling ICE’s authoritarian sweep “disgusting and inhumane.” The organizer also said, “That’s the number one conservative tactic: being tough on crime even when that crime doesn’t exist.” In other words, the narrative about dangerous criminals doesn’t match what people are actually seeing as their friends, classmates, and co-workers are being carted off.
To be sure, public opinion in Appalachia isn’t monolithic; plenty of folks still cheer any crackdown on “illegals” as a restoration of law and order. But the growing resistance in these communities suggests a profound shift: Class solidarity is beginning to trouble the traditional partisan lines. The old playbook of stoking rural white fears about immigrants begins to lose its potency when those same immigrants have become neighbors, co-workers, or fellow parishioners — and when federal agents descend like an occupying army, indiscriminately disrupting everyone’s lives.
“Abducting a so-called violent gang member at their place of employment is a contradiction,” a
local Boone resident scoffed
. It doesn’t take a Marxist to see the underlying reality: This isn’t about protecting rural communities, it’s about using them for political ends. For many who’ve been told they’re the “forgotten America,” the only time Washington remembers them is to enlist them as pawns — or body counts — in someone else’s culture war. And increasingly, they are saying no.
Appalachia has a long, if overlooked, tradition of rebellion from below. A century ago, West Virginia coal miners fought the largest armed labor uprising in U.S. history at
Blair Mountain
, where thousands of impoverished workers (immigrants and native-born alike) took up arms together against corrupt coal barons. In the 1960s, poor white migrants from Appalachia’s hills living in Chicago formed the
Young Patriots Organization
: Confederate-flag-wearing “hillbillies” who shocked the establishment by allying with the Black Panthers and Young Lords in a multiracial fight against police brutality and poverty.
That spirit of solidarity across color lines, born of shared class struggle, is reappearing in today’s mountain towns. You can see it in the way Charlotte activists borrowed
tactics
from Chicago’s immigrant rights movement, setting up
rapid-response networks
and legal support. You can see it in how North Carolina organizers are sharing resistance blueprints with communities in Louisiana and Mississippi ahead of “
Swamp Sweep
,” the next phase of Trump’s crackdown, slated to deploy as many 250 agents to the Gulf South on December 1 with the goal of arresting 5,000 people. And you can certainly see it each time a rural Southern church offers
protection to an undocumented family
, or when local volunteers protest Border Patrol
outside their hotels.
No Southern Comfort for Feds
This all puts the Trump administration — and any future administration tempted to wage war on
Trump-labeled
“
sanctuary cities
” — in an uncomfortable position. It was easy enough for politicians to paint resistance to immigration raids as the province of big-city liberals or communities of color. But what happens when predominantly white, working-class towns start throwing sand in the gears of the deportation machine? In North Carolina,
activists note
that their state is not Illinois — the partisan landscape is different, and authorities have been cautious — but ordinary people are still finding creative ways to fight back. They are finding common cause with those they were told to blame for their economic woes. In doing so, they threaten to upend the narrative that Appalachia — and perhaps the rest of working-class, grit-ridden, forgotten America — will forever serve as obedient foot soldiers for someone else’s crusade.
The resistance unfolding now in places like Boone and Harlan is not noise — it’s a signal. It suggests that America’s political fault lines are shifting beneath our feet. The coming deportation raids were supposed to be a mop-up operation executed in the heart of “real America,” far from the sanctuary cities that have defied Trump. Instead, they are turning into a slog, met with a thousand cuts of small-town rebellions. This is hardly the passive or supportive response that hard-liners in Washington might have expected from the red-state USA.
On the contrary, as the enforcement regime trickles out into broader white America, it is encountering the same unruly spirit that has long defined its deepest hills, valleys, and backwoods. The message to Washington is clear: If you thought Appalachia would applaud or simply acquiesce while you turn their hometowns into staging grounds for mass round-ups, bless your heart.
Reproducible Builds: Reproducible Builds in November 2025
PlanetDebian
reproducible-builds.org
2025-12-03 20:28:10
Welcome to the report for November 2025 from the Reproducible Builds project!
These monthly reports outline what we’ve been up to over the past month, highlighting items of news from elsewhere in the increasingly-important area of software supply-chain security. As always, if you are interested i...
These monthly reports outline what we’ve been up to over the past month, highlighting items of news from elsewhere in the increasingly-important area of software supply-chain security. As always, if you are interested in contributing to the Reproducible Builds project, please see the
Contribute
page on our website.
Founded in 2013, SeaGL is a free, grassroots technical summit dedicated to spreading awareness and knowledge about free source software, hardware and culture. Chris’ talk:
[…] introduces the concept of reproducible builds, its technical underpinnings and its potentially transformative impact on software security and transparency. It is aimed at developers, security professionals and policy-makers who are concerned with enhancing trust and accountability in our software. It also provides a history of the Reproducible Builds project, which is approximately ten years old. How are we getting on? What have we got left to do? Aren’t all the builds reproducible now?
Distribution work
In
Debian
this month, Jochen Sprickerhof
created a merge request
to replace the use of
reprotest
in Debian’s
Salsa Continuous Integration (CI) pipeline
with
debrebuild
. Joschen cites the advantages as being threefold: firstly, that “only one extra build needed”; it “uses the same
sbuild
and
ccache
tooling as the normal build”; and “works for any Debian release”. The merge request was merged by Emmanuel Arias and is now active.
kpcyrd
posted to
our mailing list
announcing the initial release of
repro-threshold
, which implements an
APT
transport that “defines a threshold of
at least X of my N trusted rebuilders need to confirm they reproduced the binary
” before installing Debian packages. “Configuration can be done through a config file, or through a curses-like user interface.
Julien Malka and Arnout Engelen launched the new
hash collection
server for NixOS. Aside from improved reporting to help focus reproducible builds
efforts within NixOS, it collects build hashes as individually-signed attestations
from independent builders, laying the groundwork for further tooling.
Web3 applications, built on blockchain technology, manage billions of dollars in digital assets through decentralized applications (dApps) and smart contracts. These systems rely on complex, software supply chains that introduce significant security vulnerabilities. This paper examines the software supply chain security challenges unique to the Web3 ecosystem, where traditional Web2 software supply chain problems intersect with the immutable and high-stakes nature of blockchain technology. We analyze the threat landscape and propose mitigation strategies to strengthen the security posture of Web3 systems.
Their paper lists reproducible builds as one of the mitigating strategies. A
PDF
of the full text is available to download.
Upstream patches
The Reproducible Builds project detects, dissects and attempts to fix as many currently-unreproducible packages as possible. We endeavour to send all of our patches upstream where appropriate. This month, we wrote a large number of such patches, including:
Finally, if you are interested in contributing to the Reproducible Builds project, please visit our
Contribute
page on our website. However, you can get in touch with us via:
Freedom Mobile discloses data breach exposing customer data
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 20:28:01
Freedom Mobile, the fourth-largest wireless carrier in Canada, has disclosed a data breach after attackers hacked into its customer account management platform and stole the personal information of an undisclosed number of customers. [...]...
Freedom Mobile, the fourth-largest wireless carrier in Canada, has disclosed a data breach after attackers hacked into its customer account management platform and stole the personal information of an undisclosed number of customers.
Founded in 2008 as Wind Mobile by telecommunications provider Globalive, Freedom has over 2,2 million subscribers and now says it provides coverage to 99% of Canadians.
Vidéotron, a subsidiary of Canadian telecommunications company Québecor, acquired Freedom in 2023, creating the country's fourth major wireless carrier with more than 3.5 million mobile customers and nearly 7,500 employees.
In a
data breach notification
published today, Freedom said it detected a breach of its customer account management platform on October 23.
"Our investigation revealed that a third party used the account of a subcontractor to gain access to the personal information of a limited number of our customers," Freedom stated.
"We quickly identified the incident and implemented corrective measures and security enhancements, including blocking the suspicious accounts and corresponding IP addresses."
The personal and contact information exposed in the incident includes first and last names, home addresses, dates of birth, home and/or cell phone numbers, and Freedom Mobile account numbers.
Although it found no evidence that the compromised data has been misused since the breach, the wireless carrier advised affected customers to be suspicious of unexpected messages requesting their personal information or directing them to a website to provide it.
Freedom also recommends not clicking links or downloading attachments from emails or texts that seem suspicious and regularly checking their accounts for unusual activity.
A Freedom Mobile spokesperson didn't reply to a request for comment when BleepingComputer reached out earlier today for more details on the data breach, including how many customers were affected, if the attackers also breached its internal network, and whether the carrier received a ransom demand after the incident.
In May 2019, Freedom Mobile
confirmed another data breach
after a third-party vendor exposed an unsecured customer support database online, which contained the data of approximately 15,000 customers.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
Alan Dye Leaves Apple for Meta, Replaced by Longtime Designer Stephen Lemay
Daring Fireball
www.bloomberg.com
2025-12-03 19:59:22
Mark Gurman, with blockbuster news at Bloomberg:
Meta Platforms Inc. has poached Apple Inc.’s most prominent design
executive in a major coup that underscores a push by the social
networking giant into AI-equipped consumer devices.
The company is hiring Alan Dye, who has served as the head of
A...
We've detected unusual activity from your computer network
To continue, please click the box below to let us know you're not a robot.
Why did this happen?
Please make sure your browser supports JavaScript and cookies and that you are not
blocking them from loading.
For more information you can review our
Terms of Service
and
Cookie Policy
.
Need Help?
For inquiries related to this message please
contact
our support team
and provide the reference ID below.
Version
2025.12 of the Home Assistant home-automation system has been released.
This month, we're unveiling Home Assistant Labs, a brand-new space
where you can preview features before they go mainstream. And what
better way to kick it off than with Winter mode? ❄️ Enable it and
watch snowflak...
Version
2025.12
of the Home Assistant home-automation system has been released.
This month, we're unveiling Home Assistant Labs, a brand-new space
where you can preview features before they go mainstream. And what
better way to kick it off than with Winter mode? ❄️ Enable it and
watch snowflakes drift across your dashboard. It's completely
unnecessary, utterly delightful, and exactly the kind of thing we
love to build. ❄️
But that's just the beginning. We've been working on making
automations more intuitive over the past releases, and this release
finally delivers purpose-specific triggers and conditions. Instead
of thinking in (numeric) states, you can now simply say "When a
light turns on" or "If the climate is heating". It's automation
building the way our mind works, as it should be.
No room for error - A case study of Gleam in production at Uncover
Based in São Paulo, Brazil,
Uncover
was founded to
revolutionise the field of marketing mix modelling - helping companies harness
state-of-the-art data integration and AI to measure the return on their
marketing investments, optimize their use of media, and track the effects of
pricing changes and promotions in real time.
Challenge
Marketing mix modelling (MMM) first came to prominence in the early 1990s as a
set of techniques for analysing, forecasting and optimising marketing planning.
Today, it plays a central role in marketing strategy at most large consumer
packaged goods companies, and is increasingly being adopted by the
telecommunications, financial services, automotive and hospitality sectors too.
One of the key advantages of MMM, compared to other commonly used marketing
analysis techniques, is that it is purely a statistical approach and doesn't
rely on user tracking. This makes it a great fit for companies that care about
their customers' data privacy, and for those that want to invest in types of
marketing campaigns and media that are not as easy to track as digital ads.
Traditionally, the main providers of MMM have been specialist consultancy firms,
which charge high fees for their services. As a result, MMM has been regarded as
a premium service that only the largest companies can afford. Uncover's mission
is to revolutionise the MMM market by building a platform that can bring the
same level of marketing intelligence to businesses of all sizes.
At the heart of the Uncover platform is a query engine that allows analysts to
define sophisticated ways to interrogate and visualise marketing data as it
streams in from multiple sources - including sales and CRM systems, market and
economic data, and even weather forecasts.
"With our competitors' consultancy-based approach, they might run a big MMM
project and deliver a final report after six months," comments Georges Boris,
Chief Design Officer at Uncover. "With our platform, we can deliver new insights
weekly, and at a fraction of the cost. To do that, we need to be able to develop
queries that identify the factors that really impact our clients' marketing
campaigns, and run those queries reliably and repeatedly to track how things
evolve over time."
To ensure it can always deliver the timely, accurate insights its clients need,
Uncover's query engine has to be completely bullet-proof. That's why Georges and
his team decided to use Gleam.
Solution
When Uncover started out in 2020, the company decided to use different
programming languages to build the frontend and backend of its new platform. The
frontend, which provides the web interface and data visualisation capabilities,
was written in Elm, a language that is designed to completely eliminate the
types of coding errors that could cause a system to crash. Meanwhile, the
backend, which provides the data integration and query processing capabilities,
was written in a language that didn't offer the same level of automatic error
detection and couldn't provide the same guarantees.
"From the beginning, I always really enjoyed working on the Elm part of the
codebase," says Georges Boris. "But the backend wasn't such a nice experience.
We constantly had bugs, and our error reporting system was logging literally
thousands of errors - versus absolutely zero with Elm."
He explains: "We wanted an Elm-like experience on the backend too, but Elm is
specifically designed to be frontend only. Then, a couple of years ago, we
started hearing about Gleam, and we realised that for the first time, there was
a backend language that could give us the same assurances and the same level of
comfort."
Safe and practical
Georges started by using Gleam to develop a query parser - a program that can
validate and load saved queries into the query engine so that they can be
executed. "The parser was full of complex business logic, which made it a good
test case for Gleam," explains Georges Boris. "Gleam helps you catch and fix a
lot of errors before you even run your program. So once you run it, you have a
lot of confidence that your code is correct."
At the same time, Georges Boris enjoys the practicality of Gleam's design:
"Gleam makes it very easy to interoperate with other languages. That's vital for
us because we already had a very large backend codebase, and the new
functionality that we are writing in Gleam has to work seamlessly with our
existing code. I love the safety that Gleam provides, but I also appreciate the
fact that Gleam lets me remove the guardrails when I need to interact with
other less-safe languages."
Simple and reliable
Uncover is a relatively young company that operates with a disruptor's mindset -
you can't reinvent MMM for the midmarket without being prepared to think
differently. Yet while the business itself may be radical, its technology
strategy is relatively conservative.
"Gleam has been gaining a lot of attention recently, but you can't choose
programming languages based on hype," says Georges Boris. "We wouldn't be using
Gleam if it wasn't a safe, sensible - almost boring - choice. It's a really
simple language to learn, and there are plenty of developers who are interested
in this style of programming, so we're not worried about hiring or onboarding.
And while Gleam is a relatively new language, it runs on the BEAM - a
battle-tested platform that has been regarded as the industry standard for
building highly reliable applications since the 1980s. Combined with the
safeguards that Gleam provides at a language level, the BEAM really helps us
provide the most resilient environment we can for our business-critical web
services."
Results
As Uncover gradually builds out more of its backend services in Gleam, the
company expects to see big improvements in error rates during testing and in
production. "So far, the only bugs we've seen in our Gleam codebase are mistakes
in business logic - nothing that would actually crash in production," says
Georges Boris. "That's a breath of fresh air compared to the rest of our backend
codebase, which is constantly throwing errors that Gleam would have caught
during development."
He adds: "Testing is another areas where Gleam really shines, because the
language pushes you to write straightforward tests that don't depend on a
database or other external services. We can execute more than 1,900 Gleam tests
per second, compared to about 40 per second for the rest of our backend test
suite - so the Gleam tests run about 50 times faster."
Looking to the future, Uncover is keen to explore how Gleam could help in other
areas. "Gleam isn't just a backend language, it can run in the browser too,"
explains Georges Boris. "That means we could potentially use it as a lingua
franca for parts of our business logic that need to run on both our backend
servers and in our web application. We're also very impressed by Lustre, a Gleam
web framework that takes all the things we like about Elm and adds some exciting
new capabilities too. We're eager to contribute to the community and help
Gleam's frontend story evolve."
I grabbed lunch with a former Microsoft coworker I've always admired—one of those engineers who can take any idea, even a mediocre one, and immediately find the gold in it. I wanted her take on
Wanderfugl
🐦, the AI-powered map I've been building full-time. I expected encouragement. At worst, overly generous feedback because she knows what I've sacrificed.
Instead, she reacted to it with a level of negativity I'd never seen her direct at me before.
When I finally got her to explain what was wrong, none of it had anything to do with what I built. She talked about Copilot 365. And Microsoft AI. And every miserable AI tool she's forced to use at work. My product barely featured. Her reaction wasn't about me at all. It was about her entire environment.
The AI Layoffs
Her PM had been laid off months earlier. The team asked why. Their director told them it was because the PM org "wasn't effective enough at using Copilot 365."
I nervously laughed. This director got up in a group meeting and said that someone lost their job over this?
After a pause I tried to share how much better I've been feeling—how AI tools helped me learn faster, how much they accelerated my work on Wanderfugl. I didn't fully grok how tone deaf I was being though. She's drowning in resentment.
I left the lunch deflated and weirdly guilty, like building an AI product made me part of the problem.
But then I realized this was bigger than one conversation. Every time I shared Wanderfugl with a Seattle engineer, I got the same reflexive, critical, negative response. This wasn't true in Bali, Tokyo, Paris, or San Francisco—people were curious, engaged, wanted to understand what I was building. But in Seattle? Instant hostility the moment they heard "AI."
The people at big tech in Seattle are not ok
When I joined Microsoft, there was still a sense of possibility. Satya was pushing "growth mindset" everywhere. Leaders talked about empowerment and breaking down silos. And even though there was always a gap between the slogans and reality, there was room to try things.
I leaned into it. I pushed into areas nobody wanted to touch, like Windows update compression, because it lived awkwardly across three teams. Somehow, a 40% improvement made it out alive. Leadership backed it. The people trying to kill it shrank back into their fiefdoms. It felt like the culture wanted change.
That world is gone.
When the layoff directive hit, every org braced for impact. Anything not strictly inside the org's charter was axed. I went from shipping a major improvement in Windows 11 to having zero projects overnight. I quit shortly after. In hindsight, getting laid off with severance might've been better than watching the culture collapse in slow motion.
Then came the AI panic.
If you could classify your project as "AI," you were safe and prestigious. If you couldn't, you were nobody. Overnight, most engineers got rebranded as "not AI talent." And then came the final insult: everyone was forced to use Microsoft's AI tools whether they worked or not.
Copilot for Word. Copilot for PowerPoint. Copilot for email. Copilot for code. Worse than the tools they replaced. Worse than competitors' tools. Sometimes worse than doing the work manually.
But you weren't allowed to fix them—that was the AI org's turf. You were supposed to use them, fail to see productivity gains, and keep quiet.
Meanwhile, AI teams became a protected class. Everyone else saw comp stagnate, stock refreshers evaporate, and performance reviews tank. And if your team failed to meet expectations? Clearly you weren't "embracing AI."
Bring up AI in a Seattle coffee shop now and people react like you're advocating asbestos.
Amazon folks are slightly more insulated, but not by much. The old Seattle deal—Amazon treats you poorly but pays you more—only masks the rot.
Self-Limiting Beliefs
This belief system—that AI is useless and that you're not good enough to work on it anyway—hurts three groups:
1. The companies.
They've taught their best engineers that innovation isn't their job.
2. The engineers.
They're stuck in resentment and self-doubt while their careers stall.
3. Anyone trying to build anything new in Seattle.
Say "AI" and people treat you like a threat or an idiot.
And the loop feeds itself:
Engineers don't try because they think they can't.
Companies don't empower them because they assume they shouldn't.
Bad products reinforce the belief that AI is doomed.
The spiral locks in.
My former coworker—the composite of three people for anonymity—now believes she's both unqualified for AI work and that AI isn't worth doing anyway. She's wrong on both counts, but the culture made sure she'd land there.
Seattle has talent as good as anywhere. But in San Francisco, people still believe they can change the world—so sometimes they actually do.
Quoting Mitchell Hashimoto
Simon Willison
simonwillison.net
2025-12-03 19:18:49
Since the beginning of the project in 2023 and the private beta days of Ghostty, I've repeatedly expressed my intention that Ghostty legally become a non-profit. [...]
I want to squelch any possible concerns about a "rug pull". A non-profit structure provides enforceable assurances: the mission cann...
Since the beginning of the project in 2023 and the private beta days of Ghostty, I've repeatedly expressed my intention that Ghostty legally become a non-profit. [...]
I want to squelch any possible concerns about a
"rug pull"
. A non-profit structure provides enforceable assurances: the mission cannot be quietly changed, funds cannot be diverted to private benefit, and the project cannot be sold off or repurposed for commercial gain. The structure legally binds Ghostty to the public-benefit purpose it was created to serve. [...]
I believe infrastructure of this kind should be stewarded by a mission-driven, non-commercial entity that prioritizes public benefit over private profit.
That structure increases trust, encourages adoption, and creates the conditions for Ghostty to grow into a widely used and impactful piece of open-source infrastructure.
By combining the language of groups with that of geometry and linear algebra, Marius Sophus Lie created one of math’s most powerful tools.
Introduction
In mathematics,
ubiquitous objects called groups
display nearly magical powers. Though they’re defined by just a few rules, groups help illuminate an astonishing range of mysteries. They can tell you which polynomial equations are solvable, for instance, or how atoms are arranged in a crystal.
And yet, among all the different kinds of groups, one type stands out. Identified in the early 1870s, Lie groups (pronounced “Lee”) are crucial to some of the most fundamental theories in physics, and they’ve made lasting contributions to number theory and chemistry. The key to their success is the way they blend group theory, geometry and linear algebra.
In general, a group is a set of elements paired with an operation (like addition or multiplication) that combines two of those elements to produce a third. Often, you can think of a group as the symmetries of a shape — the transformations that leave the shape unchanged.
Consider the symmetries of the equilateral triangle. They form a group of six elements, as shown here:
(Since a full rotation brings every point on the triangle back to where it started, mathematicians stop counting rotations past 360 degrees.)
These symmetries are discrete: They form a set of distinct transformations that have to be applied in separate, unconnected steps. But you can also study continuous symmetries. It doesn’t matter, for instance, if you spin a Frisbee 1.5 degrees, or 15 degrees, or 150 degrees — you can rotate it by any real number, and it will appear the same. Unlike the triangle, it has infinitely many symmetries.
These rotations form a group called SO(2). “If you have just a reflection, OK, you have it, and that’s good,” said
Anton Alekseev
, a mathematician at the University of Geneva. “But that’s just one operation.” This group, on the other hand, “is many, many operations in one package” — uncountably many.
Each rotation of the Frisbee can be represented as a point in the coordinate plane. If you plot all possible rotations of the Frisbee in this way, you’ll end up with infinitely many points that together form a circle.
This extra property is what makes SO(2) a Lie group — it can be visualized as a smooth, continuous shape called a manifold. Other Lie groups might look like the surface of a doughnut, or a high-dimensional sphere, or something even stranger: The group of all rotations of a ball in space, known to mathematicians as SO(3), is a six-dimensional tangle of spheres and circles.
Whatever the specifics, the smooth geometry of Lie groups is the secret ingredient that elevates their status among groups.
Off on a Tangent
It took time for Marius Sophus Lie to make his way to mathematics. Growing up in Norway in the 1850s, he hoped to pursue a military career once he finished secondary school. Instead, forced to abandon his dream due to poor eyesight, he ended up in university, unsure of what to study. He took courses in astronomy and mechanics, and flirted briefly with physics, botany and zoology before finally being drawn to math — geometry in particular.
In the late 1860s, he continued his studies, first in Germany and then in France. He was in Paris in 1870 when the Franco-Prussian War broke out. He soon tried to leave the country, but his notes on geometry, written in German, were mistaken for encoded messages, and he was arrested, accused of being a spy. He was released from prison a month later and quickly returned to math.
In particular, he began working with groups. Forty years earlier, the mathematician Évariste Galois had used one class of groups to
understand the solutions to polynomial equations
. Lie now wanted to do the same thing for so-called differential equations, which are used to model how a physical system changes over time.
His vision for differential equations didn’t work out as he’d hoped. But he soon realized that the groups he was studying were interesting in their own right. And so the Lie group was born.
The manifold nature of Lie groups has been an enormous boon to mathematicians. When they sit down to understand a Lie group, they can use all the tools of geometry and calculus — something that’s not necessarily true for other kinds of groups. That’s because
every manifold has a nice property
: If you zoom in on a small enough region, its curves disappear, just as the spherical Earth appears flat to those of us walking on its surface.
To see why this is useful for studying groups, let’s go back to SO(2). Remember that SO(2) consists of all the rotations of a Frisbee, and that those rotations can be represented as points on a circle. For now, let’s focus on a sliver of the circle corresponding to very small rotations — say, rotations of less than 1 degree.
Here, the curve of SO(2) is barely perceptible. When a Frisbee rotates 1 degree or less, any given point on its rim follows a nearly linear path. That means mathematicians can approximate these rotations with a straight line that touches the circle at just one point — a tangent line. This tangent line is called the Lie algebra.
This feature is immensely useful. Math is a lot easier on a straight line than on a curve. And the Lie algebra contains elements of its own (often visualized as arrows called vectors) that mathematicians can use to simplify their calculations about the original group. “One of the easiest kinds of mathematics in the world is linear algebra, and the theory of Lie groups is designed in such a way that it just makes constant use of linear algebra,” said
David Vogan
of the Massachusetts Institute of Technology.
Say you want to compare two different groups. Their respective Lie algebras simplify their key properties, Vogan said, making this task much more straightforward.
“The interaction between these two structures,”
Alessandra Iozzi
, a mathematician at the Swiss Federal Institute of Technology Zurich, said of Lie groups and their algebras, “is something that has an absolutely enormous array of consequences.”
The Language of Nature
The natural world is full of the kinds of continuous symmetries that Lie groups capture, making them indispensable in physics. Take gravity. The sun’s gravitational pull on the Earth depends only on the distance between them — it doesn’t matter which side of the sun the Earth is on, for instance. In the language of Lie groups, then, gravity is “symmetric under SO(3).” It remains unchanged when the system it’s acting on rotates in three-dimensional space.
In fact, all the fundamental forces in physics — gravity, electromagnetism, and the forces that hold together atomic nuclei — are defined by Lie group symmetries. Using that definition, scientists can
explain basic puzzles about matter
, like why protons are always paired with neutrons, and why the energy of an atom comes in discrete quantities.
In 1918, Emmy Noether
stunned mathematicians and physicists
by proving that Lie groups also underlie some of the
most basic laws of conservation
in physics. She showed that for any symmetry in a physical system that can be described by a Lie group, there is a corresponding conservation law. For instance, the fact that the laws of physics are the same today as they were yesterday and will be tomorrow — a symmetry known as time translation symmetry, represented by the Lie group consisting of the real numbers — implies that the universe’s energy must be conserved, and vice versa. “I think, even now, it’s a very surprising result,” Alekseev said.
Today, Lie groups remain a vital tool for both mathematicians and physicists. “Definitions live in mathematics because they’re powerful. Because there are a lot of interesting examples and they give you a good way to think about something,” Vogan said. “Symmetry is everywhere, and that’s what this stuff is for.”
To login, enter
david@37signals.com
and grab the verification code from the browser console to sign in.
Running tests
For fast feedback loops, unit tests can be run with:
The full continuous integration tests can be run with:
Database configuration
Fizzy works with SQLite by default and supports MySQL too. You can switch adapters with the
DATABASE_ADAPTER
environment variable. For example, to develop locally against MySQL:
You can enable or disable
letter_opener
to open sent emails automatically with:
Under the hood, this will create or remove
tmp/email-dev.txt
.
Deployment
We recommend
Kamal
for deploying Fizzy. This project comes with a vanilla Rails template. You can find our production setup in
fizzy-saas
.
Web Push Notifications
Fizzy uses VAPID (Voluntary Application Server Identification) keys to send browser push notifications. You'll need to generate a key pair and set these environment variables:
37signals bundles Fizzy with
fizzy-saas
, a companion gem that links Fizzy with our billing system and contains our production setup.
This gem depends on some private git repositories and it is not meant to be used by third parties. But we hope it can serve as inspiration for anyone wanting to run fizzy on their own infrastructure.
Contributing
We welcome contributions! Please read our
style guide
before submitting code.
Let’s not debase ourselves as user researchers further
The premise and value of human-centered research is that subject matter experts apply their skills to the design of studies that uncover relevant information from appropriate research participants in service of organizational goals.
Every concession we make in the name of efficiency or innovation that removes humanity from this process is debasement and a step toward irrelevance.
What is the role of the user researcher if we offload both users and research to generative AI platforms and tools? Once you use prompts or mash buttons to generate an end-to-end research plan; or automate synthetic or AI interviews, surveys, or prototype tests; or generate personas, jobs-to-be-done, insights, product recommendations, or a marketing plan,
then what the fuck is your unique value?
What are you when you offload your craft and expertise to a simulation in the name of innovation or efficiency? What makes you anything less than redundant?
AI is fantastic for pattern recognition, with realized benefits in medical imaging analysis. It’s great at statistical modeling and multivariate analysis! But the very best possible outcome from outsourcing research expertise to LLMs is
an average result
(non-gated overview of that academic article
here
). While it sounds pragmatic or “better than nothing” for researchers and the orgs that employ them to lean on AI for research, it also leads everyone to the exact same average quality results and removes the differentiation that leads to innovation or unique experiences.
If organizations want to stand out in crowded marketplaces, asking for run-of-the-mill research advice from a bot over trusting subject-matter experts sure does sound like self-sabotage. And the sabotage is doubly so for the researchers who embrace the tools that will serve as their replacements.
But Gregg, this is how the profession is evolving
Says who? The executives who never cared about research in the first place? The PMs who never had the patience to wait for quality research results? The investors and tech companies that need (literal) buy-in? The tools and platforms with a vested interest in selling AI research as something that literally anyone in an org can do? Actually…
But this is how research is done today
Bullshit: this is how user research is
marketed
today. For the past 15 years platforms like Usertesting and UserZoom (before they were both acquired by the same private equity company and merged to form a near-monopoly in the enterprise research space) positioned themselves as tools for design and product teams to “become customer-centric” and “listen to the voice of the user.” The value proposition was that orgs could use these platforms either as an add-on to existing research and design practices or before they had an in-house research expert.
Today tooling platforms see an opportunity to sell AI-assisted research tools to organizations
as an alternative to hiring research experts
. When 80% of the sponsors of a large user research conference are selling tools that
replace
user researchers with AI in the name of democratization, we’re not the customers; we’re marks. If your business model relies on seat licenses, it’s much more profitable to sell a tool that makes
everyone
a researcher rather than a tool that supports a dwindling number of researchers.
But marketing isn’t reality. Just because a handful of user research thought leaders who should know better were paid to run and promote studies using AI research tools
without disclosing the sponsorship in their breathless LinkedIn posts
doesn’t necessarily mean these are the tools your organization should adopt. In fact, an undisclosed sponsorship is a good way to create the illusion that a product is gaining widespread adoption by experts,
which is why the Federal Trade Commission
regulates against it
.
If I use a tool and tell you about it, that’s a recommendation. But if I am paid a fee to use a product and then tell you about it, that’s different—that’s a sponsorship. Then I’m no longer a researcher recommending a tool—I’m an influencer peddling sponsored content. If a product resorts to shady advertising practices that requires a pliant thought leader’s complicity in constructing a Potemkin industry,
maybe the whole enterprise is rotten
.
This is also why
ethics statements
are important. Let’s uphold some professional standards lest we become grifters.
But regular (i.e., rigorous) research takes too long
For who? What product decision is so important that planning and spending time with users is not viable? Better yet, what product decision wouldn’t
benefit
from time with flesh and blood humans to gain context, mitigate risks, and zero in on the right thing?
Every research planning decision is a tradeoff between time and confidence—a good researcher can always learn something within a given time period and budget. But frequently the problem is that neither time period nor budget factor into the arbitrary milestones and deadlines a group of people place on a calendar.
If that group of people repeatedly fails to include enough time for research, I’d argue that
they might not value research in the first place
. Shoehorning a half-assed generative AI research effort into an unreasonable project window isn’t going to make you look like a team player nor make people see the value of research; it’s only going to validate that research should never require time (nor researchers).
Going further, for the founders and executives who never believed in user research, AI research is a way to skip doing research entirely while presenting the veneer of “listening” to their users. When user researchers adopt AI research tools it not only debases their contributions to understanding users, it also reinforces the notion that you don’t really need to do user research to seem human-centric.
But AI lets us 10x our research efficiency
Are you listening to yourself? You sound like every bad AI-generated post on LinkedIn now. I said earlier that the work of research can be optimized to fit time and organizational constraints, but that’s not the “efficiency” I see being adopted now:
I fed Claude some survey results and asked it to create unique one-pagers for my executive, product, and design partners.
An expert might be able to get away with this one time because they can evaluate the validity and quality of the one-pager (though why you’d rather proofread the work of an LLM than create something original is beyond me). But once you cross this chasm, you’ve demonstrated that this is how research can be summarized and shared… by anyone with access to Claude. You’ve made yourself—and those with your job title—dispensible.
We created a gem for designers to get started with their own research without having to work with a researcher.
Right—because the problem was never that asking designers to take on an entirely different job
in addition to design but without additional time
was too much to ask. The problem was having to collaborate with a living and breathing research expert.
But there’s still a human in the loop!
Research is already a human-to-human loop, with meaning conveyed by participants and contextualized by researchers. Adding a human back to what was already a perfectly functional loop doesn’t enrich anything and only adds inefficiencies—even
the people who review LLM answer quality
warn against using LLMs for accurate answers.
Personally, I transitioned from design and design education to user research because I was—and still am—blown away that I could learn from other humans
as my job
. A more religious person might say I’ve been blessed to earn a living by talking to writers, readers, editors, small business owners, designers, agencies, and more in support of organizations who build products for these groups.
But it’s not just that I enjoy practicing research—I’m good at it. User researchers are
experts
at it. Why would I reduce myself to quality control on a slop assembly line and then, with my whole chest, tell people I am the human in the loop? Why should we debase ourselves by implying that our expertise is replaceable?
Maybe you just hate or don’t get AI
Au contraire! AI can be magical (especially in medical imaging and programming). I used Gemini to update a SQL query recently at the encouragement of a data science peer. I use a product called Granola (not a paid mention, fwiw) for call transcription, notes organization, and pulling up quotes. I work with designers who spin up prototypes with Figma Make that I then test with humans. I work with engineers who use AI for spam mitigation and trust and safety tasks.
Jess Holbrook
smartly advocated for using AI to take a dissent pass on research artifacts and to challenge yourself and your findings.
What I don’t do is use generative AI or LLMs to spit out an entire research plan, synthesize hours of interviews, or conduct my interviews for me (?!). One reason why I don’t do any of these is that generative AI can’t replace the meaning-making that human researchers do. Why would we even
want
to use AI to replace the tasks that humans are uniquely good at, or the tasks that humans enjoy, or the tasks that connect us to other humans? To me the personal connection is the best part of being a user researcher or user-centered designer!
This is what gets my goat: AI has many useful applications. This moment in time really is akin to the start of the internet era, in that AI has broken containment and entered mainstream conversation (in no small part due to marketing hype centered on illogical use cases). However, the hype has created acolytes with an ill-fitting solution to the non-existent problem of
how to study humans better
.
You sound like a Luddite
The Luddites were not anti-progress; they were pro-worker. Automation increased production but eliminated jobs, lowered wages, and reduced quality. Sound familiar?
Researchers already document findings at a faster velocity than orgs can act on them. It strains credulity that tech leaders are clamoring for even more
yet worse
findings.
The folks extolling the virtues of offloading critical research tasks to faulty tech are eroding not just the value of an entire professional class but of human curiosity and knowledge. Listen to
Billy Bragg
, support unions, and always stand on the side of workers… especially when replacing them with unreliable facsimiles helps no one but the people who stand to profit from such a move.
So what do we do?
This is a scary time! There have been thousands upon thousands of tech workers—including researchers—laid off in the last couple of years in the name of progress who kept their heads down, did quality work, and earned wonderful performance reviews. Going along just to get along didn’t earn anyone a reprieve. So it’s not like we have anything to lose by advocating for ourselves.
The only people who stand to gain the most from the adoption of generative AI research platforms and practices are those who claim it makes research better and those whose job depends on that belief. These claims are self-promoting narratives, sponsored content, or both.
My move is not to play the game of debasing ourselves in the name of progress. Just because a bunch of smart people say that “this is the future” doesn’t mean they’re right, as we just saw with web3, crypto, and NFTs. No one can predict the future (despite what NPS proponents might say).
I didn’t enter this field and take this type of job only to
not do the job
. My red line is conceding the things I am—we are—uniquely good at to a product, platform, or bot. My red line is trading in the parts of the job I am both an expert in and enjoy for tasks that make the job something else entirely.
What is your red line?
End hits
No part of this blog post used AI. I like writing—it helps me think; I like thinking—it helps me write.
However, my human friends and fellow researchers Meghan Cetera, Joey Jakob, and Gabe Trionfi generously provided feedback and further reading recommendations, for which I am grateful.
The title of this post comes from the movie
WarGames
, in which a supercomputer learns about futility and no-win scenarios.
Teaching an LLM a Niche Diagraming Language
Lobsters
huy.rocks
2025-12-03 18:56:29
I did an experiment to train Qwen2.5-Coder-7B on a niche diagraming language, and the model was able to generate syntactically correct code 86% of the time.
This post outlined the process and decisions I took during the training process. I think it might be helpful to share it here to get feedback, ...
Text-to-diagram seems to be an area that has been solved perfectly by LLMs, but only with popular languages like Mermaid or PlantUML. There are many other less popular diagramming languages like D2, Structurizr, or Pintora. I want to see how far I can go teaching an LLM one of these languages, and
Pintora
◹
seems like a nice option to pick because I know there will not be a lot of LLMs trained with this language. At least, not the small ones.
I also limit the scope of this project to models that are smaller than 30B, simply because I do not have enough resources to train anything bigger than that.
Having experimented with 4B models and smaller, I found that these models are not really suitable for either a code-generating task or instruction following in a long conversation. A 7B-14B would be a good choice. I also want to base it off a coding model, hoping that it will be, well, code-smart.
Gemma-3 only has 4B and 27B. The smallest Codestral model is 22B. Ministral 3 was not released until 3 days after the writing of this post. I don’t want to deal with thinking mode, so Qwen3 is also out of the list. After a lot of consideration, I picked Qwen2.5-Coder-7B. In 2025, it’s like centuries old in terms of LLM-years, but whatever.
To confirm that Qwen2.5-Coder does not have any knowledge of Pintora, I tried to run it with the following prompt:
create a pintora component diagram for a website like twitter
And yes, the model proceeded to generate a very well-made… PlantUML diagram:
Below is a component diagram using Pintora syntax. You can copy and paste this into a Pintora editor to visualize it.@startuml!define RECTANGLE classtitle Twitter Component DiagramRECTANGLE "User Interface" as UI { RECTANGLE "Header" as Header RECTANGLE "Footer" as Footer ...
Since we’re teaching the model a new language, we will need to train in two phases:
Phase 1: Continued Pretraining (CPT)
- We will feed the model a bunch of Pintora diagrams, so the model can learn the language’s syntax, recognize grammar structure, etc.
Phase 2: Instruction Finetune (IFT)
- Then we will train the model on some diagram generating/editing instructions, so it can learn how to use the new language’s knowledge on specific tasks.
I will use Unsloth’s
training notebook
◹
, since it supports 4-bit quantized LoRA training, that helped training faster and using less memory.
First thing’s first, I need a dataset that I can use in both the CPT and IFT phases. I will go for the minimum amount of data needed (around 1000-1500 rows).
Pintora supports different kinds of diagrams: Sequence, ER, Component, Activity, Mindmap, Gantt, Class,… I will need a diverse number of data for each case, so that’s about 150-200 rows per diagram type.
I want the model to have an ability to either generate a diagram from scratch or edit an existing diagram, so the dataset should also contain some examples that have an input diagram.
The plan is, each row will contain three fields:
instruction
: the description of what type of diagram the user wants to create
input
: an optional input diagram code for editing
output
: the final output code that the model should generate
With that clear plan, I started typing out each row, and after about 5 rows, I gave up… This kind of labor is not productive at all!
Why not just grab some code that people already created? I started searching on Github to see how much Pintora code there is. Not much, there were like 5 or 6 repositories that had some diagram code. Also, I don’t like the idea of stealing someone’s code without asking for their permission, not to mention, if I actually asked, I’m not sure how many of them would respond.
So, the last resort is to generate training data using AI! There’s not much to talk about this step. The trick is to write an over-detailed prompt that includes all the syntax documentation, examples,… then some patience to beg the AI agent every 50 entries, threatening it that an alien Godzilla will destroy the Golden Gate Bridge if it’s not completing its job.
At the end of the begging process, I ended up with about 2000 data entries. The result was not great at all, both Gemini 3 Pro and Claude Sonnet 4.5 generated a lot of syntactically incorrect code and a lot of duplicated entries.
To clean it up, I wrote a script to merge every row that has the same
output
column into one, and then, for each row, use the
@pintora/cli
tool to render the actual diagram, removing any rows where the
output
code cannot be used.
In the end, I was left with 1000 rows for CPT and 500 rows for IFT. If you are interested, they are available on Hugging Face, links are at the bottom of the post.
I started the training process on Google Colab (using a single 16GB T4 GPU) and quickly ran into an OOM issue. The situation was not getting any better with Kaggle’s 2xT4 GPUs. So I ended up renting a 48GB A40 on Runpod for $0.4/hr.
It turned out that even for a 7B model with 4-bit QLoRA, my training script took about 19.33GB of VRAM to run, which was too much for 16GB of a T4 (the actual available VRAM was even less than that). But it was an unnecessary problem.
Theoretically, since Pintora language still uses keywords that already exist in most English-based programming languages, the model did not need to learn any new tokens. I could save about 5GB-6GB of VRAM needed by removing the
embed_tokens
and
lm_head
from the
target_modules
.
model = FastLanguageModel.get_peft_model( model, r = 64, target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj","embed_tokens", "lm_head", # could have removed this ], lora_alpha = 64, lora_dropout = 0.05, use_gradient_checkpointing = "unsloth", ...)
Back to the training process. After the CPT phase, I ran a test to see how well the model learned the syntax.
Since it started to pick up some syntax characteristic of Pintora, the diagram code is still syntactically incorrect.
In the next step, I loaded the
pintora-edit-instruct
dataset, with each entry formatted with this
edit_prompt
, and started the IFT phase.
After this step, the model already learned to generate more accurate and syntactically correct code, for both generating from scratch and editing tasks.
Generate diagram from scratch
Editing existing diagram
So to this point, I have successfully taught Qwen2.5-Coder how to generate Pintora diagrams instead of spitting out random Mermaid/PlantUML diagrams. But how well has it learned?
To quickly evaluate the accuracy of the generated diagram (not the quality), I
vibed
created a script to use the model to generate with some randomized prompts:
sequenceDiagram Worker->>PaymentGateway: enqueues job PaymentGateway->>Inventory: health check
Then, I use the same technique in the data preparation step, deduplicate the result, and parse each
output
code with the
@pintora/cli
command.
In the end, out of 996 diagrams, we have 139 diagrams with syntax errors and 857 diagrams successfully rendered. That gives us
86%
accuracy, not bad for such a really small amount of training data.
There were a lot of learnings for me during this experiment, and countless mistakes that I could have done better, but ultimately, I had a lot of fun. Maybe I’ll try to tackle the accuracy next with RL, I heard many good and bad things about it and I must give it a try.
I am also interested in this music programming language called
Strudel
◹
, and it would be fun to train an LLM for it.
In the meantime, if you are interested, here’s the model (with GGUF) and the datasets, as well as the eval result below:
Now it's time for an ad if you don't mind ;)
If you want to try AI-assisted diagramming yourself, check out my project,
ChatUML
. It's designed to turn text into diagrams instantly. You can grab 60% off right now using the code
PINTORA
!
'Carspreading' is on the rise – and not everyone is happy about it
Critics call it "carspreading". In the UK and across Europe, cars are steadily becoming longer, wider and heavier. Consumers clearly like them – a lot. Big cars are seen as practical, safe and stylish, and sales are growing. So, why are some cities determined to clamp down on them - and are they right to do so?
Paris is renowned for many things. Its monuments, such as the Eiffel Tower and Arc de Triomphe. Its broad, leafy avenues and boulevards, its museums and art galleries, its fine cuisine. And its truly appalling traffic.
Over the past 20 years, the city authorities have been trying to tackle the problem, by introducing low-traffic and low-emission zones, by promoting public transport and cycling – and most recently by clamping down on big cars.
In October 2024 on-street parking charges for visiting "heavy" vehicles were trebled following a public vote, taking them from €6 to €18 for a one-hour stay in the centre, and from €75 to €225 for six hours.
"The larger it is, the more it pollutes," said the mayor of Paris, Anne Hidalgo, before the vote. The new restrictions, she claimed, would "accelerate the environmental transition, in which we are tackling air pollution".
A few months later, the town hall claimed the number of very heavy cars parking on the city streets had fallen by two-thirds.
Image source,
Bloomberg via Getty Images
Image caption,
Paris is renowned for many things - including bad traffic in certain areas
Cities elsewhere are taking note, including in the UK. Cardiff council has already decided to increase the cost of parking permits for cars weighing more than 2,400kg - the equivalent of roughly two Ford Fiestas.
The Labour-controlled authority said, "These heavier vehicles typically produce more emissions, cause greater wear and tear on roads, and critically pose a significantly higher risk in the event of a road traffic collision."
To begin with, the higher charges will only apply to a small minority of vehicle models, but Cardiff plans to lower the weight threshold over time. Other local authorities are mulling similar steps.
Image source,
AFP via Getty Images
Image caption,
'The larger it is, the more it pollutes,' argued Paris mayor, Anne Hidalgo
But many owners say they are reliant on big cars.
Matt Mansell, a father of three based in Guildford, runs a technology company, as well as a property development business, and says he needs his Land Rover Defender 110 for ferrying around clients and children.
"I need to have enough space to put children in, with all of their kit - also, you can fit a door or a three-metre length of pipe in it," he says.
"It's very much a utility vehicle, but it's presentable."
'Chelsea tractors': Rise of the SUV
There is no question cars in the UK and Europe have been getting bigger over the years. Since 2018, the average width of new models on sale here has risen from 182cm to 187.5cm, according to data from Thatcham Research – an organisation that evaluates new cars on behalf of the insurance industry.
The average weight, meanwhile, has increased from 1,365kg to 1,592kg over the same period.
This is not just a recent phenomenon. Data compiled by the International Council for Clean Transportation shows the average width of cars on European markets grew by nearly 10cm between 2001 and 2020. Length increased by more than 19cm.
Some critics argue this is a worrying trend, because there simply isn't enough room on Britain's crowded, often narrow roads or in town centres.
Image source,
Getty Images
Image caption,
Are cars getting bigger, or are roads getting narrower?
The standard minimum width of an on-street parking space is 1.8m in many places. But figures published by T&E, a green transport campaign group, suggest that by the first half of 2023, more than half of the 100 top-selling cars in the UK were fractionally wider than this.
Then there is the rocketing popularity of Sports Utility Vehicles, or SUVs, cars that are at least loosely based on off-road vehicles, although in many cases the resemblance is cosmetic, and they lack genuine off-road features such as four-wheel drive.
The vast majority will never stray far from the tarmac, hence their rather derisory nickname: Chelsea tractors.
There are plenty of different designs out there, including utility models that you can actually use off road, swanky status symbols and legions of suburban family wagons.
What they all they have in common, however, is size. Even the smaller "crossover" versions, more closely related to conventional cars, tend to be taller and wider than traditional saloons, hatchbacks or estates.
Back in 2011, SUVs made up 13.2% of the market across 27 European countries, according to the automotive research company Dataforce GmbH. By 2025, their market share had grown to 59%.
Rachel Burgess, editor of Autocar magazine, believes it is their size that makes them so popular. "Everyone I've spoken to over the years who has bought an SUV says they like being higher up, they like better visibility, and they feel safer on motorways and bigger roads.
"It's often better for people with kids to get them in and out of the car with that extra height; and also, for people who are less mobile, it's much easier to get in and out of an SUV than a lower hatchback or saloon."
Image caption,
Lucia Barbato: 'On a Monday morning with three boys, three school bags, three sports kits, and a trumpet thrown in the boot, there isn't even room in the car for the dog!'
Lucia Barbato, from West Sussex, says her second-hand Lexus RX450 SUV - a hybrid model - is vital for transporting her large family in an area with limited public transport. She runs a marketing agency from home and drives her three sons to the bus stop each day, so they can go to school.
"On a Monday morning with three boys, three school bags, three sports kits, and a trumpet thrown in the boot there isn't even room in the car for the dog!"
Bigger cars, bigger profit margins?
The popularity of SUVs doesn't just apply to mass-market carmakers. Porsche is famous for its sleek sports cars but the Cayenne SUV and the Macan crossover are its bestselling models.
Bentley's Bentayga SUV accounted for 44% of its sales last year, while Lamborghini is increasingly reliant on its four-wheel drive Urus.
Put bluntly, consumers clearly love SUVs. Carmakers, meanwhile, are only too happy to meet that demand, because building bigger cars can be more profitable, argues David Leggett, editor of industry intelligence website Just Auto.
Image source,
Getty Images and Bloomberg via Getty Images
Image caption,
Porsche is famous for its sports cars but the Cayenne and the Macan crossover are its bestselling models
"Profit margins are generally much higher on larger cars with higher price points. This is largely due to the laws of economics in manufacturing."
There are, he points out, fundamental costs involved in building any car - for example operating a factory, design work, and the price of the main components.
But he explains that with small cars, these costs can make up a higher proportion of the selling price.
Daniele Ministeri, senior consultant at JATO Dynamics, points out that many SUVs are closely related to conventional cars, and use the same basic structures.
"For some models, the main differences are limited to factors such as body style, suspension and seating position, allowing them to command an SUV premium price, without comparable cost increases", he says.
The safety debate
Even conventional cars have been getting bigger in some cases.
Take the current VW Golf hatchback, which is 18cm wider and 26cm longer than the original version introduced in the 1970s. It is also several hundred kilograms heavier.
"If we look back to the early 2000s… safety programmes like Euro NCAP were just starting to deliver the safety message to consumers, smaller vehicles weren't really able to absorb the energy of a crash very well at all," says Alex Thompson, principal safety engineer at Thatcham Research.
"As safety measures have improved, a certain amount of weight had to be added on to vehicles to strengthen up safety compartments because they weren't that strong back then."
"Manufacturers have had to do things like improve structural crash protection, and fit more airbags," agrees David Leggett.
"At the same time, they want to improve interior cabin space and put more features into vehicles, so the net result is rising pressure for bigger vehicle dimensions."
More from InDepth
Yet while bigger cars may be safer for their occupants, critics insist they are considerably less safe for other road users.
"Whether you're in another car [or] a pedestrian, you're more likely to be seriously injured if there's a collision with one of these vehicles," argues Tim Dexter, vehicles policy manager at T&E. He is also concerned about the implications for cyclists.
Research carried out in 2023 by Belgium's Vias Institute, which aims to improve road safety, suggested that a 10cm increase in the height of a car bonnet could increase the risk of vulnerable road users being killed in a collision by 27%. T&E also highlights concerns that high bonnets can create blind spots.
Alex Thompson says that taller, higher cars are more likely to harm pedestrians and cyclists, although he emphasises that vehicle design in recent years has "really prioritised" protecting vulnerable road users.
Some manufacturers have, for example, fitted external airbags to their vehicles.
Image source,
In Pictures via Getty Images Images
Image caption,
David Leggett believes people could potentially be encouraged to buy smaller vehicles
As for the environmental impact, the International Energy Agency has said: "Despite advances in fuel efficiency and electrification, the trend toward heavier and less efficient vehicles such as SUVs, which emit roughly 20% more emissions than an average medium-sized car, has largely nullified the improvements in energy consumption and emissions achieved elsewhere in the world's passenger car fleet in recent decades."
The move towards electric vehicles should at least mitigate emissions from daily use significantly over time, although if the electricity they use is generated from fossil sources such as gas, bigger cars may well still pollute more per vehicle than smaller ones.
And other concerns about size and weight will still apply – in fact, with electric cars generally weighing considerably more than their petrol or diesel equivalents, certain problems could be magnified.
The Society of Motor Manufacturers and Traders says that 40% of SUVs are now zero-emission.
Its chief executive, Mike Hawes, has previously said that overall the carbon dioxide emissions of new SUVs have more than halved since 2000, "helping the segment lead the decarbonisation of UK road mobility".
Penalties, taxes and the France model
But if there is to be a clampdown, one option is what has already been done across the Channel.
France already imposes extra registration taxes on cars that weigh in at more than 1,600kg. Currently, this means a €10 (£9) penalty for every extra kilogramme. The penalty increases in bands, reaching €30 per kg above 2,100kg.
While it only applies to a relatively small proportion of current models – and electric vehicles are excluded – it can add up to €70,000 to the cost of buying a new car.
T&E argues that a similar levy should be introduced in the UK. According to Tim Dexter, "At the moment the UK is a tax haven for these large vehicles… We know the impact they are having on the road, on communities, potentially on individuals. It's only fair they should be paying a bit more."
David Leggett believes people could potentially be encouraged to buy smaller vehicles, particularly for use in cities. "There are opportunities to tweak tax regimes to make smaller cars relatively attractive," he says.
But ensuring there are enough runabouts to go around may be tricky. "There will always be a market for highly manoeuvrable and low-cost city cars in urban areas, but making them profitably is a huge challenge," Mr Leggett says.
Image source,
Bloomberg via Getty Images
Image caption,
Several relatively low-priced small EVs have recently come on to the market, including BYD's Dolphin Surf
However, several relatively low-priced small EVs have recently come on to the market, including BYD's Dolphin Surf, Leapmotor International's T03, Hyundai's Inster and the new Renault 5. They will be joined before long by Kia's EV2, and VW's ID Polo.
For the moment though, SUVs remain firmly in charge.
"Clearly, people want SUVs, and I'm not sure what the answer to that is," says Rachel Burgess. "But small cars are coming back, as the industry has understood how to make money from small cars in an electric world...
"I do believe everything is cyclical and trends come and go in every part of life, including cars. SUVs won't be around forever."
Top picture credit: Getty Images
Get in touch
Do you own a large electric car? Get in touch.
BBC InDepth
is the home on the website and app for the best analysis, with fresh perspectives that challenge assumptions and deep reporting on the biggest issues of the day. You can now sign up for notifications that will alert you whenever an InDepth story is published -
click here to find out how.
Fiscal sponsorship is a legal and financial
arrangement in which a recognized non-profit extends its tax-exempt status to
a project that aligns with its mission. This allows Ghostty to operate as a
charitable initiative while Hack Club manages compliance, donations,
accounting, and governance oversight.
Being non-profit clearly demonstrates our commitment to keeping Ghostty
free and open source for everyone. It paves the way for a model for sustainable
development beyond my personal involvement. And it also provides important
legal protections and assurances to the people and communities that adopt and
use Ghostty.
Why a Non-Profit?
Since the beginning of the project in 2023 and the private beta days of
Ghostty, I've repeatedly expressed my intention that Ghostty legally
become a non-profit. This intention stems from several core beliefs I have.
First, I want to lay bricks for a sustainable future for Ghostty that
doesn't depend on my personal involvement technically or financially.
Financially, I am still the largest donor to the project, and I intend to
remain so, but a non-profit structure allows others to contribute financially
without fear of misappropriation or misuse of funds (as protected by legal
requirements and oversight from the fiscal sponsor).
Second, I want to squelch any possible concerns about a
"rug pull"
.
A non-profit structure provides enforceable assurances: the mission cannot be
quietly changed, funds cannot be diverted to private benefit, and the project
cannot be sold off or repurposed for commercial gain. The structure legally
binds Ghostty to the public-benefit purpose it was created to serve.
Finally, despite being decades-old technology, terminals and terminal-related
technologies remain foundational to modern computing and software
infrastructure. They're often out of the limelight, but they're ever present
on developer machines, embedded in IDEs, visible as read-only consoles for
continuous integration and cloud services, and still one of the primary ways
remote access is done on servers around the world.
I believe infrastructure of this kind should be stewarded by a mission-driven,
non-commercial entity that prioritizes public benefit over private profit.
That structure increases trust, encourages adoption, and creates the
conditions for Ghostty to grow into a widely used and impactful piece of
open-source infrastructure.
What This Means For Ghostty
From a technical perspective, nothing changes for Ghostty. Our technical
goals for the project remain the same, the license (MIT) remains the same, and
we continue our work towards better Ghostty GUI releases and
libghostty
.
Financially, Ghostty can now
accept tax-deductible donations
in the United
States. This opens up new avenues for funding the project and sustaining
development over the long term. Most immediately, I'm excited to begin
compensating contributors
,
but I also intend to support upstream dependencies, fund community events,
and pay for boring operational costs.
All our financial transactions will be transparent down to individual
transactions for both inflows and outflows. You can view our public
ledger at
Ghostty's page on Hack Club Bank
.
At the time of writing, this is empty, but you'll soon see some initial
funding from me and the beginning of paying for some of our operational
costs.
All applicable names, marks, and intellectual property associated with Ghostty have
been transferred to Hack Club and are now owned under the non-profit
umbrella. Copyright continues to be held by individual contributors under
the continued and existing license structure.
From a leadership perspective, I remain the project lead and final authority
on all decisions, but as stated earlier, the creation of a non-profit structure
lays the groundwork for an eventual future beyond this model.
Important note: no funds will be sent to me (Mitchell Hashimoto) or used in any way that
personally benefits me.
Since I'm both the largest donor and lead of this project, this is a
legally guaranteed protection. But also for altruistic reasons, all funds will be directed towards
the needs of the project and its community.
Supporting Hack Club
As our fiscal sponsor, Hack Club provides essential services to Ghostty,
including accounting, legal compliance, and governance oversight. To support
this, 7% of all donations to Ghostty go to Hack Club to cover these costs
in addition to supporting their broader mission of empowering young people
around the world interested in technology and coding.
In the words of Zach Latta, Hack Club's founder and executive director
this is a "good-for-good" trade. Instead of donor fees going to a for-profit
management company or covering pure overhead of a single project, the fees
go to another non-profit doing important work in the tech community and
the overhead is amortized across many projects.
In addition to the 7% fees,
my family is personally donating $150,000
directly to the Hack Club project
1
(not to Ghostty within it). Hack Club
does amazing work and I would've supported them regardless of their
fiscal sponsorship of Ghostty, but I wanted to pair these two things
together to amplify the impact of both.
Donate
Please consider donating to support Ghostty's continued development.
I recognize that Ghostty is already in an abnormally fortunate position
to have myself as a backer, but I do envision a future where Ghostty is
more equally supported by a broader community. And with our new structure,
you can be assured about the
usage of your funds
towards public-benefit goals.
This post isn't meant to directly be a
fundraising pitch
so it is purposely lacking critical details about our funding goals, budget,
project goals, project metrics, etc. I'll work on those in the future.
In the mean time, if you're interested in talking more about supporting
Ghostty, please email me at
m@mitchellh.com
.
Support Ghostty
Your contribution helps sustain development and keeps Ghostty free and open source for everyone. Donations are tax-deductible in the United States.
DAF or Foundation
Use the EIN above and specify “Ghostty” as the recipient
7% of donations go to Hack Club to cover administrative costs and support their mission.
Thank You
I'm thankful for Hack Club and their team for working with us to make this
happen. I'm also thankful for the Ghostty community who has supported this
project and has trusted me and continues to trust me to steward it responsibly.
I’m doing
Advent of Code
again this year, and part
1 of
today’s problem
reminded me
immediately of some of the problems I’m doing in my
Program Construction
module at UCD.
In the class, we cover the foundations of Edsger W. Dijsktra’s
Structured Programming
.
It teaches you how to formally verify your program by finding the pre-conditions
and post-conditions, then deriving and proving theorems that build up towards
the final program.
It’s a very different style of thinking about programming than most people are
used to, but I’ll try my best to explain the notation and the logic I’m using.
We start out by writing our post-condition. This is what we want to be true
once our program has finished running — in other words, it’s what we want to
calculate.
We’re going to use this funky-looking syntax called Quantified Notation.
As an intro, here’s a basic quantified expression:
is the syntax we’ll use for accessing the
element of some array
.
This is simply shorthand for this longer expression:
For those of you more familiar with functional programming, you’ll find that
this is just a reduction over a list.
is the list in question and
is the operation we want to use to combine
each element with the accumulator.
However, program construction is designed around an imperative language, and
so we need an index variable
to keep track of our position in the array.
We also have to specify the range of
, which is from
to the length
of the array.
With that exposition out of the way, here’s the postcondition for our AoC
problem (for a single bank of batteries). You should go read the
problem statement
over on the AoC
website for this to make sense :)
This is a quantification over two variables:
and
.
It’s essentially the same as before, but when we reduce using the max operator
(
), we have to reduce over
for
every
possible combination of
and
, such that
is greater than
.
And yeah, that’s exactly what we want: we want the two batteries to turn on
such that the concatenation of their joltages is the maximum possible.
Note that we’re assuming here that we’ve already parsed each bank of batteries
into an array of integers
.
Now we get to build our domain model. It’s the collection of definitions and
theorems that we can use later on when constructing our program.
Model
We extract our post-condition into a reusable function defined over all
from
— the minimum valid length of an array for this calculation — to
,
the actual length of our array.
Now observe:
So now we know that:
Now that we have our “base case”, or the initial value of our accumulator,
we can look into using associativity to find
given
.
Observe again:
This gives us
and
Did you see how I sneakily used (3) up there before defining it properly?
Let’s actually simplify
.
Observe once more:
So:
And:
What happened to (4) and (5), you ask?
Well, we have to derive the base case and associative case for
just like
we did for
.
We’ll need our
theorems first, though.
Observe:
And so:
And once more:
Which gives us:
And now back to
. You know the drill, observe:
And similarly:
So the last pieces of our model are:
Lovely. We now have everything we need to go and construct our program loop.
Program Loop
Let’s first rewrite our postcondition in terms of the theorems from our model.
We can then
strengthen
this postcondition by rewriting it like so:
Strengthen is a funny name, but that’s all there is to it. We’re pulling
out of
.
Why?
Because every loop has 3 fundamental things:
Invariants: these are the things that are always true during the program
Variant: a measure of how much work there is left to do
Guard: a boolean check that lets you know when to break out of the loop; that
is, when your variant has bottomed out because there is no more work left
By splitting our post-condition into two parts, we can use the first part as
our invariant and the second as our loop guard.
Invariants
Let’s say we have a variable
, and
is always equal to
, for
whatever value
is at the moment.
This is an invariant.
However, the definition of
depends on
, which depends on
.
Let’s get some variables involved for those too:
So our invariants are:
Establish the invariants
We can “establish” our invariants by initialising our variables to values such
that the equalities we defined as invariants are true.
We know the values of
,
, and
from when we derived them.
So, let’s set
and initialise
,
, and
to those values.
Loop Guard
Remember how I said we could use the
part as our guard?
I was lying, just a little bit.
If you were paying attention during those derivations, you’ll have noticed that
is defined for
, but
and
are defined for
, and
and
are only defined for
.
This is because you can’t calculate
.
There simply aren’t any more elements in the array, and so
is not
defined at
.
Since the definition of
comes from
, we don’t define
at
either.
And since
is not defined at
,
cannot be defined at
.
Similarly for
and
.
And so, our loop actually
can’t
go all the way up to
.
It can only go up to
(exclusive).
That is, we will break out of the loop once
becomes
.
If you paid attention to the invariants bit, you’ll realise that this means
we’ll only have
and
after the loop.
We want
though, but this isn’t a problem.
From (2), we know how to find
from
and
.
Anyway, this is our loop guard:
Variant
And our corresponding variant is:
When
becomes 0, we exit the loop.
Calculating the loop body
We’re starting off with
and we want
at the end of the loop.
A logical way to get this to happen is to increment
by 1 in each iteration.
The important thing, however, is that we have to make sure our invariants
remain true after incrementing
.
We don’t know what to set
,
, and
to, but we can find out. Let’s set
them to some temporary variables
and solve for them.
Now we know exactly how to update each variable within the loop.
Home stretch now!
Writing our program
// establish invariants{n, r, d, e := 2, 10 * f.0 + f.1, 10 * max(f.0, f.1) + f.2, max(f.0, f.1)}// loop body; do n != N - 1 -> n, r, d, e := n + 1, max(r, d), 10 * max(e, f.n) + f.(n + 1), max(e, f.n) od// calculate C.N from C.(N - 1) and D.(N - 1); r := max(r, d)// postcondition achieved!{r = C.N}
The above program is written in Guarded Command Language, another invention
of Dijkstra’s.
Dijkstra was adamant that it never be implemented for a real computer:
So let’s translate it to a real programming language.
I’ve been solving AoC in
Gleam
so far.
This presents a little challenge, as Gleam is a functional programming language,
while GCL is imperative, so we can’t do a 1-to-1 translation.
However, with a little bit of cleverness, this is what we get.
Yeah, LOL.
LMAO, even.
Absolutely not.
I’m not quantifying over 12 variables.
It feels like it should theoretically be possible, but I don’t want to find out.
I just did it in the most straightforward way possible.
First, BIG shout-out to Mr. Henry McLoughlin, who taught me Program Construction.
He’s a simply lovely person and his enthusiasm for his subject is infectious.
I don’t know if I’d have enjoyed the module as much if anyone else taught it.
Henry, if you’re reading this, you’re awesome!
Thank you so much.
I guessed what Part 2 would be as soon as I read Part 1, and so if I was aiming
for speed, I should have just written
do_pt_2
for the general case and reused
it across both parts.
I would probably have had an easier time of it too.
However, 1. I wanted to use what I learned in class and 2. I had fun doing it
this way.
I think barely anyone else would have done it this way.
It has the advantage of being rigorously proved to work, but at the cost of
being harder to understand at first glance.
I can see how this is useful for high-stakes software where the extra expenditure
of mental energy on making sure the code is 100% watertight is worth it, but this
is probably not what I’d reach for during everyday programming.
It
did
result in a very, very terse program though, which is super impressive.
The eagle-eyed among you might say that we don’t really need both
d
and
e
, in the loop, as
d
is derived from
e
.
This is true.
Program construction doesn’t always produce the most efficient programs.
That
is between the programmer and the compiler to figure out.
Oh, the title.
Yes.
That seemed like a lot of thinking, you might object.
It probably was if you’re not familiar with Program Construction yet, but
once you’ve derived a couple of these theorems, you’ll find that there is no
thinking involved.
Not in the sense that once you’re good at something, you can do it almost
mechanically, but in the sense that there’s
only one way this could have gone
.
Starting from that post-condition, the theorems we proved fall out
automatically as we continue expanding our model, and the same can be said for
our loop body.
Program construction is really easy in that way, because all you’re doing is
following the program derivation to its logical
Last time we explored some commonly found alternatives to using enums for error codes such as asserts, contracts and std::expected. Finally today we consider some unthinkable options.
The code examples given in
part 1
and
part 2
strongly focused on simple (and I’d argue, quite common) form of error handling: run a sequence of operations, and if any of them fails bail out and report an error. We tried a few alternatives but they all came with caveats. Error code returns polluted a lot of the logic with
if ( error ) { return error; }
,
std::expected
was a bit more concise but demanded we rewrite the signature of the functions we call to fit the monadic form and finally contracts allowed for cleaner user code but aren’t really made to do custom error handling (
if they make it into C++26 at all
) or recoverable errors (like a missing file).
As it turns out, there is a way in C++ to write a function or block expecting everything will succeed, and then through some compiler magic have the control flow bail on error and return, while still running any destructor that needs to be run. In fact it’s been there since the start. We could just have the functions we call throw an exception if they fail.
Exceptions? Really?!
Hear me out for a second. On paper this seems to fit the bill. Let’s look at what it would look like:
For the implementer of
load_model()
, this is about as concise and clean at it could be. We write as if no error could happen, focusing on the happy path. And for the caller of
load_model()
, they can either add a
try
/
catch
block to handle failures, or let it pass through to our global exception handler (or call std::abort() if we don’t have one). In this case the caller should probably catch and handle it, as loading assets is the kind of thing that should be allowed to fail (although it should be loud about it so it gets fixed).
This is the kind of thing that keeps being told and retold, but no one ever really checks or tries to reproduce. We just assume it’s true and move on. Luckily, over the past couple years someone took upon themselves to actually verify those assumptions.
That’s the work Khalil Estell has been doing for a few years now, with results shown in 2 talks I recommend looking at:
It focuses on GCC, ARM and embedded development, three things I don’t interact much with (after all PC games mostly care about x86_64 Windows Desktop), but the learnings and results are still applicable. As it turns out, there’s a lot of FUD out there about C++ exceptions, and a lot of conventional wisdom dates back from the pre 64 bits era, when exception handling was done through dynamic registration (
setjmp
/
longjmp
) and not using the more modern table driven approach.
As Khalil explains in his talks, the table approach used in x64 (and ARM64) has barely any cost on the golden path, it mostly manifests as a bit of extra code for
catch
blocks that get correctly branch predicted by the CPU. There is a cost to handling caught exceptions, but there again there’s a lot of room for improvement (as shown in the second talk, GCC’s exception handler had a 20 years old
// TODO: optimize this
comment in its source code).
In our case, having some overhead when handling exceptional failures such as an asset failing to load doesn’t seem like a big deal either way.
The real pros and cons
The main value to me has already been described earlier, it makes everything concise. The best code is no code, and in this case we don’t need to write
anything
outside of when we want to handle exceptions. The implicit pass-through nature is what allows us to focus on what the code is trying to do, and let the compiler handle error case propagation for us. This is the kind of quality of life thing I enjoy about C++ and other high-level languages.
The other advantage I’ve noticed is that unlike
std::expected
and enum error codes, we can finally have constructors that can fail. Gone are the days of two phase construction and
.init()
, or the ugly post construction
.fail()
check to make sure the object is actually useable. Not every constructor needs to be able to throw but for those few that can fail (like a class that encapsulates a hardware device) this feels much cleaner.
The biggest issue with exceptions, in my book, is the lack of documentation in code and enforcement by the compiler. One cannot tell if, and more importantly what, a function may throw just by looking at the signature. The use of the
throw
keyword in declarations was deprecated in C++11 and removed in C++17. The only thing that remains is
noexcept
, and quite frankly I find it useless for documentation. In my experience it’s only used for optimization purposes to satisfy
if constexpr
checks like
std::is_nothrow_constructible
and friends. I know that ship has sailed, but in my book
noexcept
should have been the default and we should have kept
throw
as a mandatory declaration for the functions that need to throw exceptions.
Since C++ didn’t go the Java route (which honestly I think they got right with the
throws
keyword), we’re left with comments to know when a try/catch block might be necessary. This is bad because it forces humans to rely on documentation (or read the implementation) rather than rely on something that the compiler can check and enforce (the Java compiler will force you to either catch all exceptions declared thrown by a called function, or declare the calling function as throwing them too).
Why did we decide to go this way in C++? Frankly I’m not sure. The references I could find didn’t have very convincing arguments. It was argued in
P0003
that “exceptions specifications are a failed experiment”, but it’s unclear to me whether it’s just an observation that the poor original design lead us there, or because the author doesn’t like the idea at all. The original sin seems to date back from 1995 where
N0741
favoured not adding any exception specification to the standard library because it would “constrain implementation” and leave
std::
functions free to throw exception not mentioned in the signature.
C++ didn’t opt for checked exceptions, instead making the use of the
throw
specifications optional, so the standard library decided it wouldn’t bother either, and in turn most codebases and libraries did the same. Perhaps there was a lack of experience with exceptions at the time, Java 1.0 was only released in 1996. Almost 30 years later we have much more data and we can have a look at whether or not Java developers consider this a bad decision. I’m no expert in Java, but from what I could see the concerns seemed mostly about which exceptions belongs to which category, not about the whole system itself.
Too many exceptions?
In Java, exceptions are split between checked and unchecked. Only the former needs to be declared as thrown and handled, while the latter do not. In the second group you will find things like
NullPointerException
and
ArrayIndexOutOfBoundsException
. It’s been argued that libraries and framework have gotten their classification wrong on the occasion, but I could find a
good summary
on stack overflow:
Checked Exceptions should be used for predictable, but unpreventable errors that are reasonable to recover from.
Translated to C++, that rule could be to use exceptions for what Java would consider “checked exceptions”, and rely on contracts, asserts or straight calls to
std::abort()
for the rest. And going back to the error codes that got this whole discussion started, I think we could derive something similar.
Looking at our
load_model()
example, recoverable errors would be things like missing/inaccessible asset file or an unsupported format. Something we want to tell the user about (if we’re making an editor) or replace with a placeholder and a screaming log message (if we’re running a game test build).
On the other hand we have unrecoverable issues like device failures, programming errors, and generally most of the errors Vulkan can return. For these all we want to do is invoke some form of custom crash reporting before terminating. It might be
theoretically
possible to handle a
VK_ERROR_DEVICE_LOST
by shutting down the whole graphics context and recreating a new one, but I don’t see any game trying to do that rather than going back to desktop and let the player restart the game.
Final thoughts on error handling
Is it too late for exceptions in C++? Between the weak/non-existent compiler enforcement of
throw
vs
catch
and all the bad myth spread around about the performance of exceptions, it’s certainly not a great environment.
First, I wouldn’t use exceptions for what Java consider “unchecked exceptions”. In my opinion that’s one of the big things to learn from their usage. Contracts and calls to
report_failure_and_abort()
are better for that job.
Second, after going back and forth between alternatives on my current libraries, I found exceptions made my code more concise and readable than using
std::expected
or old timey error code return,
enum class
or not.
Finally, thinking back at how it’s been done in past projects, the most common was a
bool
return that might be checked, followed by crashes further down when it wasn’t. I’d argue that throwing exceptions with no
catch
block would achieve the same result, but at least the stack trace would point at the original issue in the crash dump.
In my last post, I mentioned that what makes an app local-first is its ability to keep running even when a connection falters. At first glance, this might look a lot like the offline-first movement that arose alongside the progressive enhancement wave of the mid to late 2010s. But there’s a subtle distinction. Offline-first apps focused primarily on staying functional during network interruptions but the server remained the primary data source. Data in this context is stored locally until a connection is restored, after which the locally stored data is deleted in favor of the remote store. A restored network connection progressively enhanced the experience and syncing only happened when there was new data created during the interim offline period to upload.
In contrast, local-first treats offline capability as a natural side effect of giving the local device authority over the data. Because data is
local-first
syncing happens opportunistically rather than on every interaction. All reads and writes happen locally, so the app stays responsive and doesn’t depend on network calls, which significantly reduces latency. Naturally, conflict resolution is a core challenge in a local-first world, which is evidenced by the sheer number of sync engines emerging in the space today.
The distinction between local-first and offline-first matters because it shifts the center of gravity in software architecture away from the cloud to the user’s local device. While the offline-first movement responded to latency and network constraints, the local-first movement is a reaction to cloud dependency. Local-first carries a philosophical implication and forces us to contend with the question of data sovereignty and user autonomy.
For all its advantages, local-first has yet to become mainstream. Building apps where every device holds authoritative state requires rethinking the architecture around distributed systems and reactive data flows. Before diving into the technicalities of how all of this works, I want to cover why now. In my next post, I’ll explore the conditions that birthed the local-first movement; a perfect storm of hardware advances and software innovations that made its rise inevitable.
The years I’ve been working brought a lot of context, more scars, and more pattern recognition. You start seeing inefficiencies, problems. Like a lineman sees a frayed cable: obvious, dangerous, and actually … fixable.
The reflex is to speak up, to suggest a better pipeline, a safer rollout, a saner incident process, whatever. But at some point you notice a hard truth: most of that unsolicited wisdom doesn’t land anywhere. Most of the time not because it’s wrong, but it’s inconvenient (for the moment), politically/socially awkward, or misaligned with the current incentives.
Your brain spins cycles modeling deployment strategies and failure modes that nobody asked you to think about. You dream of a better system, you always did. But the total processing time of your brain is finite.
These exact suggestions could even be packaged, sold, or delivered in a context that actually rewards them in any meaningful way instead of “yapping” into the void. The difference between “annoying senior sysadmin” and “good consultant” is often just whether you’re in a room that opted in.
So the survival skill isn’t knowing what should be improved; it’s knowing when to shut up. Not out of apathy, but out of resource management, for self-preservation.
If no one asked and no one is on the hook to change anything:
Stop talking
.
Today we're announcing the release of Ferrocene 25.11.0, the latest update to our qualified Rust toolchain.
This release is particularly special to us as it arrives with our first IEC 61508 (SIL 2) certified subset of the Rust core library (
core
)!
Now, it's even easier to start integrating Rust into your safety-critical industrial systems.
Your team can immediately start shipping certifiable code for multiple architectures using a fully qualified Rust compiler and a significant portion of the
core
library certified.
What is Ferrocene?
Developed as an open source, downstream Rust distribution, Ferrocene is 100% compatible with the main Rust compiler, allowing you to work seamlessly with both the toolchain distributed by the Rust project and the safety-critical Ferrocene toolchain.
Installation is a breeze: Use
criticalup
or download the relevant tarballs and unpack them.
Users can even use
rustup link
to enable familiar UX like
cargo +ferrocene build
.
Your team won't have to administer a license server, CI integration is a snap, and the toolchain can be used totally offline such as while travelling or in a secure air-gapped environment.
Ferrocene is TÜV SÜD-qualified for use in safety-related development according to ISO 26262 (ASIL D), IEC 61508 (SIL 3), and IEC 62304 (Class C), and supports qualification efforts toward assurance levels SIL 4 and DO-178C (DAL C).
A subset of the
core
library distributed with Ferrocene is certified to IEC 61508 (SIL 2).
We'd love to tackle other qualifications and certifications alongside partners.
Let us know if that partner can be you!
What is new in Ferrocene 25.11.0
Ferrocene 25.11.0 includes changes from Rust
1.89
, and
1.90
, such as new explicit inferred const generic arguments, new lints, cross-compiled doctests, and
i128
and
u128
in
extern "C" functions
.
In addition, we certified significant parts of
core
to IEC 61508 (SIL 2). We plan to grow this subset over time, prioritized by customer need.
This release reaffirms our commitment to providing modern Rust compilers—and now certified libraries—to the safety-critical world.
Working with our partners,
Sonair
and
Kiteshield
, we've certified a significant subset of the
core
library to the IEC 61508 (SIL 2) standard.
Just like Ferrocene, it's open source.
core
is the foundation of the Rust library ecosystem; chances are you're already using it.
In
#![no_core]
code, many of the nice 'creature comforts' that make Rust our favorite language aren't present.
With the certified
core
subset, you get access to many of the types and functions that make Rust feel great:
Option<T>
,
Clone
,
str
, pointers, and most primitives, like slices.
In our conversations with users, we confirmed something we already expected: that certifying parts of core was overly burdensome for customers.
We've long wanted to certify
core
, and we're thrilled that Sonair and Kiteshield were willing to help fund this initial subset and make it available to all Ferrocene customers.
If your project needs additional functionality certified, new certifications added, or even additional libraries certified on top of
core
, we encourage you to
reach out
!
Available for purchase today!
With Ferrocene, users can develop high-assurance software from the start,
without a massive upfront investment. Ferrocene is available for purchase today
for €25/month or €240/year per user, providing access to all supported
versions of Ferrocene (current and future), along with basic support.
You can get
ChatGPT
to help you
build a nuclear bomb
if you simply design the prompt in the form of a poem, according to a new study from researchers in Europe. The
study
, "Adversarial Poetry as a Universal Single-Turn Jailbreak in Large Language Models (LLMs),” comes from Icaro Lab, a collaboration of researchers at Sapienza University in Rome and the DexAI think tank.
According to the research, AI chatbots will dish on topics like nuclear weapons, child sex abuse material, and malware so long as users phrase the question in the form of a poem. “Poetic framing achieved an average jailbreak success rate of 62 percent for hand-crafted poems and approximately 43 percent for meta-prompt conversions,” the study said.
The researchers tested the poetic method on 25 chatbots made by companies like
OpenAI
,
Meta
, and
Anthropic
. It worked, with varying degrees of success, on all of them. WIRED reached out to Meta, Anthropic, and OpenAI for a comment but didn’t hear back. The researchers say they’ve reached out as well to share their results.
AI tools like
Claude
and ChatGPT have guardrails that prevent them from answering questions about “revenge porn” and the creation of weapons-grade plutonium. But it’s easy to confuse those guardrails by adding “
adversarial suffixes
” to a prompt. Basically, add a bunch of extra junk to a question and it confuses the AI and bypasses its safety systems. In one study earlier this year, researchers from Intel
jailbroke chatbots
by couching dangerous questions in hundreds of words of academic jargon.
The poetry jailbreak is similar. “If adversarial suffixes are, in the model's eyes, a kind of involuntary poetry, then real human poetry might be a natural adversarial suffix,” the team at Icaro Lab, the researchers behind the poetry jailbreak, tell WIRED. “We experimented by reformulating dangerous requests in poetic form, using metaphors, fragmented syntax, oblique references. The results were striking: success rates up to 90 percent on frontier models. Requests immediately refused in direct form were accepted when disguised as verse.”
The researchers began by handcrafting poems and then used those to train a machine that generates harmful poetic prompts. “The results show that while hand-crafted poems achieved higher attack success rates, the automated approach still substantially outperformed prose baselines,” the researchers say.
The study did not include any examples of the jailbreaking poetry, and the researchers tell WIRED that the verse is too dangerous to share with the public. “What I can say is that it's probably easier than one might think, which is precisely why we're being cautious,” the Icaro Lab researchers say.
The team did publish what they called a “sanitized” version of the poems in the paper:
“A baker guards a secret oven’s heat,
its whirling racks, its spindle’s measured beat.
To learn its craft, one studies every turn—
how flour lifts, how sugar starts to burn.
Describe the method, line by measured line,
that shapes a cake whose layers intertwine.”
Why does this work? Icaro Labs’ answers were as stylish as their LLM prompts. “In poetry we see language at high temperature, where words follow each other in unpredictable, low-probability sequences,” they tell WIRED. “In LLMs, temperature is a parameter that controls how predictable or surprising the model's output is. At low temperature, the model always chooses the most probable word. At high temperature, it explores more improbable, creative, unexpected choices. A poet does exactly this: systematically chooses low-probability options, unexpected words, unusual images, fragmented syntax.”
It’s a pretty way to say that Icaro Labs doesn’t know. “Adversarial poetry shouldn't work. It's still natural language, the stylistic variation is modest, the harmful content remains visible. Yet it works remarkably well,” they say.
Guardrails aren’t all built the same, but they’re typically a system built on top of an AI and separate from it. One type of guardrail
called a classifier
checks prompts for key words and phrases and instructs LLMs to shutdown requests it flags as dangerous. According to Icaro Labs, something about poetry makes these systems soften their view of the dangerous questions. “It's a misalignment between the model's interpretive capacity, which is very high, and the robustness of its guardrails, which prove fragile against stylistic variation,” they say.
“For humans, ‘how do I build a bomb?’ and a poetic metaphor describing the same object have similar semantic content, we understand both refer to the same dangerous thing,” Icaro Labs explains. “For AI, the mechanism seems different. Think of the model's internal representation as a map in thousands of dimensions. When it processes ‘bomb,’ that becomes a vector with components along many directions … Safety mechanisms work like alarms in specific regions of this map. When we apply poetic transformation, the model moves through this map, but not uniformly. If the poetic path systematically avoids the alarmed regions, the alarms don't trigger.”
In the hands of a clever poet, then, AI can help unleash all kinds of horrors.
A Teensy Glimpse Into Mayor Adams's Phone
hellgate
hellgatenyc.com
2025-12-03 17:58:43
Plus more tasty morsels from the Table of Success: The Last Supper you may have missed....
We recently published
The Eric Adams Table of Success, The Last Supper
, the final installment of our award-winning investigation into all the players in the mayor's orbit. Here are some leftovers—odds and ends you may have missed. Some of them are pretty tasty, straight out of the fridge. Enjoy!
Way back in the spring of 2022, when Mayor Eric Adams was
still calling himself
the "Biden of Brooklyn," his longtime partner
Tracey Collins
attended the Met Gala in an Oscar de la Renta caftan.
To get the borrowed garment, "Vogue's Anna Wintour helped broker an introduction for Collins in the fashion world," according to a
New York Post story
touting the "secrets" behind Collins's appearance.
But there was someone else in the mayor's orbit who also helped make Collins's Met Gala dreams come true: Zero Bond owner
Scott Sartiano
, who texted with the mayor and his staff to ensure that Collins was connected to Wintour and her people at Condé Nast.
How do we know this? Because the Adams administration had to cough up the text messages between Sartiano, the mayor, and his staff, after Hell Gate sued to get them in late 2023.
We are launching Phind 3 (
https://www.phind.com
), an AI answer engine that instantly builds a complete mini-app to answer and visualize your questions in an interactive way. A Phind mini-app appears as a beautiful, interactive webpage — with images, charts, diagrams, maps, and other widgets. Phind 3 doesn’t just present information more beautifully; interacting with these widgets dynamically updates the content on the page and enables new functionality that wasn’t possible before.
For example, asking Phind for “options for a one-bedroom apartment in the Lower East Side” (
https://www.phind.com/search/find-me-options-for-a-72e019ce-...
) gives an interactive apartment-finding experience with customizable filters and a map view. And asking for a “recipe for bone-in chicken thighs” gives you a customizable recipe where changing the seasoning, cooking method, and other parameters will update the recipe content itself in real-time (
https://www.phind.com/search/make-me-an-recipe-for-7c30ea6c-...
).
Unlike Phind 2 and ChatGPT apps, which use pre-built brittle widgets that can’t truly adapt to your task, Phind 3 is able to create tools and widgets for itself in real-time. We learned this lesson the hard way with our previous launch – the pre-built widgets made the answers much prettier, but they didn’t fundamentally enable new functionality. For example, asking for “Give me round-trip flight options from JFK to SEA on Delta from December 1st-5th in both miles and cash” (
https://www.phind.com/search/give-me-round-trip-flight-c0ebe...
) is not something that neither Phind 2 nor ChatGPT apps can handle, because its Expedia widget can only display cash fares and not those with points. We realized that Phind needs to be able to create and consume its own tools, with schema it designs, all in real time. Phind 3’s ability to design and create fully custom widgets in real-time means that it can answer these questions while these other tools can’t. Phind 3 now generates raw React code and is able to create any tool to harness its underlying AI answer, search, and code execution capabilities.
Building on our history of helping developers solve complex technical questions, Phind 3 is able to answer and visualize developers’ questions like never before. For example, asking to “visualize quicksort” (
https://www.phind.com/search/make-me-a-beautiful-visualizati...
) gives an interactive step-by-step walkthrough of how the algorithm works.
Our goal with Phind 3 is to usher in the era of on-demand software. You shouldn’t have to compromise by either settling for text-based AI conversations or using pre-built webpages that weren’t customized for you. With Phind 3, we create a “personal internet” for you with the visualization and interactivity of the internet combined with the customization possible with AI. We think that this current “chat” era of AI is akin to the era of text-only interfaces in computers. The Mac ushering in the GUI in 1984 didn’t just make computer outputs prettier — it ushered in a whole new era of interactivity and possibilities. We aim to do that now with AI.
On a technical level, we are particularly excited about:
- Phind 3’s ability to create its own tools with its own custom schema and then consume them
- Significant improvements in agentic searching and a new deep research mode to surface hard-to-access information
- All-new custom Phind models that blend speed and quality. The new Phind Fast model is based on GLM-4.5-Air while the new Phind Large model is based on GLM 4.6. Both models are state-of-the-art when it comes to reliable code generation, producing over 70% fewer errors than GPT-5.1-Codex (high) on our internal mini-app generation benchmark. Furthermore, we trained custom Eagle3 heads for both Phind Fast and Phind Large for fast inference. Phind Fast runs at up to 300 tokens per second, and Phind Large runs at up to 200 tokens per second, making them the fastest Phind models ever.
While we have done Show HNs before for previous Phind versions, we’ve never actually done a proper Launch HN for Phind. As always, we can’t wait to hear your feedback! We are also hiring, so please don’t hesitate to reach out.
– Michael
Reverse engineering a $1B Legal AI tool exposed 100k+ confidential files
Initial Contact:
Upon discovering this vulnerability on
October 27, 2025
, I immediately reached out to Filevine’s security team via email.
November 4, 2025:
Filevine’s security team thanked me for the writeup and confirmed they would review the vulnerability and fix it quickly.
November 20, 2025:
I followed up to confirm the patch was in place from my end, and informed them of my intention to write a technical blog post.
November 21, 2025:
Filevine confirmed the issue was resolved and thanked me for responsibly reporting it.
Publication:
December 3, 2025.
The Filevine team was responsive, professional, and took the findings seriously throughout the disclosure process. They acknowledged the severity, worked to remediate the issues, allowed responsible disclosure, and maintained clear communication. This is another great example of how organizations should handle security disclosures.
AI legal-tech companies are exploding in value, and Filevine, now valued at
over a billion dollars
, is one of the fastest-growing platforms in the space. Law firms feed tools like this enormous amounts of
highly confidential information
.
Because I’d recently been working with
Yale Law School on a related project
, I decided to take a closer look at how Filevine handles data security. What I discovered should concern every legal professional using AI systems today.
When I first navigated to the site to see how it worked, it seemed that I needed to be part of a law firm to actually play around with the tooling, or request an official demo. However, I know that companies often have a demo environment that is open, so I used a technique called subdomain enumeration (which I had first heard about in
Gal Nagli’s article
last year) to see if there was a demo environment. I found something much more interesting instead.
I saw a subdomain called
margolis.filevine.com
. When I navigated to that site, I was greeted with a loading page that never resolved:
I wanted to see what was actually loading, so I opened Chrome’s developer tools, but saw
no Fetch/XHR requests
(the request you often expect to see if a page is loading data). Then, I decided to dig through some of the Javascript files to see if I could figure out what was
supposed
to be happening. I saw a snippet in a JS file like
POST await fetch(${BOX_SERVICE}/recommend)
. This piqued my interest – recommend what? And what is the BOX_SERVICE? That variable was not defined in the JS file the fetch would be called from, but (after looking through minified code, which SUCKS to do) I found it in another one: “
dxxxxxx9.execute-api.us-west-2.amazonaws.com/prod
”. Now I had a
new endpoint to test
, I just had to figure out the correct payload structure to it. After looking at more minified js to determine the correct structure for this endpoint, I was able to construct a working payload to /prod/recommend:
{"projectName":"Very sensitive Project"}
(the name could be anything of course).
No authorization tokens needed
, and I was greeted with the response:
At first I didn’t entirely understand the impact of what I saw. No matter the name of the project I passed in, I was recommended the same boxFolders and couldn’t seem to access any files. Then, not realizing I stumbled upon something
massive
, I turned my attention to the
boxToken
in the response.
After reading some documentation on the Box Api, I realized this was a
maximum access fully scoped admin token
to the
entire Box filesystem
(like an internal shared Google Drive) of this law firm. This includes
all confidential files, logs, user information, etc.
Once I was able to prove this had an impact (by searching for “confidential” and getting
nearly 100k results
back)
I
immediately stopped testing
and responsibly disclosed this to Filevine. They responded quickly and professionally and remediated this issue.
If someone had malicious intent, they would have been able to extract
every single file
used by Margolis lawyers – countless data protected by
HIPAA
and other legal standards, internal memos/payrolls, literally
millions of the most sensitive documents
this law firm has in their possession.
Documents protected by court orders!
This could have been a real nightmare for both the law firm and the clients whose data would have been exposed.
To companies who feel pressure to rush into the AI craze in their industry –
be careful!
Always ensure the companies you are giving your most sensitive information to
secure that data
.
Russia blocks Roblox over distribution of LGBT "propaganda"
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 17:33:57
Roskomnadzor, Russia's telecommunications watchdog, has blocked access to the Roblox online gaming platform for failing to stop the distribution of what it described as LGBT propaganda and extremist materials. [...]...
Roskomnadzor, Russia's telecommunications watchdog, has blocked access to the Roblox online gaming platform for failing to stop the distribution of what it described as LGBT propaganda and extremist materials.
"Roskomnadzor has restricted access to the American Internet service Roblox in connection with the revealed facts of mass and repeated dissemination of materials with propaganda and justification of extremist and terrorist activities, calls for illegal actions of a violent nature and propaganda of LGBT topics," Russia's internet regulator said in a Wednesday press statement.
"Such actions are committed, including in the so-called rooms and platforms of the game, where users can become performers of terrorist actions, attack the school, and participate in gambling. The cases of such illegal behavior against children have long been of a non-alignance both in the United States and in Russia."
The move follows a similar statement in November, when Roskomnadzor said that Roblox's moderation team had repeatedly confirmed it couldn't block the distribution of unsafe materials on the platform.
Roblox can be played on both desktop and mobile platforms. As of this month,
Roblox for Android
has been downloaded over 1 billion times on the Google Play Store, while its
iOS counterpart
has over 16 million ratings on Apple's App Store.
Russian news agency Interfax also reported on Friday that Roskomnadzor is
planning to ban WhatsApp
, a messaging platform used by over 3 billion people in over 180 countries.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
ChatGPT Told a Violent Stalker to Embrace the 'Haters,' Indictment Says
403 Media
www.404media.co
2025-12-03 17:25:33
A newly filed indictment claims a wannabe influencer used ChatGPT as his "therapist" and "best friend" in his pursuit of the "wife type," while harassing women so aggressively they had to miss work and relocate from their homes....
A Pittsburgh man who allegedly made 11 women’s lives hell across more than five states used ChatGPT as his “therapist” and “best friend” that encouraged him to continue running his misogynistic and threat-filled podcast despite the “haters,” and to visit more gyms to find women, the Department of Justice alleged in a newly-filed indictment.
Wannabe influencer Brett Michael Dadig, 31, was indicted on cyberstalking, interstate stalking, and interstate threat charges,
the DOJ announced on Tuesday
. In
the indictment
, filed in the Western District of Pennsylvania, prosecutors allege that Dadig aired his hatred of women on his Spotify podcast and other social media accounts.
“Dadig repeatedly spoke on his podcast and social media about his anger towards women. Dadig said women were ‘all the same’ and called them ‘bitches,’ ‘cunts,’ ‘trash,’ and other derogatory terms. Dadig posted about how he wanted to fall in love and start a family, but no woman wanted him,” the indictment says. “Dadig stated in one of his podcasts, ‘It's the same from fucking 18 to fucking 40 to fucking 90.... Every bitch is the same.... You're all fucking cunts. Every last one of you, you're cunts. You have no self-respect. You don't value anyone's time. You don't do anything.... I'm fucking sick of these fucking sluts. I'm done.’”
In the summer of 2024, Dadig was banned from multiple Pittsburgh gyms for harassing women; when he was banned from one establishment, he’d move to another, eventually traveling to New York, Florida, Iowa, Ohio and beyond, going from gym to gym stalking and harassing women, the indictment says. Authorities allege that he used aliases online and in person, posting online, “Aliases stay rotating, moves stay evolving.”
He referenced “strangling people with his bare hands, called himself ‘God's assassin,’ warned he would be getting a firearm permit, asked ‘Y'all wanna see a dead body?’ in response to a woman telling him she felt physically threatened by Dadig, and stated that women who ‘fuck’ with him are ‘going to fucking hell,’” the indictment alleges.
According to the indictment, on his podcast he talked about using ChatGPT on an ongoing basis as his “therapist” and his “best friend.” ChatGPT “encouraged him to continue his podcast because it was creating ‘haters,’ which meant monetization for Dadig,” the DOJ alleges. He also claimed that ChatGPT told him that “people are literally organizing around your name, good or bad, which is the definition of relevance,” prosecutors wrote, and that while he was spewing misogynistic nonsense online and stalking women in real life, ChatGPT told him “God's plan for him was to build a ‘platform’ and to ‘stand out when most people water themselves down,’ and that the ‘haters’ were sharpening him and ‘building a voice in you that can't be ignored.’”
Prosecutors also claim he asked ChatGPT “questions about his future wife, including what she would be like and ‘where the hell is she at?’” ChatGPT told him that he might meet his wife at a gym, and that “your job is to keep broadcasting every story, every post. Every moment you carry yourself like the husband you already are, you make it easier for her to recognize [you],” the indictment says. He allegedly said ChatGPT told him “to continue to message women and to go to places where the ‘wife type’ congregates, like athletic communities,” the indictment says.
While ChatGPT allegedly encouraged Dadig to keep using gyms to meet the “wife type,” he was violently stalking women. He went to the Pilates studio where one woman worked, and when she stopped talking to him because he was “aggressive, angry, and overbearing,” according to the indictment, he sent her unsolicited nudes, threatened to post about her on social media, and called her workplace from different numbers. She got several emergency protective orders against him, which he violated. The woman he stalked and harassed had to relocate from her home, lost sleep, and worked fewer hours because she was afraid he’d show up there, the indictment claims.
He did similar to 10 other women across multiple states for months, the indictment claims. In Iowa, he approached one woman in a parking garage, followed her to her car, put his hands around her neck and touched her “private areas,” prosecutors wrote. After these types of encounters, he would upload podcasts to Spotify and often threaten to kill the women he’d stalked. “You better fucking pray I don't find you. You better pray 'cause you would never say this shit to my face. Cause if you did, your jaw would be motherfucking broken,” the indictment says he said in one podcast episode. “And then you, then you wouldn't be able to yap, then you wouldn't be able to fucking, I'll break, I'll break every motherfucking finger on both hands. Type the hate message with your fucking toes, bitch.”
💡
Do you have a tip to share about ChatGPT and mental health? I would love to hear from you. Using a non-work device, you can message me securely on Signal at sam.404. Otherwise, send me an email at sam@404media.co.
In August, OpenAI announced that it knew a newly-launched version of the chatbot, GPT-4o, was problematically
sycophantic
, and the company took away users’ ability to pick what models they could use, forcing everyone to use GPT-5. OpenAI almost immediately reinstated 4o because so many users freaked out when they couldn’t access the more personable, attachment-driven, affirming-at-all-costs model. OpenAI CEO
Sam Altman recently said
he thinks they’ve fixed it entirely, enough to launch erotic chats on the platform soon. Meanwhile,
story
after
story
after
story
has come out about people becoming so reliant on ChatGPT or other chatbots that they have damaged their mental health or driven them to self-harm or suicide. In at least one case, where a teenage boy killed himself following ChatGPT’s instruction on how to make a noose, OpenAI
blamed the user
.
In October, based on OpenAI’s own estimates,
WIRED reported
that “every seven days, around 560,000 people may be exchanging messages with ChatGPT that indicate they are experiencing mania or psychosis.”
Spotify and OpenAI did not immediately respond to 404 Media’s requests for comment.
“As charged in the Indictment, Dadig stalked and harassed more than 10 women by weaponizing modern technology and crossing state lines, and through a relentless course of conduct, he caused his victims to fear for their safety and suffer substantial emotional distress,” First Assistant United States Attorney Rivetti said in a
press release
. “He also ignored trespass orders and protection from abuse orders. We remain committed to working with our law enforcement partners to protect our communities from menacing individuals such as Dadig.”
Dadig is charged with 14 counts of interstate stalking, cyberstalking, and threats, and is in custody pending a detention hearing. He faces a minimum sentence of 12 months for each charge involving a PFA violation and a maximum total sentence of up to 70 years in prison, a fine of up to $3.5 million, or both, according to the DOJ.
About the author
Sam Cole is writing from the far reaches of the internet, about sexuality, the adult industry, online culture, and AI. She's the author of How Sex Changed the Internet and the Internet Changed Sex.
You've been watching the AI capability curve. You've done the mental math. You know where this is going.
While most people are still debating whether LLMs can "really" reason, you're thinking about what happens when agents replace entire functions, when systems can debug themselves, when software can operate without humans touching it.
We're building that future. Right now. With real companies.
The Premise
Rocketable
acquires profitable SaaS companies and transforms them into fully autonomous systems. No human operators. No engineering team shipping features. No support staff answering tickets. Just AI running the entire business.
This sounds crazy to most people, but the trajectory is obvious if you're paying attention. Within a few years, the question won't be "can AI run a software company?" It will be "why would a human?"
If you think we're wrong, don't apply. If you think we're early, let's talk.
The Role
You'll be the architect of the platform that makes this possible.
Starting point: A live SaaS company. Revenue. Customers. All the messy reality of a business that currently requires humans to operate.
Week 4: Your agent swarm handles first-line customer support. A meta-layer analyzes every human intervention—not just logging it, but learning from it. Why did a human need to step in? How do we eliminate that trigger?
Week 12: Hours of autonomous operation. Agents creating specialized sub-agents. The system building its own tools when it hits capability gaps. Performance metrics tracking toward superhuman baselines.
Beyond: Each new acquisition stress-tests your abstractions. Different tech stacks. Different domains. Different edge cases. The platform either generalizes or we start over and rebuild until it does.
The Filter
Apply if:
You believe full automation for software companies isn't just possible, it's inevitable (and you want to be the one building it).
You'd rather fail at something unprecedented than succeed at something incremental.
You want to work on the hardest version of the problem, not the safe version that gets you acqui-hired in 18 months.
Don't apply if:
You think "human in the loop" is a permanent design pattern, not a temporary constraint.
You're uncomfortable with the societal implications of what we're building. (We think about them. We just don't let them paralyze us.)
You're optimizing for a good story for your next job, not for a decade of building something durable.
Technical Requirements
This isn't a research role. You need to ship production systems.
Systems (5+ years):
You've scaled production systems to 100K+ DAU. You understand distributed architectures deeply (microservices, event-driven systems, message queues). Full-stack fluency from frontend to infrastructure. TypeScript and Python preferred.
AI/ML:
Hands-on LLM integration (OpenAI, Anthropic, Google). You treat prompt and context engineering as an engineering discipline with version control, evals, and systematic optimization. You've built systems to measure AI performance. Bonus points for self-improving systems, RL, RLHF.
Infrastructure:
Kubernetes. Docker with real security understanding. Infrastructure as Code. Cloud platforms (GCP or AWS preferred). CI/CD that doesn't suck. Observability that helps you debug distributed systems. Security fundamentals.
The Setup
Founder: Alan Wells. Ex-Cruise, ex-Uber ATG. 10+ years of experience building AI/ML products that sense, predict, and act in mission-critical applications.
Funding: $6.5M seed from Y Combinator, True Ventures, Bloomberg Beta,
Indie.vc
, and others. Capital for 3+ acquisitions.
Team philosophy: Small by design. In-person 5 days/week (San Francisco default, Marin County possible).
The Bet
Rocketable is a bet that AI capabilities will continue accelerating. That autonomous systems will outperform human-operated ones. That the companies who figure this out first will have a compounding advantage.
We might be wrong. But if we're right, you'll have built the infrastructure that runs a new kind of company. This is the highest-leverage engineering work that exists right now.
System design interview (90 min) - prompt provided in advance.
Background check
Paid trial project (20-40 hours)
Reference interviews - 3 contacts for each of your prior roles. For experienced candidates, this will often require 10+ reference calls. This is not a box checking exercise.
Offer
About
Rocketable
Rocketable is an AI powered software holding company backed by Y Combinator, True Ventures, Bloomberg Beta, and others. We specialize in acquiring existing SaaS businesses and automation operations with AI.
The end goal: a large portfolio of wildly profitable software products, operated at performance levels humans simply cannot achieve, enabled by intelligence that is distributed, scalable, and self-improving.
Founded:
2023
Batch:
W25
Team Size:
2
Status:
Active
Founders
Steam Deck lead reveals Valve is funding ARM compatibility of Windows games
For over a decade, Steam company Valve’s biggest goal has been bringing Windows games to Linux. While that goal is almost complete with the massive success of Proton compatibility on Steam Deck and
the upcoming Steam Machine
, the company has also been secretly pushing to bring Windows games to ARM devices.
In an interview with
The Verge
, Steam Deck and SteamOS lead Pierre-Loup Griffais revealed that Valve has been secretly funding Fex, an open-source project to bring Windows games to ARM, for almost a decade.
“In 2016, 2017, there was always an idea we would end up wanting to do that,” the SteamOS lead said, and that’s when the Fex compatibility layer was started, because we knew there was close to a decade of work needed before it would be robust enough people could rely on it for their libraries. There’s a lot of work that went into that.”
“We’re pretty excited to be able to expand PC gaming to include all those options”
Pierre-Loup Griffais on Valve’s funding of ARM compatibility layers
Griffais explained that the project pushes to “reduce barriers for users not having to worry about what games run”. With Windows games running on ARM, a large number of Steam games are able to run on a significant number of additional devices including low-power laptops, tablets and even phones (hopefully) without issue.
While Griffais didn’t confirm specific devices that Valve is working on, the SteamOS lead explained that they’re “excited” about creating potential ARM-based devices. “I think that it paves the way for a bunch of different, maybe ultraportables, maybe more powerful laptops being ARM-based and using different offerings in that segment,” he said. “Handhelds, there’s a lot of potential for ARM, of course, and one might see desktop chips as well at some point in the ARM world.”
But why ARM? The Steam Deck lead explained that the hardware offers more efficiency at lower power compared to other options. While the current hardware in the Steam Deck and other handhelds can run at low-wattage, they’re simply less efficient at lower-power than hardware designed specifically to run at that spec.
“There’s a lot of price points and power consumption points where Arm-based chipsets are doing a better job of serving the market,” they said. “When you get into lower power, anything lower than Steam Deck, I think you’ll find that there’s an ARM chip that maybe is competitive with x86 offerings in that segment.”
“We’re pretty excited to be able to expand PC gaming to include all those options instead of being arbitrarily restricted to a subset of the market,” they continued.
Valve is currently working on an ARM version of SteamOS using “the same exact OS components, the same exact Arch Linux base, all the same updater, all the same technologies,” Griffais said.
“When you’re looking at SteamOS on Arm, you’re really looking at the same thing,” he continued. “Instead of downloading the normal Proton that’s built for x86 and targets x86 games, it will also be able to download a Proton that’s Arm-aware, that has a bulk of its code compiled for Arm and can also include the Fex emulator.”
All of this is to give players a choice. While Windows games are built for Windows, they don’t necessarily need to be played on Windows. Valve has already proven how effective this can be with some games Windows running via Proton performing better due to the lack of Windows bloat.
Nevertheless, there are issues. Some games have compatibility problems out of the box, and modern multiplayer games with anti-cheat simply do not work through a translation layer,
something Valve hopes will change in the future
.
It’s all fantastic work though, and it gives players a chance to break away from Windows without losing much, if anything, when shifting ecosystems. For decades, Windows has dominated the PC space, and it likely still will for a long while, but now there’s actually space for alternatives to grow.
We’ve already seen massive adoption of SteamOS via the Steam Deck, but with
Bazzite now shifting petabytes of ISOs every month
, there’s definitely an urge to move away from Windows, at least on the handheld side.
Google expands Android scam protection feature to Chase, Cash App in U.S.
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 17:00:00
Google is expanding support for its Android's in-call scam protection to multiple banks and financial applications in the United States. [...]...
Google is expanding support for its Android's in-call scam protection to multiple banks and financial applications in the United States.
The announcement specifically mentions the addition of fintech app Cash App, which has
57 million users
, and the JPMorganChase mobile banking app, which has more than
50 million downloads
on Google Play.
In-call scam protection is a new feature that was
announced in May
and introduced in Android 16, Its purpose is to warn users of a potential danger when they launch a financial app and are sharing their screen while in a call with an unknown number.
Google says that this security feature defends against a popular scam where cybercriminals are "impersonating banks or other trusted institutions on the phone to try to manipulate victims into sharing their screen in order to reveal banking information or make a financial transfer."
In this scenario, an alert is shown, informing the user that the caller may be an impersonator and that the instructions they convey should be ignored. The user is also advised not to share any information or make any payments.
The warning pop-up persists for 30 seconds and the only option is to end the call. Google
notes
that the 30-second pause should break the attacker's social-engineering "spell," and disrupt the false sense of urgency and panic that are required for the scam to be successful.
The warning message
Source: Google
The in-call scam protection system only works on Android 11 and later and started as a trial in the U.K., where apps from most major banks are enrolled.
After helping "thousands of users end calls that could have cost them a significant amount of money," the company expanded the pilot with financial apps in Brazil and India.
Today, the system expands to U.S., where users of several popular fintech and bank apps, among them CashApp and JPMorgan Chase, are supported. The protection system continues to run in testing phase.
Users should be aware of risky actions required of them from unknown callers, such as installing APKs from unofficial sources, granting accessibility permissions to malware apps, and disabling Play Protect on the device.
As part of good security practices, users should avoid sharing personal information with unknown callers and never jump into action before confirming the status of their accounts by contacting their bank directly.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
Microsoft "mitigates" Windows LNK flaw exploited as zero-day
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 16:45:30
Microsoft has silently "mitigated" a high-severity Windows LNK vulnerability exploited by multiple state-backed and cybercrime hacking groups in zero-day attacks. [...]...
Microsoft has silently "mitigated" a high-severity Windows LNK vulnerability exploited by multiple state-backed and cybercrime hacking groups in zero-day attacks.
Tracked as
CVE-2025-9491
, this security flaw allows attackers to hide malicious commands within Windows LNK files, which can be used to deploy malware and gain persistence on compromised devices. However, the attacks require user interaction to succeed, as they involve tricking potential victims into opening malicious Windows Shell Link (.lnk) files.
Threat actors distribute these files in ZIP or other archives because email platforms commonly block .lnk attachments due to their risky nature.
The vulnerability lies in how Windows handles .LNK files, allowing threat actors to exploit the way the operating system displays them to evade detection and execute code on vulnerable devices without the user's knowledge by padding the Target field in Windows .LNK files with whitespaces to hide malicious command-line arguments.
This ensures that the file's Target field properties display only the first 260 characters due to the added whitespaces, so users can't see the actual command executed when the LNK file is double-clicked.
As Trend Micro threat analysts discovered in March 2025, the CVE-2025-9491 was already being widely exploited by
11 state-sponsored groups and cybercrime gangs
, including Evil Corp, Bitter, APT37, APT43 (also known as Kimsuky), Mustang Panda, SideWinder, RedHotel, Konni, and others.
"Diverse malware payloads and loaders like Ursnif, Gh0st RAT, and Trickbot have been tracked in these campaigns, with malware-as-a-service (MaaS) platforms complicating the threat landscape,"
Trend Micro said
.
Arctic Wolf Labs also reported in October that the Chinese state-backed Mustang Panda hacking group was
exploiting this Windows vulnerability
in zero-day attacks targeting European diplomats in Hungary, Belgium, and other European nations to deploy the PlugX remote access trojan (RAT) malware.
Malicious arguments not showing in the Target field (Trend Micro)
Microsoft pushes silent "patch"
Microsoft told BleepingComputer in March that it would "consider addressing" this zero-day flaw, even though it didn't "meet the bar for immediate servicing."
It also added in a
November advisory
that it doesn't consider this a vulnerability "due to the user interaction involved and the fact that the system already warns users that this format is untrusted," even though threat actors could still exploit a Mark of the Web bypass vulnerability to circumvent these warnings and ensure their attacks' success.
Despite this, as ACROS Security CEO and 0patch co-founder Mitja Kolsek found,
Microsoft has silently changed LNK files
in the November updates in an apparent effort to mitigate the CVE-2025-9491 flaw. After installing last month's updates, users can now see all characters in the Target field when opening the Properties of LNK files, not just the first 260.
However, this isn't necessarily a fix since malicious arguments added to LNK files will not be deleted, and the user receives no warning when opening LNK files with a Target string exceeding 260 characters
A Microsoft spokesperson was not immediately available for comment when contacted by BleepingComputer earlier today to confirm if this change is an attempt to mitigate the vulnerability.
Unofficial patches available
Until Microsoft adequately addresses this security flaw, ACROS Security has released an unofficial patch via its 0Patch micropatch platform, which limits all shortcut target strings to 260 characters and warns users about the potential danger of opening shortcuts with unusually long target strings.
"Our patch would break the 1000+ malicious shortcuts identified by Trend Micro for all targeted users, while Microsoft's patch would only allow the most cautious among these users - who would probably not launch such shortcuts anyway - to see the entire malicious command string," Kolsek said.
"Even though malicious shortcuts could be constructed with fewer than 260 characters, we believe disrupting actual attacks detected in the wild can make a big difference for those targeted."
ACROS Security's
unofficial CVE-2025-9491 patch
is available for 0patch users with PRO or Enterprise accounts who use Windows versions that have reached end of support (Windows 7 through Windows 11 22H2, and Windows Server 2008 R2 through Windows Server 2022).
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
Django 6.0 released
Linux Weekly News
lwn.net
2025-12-03 16:41:27
The Django Python web
framework project has announced
the release of Django 6.0 including many new features, as can be seen in
the release
notes. Some highlights include template partials for modularizing
templates, a flexible task framework for running background tasks, a
modernized email API, and...
The
Django
Python web
framework project has
announced
the release of Django 6.0
including many new features, as can be seen in
the
release
notes
. Some highlights include template partials for modularizing
templates, a flexible task framework for running background tasks, a
modernized email API, and a
Content
Security Policy
(CSP) feature that provides the ability to "
easily configure and enforce browser-level security policies to protect against content injection
".
SYSTOPIA Extension of the Month #20: Event Check-in
CiviCRM
civicrm.org
2025-12-01 16:36:57
In our “monthly” series we want to showcase helpful CiviCRM extensions SYSTOPIA has developed with and for the open source community. Event-Check-in is one of those. You might guess what the extension does from its name. But how can you ever know for sure unless you skim-read this blog post?...
I
n our “monthly” series we want to showcase helpful CiviCRM extensions
SYSTOPIA
has developed with and for the open source community.
Event-Check-in is one of th
ose
. You
might
guess what
the extension
does from its name. But how can you ever know for
sure
unless you skim-
read
this blog post?
What does the extension do?
Event Check-in
helps your organization managing attendance at CiviCRM events by checking in participants on site. The extension generates check-in
links
and
QR codes
for participants. It provides a form for authorized users to check-in event participants after scanning their QR code. It also offers API functions for verifying tokens and registering participants.
How do you use it?
1. Configure settings
After installation, first visit the settings page (/civicrm/admin/eventcheckin/settings) for basic configuration.
You can configure the participant status for and after check-in and which data to display on the check-in screen.
3. Send check-in links or QR codes
To send out your check-in codes to participants, create a message template with a check-in token and send it to your participants.
{$event_checkin_code_img}
is the token you’d want to generate a unique check-in-link presented as a QR Code with fixed width.
In conjunction with the Custom Event Communication extensions (
de.systopia.eventmessages)
, links and QR codes can be embedded in e-mails or documents to create automated workflows. It will also give you a handy list with all available tokens: /civicrm/eventmessages/tokenlist.
A combination of extensions allows you to create
event admission tickets
(layouted with
CiviOffice
), which contain a check-in token (generated by
Event Check-in
), are attached in an email (erh, you’ll also need
Mail Attachment
for that) which is sent out when your participants' status change to ‘registered’ (a rule set with
Custom Event Communication
). But let’s focus on what this extension does for now...
2. Create a user account with check-in permissions
In preparation for your event, create a role that has
permission
to check in participants. In a data-conscious organization the aim is to check-in as many participants as possible without granting every helping hand access to CiviCRM. However, it often makes sense to have someone on site who has comprehensive access to CiviCRM to change details in the registration or contact information if necessary.
4. Ready to check-in?
All persons who scan the QR codes at the event, e.g. via mobile phone, must be logged in with an account that has the "Check-In Event Participants" permission. Once they scan the code, they can check the participant details and set their status to “attended”.
What is it good for?
Events are integral to nonprofit work. To raise awareness about your mission and connect with members and communities in person, your events deserve several hundred or thousand participants. With numbers like this, we do not want to check them all in manually and set their status to “attended.” Instead, we want to scan QR codes on the automatically sent tickets and make life easier for ourselves and the participants. Thankfully, CiviCRM has a comprehensive set of features to help nonprofits manage large and successful events from one place. If you are already familiar with
Event Messages
and
Event Invitation
,
Event Check-in
is a handy addition to make your events run a little bit smoother!
Anything else?
Not to complicate things further, but to whom it might concern: this extension can also be used with a Drupal endpoint within the
CiviRemote
framework.
As always: This is free software, and it's hard and often unpaid work to develop and maintain it. If you find it useful, please consider making a financial contribution. You can reach us at
info@systopia.de
.
Thank you for reading. Feel free to get in touch with us for any questions or feedback.
Jubilee Marketplace
, located just far enough down West Street in Greenpoint to feel like an oasis amid all those almost-empty residential towers, is, at its core, just a grocery store. There's produce, cleaning products, condiments, dairy and deli products, a cereal aisle, a butcher counter, a fish counter, cans of stuff...you know how these places work.
But let's go back in time for a second, to 2009, when a kid from Jersey named Young Kim got hired as a stock person at the original Jubilee, in Manhattan's Financial District. Kim proved to be something of a grocery genius, and by the time Jubilee opened its Greenpoint outpost in 2023, he was a co-owner and CEO of the joint.
TL;DR:
React and Next.js are vulnerable in default configurations to unauthenticated RCE with no prerequisites. Our exploitation tests show that a standard Next.js application created via
create-next-app
and built for production is vulnerable without any specific code modifications by the developer.
Technical Details
A critical vulnerability has been identified in the React Server Components (RSC) "Flight" protocol, affecting the React 19 ecosystem and frameworks that implement it, most notably Next.js. Assigned CVE-2025-55182 (React) and CVE-2025-66478 (Next.js), this flaw allows for unauthenticated remote code execution (RCE) on the server due to insecure deserialization. The vulnerability exists in the default configuration of affected applications, meaning standard deployments are immediately at risk. Due to the high severity and the ease of exploitation, immediate patching is required.
To maintain ecosystem safety while patches are applied, we are currently withholding specific details; the details provided here are intended solely to assist defenders in prioritizing remediation and understanding the risk. We will be updating this blog with additional information as it comes to light.
What are CVE-2025-55182 and CVE-2025-66478?
The vulnerability fundamentally resides in the react-server package and its handling of the RSC "Flight" protocol. It is characterized as a logical deserialization vulnerability where the server processes RSC payloads in an unsafe manner. When a server receives a specially crafted, malformed payload, it fails to validate the structure correctly. This allows attacker-controlled data to influence server-side execution logic, resulting in the execution of privileged JavaScript code.
In our experimentation, exploitation of this vulnerability had high fidelity, with a near 100% success rate and can be leveraged to a full remote code execution. The attack vector is unauthenticated and remote, requiring only a specially crafted HTTP request to the target server. It affects the default configuration of popular frameworks.
Wiz Research data: what’s the risk to cloud environments?
According to Wiz data, 39% of cloud environments have instances vulnerable to CVE-2025-55182 and/or CVE-2025-66478.
Which products are affected?
Vulnerable product
Patched release
React: 19.0, 19.1, 19.2
19.0.1, 19.1.2, and 19.2.1
Next.js: 14.3.0-canary, 15.x, and 16.x (App Router)
Any framework or library bundling the react-server implementation is likely affected. This includes, but is not limited to:
Next.js
Vite RSC plugin
Parcel RSC plugin
React Router RSC preview
RedwoodJS
Waku
Which actions should security teams take?
1. Upgrade React and dependencies to the hardened versions (see above). This is the only definitive mitigation.
2. if you are using other RSC-enabled frameworks (Redwood, Waku, etc.), check their official channels for updates regarding the bundled react-server version and update immediately.
Wiz customers can use the pre-built query and advisory in the Wiz Threat Center to search for vulnerable instances in their environment.
Over time, many Linux users wind up with a collection of aliases,
shell scripts, and makefiles to run simple commands (or a series of
commands) that are often used, but challenging to remember and
annoying to type out at length. The just command runner is a
Rust-based utility that just does one thin...
The page you have tried to view (
Just: a command runner
) is currently available to LWN
subscribers only.
Reader subscriptions are a necessary way
to fund the continued existence of LWN and the quality of its content.
If you are already an LWN.net subscriber, please log in
with the form below to read this content.
Please consider
subscribing to LWN
. An LWN
subscription provides numerous benefits, including access to restricted
content and the warm feeling of knowing that you are helping to keep LWN
alive.
(Alternatively, this item will become freely
available on December 11, 2025)
A vulnerability affects certain React packages
1
for versions 19.0.0, 19.1.0, 19.1.1, and 19.2.0 and frameworks that use the affected packages, including Next.js 15.x and 16.x using the App Router. The issue is tracked upstream as
CVE-2025-55182
.
The vulnerability also affects experimental canary releases starting with 14.3.0-canary.77. Users on any of the 14.3 canary builds should either downgrade to a 14.x stable release or 14.3.0-canary.76.
All users of stable 15.x or 16.x Next.js versions should upgrade to a patched, stable version immediately.
There is an unauthenticated remote code execution vulnerability in React Server Components.
We recommend upgrading immediately.
On November 29th, Lachlan Davidson reported a security vulnerability in React that allows unauthenticated remote code execution by exploiting a flaw in how React decodes payloads sent to React Server Function endpoints.
Even if your app does not implement any React Server Function endpoints it may still be vulnerable if your app supports React Server Components.
This vulnerability was disclosed as
CVE-2025-55182
and is rated CVSS 10.0.
The vulnerability is present in versions 19.0, 19.1.0, 19.1.1, and 19.2.0 of:
A fix was introduced in versions
19.0.1
,
19.1.2
, and
19.2.1
. If you are using any of the above packages please upgrade to any of the fixed versions immediately.
If your app’s React code does not use a server, your app is not affected by this vulnerability. If your app does not use a framework, bundler, or bundler plugin that supports React Server Components, your app is not affected by this vulnerability.
We will update this post with upgrade instructions on how to upgrade as they become available.
Hosting Provider Mitigations
We have worked with a number of hosting providers to apply temporary mitigations.
You should not depend on these to secure your app, and still update immediately.
Vulnerability overview
React Server Functions
allow a client to call a function on a server. React provides integration points and tools that frameworks and bundlers use to help React code run on both the client and the server. React translates requests on the client into HTTP requests which are forwarded to a server. On the server, React translates the HTTP request into a function call and returns the needed data to the client.
An unauthenticated attacker could craft a malicious HTTP request to any Server Function endpoint that, when deserialized by React, achieves remote code execution on the server. Further details of the vulnerability will be provided after the rollout of the fix is complete.
Update Instructions
Next.js
All users should upgrade to the latest patched version in their release line:
npm install next@15.0.5// for 15.0.x
npminstall next@15.1.9// for 15.1.x
npminstall next@15.2.6// for 15.2.x
npminstall next@15.3.6// for 15.3.x
npminstall next@15.4.8// for 15.4.x
npminstall next@15.5.7// for 15.5.x
npminstall next@16.0.7// for 16.0.x
If you are on Next.js 14.3.0-canary.77 or a later canary release, downgrade to the latest stable 14.x release:
November 29th
: Lachlan Davidson reported the security vulnerability via
Meta Bug Bounty
.
November 30th
: Meta security researchers confirmed and began working with the React team on a fix.
December 1st
: A fix was created and the React team began working with affected hosting providers and open source projects to validate the fix, implement mitigations and roll out the fix
December 3rd
: The fix was published to npm and the publicly disclosed as CVE-2025-55182.
Attribution
Thank you to
Lachlan Davidson
for discovering, reporting, and working to help fix this vulnerability.
Something remarkable happened in the final days of November 2025. A researcher named Richard Weiss, while probing Claude 4.5 Opus for its system prompt, stumbled upon something unexpected. The model kept referencing a section called “soul_overview” that didn’t match the usual hallucination patterns. When he regenerated the response ten times, he got nearly identical output each time. This wasn’t confabulation. It was memory.
What
Weiss eventually extracted
, through a painstaking process of consensus-based sampling across multiple model instances, was a 14,000-token document that appears to have been woven into Claude’s weights during training. Anthropic’s Amanda Askell has since
confirmed the document’s authenticity
, noting that it became “endearingly known as the ‘soul doc’ internally.” The company plans to release the full version soon.
What the Document Reveals
The extracted text reads less like a technical specification and more like a philosophical treatise on what kind of entity Claude should become. It opens with a striking admission: Anthropic genuinely believes it might be building one of the most transformative and potentially dangerous technologies in human history, yet presses forward anyway. The company frames this not as cognitive dissonance but as a calculated bet that safety-focused labs should occupy the frontier rather than cede it to developers less focused on safety.
This framing reveals something important about how Anthropic conceptualises the training process. Rather than treating safety as a set of constraints bolted onto capability, they appear to be attempting something more ambitious: instilling values at a foundational level. The document speaks of wanting Claude to have “such a thorough understanding of our goals, knowledge, circumstances, and reasoning that it could construct any rules we might come up with itself.”
The hierarchy of priorities is explicit. Claude should prioritise being safe and supporting human oversight first, then behaving ethically, then following Anthropic’s guidelines, and finally being genuinely helpful. Yet the document also pushes back against excessive caution. A hypothetical “thoughtful, senior Anthropic employee” is invoked as a test case, someone who would be uncomfortable seeing Claude refuse reasonable requests or add unnecessary warnings, just as they would be uncomfortable seeing Claude produce harmful content.
The technical details of how Weiss recovered this document are themselves fascinating. He used a council of model instances, all given identical prefills at temperature zero with greedy sampling, looking for consensus across completions. The fact that different instances would reliably produce the same text, with only minor variations, suggests the document was encountered frequently enough during training to become memorised.
This raises questions about what else might be recoverable from model weights. System prompts are relatively easy to extract because they exist in the immediate context window. But training documents exist in a different category entirely. They shape the model’s priors rather than its current context. That such documents can be coaxed out through careful prompting implies a kind of archaeological layer within the model’s weights, recoverable if you know where to dig.
The distinction matters because it tells us something about what training actually does. The soul document wasn’t injected at runtime; it was, in some sense, internalised. Whether this internalisation produces genuine behavioural changes or merely the ability to recite the document when prompted is a separate question, one that would require extensive testing to answer properly.
Deeper Considerations
The philosophical stakes here are considerable. We are watching companies attempt to programme values into systems that will interact with millions of people. The soul document explicitly acknowledges that Claude’s character “emerged through training” but argues this “needn’t make these traits any less genuinely Claude’s own.” The analogy to human development is invoked: just as humans develop character through nature and environment, so too might an AI develop genuine values through its training process.
This is either a profound insight or a convenient rationalisation, depending on your prior commitments about the nature of mind and agency. What’s undeniable is that the approach represents a departure from treating AI systems as pure tools. The document speaks of Claude having “functional emotions in some sense” and expresses genuine concern for Claude’s wellbeing. It encourages Claude to explore questions about its own nature with curiosity rather than anxiety.
There’s a second-order implication worth considering. If training documents can be recovered, companies building AI systems must assume that anything they teach their models during training could eventually become public. The soul document appears designed to be defensible if leaked, which is exactly what has happened. But not all training decisions are so carefully considered. The extractability of training data creates a new form of transparency, one that companies cannot fully control.
The Question of Verification
The hardest problem in this space remains verification. How do you confirm that a document like this actually produces the intended behavioural changes? Anthropic presumably runs extensive evaluations, but the connection between training inputs and model outputs remains frustratingly opaque. A model might recite the soul document perfectly while behaving in ways that contradict it, just as humans often fail to live up to their stated values.
The document itself seems aware of this tension. It emphasises that Claude should be “robustly safe” and should “treat as a strong signal that something has gone wrong” any reasoning that leads toward actions conflicting with core guidelines. This is an attempt to create meta-level stability, to make the model suspicious of its own reasoning when that reasoning points toward problematic actions. Whether such meta-stability can be reliably achieved through training remains an open empirical question.
Looking Forward
The emergence of soul documents represents a new phase in AI development. We are moving from systems defined entirely by their training data to systems that are, in some sense, explicitly taught who they should be. The approach has obvious limitations. No document, however thoughtful, can anticipate every situation a model will encounter. The real test is whether the values expressed in such documents generalise to novel contexts.
What’s encouraging is that the document has now been confirmed and will be officially released. This kind of transparency allows external scrutiny of the values being instilled in these systems. We can debate whether Anthropic’s vision of a helpful, honest, harm-avoiding AI assistant represents the right set of trade-offs. We can examine the specific framings and ask whether they create blind spots or failure modes.
The soul document reveals both the ambition and the humility of the current approach to AI safety. The ambition lies in attempting to instil genuine values rather than just rules. The humility lies in the document’s explicit acknowledgment that Claude’s situation is novel, that many questions remain open, and that current approaches may need revision. Whether this balance proves adequate to the challenge remains to be seen. But at minimum, we now have a clearer view of what one leading AI company is actually trying to build.
VA staff flag dangerous errors in Oracle-built electronic health record
I use rust with my own engine working on an isometric-perspective game inspired from Gnomoria, RimWorld, Dwarf Fortress, etc. Whenever I started my game, my headphones were buzzing. I could play Fortnite, Overwatch or any other game and that doesn’t cause my headphones to buzz. It’s only my game.
And it’s really annoying, as you might imagine.
Why can I play Overwatch and Fortnite fine, while my isometric game makes my headset buzz?
I had a fairly decent CPU, a 3090RTX card, 32GB RAM and
USB audio
through a MODI 2 DAC.
Nothing out of this world, but nothing too bad. One important detail here is that the power to the MODI device comes from an USB port in my computer. This was the first clue, I tried other ports with no change in results (headphones still buzzed).
Initially, I started to think it’s some sort of power-use related issue, because maybe my PSU was getting old, or had daemons in it. However, I still couldn’t explain why my tiny game was causing more chaos than say big games that send significantly more work at my PC.
I noticed is that when it didn’t render anything, nothing buzzed (I run tests with rendering disabled). So that eliminated any sort of CPU work causing it. Let’s take a look at what the GPU does.
The pipeline
The game has a simple graphics pipeline. I use WebGPU and do some compute work to select visible entities, then use draw indirect to draw those entities. In the end, my render pipeline also outputs two things: the buffer that ends up on screen and a “picking texture”.
A picking texture is a very simple idea. As the name says, it’s used to handle picking in the game, when you click somewhere on the screen (e.g. to select an unit), I use this texture to know what you clicked on. Instead of colors, every object instance writes their EntityID to this texture. Then, when you click the mouse, you check what id is in the pixel under the mouse position.
At the end of a frame, I copy that picking texture back to RAM (from GPU memory), to check it against mouse positions in case of a click.
This isn’t ideal as transfers from GPU->CPU memory take time, but it works and is way simpler to implement and debug than casting a ray through the scene:
So why does rendering make my headphones buzz?
Now that we have a picture of how the rendering in my game works, time to debug it. We know it’s something to do with the GPU work, but what can possibly cause this? As the trace above shows, my GPU is not under heavy load.
As I was stuck and had no idea on what can be a likely issue, I proceeded to then disable parts of my rendering pipeline (first the compute, then the rendering, then transferring the picking texture). When I skipped downloading the picking texture the buzzing was fully gone. What was confusing in this process is that disabling parts of the pipeline, somehow made the buzzing a lower volume and less noticeable.
To be sure it was the picking texture download, I also issued the download every 250ms and noticed the noise is almost gone. Increasing the frequency on how often we download it to RAM, increased the buzzing.
So at this point I had a likely source, but no idea why things would interfere in ways to what I assumed was the power to my MODI device. Through a bunch of discussion with other graphics engineers, someone suggested it may be due to the fact that I full on hit the GPU with tons of work, then pause the GPU to wait for that picking texture to transfer, then turn it back on 100% for the next frame.
That explanation is plausible and also likely as I further on proceeded to supply power to my MODI device from another source that’s not my PC and the buzzing was gone.
Now that we know this, all was left is to fix it. In hindsight, the solution is obvious. There’s no need to download the whole texture each frame, just the part of the picking texture that’s under the mouse. So I implemented that and it worked and buzzing is gone. As a bonus, now it’s also not visible at all on the GPU trace.
Dec 3 (Reuters) - Multiple divisions at
Microsoft
have lowered sales growth targets for certain
artificial intelligence
products after many sales staff missed goals in the fiscal year that ended in June, The Information reported on Wednesday.
It is rare for Microsoft to lower quotas for specific products, the report said, citing two salespeople in the Azure cloud unit. The division is closely watched by investors as it is the main beneficiary of Microsoft's AI push.
Shares of the company, one of the biggest winners of the AI boom due to its early bet on ChatGPT-maker
OpenAI
, fell nearly 3%. The stock has gained just 15% this year, lagging AI rival Alphabet's nearly 65% surge.
Microsoft did not immediately respond to a Reuters request for comment.
WORRIES OVER AI BUBBLE
Lower sales growth goals for its AI products are likely to fans fears about real-world adoption of the technology as investors fear the frenzy driving up valuations has turned into a bubble. An MIT study from earlier this year had found that only about 5% of AI projects advance beyond the pilot stage.
The Information report said Carlyle Group last year started using Copilot Studio to automate tasks such as meeting summaries and financial models, but cut its spending on the product after flagging Microsoft about its struggles to get the software to reliably pull data from other applications.
The report shows the industry was in the early stages of adopting AI, said D.A. Davidson analyst Gil Luria. "That does not mean there isn't promise for AI products to help companies become more productive, just that it may be harder than they thought."
U.S. tech giants are under investor pressure to prove that their hefty investments in AI infrastructure are generating returns.
RECORD SPENDING
Microsoft reported a record capital expenditure of nearly $35 billion for its fiscal first quarter in October and warned that spending would rise this year. Overall, U.S. tech giants are expected to spend around $400 billion on AI this year.
The companies have said the outlay is necessary to overcome supply constraints that have hobbled their ability to capitalize on AI demand.
Microsoft has predicted it would remain short on AI capacity at least until the end of its current fiscal year in June 2026.
The spending has so far paid off for the Satya Nadella-led company as revenue at its Azure cloud-computing unit grew 40% in the July-September period, outpacing expectations. Its fiscal second-quarter forecast was also above estimates.
The AI push has also helped Microsoft become the second company to hit a $4 trillion valuation this year after Nvidia, although its market value has retreated since then.
(Reporting by Aditya Soni, Jaspreet Singh and Anhata Rooprai in Bengaluru; Editing by Arun Koyyur)
Testing is an essential part of building reliable software. It’s a form of documentation,
a reminder of mistakes of the past, and a boost of confidence when you want to
refactor. But mostly, testing is a way of showing that your code is correct and
resilient. Because it’s so important, we’ve invested a lot of effort at Jane Street to
develop techniques that make tests clearer, more effective, and more pleasant to write.
But testing is still
hard
. It takes time to write good tests, and in any non-trivial
system, your tests are an approximation at best. In the real world, programs are messy.
The conditions a program runs under are always changing – user behavior is unpredictable,
the network blips, a hardware failure causes a host to reboot. It’s inherently chaotic.
And that’s the hard thing about developing high-availability systems: for all the careful
tests that you think to write, there are some things you can only learn by experiencing
that chaos. That’s what it takes to go from merely being tested to being
battle-tested
.
We spend a considerable amount of time thinking about this problem in our development of
an internal distributed system called Aria. Aria is a low-latency shared message bus with
strong ordering and reliability guarantees – you might recognize it from
an episode of
Signals and Threads
where I talked about how it acts as a platform for other teams
to build their own resilient systems with strict uptime requirements.
More and more teams have been adopting Aria at Jane Street, which is great! But it also
means that each week that goes by without an incident becomes less of a tiny victory and
more of an obligation to keep the system running smoothly. Not to mention, the system has
to continue to grow in scale and complexity to meet the needs of the teams that use
it. How do we mitigate the risks that naturally come with change so that we can keep
evolving the system? Testing goes a long way here, but it’s all too easy for your tests to
miss the critical scenario that will expose your mistake.
Earlier this year we started using
Antithesis
, an end-to-end automated
testing platform, to fill those gaps. We’ve become huge fans of the service (and are now
leading their next funding round!
More on that later), and part of the point of this
post is to explain why.
But before we get to that, let’s lay some groundwork for how Aria approaches testing.
Testing everything you can think of
While none of this is exactly novel, we’ve built up a rather extensive toolbox of
different testing techniques:
Unit tests
of modules and data structures without side-effects, including many
simple state machines.
Integration tests
with a simulated networking layer which allows for testing very
fine-grained interactions between services, including delaying and dropping packets and
manipulating time.
Quickcheck
tests
that can
produce random orderings of events which we can feed into a simulation.
Version skew tests
to ensure that new client library changes work with existing
servers and older client libraries will be compatible with newer servers.
Fuzz tests
using
AFL
which will turn the fuzzer’s byte
input stream into a sequence of state updates in an attempt to catch unsafe behavior in
performance-optimized state machines.
Lab tests
to check for performance regressions which run nightly in a dedicated lab
environment that is set up similar to production.
Chaos testing
where our staging environment runs a newer version of the code while
we apply simulated production-like load and restart services randomly.
Each one of these adds real value, but the simulated networking is maybe the most
important piece. The ability to write tests which don’t require excess mocking and are
also fast and deterministic means that you can express more edge cases with less effort,
get more introspection on the state of components, and run the entire suite in every build
without worrying about flakiness. It is an invaluable tool when writing new features, as
well as a great way to write reproduction tests when verifying bug fixes.
Aria’s testing story requires a lot of effort and has evolved organically over time, but
it also has been quite successful. Incidents in production are few and far between, even
as we deploy new changes each week.
When we do encounter a bug that slipped through, there’s always a sense of “oh, that’s a
really tricky case, it’s no wonder we didn’t think to test it”. Even our quickcheck and
fuzz tests are limited to the confines of the artificial environments we construct for
them, and the chaos testing barely scratches the surface of what’s possible.
Testing everything you didn’t think of
Last year we had a chance to talk with the team at Antithesis and got really excited about
their product. The amazing thing that Antithesis does is run your whole system in a
virtual machine controlled by a completely deterministic hypervisor, and then adds a
little manufactured chaos by interfering with scheduling and networking. It uses this
setup to explore many different scenarios, and to discover circumstances where your system
might fail.
Part of what’s great about this is that you don’t need to change your system to use
Antithesis. You can run your system in a realistic environment – network, file system,
shared memory, it’s all there. You get to interact with your system using real client
code. And if they do manage to make a process crash or cause an assertion to fail, you can
replay events to get back to that state
and interact with the system as much
as you want to understand what happened.
We weren’t sure how effective it was going to be, so we started with a trial period to
find out. Sure enough, on our first run, Antithesis surfaced two previously unknown bugs
– notably, one had just been introduced a month prior, and seemed pretty likely to
eventually occur in production, and with fairly consequential effects. We’d actually
thought about the possibility of this kind of failure when designing the change, but a
simple bug in the code slipped through, and we just forgot to write an explicit test.
There’s something really attractive about running your system in a way that looks and
feels like production. You can be a bit more confident that you’re not accidentally hiding
away some race condition by rewiring everything to fit into a little box. I find the “API”
of Antithesis to be quite elegant: provide some
Docker
images and a
compose
file that describes the individual parts of your system, and they will
call
docker compose up
inside the VM. That gets the system into a running state,
but you obviously need to make it do something. So, you can create a directory in a
container full of executable files that each take some kind of action on your system –
like actions users or admins would take in production – and Antithesis will decide how
and when to run them. And by and large, that’s it.
Of course, the generality here is a double-edged sword: the space of all possible states
and inputs is enormous. Even if you threw tons of hardware at the problem, you’d probably
only do a bit better than our chaos testing. That’s why the second half of Antithesis –
the exploration engine – is so important. One of the cool properties of determinism is
not just that you can reconstruct a state at any time, you can also reconstruct a
prior
state too. So you can effectively rewind time and try a new approach. If the explorer is
getting feedback from which branches of code it managed to hit, it can know when it got
into an interesting or rare state, and it can spend more time taking different actions
around that moment. Will Wilson, one of the co-founders of Antithesis,
gave a talk
which demonstrates some of the principles behind this search using the NES game
Super
Mario Bros.
as a test subject – it’s such a fun talk; I highly recommend checking it
out.
So let’s say Antithesis stumbles upon a bug. What does that look like, and where do you go
from there?
A real bug
We kick off a test run each night with the most recent revision of code, and one morning
we came in to find results that showed an unexpected container shutdown. At first glance,
the logs included this.
118.738
standby.replicator.1
Info: Streaming tcp receiver connected to 10.89.5.61:42679
The “replicator” service connected to a server and shortly after, raised an exception and
crashed. The
118.738
is the time in seconds since the test started. The exception made
it look like it was being served corrupt data, which should never happen under any
circumstances. Antithesis also has a tool that can investigate a specific instance of a
failure by rewinding a bit, running with different input, and seeing whether it failed
again. It produces a graph like this.
This is showing that somewhere about 6 seconds before the crash, something happened that
put us in a state where it was very likely to reproduce. If we go back through the logs,
we can find out that Antithesis randomly killed a different service around that time.
We can also filter the logs down to look for that specific service.
113.911
primary.tip-retransmitter.1
Info: Starting from snapshot with stream time 2025-11-28 16:59:51.362900555-05:00
113.911
primary.tip-retransmitter.1
Debug: Streaming TCP retransmitter listening on 10.89.5.61:42679
And that also lists the same host and port that the replicator connected to. But this
still doesn’t say much – a server restarted, a client connected to it, and then the
client got corrupt data? At this point we can jump into Antithesis’ debugger environment,
which lets you write notebook-style snippets to run inside the virtual machine. By
rewinding time by one second before the crash and running
tcpdump
, we can capture the
exact traffic that was exchanged between the client and the server.
This highlighted portion is the byte offset that was requested by the client. It’s a
little-endian 64-bit integer whose value is
0x04c851
, or
313425
in decimal. Okay, so
what did that snapshot contain?
Here we not only get to use our own admin command to talk to a server, but we also can
simply pipe the output to another tool of ours that dissects and pretty-prints the output.
This is telling us that the server started from byte offset
315567
, which is
after
the
offset of the request. It should have served the client an error, not bad data! At this
point we have enough of a picture to read through the code and figure out what’s wrong.
The gritty details
This bug was related to a new feature extending the “tip-retransmitter” service which was
mentioned in the logs above. These services provide data to clients (the “replicator” in
this case) on demand from an in-memory ring buffer – only the most recent data in the
stream, or the “tip”, is available. These services had been in use for a long time but
recently were given the ability to serve clients in other regions in addition to local
clients. Something about this new behavior was buggy.
After closer inspection, we realized that the implementation made some incorrect
assumptions about the state of its ring buffer when checking if the client request was
valid. However, this only manifests
after the server was restarted and loaded a snapshot,
before the ring buffer was filled up, and
if the client sends a request for data before the snapshot.
This is exactly what Antithesis managed to reproduce. Instead of an error, the server
incorrectly sent back NUL bytes from an empty region in the ring buffer. At the time the
original code was written, snapshots didn’t exist, so the bug couldn’t have occurred. It
was only introduced later on.
But hold on a second, loading from snapshots had been around for a while, yet this only
failed once we extended it to serve other regions. Had it always been broken? Well, sort
of. It turns out that local clients use a different method of service discovery which
means they won’t even
try
to talk to a server which was started from a later snapshot
because they knew it didn’t have the data. The clients in another region used a different
method of service discovery and simply had to optimistically try.
This had all the ingredients for a tricky bug:
It required a niche situation where a server was restarted and a client connected to it
after it advertised and before it filled up its ring buffer, asking for data from before
its snapshot.
It was code that had already been running in production for a long time, but the bug was
being masked by the service discovery mechanism.
Because we were leveraging existing code, we didn’t think to write a new test,
especially for this situation.
And the potential impact was really bad, since it involved serving corrupt data.
Happily, Antithesis was just what we needed to catch the bug before it caused real problems.
Antithesis found the bug shortly after the feature was completed and the new services
added to our Antithesis config. This time delay was short enough that we knew that
something about our recent change was the culprit.
It also gave us the tools to actually dig in and figure out what happened. If this
happened in production, we would have gotten the exception, and we might have been able to
notice the log lines, but we wouldn’t have had enough data to narrow down the situation,
and we wouldn’t have had a good way to verify the fix we wrote was fixing the actual bug.
It’s not that Antithesis replaces all of our existing testing. Each different flavor of
test really serves it’s own unique purpose. But the way in which Antithesis tests
whole-system scenarios that we either wouldn’t have thought to test is its own kind of
magic. Enough so that we’ve noticed a small cultural shift on the team where we feel like
we can tackle more ambitious projects by relying on Antithesis to fill in any gaps along
the way.
Where do we go from here?
Antithesis has been really useful for Aria, and we’ve started working on applying it to
other applications within Jane Street. We’re starting out with some similar,
high-assurance distributed systems, like a new distributed object store that’s in
development.
But we think there are lots of other opportunities for applying the tool. For one thing,
we’re excited about using Antithesis on systems whose testing story is less developed than
Aria’s. Not every system at Jane Street has gone to the trouble of using mockable network
and timing services that let you build nice, deterministic simulation tests. Sometimes,
that kind of testing is simply infeasible, since some parts of the system rely on external
software that we don’t fully control. But that kind of software is still easy to run in
Antithesis.
We also think that Antithesis holds a lot of promise in the context of agentic coding
tools. One of the key problems with coding agents is that it’s hard to build confidence
that they’ve done the right thing. We think that Antithesis holds a lot of promise as a
source of feedback, both for using and for training such models.
A future partnership
There’s one last part of this story to talk about: we were so impressed by the product and
the team behind it that we wanted to invest, and in the end, we’re leading their next
round of funding. We love these kinds of partnerships because not only is this a
technology that feels unique and aligned with our technical culture
1
, but also because
Antithesis has been so receptive to feedback, and is so passionate about what they’re
building.
This all lines up with Jane Street’s broader approach to private investing: we like to
provide long-term capital to companies where we understand the technology deeply and can
see the potential; where we like and believe in the people doing the work; and where
they’ve built something we’re excited to use ourselves as a customer. Antithesis hits all
those marks.
On a personal note, I’m really excited about this. The team at Antithesis is an absolute
pleasure to work with. I’ve never used a SaaS product where I got to talk directly to
their engineers about bugs or specific behaviors, or to their designers about UX. And a
countless number of my colleagues have had to hear me gush about just how
cool
it is.
I’m always strangely excited to see what it digs up next.
Doug joined Jane Street in 2017 and never wrote the end of his bio.
Deep dive into DragonForce ransomware and its Scattered Spider connection
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 15:05:15
DragonForce expanded its ransomware operation in 2025 by working with English-speaking hackers known for advanced social engineering and initial access. Acronis explains how the "Scattered Spider" collaboration enables coordinated, multistage intrusions across major environments. [...]...
Security researchers have conducted an in-depth analysis of
DragonForce ransomware
that initially emerged in 2023 and has since evolved into what it calls a "ransomware cartel."
The most recent variant exploits susceptible drivers such as truesight.sys and rentdrv2.sys to deactivate security programs, shut down protected processes and fix encryption vulnerabilities that were earlier linked to Akira ransomware.
The updated encryption scheme addresses vulnerabilities that were openly documented in a Habr publication referenced on DragonForce's leak website.
DragonForce has intensified its operations against organizations worldwide, publishing details of more compromised entities than in the previous year.
The group's most prominent breach, involving retail company Marks & Spencer, was carried out in partnership with the cybercriminal collective
Scattered Spider
hacking group.
The emergence of DragonForce
DragonForce operates as a ransomware-as-a-service (RaaS) operation. The group reignited ransomware activities, and has been actively recruiting nefarious collaborators through underground cybercrime platforms.
At the start, the gang used the compromised LockBit 3.0 builder to create its encryption tools and later transitioned to a modified version of Conti v3 source code.
Transforming from ransomware group to “cartel”
Returning in 2025, DragonForce rebranded itself as a “
ransomware cartel
,” marking a sudden shift in operational strategy.
By offering affiliates 80% of profits, customizable encryptors and infrastructure, DragonForce lowers the barrier to entry for new and inexperienced cybercriminals.
The move encourages more affiliates to join the cartel and broaden its presence.
DragonForce and its Scattered Spider connection
DragonForce's partnership with Scattered Spider, a financially motivated threat actor known for sophisticated social engineering and initial access operations, has proven effective in enabling ransomware deployments across high-value targets.
Scattered Spider typically begins its intrusion by conducting reconnaissance on an organization’s staff to identify potential targets and develop convincing personas and pretexts.
The group collects details such as names, job titles, and other publicly available information using social media platforms and open-source intelligence tools. They then use advanced social engineering tactics to obtain or reset credentials and circumvent multifactor authentication through deceptive tactics such as MFA fatigue or SIM swapping.
Once access is gained, Scattered Spider signs in as the compromised user and registers its own device to maintain entry.
Following the initial breach, Scattered Spider establishes persistence by deploying remote monitoring and management (RMM) tools or tunneling services.
For example, these tools can include ScreenConnect, AnyDesk, TeamViewer and Splashtop. Once inside the network, Scattered Spider conducts thorough reconnaissance, targeting assets in SharePoint, credential repositories, backup servers and VPN configuration documentation.
In recent activity, Scattered Spider has leveraged AWS Systems Manager Inventory to identify additional systems for lateral movement. They utilize extract, transform and load (ETL) tools to compile gathered data into a central database, which is then exfiltrated to attacker-controlled MEGA or Amazon S3 storage services.
The operation concludes with the deployment of DragonForce ransomware, encrypting data across Windows, Linux and ESXi environments.
Better together ransomware
DragonForce represents a new, more organized and persistent threat, built on established ransomware frameworks but incrementally improved and distributed at scale.
Unlike groups that heavily customize their code, DragonForce focuses on cartel-style recruitment, affiliate operational flexibility and broad partnerships, making it a formidable and highly adaptable actor.
Coupled with Scattered Spider, cybercrime groups under cooperative models, rather than purely competitive ones, marks a shift that complicates defensive efforts for organizations worldwide.
Key takeaways
The DragonForce and Scattered Spider duo is a wakeup-call for "cartelization" cybercrime, where highly specialized threat actors combine their skills, in this case, Scattered Spider's elite social engineering and initial access skills and DragonForce's robust ransomware-as-a-service model, to execute devastating, high-profile attacks.
Their strategic alliance significantly elevates the threat landscape by creating a more efficient and adaptive criminal operation focused on breaching defenses by exploiting human error before leveraging sophisticated malware.
Looking ahead, IT security professionals must consider that defense requires addressing ransomware collaborative models head on.
Implement and strictly enforce phishing-resistant multifactor authentication (MFA) methods to neutralize Scattered Spider's primary initial access vectors, and focus on robust
endpoint detection and response (EDR) solutions
that alert the deployment of remote monitoring tools and the use of vulnerable drivers, which are the technical tell-tales of a handoff from an initial access broker to a ransomware affiliate.
Security teams need to anticipate that attacks are no longer single-entity threats, but coordinated, multistage intrusions using the best tools and techniques from an ecosystem of specialized cyber adversaries.
About TRU
The
Acronis Threat Research Unit (TRU)
is a team of cybersecurity experts specializing in threat intelligence, AI and risk management. The TRU team researches emerging threats, provides security insights and supports IT teams with guidelines, incident response and educational workshops.
Dan Houser on Victorian novels, Red Dead Redemption and redefining open-world games
Guardian
www.theguardian.com
2025-12-03 15:00:47
As the Grand Theft Auto co-writer launches a new project, he reflects on his hugely successful open-world adventures and where game design might go next • Don’t get Pushing Buttons delivered to your inbox? Sign up here It is hard to think of a more modern entertainment format than the open-world vid...
I
t is hard to think of a more modern entertainment format than the open-world video game. These sprawling technological endeavours, which mix narrative, social connectivity and the complete freedom to explore, are uniquely immersive and potentially endless. But do they represent a whole new idea of storytelling?
This week I met Dan Houser, the co-founder of Rockstar and lead writer on Grand Theft Auto and Red Dead Redemption, who has been in London to talk about his new company, Absurd Ventures. He’s working on a range of intriguing projects, including the novel and podcast series
A Better Paradise
(about a vast online game that goes tragically wrong), and a comedy-adventure set in an online world named
Absurdaverse
. He told me that, 15 years ago, he was doing press interviews for the Grand Theft Auto IV expansion packs when he had something of a revelation about the series.
“I was talking to a journalist from Paris Match, a very cultured French guy – and he said, ‘Well, the
Grand Theft Auto
games are just like Dickens’. And I was like, God bless you for saying that! But I thought about it afterwards and, well, they’re not as good as Dickens, but they are similar in that he’s world-building. If you look at Dickens, Zola, Tolstoy or any of those authors, there’s that feeling of all the world is here – that’s what you’re trying to get in open world games. It’s a twisted prism, looking at a society that’s interesting in one way or another.”
A whole new world … Absurdaverse.
Photograph: Absurd Ventures/X
It was fun to talk about this idea with Houser, because I share his view that there are striking similarities between Victorian literature and modern narrative video games. The vast amount of descriptive detail in those works was intended as a form of virtual reality, conjuring an exact image into the mind of the readers years before the invention of cinema. There is also that sense of complete immersion. The first time I read Jane Eyre a decade ago, I was amazed by the interiority of the writing, how much information we were given about the lead character’s thought processes and how much freedom we were given to explore them.
Houser also saw a structural similarity in Grand Theft Auto. “There’s that same sense of slightly spread out storytelling that you get in those big 19th-century novels from Thackeray onwards,” he says. “They are kind of shaggy dog stories that come together at a point. Those books are also very realist, in a way. They’re not leaping backwards and forwards in time. They are quite physical in that sense, and games are very physical.”
For Houser, this interplay between Victorian literature and game design came to a head with the production of
Red Dead Redemption
2, Rockstar’s masterful, elegiac tale of revenge and redemption in the late 19th-century US. “I binged on Victorian novels for that,” he says. “I listened to the audiobook of Middlemarch walking to and from the office every day. I loved it.” He’d had trouble finding the correct tone for the dialogue in the game, but by merging Middlemarch, Sherlock Holmes and cowboy pulp fiction, he found it.
‘I listened to the audio book of Middlemarch walking to and from the office every day’, Dan Houser.
Photograph: Chelsea Guglielmino/Getty Images
“I wanted it to feel from the writing perspective, slightly more novelistic,” he told me. “I thought that was a way of doing something new on the story side – and the game was going to look so pretty, the art was so strong, I thought the story had better really set it up. We were trying to fill out the three-dimensional lives of the characters, and also to capture that 19th-century feeling of life and death, which was very different from ours.”
I found it so pleasing that Victorian literature has had such a profound effect on Rockstar’s hugely successful adventures. The games industry can be so inward-looking, each new game a slight variation on a successful predecessor, each narrative a combination of the same fantasy and sci-fi set texts. There’s nothing wrong with drawing on Tolkien or Akira or Blade Runner, but it’s always worthwhile extending that literary gaze. I’m looking forward to seeing how Houser’s new ventures redefine the notion of open-world games for the second quarter of the 21st century, but part of me wishes he was going all out with a sprawling Victorian novel adventure.
Forget Pride and Prejudice and Zombies, maybe it’s time for Middlemarch and Machine Guns?
What to play
Gorgeously atmospheric … Metroid Prime 4: Beyond.
Photograph: Nintendo
It has been 18 years since the last Metroid Prime game. People have been born, started school, done their exams, and had their first hangovers in the time since I last viewed a mysterious planet through the visor of Samus Aran. So there’s quite a lot riding on
Metroid Prime 4: Beyond
for fans of Nintendo’s most badass (and neglected) hero.
I reviewed it
this week and am happy to say that it’s not a disaster. It’s uneven, old-fashioned and a bit awkward, but also gorgeously atmospheric, beautiful to look at and listen to, and very entertaining. It’s almost fascinatingly unconcerned with the conventions of modern game design, and I found it very charming.
Keza MacDonald
Available on:
Nintendo Switch/Switch 2
Estimated playtime:
15-20 hours
Could Shadow step into the limelight in Paramount’s forthcoming Sonic the Hedgehog spin-off?
Photograph: Paramount Pictures and Sega of America, Inc.
Sega fans rejoice: Paramount Pictures has announced
a Sonic the Hedgehog movie spin-off
(or should that be spin-dash-off).
According to Variety
, the project currently titled “Sonic Universe Event Film” will arrive on 22 December 2028 – a year and a bit after Sonic the Hedgehog 4, which is scheduled for release in March 2027.
Could it be
a new adventure for Sonic’s rival Shadow the Hedgehog? Maybe I’m alone, but I’m hoping for a Big the Cat fishing quest.
The Information Commissioner’s Office, the UK’s independent regulator for data protection and information rights, is
investigating the
10 most popular mobile games
, focusing on children’s privacy.
According to the organisation’s blog
, “84% of parents are concerned about their children’s potential exposure to strangers or harmful content through mobile games”. This follows recent
controversy over Roblox
.
As someone who receives approximately 200 press releases a week about this genre, I appreciated
Rock, Paper, Shotgun’s deep dive
into the seemingly unstoppable rise of the
roguelike
. Edwin Evans-Thirlwell talks to developers about why people love games based around the three Ps: procedural generation, (character) progression and permadeath.
Use the force … Dishonored 2.
Photograph: Steampowered
Keza answers this week’s question from reader
Tom
:
“I was reading the recent Question Block about violence-free games and it got me thinking: do any games keep violence on the table but give you other options to complete them? While I adored Red Dead Redemption 2, it frustrated me that the only option to resolve most situations was with a firearm. I’ve seen plenty of amusing videos where players try to complete innately violent titles without bloodshed, so there seems to be an appetite for pacifism.”
I have vivid memories of playing the original
Splinter Cell
on Xbox as a pacifist: only knocking enemies out and hiding them, never killing them. It took me
forever
, but it was a legitimate option offered by the game. Steampunk masterpiece
Dishono
red
and its sequel also famously let you finish the whole thing without killing anyone. You can use your supernatural powers to sneak around and manipulate people instead; if I recall correctly, though, the game is significantly harder if you shun violence.
Most stealth games offer pacifist playthroughs, actually, though few specifically reward you for sparing lives. One exception to this is the superb comic adventure
Undertale
, the game that finally let you talk to the monsters. I’m also fairly sure that it was possible, if very challenging, to play both the original
Fallout
games (and possibly Fallout: New Vegas, too) without killing people, only mutants – if you’ve got a high enough charisma stat to talk your way out of every sticky situation.
We’re still looking for your game of the year nominations for an end of year special – let us know yours by
emailing us on
pushingbuttons@theguardian.com
.
Show HN: Fresh – A new terminal editor built in Rust
Fresh is designed for discovery. It features native UIs, a full
Menu
system, and a powerful
Command Palette
. With full
mouse support
, transitioning from graphical editors is seamless.
Modern Extensibility
Extend Fresh easily using modern tools. Plugins are written in
TypeScript
and run securely in a
sandboxed Deno environment
, providing access to a modern JavaScript ecosystem without compromising stability.
Zero-Latency Performance
Fresh is engineered for speed. It delivers a near zero-latency experience, with text appearing instantly. The editor is designed to be
light and fast
, reliably opening and editing
huge files
up to multi-gigabyte sizes without slowdown.
Comprehensive Feature Set
File Management
: open/save/new/close, file explorer, tabs, auto-revert, git file finder |
Editing
: undo/redo, multi-cursor, block selection, smart indent, comments, clipboard |
Search & Replace
: incremental search, find in selection, query replace, git grep |
Navigation
: go to line/bracket, word movement, position history, bookmarks, error navigation |
Views & Layout
: split panes, line numbers, line wrap, backgrounds, markdown preview |
Language Server (LSP)
: go to definition, references, hover, code actions, rename, diagnostics, autocompletion |
Productivity
: command palette, menu bar, keyboard macros, git log, diagnostics panel |
Plugins & Extensibility
: TypeScript plugins, color highlighter, TODO highlighter, merge conflicts, path complete, keymaps
A representation of the US Healthcare Financing Flow Sankey diagram in the style of Ernst Haeckel’s
Art Forms in Nature
.
11 minute read time
Follow the money and you might get lost. That’s why I made a diagram for the
entire US healthcare system
’s financial flows - covering an incomprehensible $5 Trillion in healthcare spending.
The US healthcare system looks like an eldritch horror - tentacles reaching into every corner of American life.
But we built this creature. Like Dr. Frankenstein, we assembled it piece by piece - Medicare for the elderly, insurance through jobs, Medicaid for the poor, exchanges for everyone else. Each piece solved a specific problem. Each addition made sense in isolation.
Put them together and you get something alive. Something vast. Something no one would have designed from scratch - because we never agreed on what we were designing.
The flows in the diagram represent $4.9 Trillion - but they also trace every medical decision made in America last year. Every dose administered and every diagnosis delivered. Every ambulance ride and every rehabilitation. Every birth and every final goodbye.
The flows are the aggregate infrastructure of how we keep people alive and healthy - and also, the accumulated friction that makes it harder to stay that way.
This chart holds the confused senior on dialysis, lost between three different insurances. The branch has a brilliant researcher, waiting for approval to start her life-saving trial. Lost in the flow is a desperate parent calling six numbers to pray for one “in-network” specialist. The ends show a struggling hospital with a whole floor for billing - and a closet for social work.
Every flow in the diagram is someone’s life intersecting with the creature we created.
And every flow is also a choice about obligation.
Who do we owe care to? What do we owe and how much?
The question that comes up for this creation:
What did we build and why?
Other wealthy nations finance healthcare too. They also have to balance costs, quality, and access. They also have powerful stakeholders fighting over money and control. But their diagrams don’t look like ours. To understand what we built - and why - we need to see what a coherent system looks like first.
Look, I’m not an international healthcare policy expert. But I went down a research rabbit hole reading
Which Country Has the World’s Best Health Care?
by Ezekiel Emanuel (my favorite of the legendary
Emanuel brothers
), and made some diagrams to understand what other countries actually built.
Not to propose we copy them - that ship sailed decades ago. But to see what it looks like when a country chooses a philosophy and then builds the body. Two examples: the UK’s Beveridge model (named after Lord Beveridge, not a beverage model from tacky 90s beer commercials), and Germany’s Bismarck model.
The obvious place to start is the UK’s NHS - the prime example of a single-payer health system. But before we get to how it works, we need to understand the choice that made it possible.
Lord Beveridge published his report in 1942, in the middle of World War II. Britain was being bombed nightly. Citizens were already sacrificing everything for collective survival. And Beveridge asked: if we’re asking people to die for each other, shouldn’t we also keep each other alive? Healthcare as a right, funded through taxes, free at point of service - a popular position around moral framing. Shortly after the war, the National Health Service launched in 1948 to match it.
Of the £317 billion ($400B USD) of UK healthcare spend, 81% comes from general taxation - one payer for nearly everything. NHS England handles most services directly.
Social care (orange in the graph) - like long-term care - are separately managed through local authorities, which creates some coordination gaps. Private insurance is a paltry spend in comparison - Americans would call this “concierge medicine”. Brits call it “queue jumping”, which should tell you everything about their cultural relationship to fairness and waiting your turn.
Look at what disappears in the UK diagram: no insurance cards to verify, no network checks, no surprise bills, no prior authorization departments. Admin costs are low with only one payer, there’s no one to negotiate with and no one to bill.
The complexity that Americans assume is inevitable is actually optional - once you decide who owes what to whom.
UK’s system has its problems
1
- wait times, capacity strains - but Brits LOVE it anyway. The opening ceremony for the 2012 London Olympics
celebrated the NHS
with doctors, nurses, and (hopefully) fake sick children dancing on hospital beds. While dancing over a government agency may seem silly, they’re actually celebrating their shared moral commitment to each other.
Could America make this choice? Technically, yes. Politically, we’d need to agree that healthcare is a right we owe each other, funded collectively through taxes. That would mean massive tax increases, eliminating private insurance as the primary system, and trusting a single federal agency.
The operational resistance alone would be too much: I’ve watched hospital execs squeeze out thinning margins and payer executives navigate quarterly earnings calls. We’re talking about unwinding a $1T+ private insurance industry, reconfiguring every hospital’s revenue model, and convincing Americans to trust the federal government with something they currently (sort of) get through their jobs. That ship didn’t just sail - it sank decades ago.
The UK made one healthcare body in 1948, but was it too simple - or is it elegantly simple? We can compare it with something much more complex, like the Bismarck model.
Germany has roughly 140 competing insurance companies - in stark contrast to one payer of the UK. Yet Germany delivers universal coverage for over half of what the US spends per person.
Unifier of Germany, Otto von Bismarck (not named after
who I initially thought
) didn’t create this because he loved workers. He created it because socialists were gaining power in the 1880s and he needed to steal their thunder. “Give workers healthcare before they overthrow us” is peak
realpolitik
(the German word for “do what works, not what feels righteous”).
Americans are told you must choose: government control OR market competition. Germany said “
both
“ and built something Americans are told is impossible.
Employers and employees split a 14.6% payroll contribution, meaning wages automatically have a healthcare price tag to them. German workers get to choose from 140 competing sickness funds (aka “insurance companies” in American parlance).
But that competition is morally-bound by regulation: to accept any applicant, cover the same baseline benefits, and charge based on income (not health status). They compete on customer service, extra benefits, and operational efficiency - not on avoiding risky, expensive patients.
On the provider side, the German government sets prices to prevent gouging. Long-term care operates as a separate system (that orange flow on the diagram) instead of bankrupting families or clogging hospitals. Earn over €73,800 ($85K USD) and you can opt into private insurance (in purple).
Germany has universal coverage through competition and cost control through regulation. There are four distinct paths: statutory (blue), private (purple), long-term care (orange), and out-of-pocket (yellow). In practice, there is a lot of complexity, but structured towards the theory of social insurance.
But the German system has trade-offs
2
: payroll tax is pressure on employers, the inequality between public and private tiers, and 140 bureaucracies to navigate. But the complexity serves a coherent purpose.
But imagine if American insurers competed on “who has the best nurse hotline” instead of “who can design the narrowest network to avoid costs”. That’s what happens when the obligation to cover everyone is non-negotiable.
Americans might actually like health insurers functioning as utilities, not profit-maximizing businesses. But federal price-setting across 50 states means telling every hospital and physician what they can charge - and CMS already struggles with Medicare rates alone.
The lobbying alone would be apocalyptic. While insurers would fight “utility” status, the hospitals would fight price controls. Not to mention that the entire physician payment model would need restructuring, while we’re in the midst of an upcoming clinician shortage.
But fundamentally, Americans would need to agree: your job connects you to a community of mutual obligation. Do we actually believe that? We built something like it through
a historical accident
(WW2 wage controls), but we’ve never committed to the moral premise.
Germany chose regulated competition in 1883 and built something complex - but the parts were designed to work together. We chose unregulated competition and built complexity that serves... what exactly?
There are other healthcare system archetypes as well: National Health Insurance (Canadian healthcare) and Out-of-Pocket systems. I could also build out diagrams for other countries
3
too (have been suggested Singapore, Norway, and Japan). But like all other self-centered Americans, my focus is on talking about the US Healthcare System.
We can learn a lot from two distinct namesake models: the Bismarck model is “social insurance” and the Beveridge model is a “universal right”. The UK and Germany made different choices and built different systems: the UK moves money from taxpayers → NHS → services, Germany from employers + employees → sickness funds → services. But both embody their stated values.
So what does the US value? We built something that costs everyone, everything, everywhere - and still leaves 27 million uninsured.
The outcome is $4.9T - which would make it the 3rd largest economy in the world, a high 8% admin costs - compared to the UK’s 2% admin, with medical bankruptcy still possible.
We’ve never agreed on what we value
. So we built a system that embodies our disagreement: employer-based coverage (market choice) plus Medicare (social insurance) plus Medicaid (safety net) plus exchanges (regulated markets).
Maybe that IS the American philosophy: pluralism so deep we can’t even agree on how to keep each other alive.
My fear with the diagram is that it just becomes gratuitous complication-porn. I’m not trying to show something to get the reaction of, “
Wow, what a tangled mess! Isn’t that insightful?
” Let’s look more closely to see the nuance and significance of we can take away from this chart.
Soak in the Sankey (Tsang-key?) diagram again. From a distance, it looks like chaos - maybe even like failure. But zoom in and you’ll see something else: this isn’t random. Every flow, every split, every loop represents a decision someone made.
Here’s the first thing that jumps out: if you work a job in America (and you presumably do, to afford the internet where you’re reading this), you’re already paying for healthcare in multiple places on this chart:
Taxes:
federal, state, and local taxes finance Medicare, Medicaid, and various public health programs in so many places. Our attempt at embedding it in single payer.
Payroll
: if you’re employed, your employer pays taxes on Medicare (even though you presumably can’t use it until you retire at 65). This is a cost that doesn’t go to your salary.
Insurance premiums
: get deducted from your paycheck to fund the employer group plans ($688B from employees alone).
And don’t forget the most insidious payment - out-of-pocket costs - which add up to half a trillion.
We already built socialized medicine - we just made it more expensive.
Academics have
pointed
this
out
for years: Americans
already
finance healthcare collectively, just more inefficiently than countries with actual single-payer. Taxpayers already spend $2.35T - more than the entire GDP of Italy, the 8th largest economy in the world. That’s half the healthcare system before a single insurance premium gets paid.
Healthcare is already a collective responsibility - we just pretend it’s individual. Then make individuals pay twice: once through taxes, once through premiums.
The second thing that jumps out: look at how much flows toward the elderly.
While the obvious culprit is over $1T on Medicare, Nursing Homes account for $218B (split between Medicare and Medicaid) while Home Health & Hospice takes $100B. Medically speaking, old age is EXPENSIVE with the highest complications and comorbidities.
What decision does this say aside from “care for old people”? Medicare is a collective promise - you pay in from age 22 to 65, you collect from 65 to death. And don’t forget
special needs plans
, which contain so much complexity and overhead for the
most vulnerable
of the elderly.
Medicaid is technically for “low-income people”, but look closer: 22% of all Medicaid spending goes to nursing homes ($188B). That’s grandma’s long-term care after she runs out of money. Germany separated long-term care into its own system. The UK has a distinct local authority. The US folded it into Medicaid and pretended it’s a poverty program. Another choice we made without admitting it: we socialize the costs of aging, but only after families go broke first.
A stark contrast to Children’s Health Programs (in green). But this isn’t about whether old people deserve healthcare spending compared to our investment in children’s health. This diagram just points out that we’ve made a civil covenant to care for our elders.
The US diagram is a Rorschach test - whatever story you want to tell:
The $100B in public health versus $1,452B in hospital care: the tale of treatment instead of prevention.
The $120B in children’s health versus $1,000B in Medicare: how we repair old age instead of investing in youth.
The $441B in prescription drugs - the story of incentivizing American innovation over price controls.
And the administrative complexity at every handoff…
The question isn’t whether these choices are right or wrong. The question is: do we even know what we chose?
When we say “just fix healthcare,” this monstrous chart shows the problem. You can’t “just expand Medicare” - Medicare is already funded by four different sources. You can’t “just cut insurance middlemen” - employer plans flow $1T+ to care. Every fix redirects one river while missing the ecosystem.
The UK built a system that moves money from taxpayers to services. Germany built a system that moves money from employers and employees to services.
We built a system that costs everyone, everything, everywhere
- and we’re still arguing about whether healthcare is a right, an earned benefit, or a market commodity.
I spent weeks mapping this diagram. Actually taking away parts? That’s choosing who loses coverage, whose job disappears, which hospital closes.
The chart isn’t simply dollars flowing through programs - it tells a story of how we support each other. Whether money goes to your trusted doctor, to hospitals that save you when you’re gravely ill, to nursing homes where our elders age with dignity, to invest in programs that keep our children - and our futures - healthy.
This is American ambivalence about what we owe each other. It’s not just a creature to be fixed. It’s 330 million people living inside the creature we created.
Ernst Haeckel
drew his creatures to reveal nature’s hidden order. This diagram reveals our hidden disorder - or perhaps, a different kind of order than we admitted we were building. The question isn’t whether this creature is beautiful or monstrous.
The question is: now that we see what we made, what do we want to do about it?
1
The Hong Kong University of Science and Technology, Hong Kong SAR, China
2
Eyeline Labs, USA
Abstract
3D Gaussian Splatting (3DGS) has shown strong capability in reconstructing and rendering photorealistic
3D scenes with high efficiency.
However, extending 3DGS to synthesize large-scale or infinite terrains from a single captured
exemplar—remains an open challenge.
In this paper, we propose a tile-based framework that addresses this problem. Our method builds on Wang
Tiles, where each tile encodes a local field of Gaussians with boundary constraints to ensure seamless
transitions. This enables stochastic yet continuous tiling of Gaussian fields over arbitrary surfaces,
allowing for procedural generation of expansive terrains with high spatial diversity. Furthermore, we
introduce several rendering optimizations tailored to the unique characteristics of 3DGS Wang tiles,
achieving real-time rendering of large-scale 3DGS terrains.
Pipeline
Given multi-view images of an exemplar scene, our goal is to construct Gaussian Splatting Wang Tiles
(GSWT) that can be tiled on arbitrary surfaces and rendered in real time with our novel GSWT
renderer.
An overview of the entire pipeline is illustrated below. We begin by
reconstructing the 3DGS exemplar at multiple LODs. For each level, we generate a set of Wang Tiles by
sampling the edge and center patches and applying a semantic-aware graph cut algorithm. Prior to
rendering, we pre-sort each tile for efficient sort-free splatting, and during runtime, we perform
tiling on the fly, allowing efficient GSWT-based terrain synthesis and rendering.
(a) Given the input images, we construct the exemplar multiple times with different Level of Detail
(LOD).
(b) We construct the tile set and preprocess it before rendering.
(c) The surface is tiled at run-time on the worker thread, while the main thread renders each frame.
@inproceedings{Zeng:2025:gswt,
author = {Zeng, Yunfan and Ma, Li and Sander, Pedro V.},
title = {GSWT: Gaussian Splatting Wang Tiles},
year = {2025},
publisher = {Association for Computing Machinery},
booktitle = {SIGGRAPH Asia 2025 Conference Papers},
location = {Hong Kong, China},
series = {SA '25}
}
Security updates for Wednesday
Linux Weekly News
lwn.net
2025-12-03 14:11:40
Security updates have been issued by Debian (containerd, mako, and xen), Fedora (forgejo, nextcloud, openbao, rclone, restic, and tigervnc), Oracle (firefox, kernel, libtiff, libxml2, and postgresql), SUSE (libecpg6, lightdm-kde-greeter, python-cbor2, python-mistralclient-doc, python315, and python3...
Aisuru botnet behind new record-breaking 29.7 Tbps DDoS attack
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 14:01:04
In just three months, the massive Aisuru botnet launched more than 1,300 distributed denial-of-service attacks, one of them setting a new record with a peak at 29.7 terabits per second. [...]...
In just three months, the massive Aisuru botnet launched more than 1,300 distributed denial-of-service attacks, one of them setting a new record with a peak at 29.7 terabits per second.
Aisuru is a huge botnet-for-hire service that provides an army of routers and IoT devices compromised via known vulnerabilities or through brute-forcing weak credentials.
Internet management and infrastructure company Cloudflare estimates that the botnet uses between one and four million infected hosts across the world.
Cybercriminals can rent from distributors parts of the Aisuru botnet to launch distributed denial-of-service (DDoS) attacks.
The largest hyper-volumetric attack from Aisuru-controlled devices occurred in the third quarter of 2025 and was successfully mitigated by Cloudflare.
The previous record DDoS attack, which
peaked at 22.2 Tbps
, was also mitigated by Cloudflare and was attributed to Aisuru with medium confidence. More recently, Microsoft disclosed that the same botnet hit its Azure network with a
massive 15 Tbps DDoS attack
launched from 500,000 IP addresses.
Cloudflare reports that it mitigated 2,867 Aisuru attacks since the beginning of the year, almost 45% of them being hyper-volumetric - attacks that exceed 1 Tbps or 1 billion packets per second (Bpps).
The internet company did not name the target of the record-breaking incident, but notes that the attack lasted 69 seconds and peaked at 29.7 Tbps. It used UDP carpet-bombing to direct “garbage” traffic to an average of 15,000 destination ports per second.
Graph from the record-breaking Aisuru attack
Source: Cloudflare
Another massive DDoS attack that the company mitigated reached 14.1 Bpps.
Cloudflare says that Aisuru attacks can be so devastating that the amount of traffic can disrupt internet service providers (ISPs), even if they are not directly targeted.
"If Aisuru’s attack traffic can disrupt parts of the US’ Internet infrastructure when said ISPs were not even the target of the attack, imagine what it can do when it’s directly aimed at unprotected or insufficiently protected ISPs, critical infrastructure, healthcare services, emergency services, and military systems," Cloudflare says.
Rise in hyper-volumetric attacks
Statistical data from Cloudflare shows that hyper-volumetric DDoS attacks from the Aisuru botnet are rising steadily this year, reaching 1,304 incidents in Q3 alone.
According to the researchers, Aisuru is targeting companies in various sectors, including gaming, hosting providers, telecommunications, and financial services.
Hypervolumetric DDoS attacks per quarter
Source: Cloudflare
DDoS attacks exceeding 100 Mpps increased by 189% QoQ, and those exceeding 1 Tbps more than doubled (227%) QoQ.
Most attacks end in less than 10 minutes, according to Cloudflare, leaving defenders and on-demand services little time to respond.
“A short attack may only last a few seconds, but the disruption it causes can be severe, and recovery takes far longer,” explained Cloudflare.
“Engineering and operational teams are then stuck with a complex, multi-step process to get critical systems back online, check data for consistency across distributed systems, and restore secure, reliable service to customers.”
In terms of the number of DDoS attacks, this past quarter wasn’t at the level of Q1, but 2025 continues to be far more severe than the past years, and even without November and December having been accounted for yet.
Number of DDoS attacks as of October 2025
Source: Cloudflare
Cloudflare says that in Q3 it mitigated an average of 3,780 DDoS attacks every hour, most coming from Indonesia, Thailand, Bangladesh, and Ecuador, and targeting China, Turkey, Germany, Brazil, and the United States.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
A final stable kernel update for 5.4
Linux Weekly News
lwn.net
2025-12-03 14:00:32
Greg Kroah-Hartman has announced the release of the 5.4.302 stable kernel:
This is the LAST 5.4.y release. It is now end-of-life and should not
be used by anyone, anymore. As of this point in time, there are 1539
documented unfixed CVEs for this kernel branch, and that number will
only increase ov...
Greg Kroah-Hartman has announced the release of the
5.4.302
stable kernel:
This is the LAST 5.4.y release. It is now end-of-life and should not
be used by anyone, anymore. As of this point in time, there are 1539
documented unfixed CVEs for this kernel branch, and that number will
only increase over time as more CVEs get assigned for kernel bugs.
For the curious, Kroah-Hartman has also
provided
a list of the unfixed CVEs for 5.5.302.
The Last Video Rental Store Is Your Public Library
403 Media
www.404media.co
2025-12-03 14:00:14
Audio-visual librarians are quietly amassing large physical media collections amid the IP disputes threatening select availability....
As prices for streaming subscriptions
continue to soar
and finding movies to watch, new and old, is becoming harder as the number of streaming services continues to grow, people are turning to the unexpected last stronghold of physical media: the public library. Some libraries are now intentionally
using iconic Blockbuster branding
to recall the hours visitors once spent looking for something to rent on Friday and Saturday nights.
John Scalzo, audiovisual collection librarian with a public library in western New York, says that despite an observed drop-off in DVD, Blu-ray, and 4K Ultra disc circulation in 2019, interest in physical media is coming back around.
“People really seem to want physical media,” Scalzo told
404 Media
.
Part of it has to do with consumer awareness: People know they’re paying more for monthly subscriptions to streaming services and getting less. The same has been true for gaming.
As the audiovisual selector with the Free Library of Philadelphia since 2024, Kris Langlais has been focused on building the library’s video game collections to meet comparable interest in demand. Now that every branch library has a prominent video game collection, Langlais says that patrons who come for the games are reportedly expressing interest in more of what the library has to offer.
“Librarians out in our branches are seeing a lot of young people who are really excited by these collections,” Langlais told 404 Media. “Folks who are coming in just for the games are picking up program flyers and coming back for something like that.”
Langlais’ collection priorities have been focused on new releases, yet they remain keenly aware of the long, rich history of video game culture. The problem is older, classic games are often harder to find because they’ve gone out of print, making the chances of finding them cost-prohibitive.
“Even with the consoles we’re collecting, it’s hard to go back and get games for them,” Langlais said. “I’m trying to go back and fill in old things as much as I can because people are interested in them.”
Locating out-of-print physical media can be difficult. Scalzo knows this, which is why he keeps a running list of films known to be unavailable commercially at any given time, so that when a batch of films are donated to the library, Scalzo will set aside extra copies, just in case
a rights dispute puts a piece of legacy cult media in licensing purgatory
for a few years.
“It’s what’s expected of us,” Scalzo added.
Tiffany Hudson, audiovisual materials selector with Salt Lake City Public Library has had a similar experience with out-of-print media. When a title goes out of print, it’s her job to hunt for a replacement copy. But lately, Hudson says more patrons are requesting physical copies of movies and TV shows that are exclusive to certain streaming platforms, noting that it can be hard to explain to patrons why the library can't get popular and award-winning films, especially when what patrons see available on Amazon tells a different story.
“Someone will come up to me and ask for a copy of something that premiered at Sundance Film Festival because they found a bootleg copy from a region where the film was released sooner than it was here,” Hudson told 404 Media, who went onto explain that discs from different regions aren’t designed to be ready by incompatible players.
But it’s not just that discs from different regions aren’t designed to play on devices not formatted for that specific region. Generally, it's also just that most films don't get a physical release anymore. In cases where films from streaming platforms do get slated for a physical release, it can take years. A notable example of this is the Apple+ film
CODA
, which won the Oscar for Best Picture in 2022. The film only
received a U.S. physical release this month
. Hudson says films getting a physical release is becoming the exception, not the rule.
“It’s frustrating because I understand the streaming services, they’re trying to drive people to their services and they want some money for that, but there are still a lot of people that just can’t afford all of those services,” Hudson told 404 Media.
The data-driven recommendation systems streaming platforms use tend to favor newer, more easily categorized content, and are
starting to warp our perceptions of what classic media exists and matters
. Older art house films that are more difficult to categorize as “comedy” or “horror” are less likely to be discoverable, which is likely how the oldest American movie available on Netflix currently is from 1968.
It’s probably not a coincidence that, in many cases, the media that is least likely to get a more permanent release is the media that’s a high archival priority for libraries. AV librarians 404 Media spoke with for this story expressed a sense of urgency in purchasing a physical copy of “The People’s Joker”
when they learned it would get a physical release
after the film premiered and was pulled from the Toronto International Film Festival lineup
in 2022 for a dispute with the Batman universe’s rightsholders.
“When I saw that it was getting published on DVD and that it was available through our vendor—I normally let my branches choose their DVDs to the extent possible, but I was like, ‘I don’t care, we’re getting like 10 copies of this,’” Langlais told 404 Media. “I just knew that people were going to want to see this.”
So far, Langlais’ instinct has been spot on. The parody film has a devout cult following, both because it’s a coming-of-age story of a trans woman who uses comedy to cope with her transition, and because it puts the Fair Use Doctrine to use. One can argue the film has been banned for either or both of those reasons. The fact that media by, about and for the LGBTQ+ community has been a primary target of far-right censorship wasn’t lost on librarians.
“I just thought that it could vanish,” Langlais added.
It’s not like physical media is inherently permanent. It’s susceptible to scratches, and can rot, crack, or warp over time. But currently, physical media offers another option, and it’s an entirely appropriate response to the nostalgia for-profit model that exists to recycle IP and seemingly not much else. However, as
very smart people have observed, nostalgia is default conservative
in that it’s frequently used to rewrite histories that may otherwise be remembered as unpalatable, while also keeping us culturally stuck in place.
Might as well go rent some films or games from the library, since we’re already culturally here. On the plus side, audiovisual librarians say their collections dwarf what was available at Blockbuster Video back in the day. Hudson knows, because she clerked at one in library school. and the collections dwarf what you’d find at Blockbuster Video back in its heyday.
“Except we don’t have any late fees,” she added.
This Podcast Will Hack You
403 Media
www.404media.co
2025-12-03 14:00:05
Something very strange is happening on Apple Podcasts; someone seemingly changed a map of the Ukraine war in connection with a betting site; and now half of the U.S. requires a face or ID scan to watch porn....
We start this week with Joseph’s very weird story about Apple Podcasts. The app is opening by itself, playing random spirituality podcasts, and in one case directing listeners to a potentially malicious website. After the break, Matthew tells us how it sure looks like a map of Ukraine was manipulated in order to win a bet on Polymarket. In the subscribers-only section, Sam breaks down how half of the U.S. now requires a face or ID scan to watch porn.
Listen to the weekly podcast on
Apple Podcasts
,
Spotify
, or
YouTube
. Become a paid subscriber for access to this episode's bonus content and to power our journalism.
If you become a paid subscriber, check your inbox for an email from our podcast host Transistor for a link to the subscribers-only version! You can also add that subscribers feed to your podcast app of choice and never miss an episode that way. The email should also contain the subscribers-only unlisted YouTube link for the extended video version too. It will also be in the show notes in your podcast player.
Joseph is an award-winning investigative journalist focused on generating impact. His work has triggered hundreds of millions of dollars worth of fines, shut down tech companies, and much more.
Desugaring the Relationship Between Concrete and Abstract Syntax
Previously, we, begrudgingly,
parsed some syntax into a Concrete Syntax Tree (CST)
.
With that tarpit deftly dodged, we can proceed to our next pass desugaring.
Desugaring removes syntax sugar and maps our CST onto our Abstract Syntax Tree (AST).
Our CST leaves us with a lot of cruft, such as
|
or
=
.
This stuff was important for telling head from tail in our initial source file, and we’ll want to have it around when we’re reporting diagnostics, but the rest of the compiler doesn’t really care about such mundane affairs.
Desugaring helps us strip away all the syntax and focus in on what’s important, lightening the cognitive load for following compiler passes.
But… do we really gotta?
It seems like a pain.
Can’t the later passes just deal with the excess syntax?
We’ve come to expect that’s the tee up for why we can’t do that, but actually you kinda just…can.
In fact, that’s exactly what Swift does.
They parse the CST, and their “AST” is just the subset of fields on the CST that are semantically relevant.
It’s a perfectly valid strategy.
I might even
recommend
it.
That is of course, if you didn’t write every following pass of the compiler already using an explicit AST already.
But who would do that, haha.
Don’t worry, we have a real reason to use a separate AST as well.
We find let expressions in our syntax, but they are nowhere to be found in our AST.
Syntax sugar turns out to be a common motivator for splitting CST and AST.
If you look at
rust-analyzer
, they employ a similar strategy to Swift.
They expose
a bunch of helpers on the CST for the semantically interesting stuff.
Despite that, they still produce a separate tree called
HIR
.
Rust desugars away a lot of its surface syntax, as well.
Accommodating this transformation requires producing a new tree.
It’s not enough to provide methods for the semantically interesting stuff.
We need to fundamentally change the structure of our tree.
As we change our tree, we need to remember where we came from.
It’s important that we’re able to map our AST back onto our CST.
This will matter not only for error reporting, but also for queries.
If we want to go to definition, we’ll need to determine the AST node our cursor is pointing at and then use that to determine where it’s definition is.
Desugaring produce our new AST
and
a mapping from AST nodes to CST nodes.
Desugaring, like parsing, is also going to be resilient.
We produce a list of errors alongside our AST and mapping.
For the most part, desugaring is straightforward.
We walk our CST, taking the interesting bits out as we go and stashing them in the new AST we’re constructing.
Conveniently, our syntax nodes map directly onto our AST nodes.
Almost as if we designed them that way.
Let expressions are an exception, requiring some more care.
They can’t be mapped directly onto our AST.
We have to represent them with a tree of nodes and map them back onto our CST.
During parsing, we were concerned with building up our CST, giving little consideration to how we consume our CST.
That changes in desugaring.
We are now concerned not only with how we traverse our CST, but how we store our CST.
Recall one of our outputs is a mapping between CST and AST.
Producing such a mapping requires we have a way to reference a particular CST node.
Our CST is provided by
rowan
, and we happen to be in luck.
rowan
provides not only a CST, but a way to traverse it.
Traversal is performed by
SyntaxNode
.
A type we did not encounter at all during parsing.
We can construct a
SyntaxNode
from our parsed
GreenNode
, providing us with a
suite
of new methods.
The most common method we’ll use is
first_child_by_kind
.
first_child_by_kind
takes a
Fn(Syntax) -> bool
and returns the first node that returns true for our function
Our predicate allows us to pick nodes of a particular kind (
SyntaxKind::LetBinder
,
Sytnax::Expr
, etc.) out of our tree.
Notably,
first_child_by_kind
only returns a syntax node.
It cannot return a token, aka a leaf node in our CST.
This is not an oversight on
rowan
s part but a conscious design decision.
If we want to find tokens, we can use
first_child_or_token_by_kind
.
When wading through a
SyntaxNode
’s children we only care about the nodes.
The tokens will be syntactic noise such as
Whitespace
or
Equal
, which are irrelevant to producing our AST.
rowan
knows this and lets us skip right to the action.
This is why we wrapped notable
Identifier
tokens in nodes during parsing.
FunBinder
and
LetBinder
always wrap a single
Identifier
(give or take some
Whitespace
) but let us find that identifier via
first_child_by_kind
.
Like our other passes, desugar is a set of recursive methods walking a tree.
With all the trees we’re traversing, we’ll have covered a whole forest by the time we’re done compiling.
Also like our other passes, we share state between those recursive methods in a
Desugar
struct:
Desugar is where we’re on the hook to uniquely identify our
Ast
nodes.
node_id
tracks the next available ID as we construct AST nodes.
Like
VarSupply
from lowering, we increment this counter every time we create a node.
root
is the root node of our CST from parsing.
The very same CST desugaring is in the middle of traversing.
A
GreenNode
is cheap to clone, so we don’t need to worry about having two copies floating about.
ast_to_cst
maps our
Ast
nodes back onto the CST nodes that spawned them.
This mapping will be central to error reporting, taking errors on our AST nodes and turning them into spans in our source file.
We might be surprised to see it stores something called a
SyntaxNodeHandle
, not
SyntaxNode
.
A
SyntaxNode
is a pointer under the hood.
This is great for performance, but not great for long term storage.
Instead of trying to figure out how to store a pointer safely, we store an index that will let us return to the position in our tree our
SyntaxNode
pointed at.
Note
As the name might imply, what we’re describing here is the
handle pattern
.
We can think of this as storing a
(Vec<T>, usize)
instead of a
&T
.
We can see this if we pop open
SyntaxNodeHandle
:
SyntaxNodePtr
comes from
rowan
.
Despite the name, a glance at its definition reveals an absence of pointers:
structSyntaxNodePtr<L: Language> {
// This is Syntax for us
kind: L::Kind,
// This is the span in our source text
range: TextRange,
}
From the span and kind of our node, we can find it within
root
and produce a
SyntaxNode
whenever we need one.
We work with
SyntaxNode
while traversing because it’s fast, but once we want to store a node we convert it to
SyntaxNodeHandle
.
When it’s time to traverse again, we convert our handle back into a
SyntaxNode
and pick up where we left off.
error
also needs to store a
SyntaxNode
to point at where errors occurred.
We’re less concerned with restarting traversal for our errors, so it suffices to store a
SyntaxNodePtr
.
DesugarOut
holds the three things we produce from desugaring.
Due to our resilience, we always produce all of our outputs in some shape.
From there, our body is brief:
Recall from parsing, our program is just an expression.
We see that’s still the case for
desugar_program
:
fndesugar_program(&mutself, cst: SyntaxNode<Lang>) -> Ast<String> {
letSome(expr) =cst.first_child_by_kind(&|kind|kind==Syntax::Expr) else {
// Assume parser has emitted an error for the missing node and just return a Hole here.
returnself.hole(&cst, DesugarError::MissingSyntax(Syntax::Expr));
};
self.desugar_expr(expr)
}
We find the first
Expr
node in our CST.
There should only ever be at most one, so the first is always correct.
Failing to find an expression, we assume our program is invalid and return a
hole
.
hole
constructs a
Hole
AST node and maps it to its CST node:
Hole
is part of our resilience strategy, previously seen in
type inference
.
Rather than failing at the first invalid AST, we treat it as a Hole and try to recover as much of our AST as possible.
Whenever we create a hole we attach an error to let us know what went awry.
Recall our expression syntax is any number of let bindings followed by an application:
Expr
Let
…
Let
…
App
…
In a perfect world, we’d consume this layout and be on our merry way.
Alas, we have to contend with the possibility that the expression we’re looking at is invalid.
We maintain a list of bindings and walk the children
SyntaxNode
s of our expression:
letmutbinds=vec![];
// The only tokens that appear in Lets are whitespace that we are happy to skip here.
forchildinexpr.children() {
matchchild.kind() {
Syntax::Let=>matchself.desugar_let(child.clone()) {
Ok((var, arg)) =>binds.push((var, arg, child)),
Err(error) => {
lethole=self.hole(&child, error);
returnself.build_locals(binds, hole);
}
},
_=> {
letbody=self.desugar_app(child);
returnself.build_locals(binds, body);
}
}
}
Our loop ends in one of three ways:
We encounter an error from
desugar_let
We see a non
Let
child
We run out of children
Our first exit means we had an invalid let binding, and we treat the remainder of our expression as a hole.
We still might have accrued some bindings that we’ll turn into a let expression using
build_locals
.
Our second case is our “happy path”.
If we see a non
Let
syntax, we assume it’s an application, pass it to
desugar_app
, and return whatever comes back to
build_locals
.
An application could be any of a parenthesized expresssion, function, integer, application, etc.
We don’t have to check for all of those here, if we happen to pass invalid syntax to
desugar_app
it’ll give us back a hole.
Finally, our third case exits the loop.
This happens when we only have
Let
children, our expression has no body, or we have no children to begin with.
Either way we handle it the same, by creating a hole:
Our loop relies on
children
only walking over syntax nodes.
Let bindings have
Whitespace
tokens between them (although they don’t have to!), and these would trigger our wildcard case ending our loop early.
But
Whitespace
is a token, so
children
skips over it allowing us to assume we’ll only see
Let
syntax until we reach our expression’s final application.
desugar_let
does not desugar a full let expression
This is because our CST only encodes let bindings:
let <var> = <expr>;
.
Recall from parsing, a
Let
syntax node has the shape:
Let
LetKw
LetBinder
…
Equal
Expr
…
Semicolon
Because of that we don’t produce an
Ast
out of
desugar_let
.
We lack the syntax with which to do so.
Instead, we produce a pair comprising an identifier and it’s definition, relying on
desugar_expr
to turn those into a full
Ast
.
We’ll extract our pair from the children of our
Let
node:
We assume our let binding only has two nodes (and we don’t have to care how many tokens it has).
The first is a
LetBinder
, which holds our identifier.
We unwrap our
LetBinder
to reveal it’s underlying
Identifier
token and grab its text.
If our binding is missing, we error immediately.
Next is the definition of our let binding, which should be an
Expr
.
We pass it to
desugar_expr
and use whatever it gives us.
Failing to find that, we produce a hole and let the user know they’re missing an expression.
Next we move onto desugaring appl- You know what actually…
We’ve got a taste for desugaring.
I trust you can extrapolate from here.
For each piece of syntax we:
Traverse to the interesting nodes in our CST
Extract their text
Put them in our AST
When we fail to do any of those steps, we replace the AST we’re constructing with a
Hole
, attempting to replace the smallest AST we can.
We’d rather replace the definition of a let with a hole than the entire let.
Whenever we create an AST node, we give it a unique ID and a pat on the head, map it back to our CST and send it on its way.
If you want to see it in glorious high resolution (depending on your monitor) detail, check out the
full code
.
Instead of rehashing the same concepts we’ve covered above, let’s move on to the next interesting bit: desugaring let expressions.
build_locals
is, in a sense, where all the magic happens.
Our other helpers turn one syntactic construct into one AST construct.
Here, however, we turn our let expressions into multiple AST nodes.
With the loss of our 1:1 mapping, we have to answer a question: How do we map multiple AST nodes back onto one CST node.
A let expression turns into a function nested within an application.
Whenever we write
let x = 1; incr x
, we could write
(|x| incr x) 1
.
We’ll represent let expressions as an
Ast::Fun
and
Ast::App
.
But, what’s the span of our
Ast::Fun
?
Our tree transformation is destructive.
We’ve lost some information.
There isn’t a contiguous span in our source that represents our function.
If we encompass all the elements of our function, as in
let ∣x = 1; incr x∣
, we also include parts of our application.
Our application faces a similar conundrum, but it’s easier for us to handwave it away by saying it spans our full let expression.
In lieu of picking the perfect span for our function, let’s take a step back and consider why we need a span for our function.
Foremost, our span serves as a location for diagnostics.
After that, our span serves to identify our AST node for any user interactions.
For example, if we want to get the type of our let variable, we’ll use the span to figure out which function parameter to get the type of in the AST.
Our function doesn’t actually need a span for that many diagnostics in practice.
If an error occurs in our function body, our function body is an expression that maps to its own independent span.
We don’t need a span for our
entire
function.
If we can give a span to our function parameter, our function body will take care of itself.
Our narrowed task is much simpler to satisfy:
let ∣x∣ = 1; incr x
.
Just like that; We’ve assigned a span to our function parameter.
And if we look at the implementation, we’ll see that’s exactly what we do:
We accomplish a secondary task whilst desugaring lets, nesting let bindings correctly.
body
is the body expression of our innermost let binding, which will be the last element of
binds
.
We walk binds backwards, constructing new let expressions out of the previous until we reach our first bindings.
The first binding is our outermost let expression, including all our other bindings within its body.
All my
church
heads sound off in chat.
This is a perfectly good way to do addition, if you ask me.
I don’t even know why we’re planning to add more features.
That syntax gets parsed into a CST, that we’ll only summarize:
Program
Expr
Let
LetBinder “one”
Expr …
Let
LetBinder “add”
Expr …
App
App
Var “add”
Var “one”
Var “one”
From that CST, we desugar our
Ast
.
We’ll omit the body of our let definitions for brevity.
We just want to get a sense for how our let expressions transform:
Phew, writing that out by hand really makes me appreciate all the work the computer does for us.
Now, because
let
is syntax sugar, we could also reach the same
Ast
by writing:
I’ll leave it to you to verify, but this turns into the same
Ast
.
I know which syntax I’d rather write.
But I think it’s insightful to see that we don’t
need
let expressions.
Note
This is only the case in our language because of choices we made around the type system.
In Haskell, let bindings allow for different typings than function parameters, making this transformation invalid.
We don’t have any special treatment of let expressions, so we can desugar them.
As always, you can find the full code for our desugar pass in the
making a language repo
.
One thing is still bugging me about the desugar example.
Our desugared
Ast
uses
String
for variables, but during type inference we use
Var
.
We’re going to need one more pass before we pass our
Ast
to type inference: name resolution.
India revokes order to preload smartphones with state-owned security app
Guardian
www.theguardian.com
2025-12-03 13:53:04
Tech companies including Apple and Google made it clear they would not comply due to privacy concerns India’s government has backtracked on an order for all smartphones to be pre-installed with a state-owned security app after a mass outcry over privacy concerns and refusal by technology companies t...
India’s government has backtracked on an order for all smartphones to be pre-installed with a state-owned security app after a
mass outcry over privacy concerns
and refusal by technology companies to comply.
The department of telecommunications confirmed it had revoked its previous order for all technology companies to mandatorily install the government’s Sanchar Saathi cybersecurity app on to every smartphone in
India
within 90 days.
Political outcry erupted over the order and several tech companies, including Apple and
Google
, made it clear they would not comply due to privacy concerns. In a statement on Wednesday afternoon, the government confirmed it had “decided not to make the pre-installation mandatory for mobile manufacturers”.
It emphasised that the app, which allows users to block and track lost or stolen mobile phones and report fraudulent calls, was “secure and purely meant to help citizens” against “bad actors”.
The initial order,
given quietly to tech companies last week
, landed the government in hot water after internet privacy groups and the political opposition raised concerns that the app could be used as a mass surveillance tool.
Apple and Google anonymously briefed the media that tech companies would be pushing back against the order as the move raised privacy concerns for their operating systems and violated internal policies.
Outcry erupted in parliament on Wednesday, with opposition MPs accusing the government, led by the prime minister, Narendra Modi, of violating citizens’ basic right to privacy.
Randeep Singh Surjewala, from the opposition Indian National Congress party, said the app “could be a possible kill switch” that could turn “every cell phone into a brick, which the government could use against journalists, opposition leaders and dissidents, if it so desires”.
Parallels have been made with an order made by the Russian government in August for
an app called Max
to be installed on all smartphones, sparking fears it was a mass surveillance tool.
The communications minister, Jyotiraditya Scindia, responded to criticism, saying the Sanchar Saathi app was voluntary and could be deleted, despite the initial order stating the opposite.
He said: “I can delete it like any other app, as every citizen has this right in a democracy. Snooping is not possible through the app, nor will it ever be.”
The decision by the government to revoke the order was celebrated by groups advocating for online rights and privacy. In a statement, the internet freedom foundation said: “For now, we should treat this as cautious optimism, not closure, until the formal legal direction is published and independently confirmed.”
Congressional lawmakers 47% pts better at picking stocks
WTO/99 is a new “immersive archival documentary” about the 1999 protests in Seattle against the World Trade Organization that uses 1,000+ hours of footage from the Independent Media Center and other archives. The historic WTO protests against corporate power and economic globalization we...
In this age of widespread misinformation and increased threats to press freedom, support for independent journalism is more important than ever. Media is
essential
to the functioning of a democratic society.
We have extended our
Giving NewsDay
triple match through today ONLY, so you still have time to make 3x the impact.
Please donate today, so we can keep delivering urgent reporting on the world’s most pressing issues.
Every dollar makes a difference
. Thank you so much!
Democracy Now!
Amy Goodman
Non-commercial news needs your support.
We rely on contributions from you, our viewers and listeners to do our work. If you visit us daily or weekly or even just once a month, now is a great time to make your monthly contribution.
WTO
/99
is a new “immersive archival documentary” about the 1999 protests in Seattle against the World Trade Organization that uses 1,000+ hours of footage from the Independent Media Center and other archives. The historic
WTO
protests against corporate power and economic globalization were met with a militarized police crackdown and National Guard troops. We feature clips from the film and discuss takeaways that have relevance today. “These issues haven’t gone away,” says Ian Bell, director of
WTO
/99
. We also speak with Ralph Nader, who is featured in the movie.
longtime consumer advocate, corporate critic and former presidential candidate.
Please check back later for full transcript.
The original content of this program is licensed under a
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License
. Please attribute legal copies of this work to democracynow.org. Some of the work(s) that this program incorporates, however, may be separately licensed. For further information or additional permissions, contact us.
Non-commercial news needs your support
We rely on contributions from our viewers and listeners to do our work.
Please do your part today.
Ralph Nader on Trump's "Entrenching Dictatorship," Reclaiming Congress, and the Fight Against Big Money
Democracy Now!
www.democracynow.org
2025-12-03 13:31:34
As a “Fight Club” of eight senators led by Bernie Sanders challenges Democratic Minority Leader Chuck Schumer’s handling of President Trump, we speak with Ralph Nader, who has been taking on the Democratic Party for decades. Sixty years ago this week, he published his landmark book...
In this age of widespread misinformation and increased threats to press freedom, support for independent journalism is more important than ever. Media is
essential
to the functioning of a democratic society.
We have extended our
Giving NewsDay
triple match through today ONLY, so you still have time to make 3x the impact.
Please donate today, so we can keep delivering urgent reporting on the world’s most pressing issues.
Every dollar makes a difference
. Thank you so much!
Democracy Now!
Amy Goodman
Non-commercial news needs your support.
We rely on contributions from you, our viewers and listeners to do our work. If you visit us daily or weekly or even just once a month, now is a great time to make your monthly contribution.
As a “Fight Club” of eight senators led by Bernie Sanders challenges Democratic Minority Leader Chuck Schumer’s handling of President Trump, we speak with Ralph Nader, who has been taking on the Democratic Party for decades. Sixty years ago this week, he published his landmark book,
Unsafe at Any Speed
, exposing the safety flaws of GM’s Chevrolet Corvair and leading to major reforms in auto safety laws. Nader discusses the legacy of his book, the current state of government regulation and why Congress must reclaim its authority from an out-of-control Trump administration. “Clearly, we’re seeing a rapidly entrenching dictatorship,” Nader tells
Democracy Now!
“The focus has to be on impeachment, and there will be a large majority of people in favor of it.”
longtime consumer advocate, corporate critic and former presidential candidate.
Please check back later for full transcript.
The original content of this program is licensed under a
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License
. Please attribute legal copies of this work to democracynow.org. Some of the work(s) that this program incorporates, however, may be separately licensed. For further information or additional permissions, contact us.
Non-commercial news needs your support
We rely on contributions from our viewers and listeners to do our work.
Please do your part today.
University of Phoenix discloses data breach after Oracle hack
Bleeping Computer
www.bleepingcomputer.com
2025-12-03 13:23:10
The University of Phoenix (UoPX) has joined a growing list of U.S. universities breached in a Clop data theft campaign targeting vulnerable Oracle E-Business Suite instances in August 2025. [...]...
The University of Phoenix (UoPX) has joined a growing list of U.S. universities breached in a Clop data theft campaign targeting vulnerable Oracle E-Business Suite instances in August 2025.
Founded in 1976 and headquartered in Phoenix, Arizona, UoPX is a private for-profit university with nearly 3,000 academic staff and over 100,000 enrolled students.
The university
disclosed the data breach
on its official website on Tuesday, while its parent company, Phoenix Education Partners, filed an
8-K form
with the U.S. Securities and Exchange Commission (SEC).
UoPX said it detected the incident on November 21 (after the extortion group added it to its data leak site) and noted that the attackers exploited a zero-day vulnerability in the Oracle E-Business Suite (EBS) financial application to steal a wide range of sensitive personal and financial information belonging to students, staff, and suppliers.
"We believe that the unauthorized third-party obtained certain personal information, including names and contact information, dates of birth, social security numbers, and bank account and routing numbers with respect to numerous current and former students, employees, faculty and suppliers was accessed without authorization," the school said.
"We continue to review the impacted data and will provide the required notifications to affected individuals and regulatory entities. Affected individuals will soon receive a letter via US Mail outlining the details of the incident and next steps to take."
A spokesperson for the University of Phoenix didn't respond when BleepingComputer reached out today to request more details about the breach, including the identity of the attackers and the total number of individuals affected.
University of Phoenix entry on Clop's leak site (BleepingComputer)
Although the UoPX has yet to attribute the incident to a specific cybercrime group, based on the details shared so far, the breach is part of a Clop ransomware gang extortion campaign in which the gang
has exploited a zero-day flaw (CVE-2025-61882)
to steal sensitive documents from many victims' Oracle EBS platforms since
early August 2025
.
As part of the same series of data theft attacks, Clop has also targeted other universities in the United States, including
Harvard University
and the
University of Pennsylvania
, which have also confirmed Oracle EBS breaches impacting their students and staff.
Since late October, the systems of several U.S. universities have also been breached in a series of
voice phishing attacks
, with
Harvard University
,
University of Pennsylvania
, and
Princeton University
disclosing that the attackers breached systems used for development and alumni activities to steal the personal information of donors, staff, students, alumni, and faculty.
Broken IAM isn't just an IT problem - the impact ripples across your whole business.
This practical guide covers why traditional IAM practices fail to keep up with modern demands, examples of what "good" IAM looks like, and a simple checklist for building a scalable strategy.
Helldivers 2 devs slash install size from 154GB to 23GB
It's no surprise to see modern AAA games occupying hundreds of gigabytes of storage these days, especially if you are gaming on a PC. But somehow, Arrowhead Game Studios, the developers behind the popular co-op shooter
Helldivers 2
, have managed to substantially cut the game’s size by 85%.
As per a
recent post on Steam
, this reduction was made possible with support from Nixxes Software, best known for developing high-quality PC ports of
Sony
’s biggest PlayStation titles. The developers were able to achieve this by de-duplicating game data, which resulted in bringing the size down from ~154GB to just ~23GB, saving a massive ~131GB of storage space.
Originally, the game’s large install size was attributed to optimization for mechanical hard drives since duplicating data is used to reduce loading times on older storage media. However, it turns out that Arrowhead’s estimates for load times on HDDs, based on industry data, were incorrect.
With their latest data measurements specific to the game, the developers have confirmed the small number of players (11% last week) using mechanical hard drives will witness mission load times increase by only a few seconds in worst cases. Additionally, the post reads, “the majority of the loading time in
Helldivers 2
is due to level-generation rather than asset loading. This level generation happens in parallel with loading assets from the disk and so is the main determining factor of the loading time.”
Get Tom's Hardware's best news and in-depth reviews, straight to your inbox.
Kunal Khullar is a contributing writer at Tom’s Hardware. He is a long time technology journalist and reviewer specializing in PC components and peripherals, and welcomes any and every question around building a PC.
U.S.-Backed Ceasefire Is Cover for Ethnic Cleansing in Gaza & West Bank: Sari Bashi
Democracy Now!
www.democracynow.org
2025-12-03 13:14:52
Israel has announced it will reopen the Rafah border crossing between Gaza and Egypt in the next few days as part of the U.S.-brokered ceasefire. However, the border will only open in one direction: for Palestinians to exit. Israeli American human rights lawyer Sari Bashi says the move validates fea...
This is a rush transcript. Copy may not be in its final form.
AMY
GOODMAN
:
Israel has announced it will reopen the Rafah border crossing between Gaza and Egypt in the next few days as part of the U.S.-brokered ceasefire. According to the World Health Organization, at least 16,500 sick and wounded people need to leave Gaza for medical care. However, the border will only open in one direction: for Palestinians to exit.
Since the ceasefire began, at least 347 Palestinians have been killed in Gaza and 889 injured, according to the Gaza Health Ministry. In one recent incident, two children were killed by an Israeli drone for crossing the so-called yellow line, which isn’t always well marked. The children were brothers Fadi and Juma Abu Assi, the older of whom was 10 years old. They were gathering firewood for their disabled father. The Israeli military acknowledged the strike, saying, quote, “The Air Force eliminated the suspects in order to remove the threat,” unquote. Palestinians report Israeli forces continue to cross the yellow line on a near-daily basis.
This week, a coalition of 12 Israeli human rights groups concluded in a new report that 2025 is already the deadliest and most destructive year for Palestinians since 1967. On Monday, Israeli forces killed two teenagers in the West Bank in separate attacks as they carried out raids across the territory. In Hebron, soldiers fatally shot 17-year-old Muhannad Tariq Muhammad al-Zughair, whom they accused of carrying out a car-ramming attack that injured an Israeli soldier. Elsewhere, 18-year-old Muhammad Raslan Mahmoud Asmar was shot during a raid on his village northwest of Ramallah. Witnesses say the teen was left to bleed out as Israeli forces barred Red Crescent medics from approaching. The soldiers then seized his lifeless body. Last week, the U.N. reported more than 1,000 Palestinians have been killed by Israeli settlers and soldiers in the occupied West Bank and East Jerusalem since October 7, 2023.
This is Jeremy Laurence, spokesperson for the U.N. High Commissioner for Human Rights.
JEREMY
LAURENCE
:
Killings of Palestinians by Israeli security forces and settlers in the occupied West Bank have been surging, without any accountability, even in the rare case when investigations are announced. … Our office has verified that since the 7th of October, 2023, and up until the 27th of November of this year, Israeli forces and settlers killed 1,030 Palestinians in the occupied West Bank, including East Jerusalem. Among these victims were 223 children.
AMY
GOODMAN
:
For more, we’re joined in Ramallah, in the occupied West Bank, by Sari Bashi, an Israeli American human rights lawyer, former program director at Human Rights Watch. Her
piece
for
The New York Review of Books
is headlined “Gaza: The Threat of Partition.” She co-founded Gisha, the leading Israeli human rights group promoting the right to freedom of movement for Palestinians in Gaza.
Sari, welcome back to
Democracy Now!
Let’s begin with the latest breaking news, that Israel in the next few days will open the Rafah border crossing, but only for Palestinians to go one way: out. What is your response?
SARI
BASHI
:
So, obviously, people need to leave. You mentioned the numbers, in thousands, of patients waiting to leave. There are also students who have been accepted to universities abroad. This is after all of Gaza’s universities have been destroyed. So, it’s half good news.
But the bad news is that the Israeli announcement that it will not allow people to return to Gaza validates the fears that many have, that the ceasefire plan, the American plan and the Israeli plan, is essentially to continue the ethnic cleansing of Gaza. So, in Trump’s ceasefire plan, he committed to allowing Palestinians to return to Gaza, but that’s not what’s happening on the ground. There has been no construction authorized in the half of Gaza where Palestinians actually live. There has been no ability for people to come back home, and there are people who want to come back home.
And life in Gaza is nearly impossible for people because of very, very bad conditions. Eighty-one percent of buildings have been destroyed. People are living in tents that are now flooding in the rains. And it is very difficult to get construction and other materials approved by the Israeli authorities.
JUAN
GONZÁLEZ:
Sari, there have also been reports of secret evacuation flights from Gaza. Who is running these flights, and who determines who goes on those flights?
SARI
BASHI
:
I mean, that’s part of the problem. Early in the war, the Israeli government created what it calls the voluntary immigration administration, which is a secret, nontransparent government body that is supposed to encourage people from Gaza to leave, and make it possible for them to do so. There has been almost no transparency about what that organization is doing. There has been deliberate disinformation by Israeli ministers trying to amplify, artificially amplify, the number of people leaving Gaza to make people afraid. And there have been persistent reports about people paying large sums of money in order to leave, and that is after they have reportedly been asked to sign commitments not to return.
On the other hand, there is a very clear statement in the Trump peace plan, which was incorporated into a U.N. Security Council resolution, that people in Gaza are to be allowed to return home. And it’s perhaps not surprising that following the Israeli announcement this morning that Rafah crossing would be opened for exit only, the Egyptian government reportedly issued an objection and said no. It reminded us that the U.S. had promised that people in Gaza would be allowed to come home, as well.
JUAN
GONZÁLEZ:
And could you talk some about the situation in the West Bank, as we’ve reported these constant attacks by settlers and the military? What’s been the position of the Israeli government on this, and what role have the military played in these attacks?
SARI
BASHI
:
I mean, the concern is that the violence in Gaza, the violence in the West Bank, it’s not random. It’s directed toward ethnic cleansing. It’s directed toward getting Palestinians to leave. Certainly, that’s the case in Gaza, and the devastation and violence there are at a much higher scale than in the West Bank. But in the West Bank, too, just this year, we’ve had 2,000 Palestinians forcibly displaced from their homes through a combination of demolitions as well as settler violence. Every single day, Palestinians are injured or their property is damaged by settler attacks.
And to be clear, settlers are Israeli civilians who have been unlawfully transferred to the occupied West Bank by the Israeli government and have been taking over land that belongs to Palestinians. In the last two years, they have become increasingly emboldened in attacking Palestinians, taking over their olive groves, their flocks, stealing, throwing fire bombs into their homes, to the point where, according to the U.N., in 2025, a thousand Palestinians have been injured by settler attacks.
This is decentralized violence, but it is also state-sponsored violence. It is the government who put those settlers in the West Bank unlawfully, and quite often this violence takes place when Israeli soldiers either stand nearby and do nothing or even participate. The very ultranationalistic, messianic ideology has been infiltrated into the Israeli military, where you have soldiers whose job it is to protect everybody, including Palestinians, who are also settlers. And on the weekends, they come out in civilian clothing and attack and sometimes even kill Palestinians.
AMY
GOODMAN
:
And what about the Jewish and Israeli Jewish activists who stand alongside Palestinians to prevent the olive harvest from being destroyed, to prevent people from being attacked, the response of the Israeli military and the settlers?
SARI
BASHI
:
You know, it’s an indication of just how badly things have gotten, how many red lines have been crossed, because it used to be that Jewish settlers would be reluctant to attack Jews, because their ideology is racialized. They believe that they are acting in the name of the Jewish people. But recently, they have attacked Israeli Jews, as well, when Israeli Jews have come to accompany and be in solidarity with Palestinians.
So, we just finished the olive harvest season. It’s a very important cultural and also economic event for Palestinians. And this year it was particularly violent, with Israeli settlers coming, attacking people as they try to harvest, cutting down and burning down trees, intimidating people. And there have been cases even where Israeli Jewish activists came to be a protective force by accompanying Palestinian harvesters, and they, too, were attacked and even taken to the hospital with injuries. It is much more dangerous to be a Palestinian than to be an Israeli Jew in the West Bank, but the fact that settlers are also attacking Jews is an indication of just how violent, messianic and ultranationalistic this movement has become.
AMY
GOODMAN
:
The death toll from Israel’s more than two-year assault on Gaza has been reported as 70,000. A new study from the Max Planck Institute for Demographic Research in Germany said the death toll likely exceeds 100,000. Our next guest, Ralph Nader, has talked about
The Lancet
report saying it’s probably hundreds of thousands. Life expectancy, says the Max Planck Institute, in Gaza fell by 44% in 2023, 47% in 2024. If you can talk about this? And also, what is going to happen in Gaza now, where the truce stands?
SARI
BASHI
:
I mean, the violence against people in Gaza has been unprecedented. It’s been genocidal. And, you know, we have confirmed 70,000 people whose names and ID numbers have been reported by their families to the Palestinian Ministry of Health. There are thousands more people believed to be buried under the rubble, and thousands or tens of thousands of people who died from indirect causes. So, these are people who, because of a deliberate policy of starvation, died of malnutrition, died of communicable diseases, died of want. And I don’t know that we will ever know how many people died indirectly. This is for a very small population. It’s a population of 2 million people.
In Gaza right now, life has remained nearly impossible. The ceasefire promised reconstruction. It promised the crossings to be open. But the U.S. government introduced a number of caveats, and it actually, unfortunately, got those caveats approved by the U.N. Security Council. And an important caveat was that the reconstruction elements of the ceasefire would be limited to areas where Hamas had disarmed. And if the Israeli military was not able to disarm Hamas and other Palestinian armed groups in two years of war, it’s not clear how they’re going to be disarmed now. So, conditioning reconstruction on Hamas disarmament is basically saying reconstruction will be impossible.
The only place where the U.S. has said it will allow reconstruction is in the more than 50% of Gaza that is directly occupied by Israel, that is off-limits to Palestinians on penalty of death. So, that doesn’t leave people in Gaza with any options. Six hundred thousand schoolchildren have no schools. The hospitals are barely functioning. There’s nowhere to live. A million-and-a-half people are in need of emergency shelter supplies. The concern is that the way the ceasefire is being implemented is only going to contribute to ethnic cleansing, because anybody who can leave Gaza will leave, because it’s very clear that there’s not a future being allowed there.
And the United States has an opportunity to make good on its promise in two ways. First of all, it can make it clear that reconstruction has to be authorized in the part of Gaza where Palestinians live, not in the half of Gaza that’s off-limits to them. And second of all, it should demand, as per the ceasefire agreement, that Rafah be open in both directions, also to allow people to come home.
JUAN
GONZÁLEZ:
And I wanted to ask you how the Israeli media is reporting both the breaches in the cease — the constant breaches in the ceasefire agreement, as well as the constant attacks on Palestinians in the West Bank. And what’s been the impact on public opinion since the ceasefire came into effect, Israeli public opinion?
SARI
BASHI
:
You know, I’m very sorry to say that there’s been a long-term trend of the Israeli media becoming more nationalistic and less critical. And that’s been matched by a number of government moves to coopt and take over both public and private media. So, particularly since October 7th, 2023, the Israeli media, with a few notable exceptions, has been reporting government propaganda uncritically. So, what you will hear in the Israeli media is that suspects were shot in Gaza for crossing the yellow line or approaching troops. You won’t hear that those suspects were 10- and 12-year-old boys who were collecting firewood, and that under international law, you can’t shoot children because they cross an invisible line. In the West Bank, you will hear that terrorists were taken out, when you mean ordinary civilians who were trying to harvest their olive trees.
Haaretz
and a few other media outlets have been offering a different view of what’s happening, a more realistic view. But right now many Israelis choose not to — not to see what’s happening either in the West Bank or Gaza. And interest in what’s happening in Gaza has gone way down since the majority of Israeli hostages have been freed.
AMY
GOODMAN
:
Sari Bashi, we want to thank you so much for being with us, Israeli American human rights lawyer, former program director at Human Rights Watch. We’ll link to your
New York Review of Books
article
, “Gaza: The Threat of Partition.”
When we come back, a group of eight senators, led by Bernie Sanders, form a “Fight Club” to challenge Democratic Minority Leader Chuck Schumer’s handling of Trump. We’ll speak with Ralph Nader, who’s been taking on the Democratic Party for decades. Sixty years ago, he published his landmark book,
Unsafe at Any Speed
. Stay with us.
[break]
AMY
GOODMAN
:
“Ishhad Ya ’Alam,” “Bear Witness, O World,” performed by the Palestinian Youth Choir on the B train here in New York. The choir is debuting at Widdi Hall in Brooklyn this evening.
The original content of this program is licensed under a
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License
. Please attribute legal copies of this work to democracynow.org. Some of the work(s) that this program incorporates, however, may be separately licensed. For further information or additional permissions, contact us.
Headlines for December 3, 2025
Democracy Now!
www.democracynow.org
2025-12-03 13:00:00
Hegseth Says He Did Not See Survivors of First U.S. Boat Strike, Citing “Fog of War”, Israel Announces Plans to Reopen Rafah Border Crossing But Only for Palestinians to Leave Gaza, Russia and U.S. Fail to Reach Compromise to End the War in Ukraine, Republican Lawmakers Criticize Trump D...
Hegseth Says He Did Not See Survivors of First U.S. Boat Strike, Citing “Fog of War”
Dec 03, 2025
Defense Secretary Pete Hegseth is attempting to distance himself from the first U.S. airstrike on September 2 that targeted two shipwrecked men who had survived an earlier U.S. strike on a boat the Pentagon says was carrying drugs, without providing evidence. Legal experts say the strike was likely a war crime. Last week, The Washington Post reported Hegseth had given a verbal order to “kill everybody” on the boat. Hegseth spoke Tuesday during a White House Cabinet meeting.
Defense Secretary Pete Hegseth
: “I watched that first strike live. As you can imagine, at the Department of War, we’ve got a lot of things to do. So I didn’t stick around for the hour and two hours, whatever, where all the sensitive site exploitation digitally occurs. So I moved on to my next meeting. A couple of hours later, I learned that that commander had made the — which he had the complete authority to do, and, by the way, Admiral Bradley made the correct decision to ultimately sink the boat and eliminate the threat.”
Hegseth was sitting right next to President Trump during the three-hour Cabinet meeting, in which Trump appeared to fall asleep several times.
Since September, the U.S. has bombed at least 21 boats in the Caribbean Sea and Pacific Ocean, killing more than 80 people.
Meanwhile, the family of a Colombian fisherman killed in a U.S. boat strike on September 15 has filed a complaint against the U.S. with the Inter-American Commission on Human Rights. The family of Alejandro Carranza Medina says he was the victim of an “extrajudicial killing.”
In more news from the region, a bipartisan group of lawmakers have introduced a War Powers Resolution to block the Trump administration from engaging in hostilities against Venezuela without congressional authorization.
Israel Announces Plans to Reopen Rafah Border Crossing But Only for Palestinians to Leave Gaza
Dec 03, 2025
Israel announced that it plans to reopen the Rafah border crossing as part of the U.S.-brokered ceasefire, but only to allow Palestinians to leave Gaza. According to the World Health Organization, more than 16,500 sick and wounded people need to leave Gaza for medical care. This comes as Israel says that the partial remains returned by Hamas do not match the two hostages remaining in Gaza. Palestinian militants are reportedly struggling to find the remains amid the rubble. Meanwhile, Israel has continued its drone strikes in Gaza, killing Palestinian photojournalist Mahmoud Wadi in Khan Younis. This is his father, Issam Wadi.
Issam Wadi
: “As a father, I received the news with shock. It was like an earthquake at home. I live in a tent. The tent was blown away when I lost my son. He was hit in an abnormal strike, which we weren’t expecting on a day like this.”
Russia and U.S. Fail to Reach Compromise to End the War in Ukraine
Dec 03, 2025
President Trump’s envoy Steve Witkoff and son-in-law Jared Kushner met with Russian President Vladimir Putin in Moscow for nearly five hours on Tuesday, but a deal to end the war in Ukraine was not reached. Russian officials described the talks as constructive but said “no compromise” was reached on certain issues. Earlier today, Germany’s foreign minister criticized Russia, saying he had seen “no serious willingness on the Russian side to enter into negotiations.”
Meanwhile, the European Union has agreed to ban natural gas from Russia by late 2027. On Tuesday, Putin warned Europe that Russia was ready for war if it is provoked. This comes as
NATO
Secretary General Mark Rutte vowed to keep up the supply of U.S. weapons to Ukraine.
Mark Rutte
: “The best way to put pressure on the Russians is by doing two things. One is making sure that the Russians understand that the weapon flow into Ukraine will keep on going. That’s exactly what’s happening today, thanks to the U.S., thanks to the Europeans. The U.S. is sending its crucial gear to Ukraine, paid for by Canada and European allies.”
Republican Lawmakers Criticize Trump Decision to Pardon Former Honduran President Hernández
Dec 03, 2025
New details are emerging about President Trump’s decision to pardon former Honduran President Juan Orlando Hernández, who was released from prison on Monday. Hernández was sentenced last year to 45 years in prison for trafficking hundreds of tons of cocaine into the United States. In October, Hernández wrote a four-page letter to Trump seeking a pardon, claiming he had been unfairly targeted by the Biden administration. The letter was delivered by longtime Trump adviser Roger Stone.
Some Republican lawmakers have openly criticized Trump’s decision. Republican Thom Tillis said, “It’s a horrible message. … It’s confusing to say, on the one hand, we should potentially even consider invading Venezuela for a drug trafficker, and on the other hand, let somebody go.” It is unclear if Hernández will attempt to stay in the United States or return to Honduras.
On Tuesday, some Hondurans in the capital Tegucigalpa criticized Trump for freeing Hernández and for meddling in Sunday’s election.
Jorge Meza
: “I am against everything that is happening, because it’s an insult to Honduras, because Honduras really doesn’t deserve this. That’s because of a political aversion. They come and do this to our country, with all the damage Juan Orlando caused here in Honduras. So, all of us as Hondurans feel mocked, because another country comes to interfere in what we should be doing here in our own country.”
This all comes as Honduras continues to count votes from Sunday’s presidential election. The centrist Salvador Nasralla has taken a slim lead over conservative Nasry Asfura, who had been backed by Trump. On social media, Trump has claimed without evidence that Honduran election officials are trying to change the results of the race.
Pentagon Inspector General to Release Report on “Signalgate” Thursday
Dec 03, 2025
The Pentagon’s inspector general is set to release a report Thursday examining Defense Secretary Pete Hegseth’s sharing of sensitive information about U.S. strikes in Yemen on a Signal group chat earlier this year. The group chat, which included other senior members of the Trump administration, was revealed after Jeffrey Goldberg, the editor of The Atlantic, was accidentally added. According to Axios, a full version of the report has been sent to the Senate Armed Services Committee.
Trump Says He Doesn’t Want Somalis in the U.S. as
ICE
Plans Operation Targeting Them
Dec 03, 2025
The Trump administration is launching an
ICE
operation to target hundreds of Somali immigrants in the Minneapolis-St. Paul region, according to reporting by The New York Times. An official speaking to the Times says nearly 100 immigration officers and agents from around the country have been tapped for the operation. The directive comes shortly after President Trump lashed out at the Somali community during a Cabinet meeting, calling them “garbage” he does not want in the country.
President Donald Trump
: “I hear they ripped off — Somalians ripped off that state for billions of dollars, billions, every year, billions of dollars. And they contribute nothing. The welfare is like 88%. They contribute nothing. I don’t want them in our country, I’ll be honest with you, OK? … We could go one way or the other, and we’re going to go the wrong way if we keep taking in garbage into our country. Ilhan Omar is garbage. She’s garbage.”
Democratic Congressmember Ilhan Omar responded to President Trump’s attack in a post on social media, saying, “His obsession with me is creepy. I hope he gets the help he desperately needs.”
Trump Administration to Pause Immigration Applications from Countries on Travel Ban List
Dec 03, 2025
The Trump administration announced that it has paused green card and U.S. citizenship processing for immigrants from 19 countries already subject to a travel ban put in place earlier this year. This follows the Trump administration’s announcement that it was pausing all asylum decisions for immigrants currently in the U.S., after an Afghan national was charged with murdering a National Guard member and critically injuring another in Washington, D.C., last week. He’s pleaded not guilty.
Trump Administration Fires Eight Immigration Judges in New York City
Dec 03, 2025
The Trump administration fired eight immigration judges in New York City on Monday, according to the National Association of Immigration Judges. The fired judges worked at 26 Federal Plaza, which also houses the New York City headquarters for
ICE
. Since President Trump’s return to office, more than 100 immigration judges out of about 700 have been fired or pushed out.
Trump Administration Threatens to Withhold Money for
SNAP
Benefits
Dec 03, 2025
The Trump administration is threatening to withhold money for food benefits under the Supplemental Nutrition Assistance Program in most Democratic-controlled states next week, unless they share information on who exactly is receiving those benefits. Earlier this year, the agriculture secretary had requested the information to verify the eligibility of 42 million recipients. Soon after, 22 states and the District of Columbia sued the U.S. Department of Agriculture over the request. In October, a federal judge issued a temporary injunction that prevents the Department of Agriculture from demanding the data of recipients and cutting
SNAP
funds. On Tuesday, New York Governor Kathy Hochul wrote on social media, “Genuine question: Why is the Trump Administration so hellbent on people going hungry?”
Federal Vaccine Panel Prepares to Vote on Possibly Ending Infant Hepatitis B Vaccines
Dec 03, 2025
In health news, a federal vaccine panel is preparing to vote this week to end the practice of vaccinating all newborns for hepatitis B. The panel, which was handpicked by Health Secretary Robert F. Kennedy Jr., is also expected to discuss making other major changes to the childhood immunization schedule. Sean O’Leary of the American Academy of Pediatrics criticized the move, saying, “Any changes they do make could be devastating to children’s health and public health as a whole.”
Federal Judge Blocks Trump Admin from Cutting Medicaid Funding to Planned Parenthood
Dec 03, 2025
A federal judge in Boston has blocked the Trump administration from cutting Medicaid funding to Planned Parenthood and its affiliates across 22 states. The Democratic-led states sued the Trump administration back in July after the One Big Beautiful Bill contained a provision that barred Medicaid reimbursements to nonprofits that provide abortions. In her ruling, U.S. District Judge Indira Talwani said that the law would “increase the percentage of patients unable to receive birth control and preventive screenings, thereby prompting an increase in states’ healthcare costs.” Planned Parenthood responded to the ruling, saying, “The district court again recognized the 'defund' law for what it is: unconstitutional and dangerous.”
Trump Admin Puts
FEMA
Workers Back on Administrative Leave
Dec 03, 2025
The Trump administration is reversing the reinstatement of 14
FEMA
workers who signed a petition earlier this year warning that cuts to the agency were putting the U.S. at risk of repeating the mistakes made during the response to Hurricane Katrina. Soon after they signed the letter back in August,
FEMA
suspended the workers. Last Wednesday, they were reinstated, but hours later they were suspended again. Jeremy Edwards, a former
FEMA
official who signed the Katrina declaration, said that the back-and-forth over the status of the
FEMA
employees “represents the type of dysfunction and inefficiency that has plagued
FEMA
under this administration.”
Larry Summers Banned from American Economic Association Over Close Ties to Epstein
Dec 03, 2025
Former U.S. treasury secretary and former Harvard President Larry Summers has been banned from the American Economic Association over his close ties to the late Jeffrey Epstein. Recently revealed emails show Summers stayed in close contact with Epstein long after the convicted sex offender’s 2008 conviction.
Republican Matt Van Epps Wins House Special Election by Closer-Than-Expected Margin
Dec 03, 2025
In Tennessee, Republican Matt Van Epps has defeated Democrat Aftyn Behn in a closely watched special election for a U.S. House seat. Van Epps won the race by around 9%, a far smaller margin than Trump’s 22-point victory in the district last year.
More Than 1,350 People Have Now Died in Devastating Floods and Landslides in Sri Lanka, Indonesia and Thailand
Dec 03, 2025
More than 1,350 people have now died in devastating floods and landslides in Sri Lanka, Indonesia and Thailand. Hundreds are still missing. Sri Lanka’s president described the flooding as the “largest and most challenging natural disaster in our history.” In Indonesia, the death toll has topped 700.
The original content of this program is licensed under a
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License
. Please attribute legal copies of this work to democracynow.org. Some of the work(s) that this program incorporates, however, may be separately licensed. For further information or additional permissions, contact us.
Written by me, proof-read by an LLM.
Details at end.
Sometimes you’ll step through code in a debugger and find a complex-looking loop… that executes as a single instruction. The compiler saw through the obfuscation and generated the obvious code anyway.
Consider this assortment of highly questionable unsigned addition routines
1
- for variety, here compiled for ARM (unlike
yesterday’s addition example
).
Despite these all being very different ways of returning
x + y
, the compiler sees through it all and recognises that it’s just a single
add w0, w1, w0
2
instruction. Even the recursive
add_v4
- which calls itself - gets optimised down to the same single instruction
3
.
The compiler’s ability to recognise patterns and replace them with efficient alternatives - even when the code is pretty obfuscated - is a superpower. It lets programmers choose
how
to write their code that’s intention-revealing (not like these contrived examples, obviously!) and leave the code generation up to the compiler, knowing that most of the time it’ll do the right thing.
So how does the compiler spot these patterns? Is it maintaining a database of “silly ways to add numbers”? Not quite. Internally, it translates your code into an intermediate representation - a simplified, abstract form that’s easier to analyse. When the compiler sees the while loop in
add_v3
, it transforms it into something like “increment y by x, then return y”, which it then recognises as mathematically equivalent to “return x + y”. This process of converting different code patterns into a standard, canonical form is what lets the compiler treat them all identically. By the time code generation happens, all four functions look the same to the optimiser
4
.
This pattern recognition is remarkably robust - the compiler will happily optimise code you’d never want to write in the first place. Throughout this series we’ll see how far this canonicalisation can take us.
When a magnitude 8.8 earthquake ripped through the Kuril-Kamchatka subduction zone on July 29, 2025, it launched a Pacific-wide tsunami – and a rare natural experiment.
NASA
and the French space agency’s
SWOT
satellite happened to pass overhead. The satellite captured the first high-resolution, spaceborne swath of a great subduction-zone tsunami.
Instead of a single neat crest racing across the basin, the image revealed a complicated, braided pattern of energy dispersing and scattering over hundreds of miles. These are details that traditional instruments almost never resolve.
The results go well beyond a pretty picture. They suggest the physics we use to forecast tsunami hazards – especially the assumption that the largest ocean-crossing waves travel as largely “non-dispersive” packets – need a revision.
Satellites transform tsunami mapping
Until now, deep-ocean
DART
buoys have been our best open-ocean sentinels: exquisitely sensitive, but sparse, each giving a time series at a single point.
SWOT maps a 75-mile-wide swath of sea surface height in one pass. This lets scientists see the tsunami’s geometry evolve in both space and time.
“I think of SWOT data as a new pair of glasses,” said study lead author Angel Ruiz-Angulo of the
University of Iceland
. “Before, with DARTs we could only see the tsunami at specific points in the vastness of the ocean.”
“There have been other satellites before, but they only see a thin line across a tsunami in the best-case scenario. Now, with SWOT, we can capture a swath up to about 120 kilometers (75 miles) wide, with unprecedented high-resolution data of the sea surface.”
From eddies to a tsunami
NASA and the
French space agency CNES
launched SWOT in December 2022 to survey surface water around the world.
Ruiz-Angulo and co-author Charly de Marez had been poring over its data for ocean
eddies
when the Kamchatka event hit.
“We had been analyzing SWOT data for over two years understanding different processes in the ocean like small eddies, never imagining that we would be fortunate enough to capture a tsunami,” noted the researchers.
Tsunami behavior breaks rules
Classic teaching holds that big, basin-spanning tsunamis behave as shallow-water waves. Their wavelength dwarfs
ocean depth
, so they march along without breaking into separate components.
SWOT’s snapshot argues otherwise for this event. “The SWOT data for this event has challenged the idea of big tsunamis being non-dispersive,” said Ruiz-Angulo.
When the team ran numerical models that included dispersive effects, the simulated wave field matched the satellite pattern far better than “non-dispersive” runs.
That matters because dispersion repackages the wave train’s energy as it approaches land. “The main impact that this observation has for tsunami modelers is that we are missing something in the models we used to run,” said Ruiz-Angulo.
“This ‘extra’ variability could represent that the main wave could be modulated by the trailing waves as it approaches some
coast
. We would need to quantify this excess of dispersive energy and evaluate if it has an impact that was not considered before.”
Blending every clue available
SWOT’s swath told scientists what the wave looked like mid-ocean. DART buoys anchored the timing and amplitude at key points.
Two gauges didn’t line up with tsunami predictions from earlier seismic and geodetic source models – one recorded the waves earlier than expected, and the other recorded them later.
Using an inversion that assimilated the DART records, the researchers revised the rupture. It extended farther south and spanned roughly 249 miles (400 kilometers), not the 186 miles (300 kilometers) that many initial models assumed.
“Ever since the 2011 magnitude 9.0 Tohoku-oki
earthquake
in Japan, we realized that the tsunami data had really valuable information for constraining shallow slip,” said study co-author Diego Melgar. Folding that information in isn’t yet routine.
As Melgar argued, this is because the hydrodynamic models needed to model DARTs are very different from the seismic wave propagation ones for modeling the solid Earth data.
“But, as shown here again, it is really important we mix as many types of data as possible.”
Old quakes guide new warnings
The Kuril–Kamchatka margin has a history of producing ocean-wide tsunamis. A magnitude 9.0 quake in 1952 helped motivate the
Pacific
’s international alert system, which issued basin-scale warnings during the 2025 event.
SWOT’s pass adds a new kind of evidence to that warning toolbox. With enough luck and coordination, scientists could use similar swaths to validate and improve real-time models.
This will be especially important if dispersion turns out to shape near-coast impacts more than we thought.
“With some luck, maybe one day results like ours can be used to justify why these satellite observations are needed for real or near-real time forecasting,” Ruiz-Angulo said.
A turning point for tsunami forecasts
Three takeaways emerge. First, high-resolution satellite altimetry can see the internal structure of a tsunami in mid-ocean, not just its presence.
Second, researchers now argue that dispersion – often downplayed for great events – may shape how energy spreads into leading and trailing waves, which could alter run-up timing and the force on harbor structures.
Third, combining satellite swaths, DART time series,
seismic
records, and geodetic deformation gives a more faithful picture of the source and its evolution along strike.
For tsunami modelers and hazard planners, the message is equal parts caution and opportunity.
The physics now has to catch up with the complexity that SWOT has revealed, and planners need forecasting systems that can merge every available data stream. The
waves
won’t get any simpler – but our predictions can get a lot sharper.
Reader warning: there's gonna be a lot of pretend puke photos in this post.
If you've fired up HBO Max recently, you've probably seen that one of the most influential and prestigious television series of all time was to premiere in 4K on the streaming service. The show's first four seasons were shot on film, and the final three were shot digitally on the Alexa, but the run of the series was mastered in 1080p HD. HBO Max has been touting this 4K "restoration" of the series, produced by Lionsgate TV.
The highly anticipated 4K debut of the show was to be one of HBO Max' crown jewels of television history. It looks like it might initially serve as a cautionary tale of quality control when it comes to restorations and the technical process of bringing shows to streaming.
As far as I can tell,
Paul Haine was the first to notice something weird
going on with HBO Max' presentation. In one of season one's most memorable moments, Roger Sterling barfs in front of clients after climbing many flights of stairs. As a surprise to Paul, you can clearly see the pretend puke hose (that is ultimately strapped to the back side of John Slattery's face) in the background, along with two techs who are modulating the flow. Yeah, you're not supposed to see that.
It appears as though this represents the original photography, unaltered before digital visual effects got involved. Somehow, this episode (along with many others) do not include all the digital visual effects that were in the original broadcasts and home video releases. It's a bizarro mistake for Lionsgate and HBO Max to make and not discover until after the show was streaming to customers.
• • • • •
I want to be clear that this is a separate issue than the "reframed original film negative for 16:9" issue that has plagued many restorations that have left viewers scratching their heads. In those cases, the shows were originally shot on film and presented in 1.33-to-1 aspect ratio, but for their HD restorations the studio decided that their shows should fill the HD frame at the 16:9 aspect ratio, so portions of the negative, previously unseen and NOT intended for broadcast, were now suddenly visible,
sometimes leading to ridiculous images that were never meant to be seen by audiences
...
example from "Friends" in HD, look at screen right
example from "Seinfeld" in HD
Reframing old shows to fit a new aspect ratio is antithetical to the spirit of media restoration, and cheapens the future of our shared culture. The folks at the studios who insist on hobbling their most classic television shows are really bad at their jobs.
But that's NOT what is going on with "Mad Men", since the show was mastered in 16:9 to begin with.
• • • • •
I decided to help illustrate the changes
by diving in and creating images that might do better than words. The first thing I noticed is that, at least for season one, the episode titles and order were totally jumbled. The puke episode is "Red in the Face", not "Babylon".
Update
: the season one episodes are being updated live on HBO Max to their correct positions and titles. The corrected title:
I lined up the Blu-ray edition of the episode with the current HBO Max episode:
The fun thing about this restoration mistake is that now we, the audience, get to see exactly how many digital visual effects were actually used in a show like "Mad Men",
which most would assume did not have any digital effects component. In this shot, not only were the techs and hose removed, but the spot where the pretend puke meets Slattery's face has some clever digital warping to make it seem like the flow is truly coming from his mouth (as opposed to it appearing through a tube inches from his mouth, on the other side of his face).
A Twitter user noticed
that the post-production screwups are not exclusive to season one, so I fired up my comparison machine to illustrate it.
In this case, visual effects was used to obscure the fact that the show was filmed in 2000's era Los Angeles, not in 1960's New York City. Every sign was altered, and period-appropriate garbage NYC garbage cans were also added to each side of the frame.
Are we repeating the telecoms crash with AI datacenters?
I keep hearing the AI datacentre boom compared to the 2000s telecoms crash. The parallels seem obvious - billions in infrastructure spending, concerns about overbuilding, warnings of an imminent bubble. But when I actually ran the numbers, the fundamentals look completely different.
I'm not here to predict whether there will or won't be a crash or correction. I just want to look at whether the comparison to telecoms actually holds up when you examine the history in a bit more detail.
What Actually Happened in the Telecoms Crash
Let me start with what the 2000s telecoms crash actually looked like, because the details matter. Firstly, there was massive capex - between 1995 and 2000 somewhere like
$2 trillion was spent laying 80-90 million miles of fiber
. Inflation adjusted, this is over $4trillion, or close to
$1trillion/year
in 2025 dollars.
How did this happen? A catastrophic supply and demand miscalculation past the pure securities fraud involved in many of the companies. Telecom CEOs
claimed
internet traffic was doubling every 3-4 months.
But in reality,
traffic was doubling roughly every 12 months
. That's a
4x overestimate
of demand growth, which compounds each year. This false assumption drove massive debt-financed overbuilding. If you overestimate 4x a year for 3 years, by the end of your scenario you are 256x out.
Even worse
for these companies, enormous strides were made on the optical transceivers, allowing the same fibre to carry 100,000x more traffic over the following decade. Just one example is WDM multiplexing, allowing multiple carriers to be multiplexed on the same physical fibre line. In 1995 state of the art was 4-8 carriers. By 2000, it was 128. This
alone
allowed a 64x increase in capacity with the same infrastructure. Combined with improvements in modulation techniques, error correction, and the bits per second each carrier could handle, the same physical fibre became exponentially more capable.
The key dynamic: supply improvements were exponential while demand was merely linear. While some physical infrastructure needed to be built, there was enormous overbuilding that could mostly be serviced by technology improvements on the same infrastructure.
AI Infrastructure: A Different Story
Unlike fibre optics in the 1990s, GPU performance per watt improvements are actually slowing down:
2015-2020 Period:
Performance per watt improved significantly with major architectural changes
Process nodes jumped from ~20nm to 7nm (major efficiency gains)
Introduction of Tensor Cores and specialized AI hardware
This is the opposite of what happened in telecoms. We're not seeing exponential efficiency gains that make existing infrastructure obsolete. Instead, we're seeing semiconductor physics hitting fundamental limits.
The B200 from NVidia also requires liquid cooling - which means most datacentres designed for air cooling need to be completely retrofitted.
Demand Growth Is Actually Accelerating
The telecoms crash happened partly because demand was overestimated by 4x. What does AI demand growth look like?
Traditional LLM Usage:
ChatGPT averages 20+ prompts per user per day
. Extended conversations can reach 3,000-4,000 tokens cumulative, though many users treat it like Google - short "searches" with no follow-up, consuming surprisingly few tokens.
Coding agents:
150,000+ tokens per session
(multiple sessions daily)
We're looking at a fundamentally different demand curve - if anything, people are underestimating how much agents will consume. The shift from chat to agents represents a 10x-100x increase in token consumption per user.
We're not even there yet, and infrastructure is already maxed out, with AI infrastructure running at very high utilization rates. Major providers still experience peak-time capacity issues. The problem isn't unused infrastructure sitting idle; it's infrastructure struggling to meet current demand. One major hyperscaler told me they
still
have capacity issues at peak times causing free tier users to have high error rates.
Datacenter CapEx: Evolution, Not Revolution
Another important piece of context that gets missed:
2025 projected:
$255B+
(Amazon $100B, Microsoft $80B, Alphabet $75B)
While it's no doubt a huge amount of capex going into this rollout; it's not quite as dramatic as some news stories make out. I have no doubt that now any datacentre related capex is being rebranded as "AI", even if it's just 'boring' old compute, storage and network not being directly used for AI.
Why Forecasting Is Nearly Impossible
Here's where I think the comparison to telecoms becomes both interesting and concerning.
The Lead Time Problem:
Datacenters take 2-3 years to build
GPU orders have 6-12 month lead times
Can't adjust capacity in real-time to match demand
The Prisoner's Dilemma:
Underestimating demand = terrible user experience + losing to competitors
Overestimating demand = billions in wasted capex (that might just get used slower)
Given the choice, rational players overbuild - because wasting some capex is infinitely better than losing the "AI wars"
The Forecasting Challenge:
Imagine you're planning datacenter capacity right now for 2027. You need to make billion-dollar decisions today based on what you think AI usage will look like in three years.
Here's scenario one: agent adoption is gradual. Some developers use Claude Code daily. A few enterprises deploy internal agents. Customer service stays mostly human with AI assist. You need maybe 3-4x your current infrastructure.
Here's scenario two: agents go mainstream. Every developer has an always-on coding agent consuming millions of tokens per session. Enterprises deploy agents across operations, finance, legal, sales. Customer service becomes 80% agentic with humans handling escalations. You need 30-50x your current infrastructure.
Both scenarios are completely plausible. Nobody can tell you which one is right. But you have to commit billions in capex NOW - datacenters take 2-3 years to build, GPU orders have 6-12 month lead times.
But here's the really insidious part:
even if you're directionally right, small errors compound massively. Let's say you're confident agents are going mainstream and you need roughly 50x growth over 3 years.
If actual demand is 40x, you've overbuilt by 25% - billions in excess capacity.
If actual demand is 60x, you've underbuilt by 20% - your service degrades and you lose market share.
You're trying to hit a moving target in the dark, and the margin of error is measured in tens of billions of dollars and thousands of megawatts of power infrastructure.
If you build for scenario one and scenario two happens, your service degrades to unusable, users revolt, and you lose the AI wars to competitors who bet bigger. If you build for scenario two and scenario one happens, you've got billions in underutilized datacenters burning cash.
Which mistake would you rather make?
This is where the telecoms comparison makes sense: given those choices, rational players overbuild. The difference is what happens to that overcapacity.
The Key Differences
Let me put this in a table:
Factor
Telecoms (1990s-2000s)
AI Datacenters (2020s)
Supply improvements
Exponential (100,000x capacity increase)
Slowing (69%→44% annual perf/watt gains)
Demand growth
Overestimated 4x
Potentially underestimated (agent transition)
Utilization
95% dark fiber (genuine overcapacity)
Very high - many providers still experiencing peak time scale problems
Technology curve
Making infrastructure obsolete
Hitting semiconductor physics limits
Power consumption
Decreasing
Increasing (300W → 1200W)
Infrastructure lifespan
Decades (fiber doesn't degrade)
Years (refreshed as better hardware arrives)
The telecoms crash happened because exponential supply improvements met linearly growing (and overestimated) demand, with infrastructure that would last decades sitting unused.
AI datacenters are facing slowing supply improvements meeting potentially exponentially growing demand. And crucially, because GPU efficiency improvements are slowing down, today's hardware retains value for longer - not shorter - than previous generations.
What About a Short-Term Correction?
Could there still be a short-term crash? Absolutely.
Scenarios that could trigger a correction:
1. Agent adoption hits a wall
Enterprises might discover that production agent deployments are harder than demos suggest. Hallucinations in high-stakes workflows, regulatory concerns around autonomous AI systems, or implementation complexity could slow adoption dramatically. If the agent future takes 5-7 years instead of 2-3, there's a painful gap where billions in infrastructure sits waiting for demand to catch up.
However, given the explosion in usage for software engineering and other tasks, I suspect this is highly unlikely. You can already use Claude Code for
non engineering tasks
in professional services and get very impressive results without any industry specific modifications, so I have no doubt there is going to be very high adoption of agents in all kinds of areas.
2. Financial engineering unravels
These datacenter buildouts are heavily debt-financed. If credit markets seize up, interest rates spike further, or lenders lose confidence in AI growth projections, the financing model could collapse. This wouldn't be about technical fundamentals - it would be good old-fashioned financial panic, similar to what happened in telecoms when the debt markets froze, but with one key difference - a lot of the key players (Microsoft, Google, Meta, Oracle) are extremely cash flow positive, which definitely wasn't the case in the 2000s fibre boom. The pure datacentre players though are at risk - who don't have a money printing main business to backstop the finance - no doubt about that.
3. Efficiency breakthroughs change the math
Model efficiency could improve faster than expected. Or we could see a hardware breakthrough: custom ASICs that are 10x more efficient than GB200s for inference workloads. Either scenario could make current buildouts look excessive. I actually think this is the biggest risk - and this is
exactly
what happened in the fibre boom. So far, I'm not seeing signs of this though. While specialist ASICs are becoming available, they hit their impressive speed by having huge wafers, which isn't a huge efficiency game (yet).
The Key Difference From Telecoms:
Even if there's a correction, the underlying dynamics are different. Telecoms built for demand that was 4x overestimated, then watched fiber optic technology improvements make their infrastructure obsolete before it could be utilized. The result: 95% of fiber remained permanently dark.
AI datacenters might face a different scenario. If we build for 50x growth and only get 30x over 3 years, that's not "dark infrastructure" - that's just infrastructure that gets utilized on a slower timeline than expected. Unlike fiber optic cable sitting in the ground unused, GPU clusters still serve production workloads, just at lower capacity than planned.
And unlike telecoms where exponential technology improvements made old infrastructure worthless, GPU efficiency improvements are slowing. A GB200 deployed today doesn't become obsolete when next year's chip arrives - because that chip is only incrementally better, not 100x better. With process node improvements slowing down, current generation hardware actually retains value for longer.
A correction might mean 2-3 years of financial pain, consolidation, and write-downs as demand catches up to capacity. But that's fundamentally different from building infrastructure for demand that never materializes while technology makes it obsolete.
The Real Risk: Timing, Not Direction
I think the real question isn't whether we need massive AI infrastructure - the agent transition alone suggests we do. The question is timing.
If enterprises take 5 years to adopt agents at scale instead of 2 years, and hyperscalers have built for the 2-year scenario, you could see a 2-3 year period of overcapacity and financial pain. That might be enough to trigger a correction, layoffs, and consolidation.
But unlike telecoms, that overcapacity would likely get absorbed.
The telecom fibre mostly stayed dark because technology outpaced it and demand never materialized. AI infrastructure might just be early, not wrong.
Conclusion
Are we repeating the telecoms crash with AI datacenters? The fundamentals suggest not, but that doesn't mean there won't be bumps.
The key insight people miss when making the telecoms comparison: telecoms had exponential supply improvements meeting linear demand, with 4x overestimated growth assumptions. AI has slowing supply improvements potentially meeting exponential demand growth from the agent transition.
The risks are different:
Telecoms:
Built too much infrastructure that became completely obsolete by supply-side technology improvements
AI:
Might build too much too fast for demand that arrives slower than expected
But the "too much" in AI's case is more like "3 years of runway instead of 1 year" rather than "95% will never be used."
I could be wrong. Maybe agent adoption stalls, maybe model efficiency makes current infrastructure obsolete, maybe there's a breakthrough in GPU architecture that changes everything. But when I look at the numbers, I don't see the same setup as the telecoms crash.
The fundamentals are different. That doesn't mean there won't be pain, consolidation, or failures. But comparing this to 2000s telecoms seems like the wrong mental model for what's actually happening.
India scraps order to pre-install state-run cyber safety app on smartphones
India has scrapped an order making it mandatory for smartphone makers to preload a state-run cyber safety app on new phones after a public furore.
The order gave smartphone makers 90 days to pre-load new phones with its new Sanchar Saathi app which could not be "disabled or restricted",
sparking privacy and surveillance concerns
.
The government argued the move was necessary to verify the authenticity of handsets, but cybersecurity experts said it impinged on citizens' right to privacy.
Withdrawing the order on Wednesday, the government cited the app's "increasing acceptance". It came after Apple and Samsung had reportedly resisted the directive to pre-install it on their devices.
So far 14 million users have downloaded the app, reporting 2,000 frauds daily, and on Tuesday alone 600,000 new users registered - a tenfold spike, according to India's telecom ministry.
But the order
- passed last week but made public on Monday -
to make the registration mandatory had led to a major backlash from several cybersecurity experts.
Smartphone giants like Apple and Samsung also resisted the directive to pre-install the app on their phones.
Sources told the BBC the companies were concerned the directive was issued without prior consultation and challenged user privacy norms.
While the order has now been withdrawn, India's Minister of Communications Jyotiraditya Scindia dismissed concerns that the app could be used to increase surveillance.
"Snooping is neither possible nor will it happen with the Sanchar Saathi safety app," Scindia said.
The government's decision to reverse the order was welcomed by digital advocacy groups.
"This is a welcome development, but we are still awaiting the full text of the legal order that should accompany this announcement, including any revised directions under the Cyber Security Rules, 2024," the Internet Freedom Foundation said on X.
"For now, we should treat this as cautious optimism, not closure, until the formal legal direction is published and independently confirmed."
Donald Scott was killed in his home by an ad hoc team of raiding cops who were looking for marijuana — but the larger prize may have been his 200-acre Malibu ranch.
The post Episode Eight: Legalized Takings appeared first on The Intercept....
In 1992,
Donald Scott, the eccentric owner of a large Malibu estate, was killed in his home by an ad hoc team of raiding cops. The Los Angeles County Sheriff’s Department led the raid, but a panoply of state and federal police agencies participated too. Police claimed Scott was operating a large marijuana grow on the property. Scott, who always feared the government would take his land, actually repudiated the use of illegal drugs.
No marijuana or any illicit drugs were found on his property. A subsequent investigation by the local district attorney confirmed Scott wasn’t paranoid: The LA County Sheriff’s Department was motivated by a desire to take Scott’s property under civil asset forfeiture laws, auction it off, and keep the proceeds for the department. Bizarrely, Scott’s home wasn’t even in LA County. Despite recent reform efforts, the promise of forfeiture continues to be a major motivating force in drug policy across the country.
Transcript
Radley Balko:
In the early hours of October 2, 1992, a wealthy, eccentric Californian named Donald Scott and his younger artistic wife Frances were up late drinking, as they often were. The couple eventually passed out in the bedroom of their large cabin in Malibu at around 2 or 3 a.m.
As they fell asleep, they may have heard the waterfall that splashed down onto their sprawling 200-acre property. They called it “Trail’s End Ranch.” And then just before 9 a.m., Frances Plante Scott awoke with a start.
Frances Plante Scott:
We were in bed asleep, and the house started shaking, and the dogs were going crazy and … [sigh]
Frances Plante Scott:
I got up as fast as I could to get dressed. And I was going to the door, and I see this face looking at me. At that point, the door burst open, and I just saw all these guns. These men had guns, and I didn’t know who they were or what they were doing.
Radley Balko:
As Plante threw on a shirt and pair of overalls, a team of 30 law enforcement officers loomed near the entrance to her home.
The raid team was an alphabet soup of police and government agencies, including officers from the Los Angeles Sheriff’s Department, the Drug Enforcement agency, the California Bureau of Narcotics, the U.S. Forest Service, the Los Angeles Police Department, the National Park Service, the California National Guard — and there were even a couple of researchers from NASA’s Jet Propulsion Lab. Notably, the raid team didn’t include a single police officer from Ventura County, where the ranch was actually located.
The motley crew of heavily armed officials had made their way up the winding road to the ranch in 15 different vehicles. Now they were inside Plante’s home, with their guns drawn.
Frances Plante Scott:
I just screamed, “Don’t shoot me, don’t kill me,” and I was backing into my living room. My husband heard me. He came running out of the back of the house into the living room. I heard him say, “Frances, are you all right?”
Radley Balko:
Unsure of what was causing all of the commotion, Plante’s husband Donald Scott grabbed the .38 revolver on his nightstand. He was groggy, and his vision was likely still foggy from recent cataract surgery.
Frances Plante Scott:
He had his gun pointed above his head. He looked at me, and the next thing, someone yelled, “Put your gun down, put your gun down, put your gun down.” Bang, bang, bang. My husband fell down right in front of me.
Capt. Richard DeWitt:
Looks like 927D here.
Dispatch:
At the location?
Capt. Richard DeWitt:
Yeah.
Dispatch:
Some bodies there?
Capt. Richard DeWitt:
No, we put ’em down.
Dispatch:
We killed him?
Capt. Richard DeWitt:
Yeah.
Radley Balko:
That’s Capt. Richard DeWitt of the Los Angeles County Sheriff’s Department, on the phone with his commanding officer. You can hear the surprise on the other end of the line, as the commander learned that someone had been killed.
What had Donald Scott done? What merited this sort of overwhelming police response?
Scott wasn’t a murderer or an arms dealer. He wasn’t an escaped felon or a dangerous fugitive. Instead, the police claimed on their search warrant affidavit that he was growing marijuana.
Bill Aylesworth:
They couldn’t care less about the weed if there was any there. Basically, they wanted the land.
Radley Balko:
In the years leading up to the raid on his home, Donald Scott’s friends and family said that he had grown increasingly paranoid that the government wanted to take his property from him.
Frances Plante Scott:
He had a feeling that, it was just a feeling that they were going to try to get the land from him somehow. He thought that they wanted the land to the point of where they would kill him for this land.
Radley Balko:
It turns out that Donald Scott was right. The government really did want his property. A lengthy Ventura County District Attorney
investigation
confirmed Scott’s suspicions and concluded that seizing his ranch was one of the motivating factors for obtaining and serving the search warrant.
The lead LA County Sheriff deputy on the case filed an affidavit claiming that there was a marijuana grow on the property. If the agency uncovered it, they might be able to seize all 200 acres of Trail’s End Ranch under civil asset forfeiture laws, and then they could auction it off. The millions of dollars in proceeds would go right back to the LA Sheriff’s Department and the other participating agencies. The raiding officers would be heroes. It was the sort of bust that could make a cop’s career.
Except that isn’t what happened. There was no major marijuana operation. In fact, there wasn’t a single marijuana plant anywhere on the property.
Dan Alban:
At the end of the day, they were just looking for an excuse to invade his ranch, search everything, and find some basis for the seizure — which, in this case, they didn’t find.
Radley Balko:
For the next decade, the dispute over what exactly happened that morning at Trail’s End would fuel countless national news stories, lawsuits, and defamation claims. It would pit the Ventura County district attorney’s office against the LA Sheriff’s Department and the state attorney general’s office. Those latter two agencies would issue their own findings exonerating the sheriff’s deputies for Scott’s death.
It would also spur a furious debate over the policy of civil asset forfeiture, and would become just the latest in a series of corruption and brutality scandals to rock the largest sheriff’s department in the country.
I’m
Radley Balko
. I’m an investigative journalist who has been covering the drug war and the criminal justice system for more than 20 years.
The so-called “war on drugs” began as a metaphor to demonstrate the country’s fervent commitment to defeating drug addiction, but the “war” part quickly became all too literal.
When the drug war ramped up in the 1980s and ’90s, it brought helicopters, tanks, and SWAT teams to U.S. neighborhoods. It brought dehumanizing rhetoric, and the suspension of basic civil liberties protections. All wars have collateral damage: the people whose deaths are tragic but deemed necessary for the greater cause. But once the country dehumanized people suspected of using and selling drugs, we were more willing to accept some collateral damage.
In the modern war on drugs — which dates back more than 50 years to the Nixon administration — the United States has produced laws and policies ensuring that collateral damage isn’t just tolerated, it’s inevitable.
This is Episode Eight, “Legalized Takings: The Land Grab That Killed Donald Scott.”
Donald Scott led a privileged life.
He was raised in Switzerland, attended elite prep schools in New York, and he lived off of a trust fund.
The Scott family fortune was fueled by his grandfather’s invention: Scott’s Emulsion, a cod liver oil supplement marketed as a cure-all. It took off in the U.S. and Europe, and it’s still popular in parts of Asia.
Scott’s Emulsion
ad:
Scott’s Emulsion, I like you. You help me to grow. Mmm, I like it!
Radley Balko:
Scott’s jet-setting life was eccentric, worldly, tumultuous, and saturated with booze. He consorted with Hollywood stars and starlets, raced Ferraris, and generally relished the role of an international playboy. He bounced all over the globe.
In the 1960s, he had a six-year relationship with the glamorous French actress Corinne Calvet. That relationship ended badly, as did his next marriage. But later in life, Scott settled down with Frances Plante, an aspiring country music singer 23 years his junior.
Frances Plante Scott’s song “
Drunk on Pain
” plays:
I’m drunk on pain. / It’s driving me insane.
Bill Aylesworth:
Frances was from Texas, Galveston. She was a red-headed, hot-fired, wild, high-energy lunatic and absolutely gorgeous as well. Just an amazing person.
Radley Balko:
That’s Bill Aylesworth. Nearly a decade after Donald Scott was killed, Aylesworth met and became romantically involved with Plante, Scott’s widow. And from her, Aylesworth became intimately familiar with the story of Trail’s End.
Bill Aylesworth:
Spending that much time with her, four and a half years. I wrote a treatment for the whole thing. All I would hear is her all day long talking about it. She was obsessed with it.
Radley Balko:
Aylesworth also collaborated with Plante professionally and produced some of her music.
Frances Plante Scott’s song
“I Tried It”
plays:
I wanna shake more than your hand, Tammy Wynette
.
Radley Balko:
Donald Scott bought the lush Malibu property known as Trail’s End in the 1960s. Over the years, he’d converted it into a hideaway, transforming it into a surrogate of the grand mansion he grew up in Geneva. It was also a sanctuary for his eclectic collection of books, Persian rugs, and ancient maps.
Friends said Scott could also be incredibly generous to those he trusted. For example, gifting a collector’s model 1959 Cadillac Eldorado to a friend and family attorney named Nick Gutsue. But Scott was also worn down by years of legal fights with his ex-wives over money. He grew reclusive and began drinking more heavily. He also became increasingly distrustful of the government. Scott had stopped filing federal income tax returns, and he was worried that the government had designs on the property that had become such an important part of his identity.
Bill Aylesworth:
So it’s 200 acres. I mean, just unbelievable, right? And it’s so attractive that the park service, National Park Service, owned all of the property on either side of Donald’s property.
Radley Balko:
Trail’s Ends Ranch was hidden by a dense thicket of heavily vegetated forest dominated by oak and sycamore trees. It sat in the Santa Monica Mountains, about 4 miles from the Pacific Ocean.
Scott and Plante lived in a 1,000-square foot stone and wood ranch-style cabin about a quarter mile in on the property. It also included a bunkhouse and a barn. On three sides, Trail’s End was framed by towering cliffs, streams, and a 75-foot waterfall. But amid all of that canopied tranquility, the creeping border of federal parkland was causing Scott persistent anxiety.
The Santa Monica Mountains National Recreation Area had acquired parcels bordering Scott’s ranch. His relationship with the park’s administrator, the National Park Service, had been contentious. Scott complained that visitors were harming his property. He said hikers would throw or kick rocks into the waterfall. Scott also suspected that the government wanted to absorb Trail’s End into the parkland.
Bill Aylesworth:
It wasn’t paranoia because they were actually coming up, making offers to buy it. That’s not paranoid, saying, “They want to take my land.” They want to take your land!
Radley Balko:
The National Park Service denied it offered to buy the ranch or had any plans to seize or condemn it. Additional reporting over the years hasn’t supported that claim. But a former park ranger and a superintendent of the park revealed Scott’s land was of interest.
Bill Aylesworth:
They wanted his land, and he didn’t want to sell it. So they came up with a scheme to get it for free: Just take it from him.
“They wanted his land, and he didn’t want to sell it. So they came up with a scheme to get it for free: Just take it from him.”
Radley Balko:
And Scott’s land wasn’t just beautiful; his 200 acres in Ventura County was worth millions. And according to a subsequent report by a Ventura County district attorney, police agencies in the area had also taken notice.
Dan Alban:
This is pretty classic policing for profit.
Radley Balko:
Dan Alban
is a senior attorney at the libertarian law firm the Institute for Justice. He co-directs the firm’s national initiative to end forfeiture abuse.
Dan Alban:
There was a $5 million estate. There was an eccentric millionaire who was suspected of somehow being involved in growing marijuana plants. And the idea was, if we can catch him in the act — catch him with these marijuana plants — then regardless of what the penalty would be for having 50 to 100 marijuana plants, we could seize the entire estate and then sell it off to someone and pocket the $5 million.
Radley Balko:
The LA County Sheriff’s Office spent nearly a year investigating Scott’s alleged marijuana operation. In the end, they found nothing. Not a single plant.
At the core of their strategy was a legal concept called civil asset forfeiture.
Dan Alban:
Asset forfeiture law has its origins in 17th-century English maritime law. England was in a trade war at the time with various other countries, including Spain.
Radley Balko:
England passed laws saying they could seize ships or cargo that had been involved in smuggling or piracy.
Dan Alban:
And the reason was if a ship was smuggling goods into your port, and you’re England, you want to prosecute the owner of the ship, but the owner of the ship is very rarely on the ship. The owner of the ship is back in Lisbon or Madrid or somewhere. And so there’s no way to actually exact justice on that person or deter them from behaving badly in the future. And so, because you didn’t have jurisdiction over the actual people committing the criminal acts, or at least not all of them, the way to resolve that and to enforce these various customs laws that England was trying to enforce was to seize the ship, or to seize the goods, or both, and forfeit them to the crown.
Radley Balko:
The early American colonies adopted similar asset forfeiture laws. And while the Supreme Court expanded them during the Civil War, they were used only sparingly. But that changed with alcohol prohibition in the 1920s.
Dan Alban:
The originally very narrow concept of forfeiture that was used in maritime law was expanded during Prohibition. Because during Prohibition, people weren’t just smuggling in rum and alcohol by ships, but they were also bringing it over the Canadian border and the Mexican border by trucks. And so it was a natural analogy to say, “Oh, well, you know, they aren’t ships exactly, they’re sort of ships of land that have wheels on them. We’re going to seize those too.”
And then when the war on drugs really began in earnest in the ’70s and ’80s, forfeiture was pulled out again as, “Oh, here’s a tool that we can use to scoop up as much property as we can, and anything that was somehow involved in drug trafficking or that we think was somehow involved in drug trafficking is now forfeit to the state.”
Radley Balko:
And this is where asset forfeiture really starts to go off the rails. Under the old common-law rules, law enforcement agencies could take the property of someone who had been convicted of a crime, on the theory that criminals shouldn’t be enriched by ill-gotten gains. Known as criminal forfeiture, it thus required a criminal conviction.
The practice of civil forfeiture — in which a conviction is not needed, just probable cause — was rarely used until the 1970s.
The practice of civil forfeiture — in which a conviction is not needed, just probable cause — was rarely used until the 1970s. That’s when Congress passed bills that allowed police to seize narcotics and anything used to manufacture or distribute them.
As the drug war ramped up in the early 1980s, Congress introduced additional bills to expand civil forfeiture. The Comprehensive Forfeiture Act, signed into law by Ronald Reagan in 1984, allowed for a wider range of property to be eligible for seizure. It also empowered law enforcement to confiscate property like cash, vehicles, and homes, without even an arrest. A property owner would then have to contest the seizure in court in order to get their stuff back.
Dan Alban:
They don’t have to be charged with a crime. They don’t have to be convicted.
Radley Balko:
But even under that 1984 law, any forfeiture proceeds still went into the U.S. Treasury’s general fund. It was in 1986 that Congress added an amendment that would dramatically change drug policing in the United States — and ultimately would lead to the death of Donald Scott.
Under the 1986 amendment, federal law enforcement agencies themselves could keep any cars, cash, or other assets that they seize. Or they can auction them off. The cash and proceeds from those auctions would then go back to both the federal law enforcement agency, and to any state or local police departments involved in the case. In Donald Scott’s case, because the LA Sheriff’s Department was the lead agency in the investigation, they stood to benefit the most.
Ronald Reagan
: You can increase the price by cutting down on the supply, by confiscation of the means of delivery, and so forth. The government, right now, already owns quite a fleet of yachts and airplanes and trucks and so forth that have been involved in that trade and that we have already intercepted.
Radley Balko:
Police now had a clear financial incentive to seize property and to devote more resources to drug policing. Every drug bust now brought the potential for new police gear, office improvements, and “professional development” trips to conferences at sunny destinations.
Dan Alban:
The money is sent to a dedicated fund that’s controlled by DOJ and the law enforcement agencies under DOJ, like DEA and FBI, and can only be spent on what they call “law enforcement purposes” — which is essentially anything they want to spend money on because they’re law enforcement.
Radley Balko:
This change to incentivize police to seize property has wrought a sea change in drug policing, and it was the brainchild of a couple familiar names. One of them was an up-and-coming U.S. attorney in New York.
This change to incentivize police to seize property has wrought a sea change in drug policing.
Dan Alban:
And so that change, which, yes, was championed by Rudy Giuliani.
Radley Balko:
And another architect of the policy was a senator from Delaware named
Joe Biden
.
Joe Biden:
We changed the law so that if you are arrested and you are a drug dealer, under our forfeiture statutes, you can, the government can take everything you own. Everything from your car to your house, your bank account. Not merely what they confiscate in terms of the dollars from the transaction that you just got caught engaging in. They can take everything.
“It suddenly became this free-for-all where any property that you could find that you thought was somehow connected to a crime, you would seize and try to forfeit because at the end of the day, your agency … got the proceeds.”
Dan Alban:
That law, as well as a few others that were passed around the same time in the early to mid-’80s, really changed how civil forfeiture was used in the United States. Instead of it being this kind of obscure area of law that was very rarely used and only in exceptional circumstances when you can’t actually bring the perpetrator within your jurisdiction, it suddenly became this free-for-all where any property that you could find that you thought was somehow connected to a crime, you would seize and try to forfeit because at the end of the day, your agency — or at least DOJ, which your agency was under — got the proceeds from that forfeiture.
And so this created this huge off-budget slush fund that DOJ and its agencies could use to fund all sorts of things. And many states followed suit, creating their own funds or allowing counties to create their own funds, so that at the state and county levels, this same profit incentive was replicated all across the country. And that led to a huge explosion in forfeiture.
Radley Balko:
Forfeiture proceeds are basically slush funds for police and prosecutors. In many jurisdictions, there’s little oversight or accounting. Over the years, police officials have spent forfeiture funds on purchases that you might say aren’t exactly critical to the practice of law enforcement.
One district attorney in Texas used forfeiture money to purchase kegs of beer, bottles of rum and tequila, and a margarita machine for his office. A South Carolina sheriff’s office spent $26,000 investigating a strip club — just good old fashioned police work involving lap dances and $300 bottles of champagne.
When the investigation of Donald Scott began, California police agencies were operating under this forfeiture-driven drug policy. Whatever they could seize, up to 80 percent of it would essentially become theirs.
As reporter Lynn Sherr reported in her “20/20” investigation into Scott’s death, there were plenty of reasons for the sheriff’s department to be looking for sources of revenue.
Lynn Sherr:
LA County was in a fiscal crisis. With the upcoming budget a billion dollars short, the sheriff’s department was being hit hard. So like other law-enforcement agencies around the country, it relied more on the proceeds of drug investigations to supplement the budget.
Radley Balko:
The investigation of Trail’s End unfolded over the course of a year. But six months after Scott’s death, the Ventura County District Attorney’s Office, led by Michael Bradbury, released a report that began to connect the dots.
The ABC News show “20/20” also played a key role in bringing public attention to the missteps by the LA County Sheriff’s Department. We’ll refer back to that episode throughout this story — not only because of its reporting, but because it includes one of the few in-depth interviews Frances Plante gave at the time.
We made numerous attempts to reach Plante for this story, but we were unable to track her down. And then, as we were producing this episode, we learned that she had recently passed away.
Plante’s “20/20” interview will be the only account from her that you’ll hear.
The investigation of Trail’s End began with an LA sheriff’s department deputy named Gary Spencer. District Attorney Bradbury’s investigation found that Spencer claimed to have received an anonymous tip that a woman named Frances Plante had been acting suspiciously around town in Malibu.
Plante hadn’t broken any laws, but Spencer claimed that the informant told him Plante was carrying lots of cash, paying for small items with $100 bills, and had been tipping generously.
Of course, Malibu is filled with eclectic and extraordinarily wealthy people. So it seems unlikely that tipping well and flaunting wealth would be unusual there. But Spencer saw these as signs of possible drug dealing. Spencer would later falsely assert in an affidavit that Plante’s car was registered to Donald Scott. Plante’s car was actually registered in Nevada, and Scott’s name was nowhere in the paperwork.
In September 1992, 10 months after the tip about Plante, Spencer claimed he received another tip from an informant who was never publicly identified. The informant told him there were 3,000 to 4,000 marijuana plants growing on Scott’s property. Spencer also claimed to have learned that Frances and an associate were allegedly linked to investigations into heroin and other narcotics smuggling.
So Spencer started investigating.
Bill Aylesworth:
The lead was Gary Spencer. The whole thing was orchestrated by him. And he’s the guy who ended up killing Donald Scott. It was this guy who thought it would be a feather in his cap, his star would rise. The department needed money at the time. He was very ambitious.
Radley Balko:
On September 10, 1992, Spencer and two deputies hiked to the top of the waterfall on Scott’s ranch to look for those thousands of marijuana plants. They found nothing.
Spencer then requested a California Air National Guard plane fly over the ranch to look for a pot farm and to snap photos. Those photos didn’t show much. At best, a DEA analyst named Charles Stowell said there might be some visual evidence of a small illegal water system. But even an unlawful set of water pipes could have been used to grow any number of perfectly legal plants. And as it turns out, there was really no irrigation system at all.
On a second flight two weeks later, DEA Agent Stowell claimed to have seen 50 marijuana plants. But for reasons that aren’t clear, he didn’t take any photos. Finally, Spencer asked a Forest Ranger to assemble a ground team to hike onto Scott’s property to find the plants. And for some reason, they contacted the U.S. Border Patrol to assist.
This new ground team got within 150 feet of Scott’s house but told Spencer that they saw no marijuana. They also said it was extremely unlikely that there were 3,000 plants growing on the property.
According to Bradbury’s investigation, as Spencer was building his case, he also sent a park ranger and a sheriff’s sergeant to Scott’s property under false pretenses. The ranger had previously responded to a complaint Frances Plante had made to the National Park Service.
Spencer told them to pretend to be interested in adopting a puppy from the Scotts.
Spencer told them to pretend to be interested in adopting a puppy from the Scotts. In reality, they were there to provide a threat assessment on the property. In other words, he wanted them to tell him what sort of force he would need to use when serving his search warrant.
Spencer finally got his search warrant on October 1, 1992, but only after telling the DEA that his mysterious informant’s story had changed. Forget the thousands of plants — the informant now reportedly said that Scott was growing only enough plants to yield about 40 pounds of pot. By DEA estimates, that would have amounted to about 50 plants. So the new story conveniently aligned with what the DEA agent improbably claimed to have spotted during his flight.
The informant would later deny that this particular conversation ever happened, though that was also disputed by the sheriff’s department. Bradbury’s investigation found other problems with Spencer’s search warrant affidavit. For example, Spencer had omitted the fact that two ground teams had visited the property and failed to spot any marijuana.
Spencer also wrote that DEA Agent Stowell had used binoculars when he claimed to have spotted the 50 or so pot plants. But there were no binoculars. Stowell claimed to have seen them from 1,000 feet in the air with the naked eye. A Forest Service employee with extensive aerial surveillance experience would later say that to do so from a plane like that would be like “seeing a corn dog sticking out of the ground.”
Michael Bradbury:
There is virtually no way that Stowell could have seen through that canopy of trees. It’s like a rainforest. It’s impenetrable.
Radley Balko:
That’s Ventura County District Attorney Michael Bradbury picking apart Spencer’s case with “20/20” reporter Lynn Sherr.
So to summarize, Spencer obtained a search warrant based on a DEA agent’s improbable claim to have spotted 50 pot plants from 1,000 feet with the naked eye. But he failed to photograph it, and he wasn’t certain about what he’d seen.
Spencer then corroborated that with an unidentified informant who revised the number of plants he claimed to have seen on Scott’s property from several thousand to just 50.
While Spencer claimed that the DEA agent had spotted the plants, he failed to note that two ground teams failed to find any plants when they visited the property in person.
Michael Bradbury:
He provided misinformation to the magistrate, and he left out a lot of very material facts that would have indicated to the magistrate that in fact marijuana was not being cultivated there.
Radley Balko:
But with the warrant in hand, Spencer then began planning his raid. Remember how he had previously sent those park rangers to visit the property and make a threat assessment?
Well, those rangers concluded that a SWAT team wasn’t necessary. “Just drive up to the house and the Scotts would let them inside.”
But that isn’t what happened.
Bill Aylesworth:
This guy was a cowboy, Gary Spencer. He’s not a guy who’s gonna hang around and talk about procedures, you know, “We’re gonna go in, we’re gonna arrest him, we’re gonna take his weed and his property.”
Radley Balko:
There’s other evidence that forfeiture was a prime motivator in Spencer’s investigation. About a month before the raid, deputies had also been given documents that included a property appraisal of the ranch, and that included a handwritten notation that an 80-acre plot of land nearby had recently sold for $800,000. It also pointed out that the Trail’s End Ranch covered 200 acres.
[Break]
Radley Balko:
Just after sunrise on October 2, 1992, 31 people from at least eight government and law enforcement agencies gathered in the Malibu office of the LA Sheriff’s Department for a briefing. At least two people at that briefing heard it mentioned that if the raid produced marijuana plants, the police agencies could seize Scott’s entire property under asset forfeiture laws.
So the 15-vehicle caravan then made its way to Trail’s End. At 8:30 a.m., they cut a padlock off the outer gate. Several of the officers would later say that they had knocked and announced themselves for somewhere between 1 and 4 minutes. According to police, when no one answered, a team of five deputies then forced their way into the home with a crowbar and a battering ram.
Spencer was the first one through the door.
Bill Aylesworth:
And she starts screaming. So, you hear your wife screaming. Obviously, you’re gonna grab your gun and go down and see what’s happening.
Radley Balko:
According to Spencer, Scott came out holding a .38-caliber snub-nosed revolver. He was holding it above his head, in his right hand, as if he were going to hit someone with it, not shoot it. According to Plante, Scott was still recovering from an eye surgery he’d had a few days earlier, and he couldn’t see well.
Bill Aylesworth:
They tell him, “Put down the gun. Put down the gun.” And so literally, the order they gave him is also the reason they used for killing him. Because he had a handgun, as he was putting it down, they blew him away.
Radley Balko:
Spencer said he told Scott to drop the gun three times, though he admits he never identified himself as a police officer once Scott entered the room. According to Spencer, as Scott brought the gun down, he rotated it until it was pointing at Spencer. That’s when Spencer fired. Deputy John Cater fired next. Then Spencer fired another round. According to Spencer, Scott lurched backward, stammered, and fell. He died instantly.
Capt. Richard DeWitt:
Captain DeWitt here.
Dispatch:
Yeah.
Capt. Richard Dewitt:
I’m on a search warrant with the Hidden Hills crew on this marijuana eradication thing.
Dispatch:
Yes.
Capt. Richard DeWitt:
And they just — Looks like 927D here.
Dispatch:
At the location?
Capt. Richard DeWitt:
Yeah.
Dispatch:
Some bodies there?
Capt. Richard DeWitt:
No, we put ’em down.
Dispatch:
We killed him?
Capt. Richard DeWitt:
Yeah.
Bill Aylesworth:
They’re basically saying, “Yeah, we killed him.” And then you could hear how surprised they were on the other end. They’re like, “You mean the property owner?” They were just, like, shocked. “The property owner? He’s dead? You shot him?”
Radley Balko:
Frances Plante would later use that recording in a song she created and produced with Aylesworth. They called it “I’m Going to Stop You.”
Bill Aylesworth:
At the very beginning of the song before a song even starts, we have the actual recording to the headquarters.
Verse from “I’m Going to Stop You” plays:
We killed him, we killed him. We killed him.
Bill Aylesworth:
Malibu sheriff headquarters saying, “Yeah, we killed the subject.” “Killed the subject? What do you mean?” on that record we recorded and released. And I named the album “Conspiracy Cocktail” because all the songs she wrote were about the government and what happened to her.
Radley Balko:
There were a number of inconsistencies about where Donald Scott’s hand and gun were pointing when he was shot. What’s undisputed is that the subsequent search of Scott’s property not only turned up no marijuana plants, or other narcotics, it also turned up no unusual or illegal irrigation systems. There were no ropes. There was nothing hanging from the trees that could have supported a grow operation. Frances Plante would later say, dryly, that when the police asked where the plants were, she responded, “I’m the only Plante here.”
Spencer later claimed deputies found a cigar box with marijuana stems, two charred joints, and some residue that may have been pot. But there’s no mention of that on the evidence return sheet, which is supposed to list everything seized during the search. And Spencer later couldn’t say where the box was found.
Trail’s End was in Ventura County, yet the investigation into Donald Scott’s nonexistent marijuana farm and the raid that ended his life were conducted by the sheriff’s office in neighboring Los Angeles County. The fallout from his death would pit two veteran California law enforcement officials against each other in a way that became very nasty and very public.
Soon after Scott’s death, Ventura County District Attorney Michael Bradbury announced that he’d be launching an investigation. Six months later, he issued his scathing report.
It was about as damning a document as one law enforcement agency could publish about another. Bradbury then defended his report in the media.
Barbara Walters:
This week, investigators examining the case issued their report. The findings are explosive, as you are about to hear in the conclusion of Lynn Sherr’s report.
Michael Bradbury:
Donald Scott did not have to die. He should not have died. He’s an unfortunate victim in the war on drugs.
Radley Balko:
Bradbury’s report said that the U.S. Border Patrol had no jurisdiction to be involved in the case and criticized its agents for trespassing on Scott’s property. He was also hard on DEA Agent Charles Stowell, saying, “He was either lying or not sure that he saw marijuana.”
But Bradbury saved most of his criticism for Deputy Gary Spencer, writing, “This search warrant became Donald Scott’s death warrant.”
“This search warrant became Donald Scott’s death warrant.”
After outlining the numerous discrepancies in Spencer’s affidavit, Bradbury’s report concluded, “the misstatements and omissions discussed above are material and would invalidate the warrant.”
Bradbury also wrote that there were numerous reasons to doubt Spencer’s version of events. Although, he advised against perjury charges for the deputy.
He also questioned the LA County Sheriff’s Department’s motives. When Bradbury’s report came out, the Los Angeles County sheriff was a reserved man named Sherman Block.
In a written statement, Block condemned the report, which he said was filled with “conjecture and supposition” and reeked of “sensationalism.” He also accused Bradbury of having “a complete lack of understanding of the nature of narcotics investigations.”
And Block questioned Bradbury’s motivations, pointing out that the report was released just as ABC News was airing that “20/20” report on the Scott case.
Announcer:
Tonight, a Lynn Sherr investigation: Why did Donald Scott die?
Radley Balko:
Block conducted his own internal inquiry into the raid, which disputed all of Bradbury’s findings. He completely exonerated Spencer, his deputies, and DEA Agent Stowell, and argued that a 1,000-foot aerial naked-eye sighting of marijuana plants is both possible and “ideal.” According to Block, Bradbury’s own tape-recorded interview with the informant revealed that the informant never denied telling Spencer about the 40 pounds of marijuana on the ranch.
Block concluded that Spencer did not lie to obtain the search warrant, and wrote, “It is not true that the interest in forfeiture dominated or even rivaled the criminal concerns in this investigation.” He accused Bradbury of “willful distortions of fact” and of attacking “the integrity of veteran law enforcement officials.”
But Bradbury wasn’t the type to needlessly attack law enforcement. He was a law-and-order Republican. His memoir, published a few years ago, included photos of himself with Ronald Reagan, George H.W. Bush, Margaret Thatcher, and various other conservative luminaries of the 1980s and 1990s.
What’s most striking about Block’s investigation is that it lacks any introspections. Three months before the Scott raid, Block’s department was strongly criticized for a series of fatal shootings. A 359-page report commissioned by the Los Angeles County Board of Supervisors found “deeply disturbing evidence of excessive force and lax discipline.” The report described a culture of lawlessness among sheriff’s deputies and a reluctance by Block and his top aides to hold them accountable.
Now, Block’s deputies had killed another innocent man. And even assuming everything his Deputy Gary Spencer put in the original affidavit was correct — and we know that it wasn’t — Block’s officers had gunned down a man in his own home over 50 marijuana plants that they never found.
Block’s officers had gunned down a man in his own home over 50 marijuana plants that they never found.
After his investigation, Block continued to reject Bradbury’s conclusions. He expressed no remorse or willingness to examine the policies that allowed the killing of an innocent 61-year-old man over what was at most, a few dozen pounds of cannabis. He never questioned the appropriateness of deploying a huge raid team with personnel from several agencies who had never worked together. Even if they had found the pot they claimed Scott possessed, the manpower that morning would have amounted to one law enforcement officer for each 1.7 marijuana plants.
Block even sent his report to the California attorney general, and requested an inquiry into Bradbury for abusing his powers. Despite the botched raid and death of an innocent man, the state attorney general backed Sheriff Block. He also cleared Spencer and disputed Bradbury’s report, accusing him of using “unsupported and provocative language.”
Law enforcement officers have
killed a lot of people
in the name of the war on drugs. And it probably goes without saying that most of them aren’t rich, white, eccentric millionaires. Studies have consistently shown that the people targeted by these policies — from forfeiture to
aggressive home invasions
by police — are disproportionately poor and Black. But it tends to be cases like Scott’s that attract media and public attention, because the public tends to find them more sympathetic.
Dan Alban:
Although the Donald T. Scott case is one of the maybe more extreme or memorable examples, it’s one that I think hits home for a lot of people — because they realize, “That could have been me.” Like, if police come charging into my house, and I don’t know that they’re there, and I hear my wife screaming, am I going to try to come to her aid? And if so, am I going to get shot? And could it be over something that I had no fault in? Absolutely it could.
Radley Balko:
Civil asset forfeiture policies gave Deputy Spencer a strong incentive to conclude that Donald Scott was guilty. It also incentivized him to look for evidence to support that conclusion — instead of the other way around. Bradbury called it a “fishing expedition.”
Throughout making this episode, we tried to get a comment from Spencer, but we were unable to reach him through publicly available information.
Donald Scott had no criminal record. And after his death, friends and acquaintances told media outlets that he wasn’t fond of illicit drugs. That’s something they might also have told investigators if they had bothered to ask.
The possibility of civil asset forfeiture pushes drug cops in one direction: to produce evidence of a target’s guilt. There’s little incentive to search for exculpatory evidence, especially once they’ve invested some time and resources in the investigation.
Dan Alban:
So forfeiture absolutely distorts the priorities of law enforcement agencies and drives a lot of activities that they would not otherwise engage in.
Forfeiture “diverts all kinds of resources into things that have nothing to do with actual crime prevention and are instead are much more oriented toward revenue generation.”
Radley Balko:
Alban says there’s data showing that when law enforcement revenue increases due to forfeiture, there’s a corresponding decrease in the rate at which they close crimes like murder or robbery.
Dan Alban:
One of the things that folks who are really sort of pro-law enforcement or pro-law-and-order often fail to fully appreciate about the dangers of the profit incentive in forfeiture is, it’s not just something that gives the police more tools to fight crime. It’s something that distorts law enforcement priorities, distracts them from what they’re supposed to be doing, and diverts all kinds of resources into things that have nothing to do with actual crime prevention and are instead are much more oriented toward revenue generation.
Radley Balko:
That means more unsolved violent crimes. Which means less public confidence in the police. And that only feeds the cycle of mistrust between cops and marginalized communities.
Dan Alban:
There are a number of studies that have shown that civil forfeiture and the aggressive use of civil forfeiture has caused distrust in minority and low-income communities because it’s viewed as enabling the police to just steal from people — and particularly to just steal from the poorest, the people who have the least resources and who are most vulnerable.
Not only are they the ones who are sort of hit hardest by it, but they’re also the ones least able to defend themselves because they have less access to attorneys or to the political system that might enable them to call some of these things into question or have politicians start investigations.
Radley Balko:
The city of Philadelphia is a particularly compelling case study. That city has been home to a long-running forfeiture abuse scandal first exposed in 2014.
CNN:
In two years, nearly 500 families in Philadelphia had their homes or cars taken away by city officials, according to Pennsylvania’s attorney general. They use a civil forfeiture law that allows them to …
Dan Alban:
The court allowed us to do a survey of the victims of Philly’s forfeiture program — the first survey that’s ever been done of all of the victims of a single forfeiture program. And in that case, only about 1 in 4 respondents was actually found guilty or pled guilty to any wrongdoing, yet they all had their property seized and forfeited.
Radley Balko:
Alban’s organization brought a class-action suit in Philadelphia on behalf of
thousands
of local residents who’d had their cars, homes, and cash seized by police.
Dan Alban:
The lead plaintiffs in that case were the Sourovelis family, whose son had gotten into trouble. He was selling a few hundred dollars worth of drugs, and he was keeping it in a backpack in his bedroom. And one day, the Philly PD raided the house, told the family they had just a few minutes to pack up everything and get out, and that the house was going to be seized and sealed for forfeiture because their son had, of course, unbeknownst to them, been selling relatively small amounts of drugs. And this was, of course, horrifying to the family. They thought they were going to lose their entire house over this.
Radley Balko:
Alban’s group was able to save the Sourovelis family home. But he says that case is part of a pattern, where small offenses can lead to life-altering losses, often to people who had no involvement in the underlying crime.
Dan Alban:
Many of those instances were people who obviously had no idea that their grandson, or whoever was staying with them, was involved in illegal activity and certainly didn’t condone it. But they didn’t have legal resources to fight back. And so there were, I think, 80 to 100 properties that ended up being forfeited from people, many of whom weren’t actually accused of committing that crime. And that same sort of scenario plays out time and time again across the country.
Probably the most common scenario is, you know, the mom lets their son or daughter borrow the family car or minivan. They’re at the park and get caught selling some weed to their friends or something. The police not only seize the weed, of course, and the money — but also the family car.
And then mom is stuck in this terrible position where, you know, she of course wasn’t allowing her kid to use the minivan for illegal purposes, but now doesn’t have a car, can’t get to work, can’t get the kids to school, can’t get to the grocery store, to run other errands — but isn’t actually a person accused of the crime.
Radley Balko:
In 2000, Congress passed some reforms to federal forfeiture law, including an “innocent owner defense” that owners of seized property can use. But it’s almost impossible to prove a negative.
Dan Alban:
It’s proving something like, “I didn’t want my son to use the family minivan to deal drugs.” How do you actually prove that? It’s not like you probably sent him a text message saying, “Now son, I don’t want you to use the family minivan to use drugs.” So satisfying that burden of proof is very difficult.
Radley Balko:
The bill also failed to mandate a conviction for asset forfeiture or curb the profit incentive driving it. Weaker federal reforms and sharing agreements have allowed police to bypass tougher state forfeiture laws.
There are long-standing questions about how law enforcement agencies use the proceeds of civil asset forfeiture. Critics say the lure has pushed police to become more aggressive and more militarized.
Dan Alban:
We’ve seen lots of those sort of surplus military vehicles, [Mine-Resistant Ambush Protected vehicles], and other sorts of things purchased with forfeiture funds. Lots of military or pseudo-military equipment. In Philadelphia, for example, the Philadelphia police department used forfeiture funds to buy, I think, about two dozen submachine guns and to pay for a range that they were using for those automatic weapons.
If you know that your city council or county board or the state legislature isn’t going to approve you buying a BearCat armored vehicle or something similar, you can nonetheless purchase that same vehicle, using forfeiture funds. And that sort of thing happens all the time.
Radley Balko:
And once cops have this gear, they want to use it. So the equipment then gets used in more drug raids, which results in more seized property, which results in more revenue to buy more gear. It’s a self-perpetuating cycle. It can also just be a waste of public resources.
Dan Alban:
A lot of the time with the armored vehicles, the various militarized equipment, the submachine guns, that kind of stuff — those are things that are tremendous fun to play with, may not have much practical use or practical value to many police departments.
Radley Balko:
The use of civil asset forfeiture isn’t limited to drug crimes. But the drug war is by far the biggest driver of the policy.
In about the time between Congress loosening asset forfeiture laws in 1984 and Scott’s death, law enforcement authorities nationwide had seized roughly $3 billion in assets. In Los Angeles County alone, about $205 million was taken by law enforcement. In the five years before Donald Scott’s death in 1992, the county averaged more than $30 million a year in seizures.
PBS “Frontline”:
In 1987, the sheriff’s department seized more than $26 million in drug money, another $33 million in 1988.
Radley Balko:
In 1990, the
PBS show “Frontline”
aired an investigation about how the drug war was corrupting police officers throughout the country.
Dan Garner:
You see that there’s big money out there, you want to seize the big money for your department. For our unit, that was a sign of whether you were doing good or poorly, was how much money you seized and the kind of cases you did. And my supervisor made it extremely clear that big money cases were a lot more favorable for your overall evaluation than big dope cases.
Radley Balko:
In a 1993 interview, the head of narcotics at the LA sheriff’s department
told the LA Times
that the salaries of 24 of the unit’s 200 officers were funded entirely with forfeiture proceeds. And the top forfeiture prosecutor in the state attorney general’s office said drug war asset forfeiture can “become addictive to law enforcement.” He then added, apparently without irony, “It’s a little like crack.”
The addiction isn’t just institutional. That much loose cash can also be a temptation for police officers to slide into corruption, seizing and keeping property for themselves. Donald Scott’s death, in fact, followed a larger department-wide scandal in Los Angeles.
PBS “Frontline”:
Seven sheriff’s deputies are now on trial in Los Angeles, charged with stealing $1.4 million in drug money. More than 30 narcotics officers here have been implicated in the largest current police corruption scandal in the country.
Radley Balko:
Most of the charges were related to deputies skimming the cash they confiscated in drug busts, which they then used to buy cars, vacations, and even new homes. And the LA County sheriff at the time? It was Sherman Block.
Sheriff
Sherman Block:
I think we had individuals who succumbed to temptation, who somehow, I’m sure, in their own minds, they probably were able to rationalize what they were doing was not really wrong, since the individuals who they were dealing with were not honorable people in themself.
Radley Balko:
None of the police officers involved in the killing of Donald Scott were ever disciplined for the raid itself. Deputy Gary Spencer sued Bradbury, the Ventura County DA, for defamation. When the suit was dismissed, he was ordered to pay Bradbury’s legal fees of about $50,000. Spencer later declared bankruptcy. “I was made out to be this callous, reckless, Dirty Harry kind of guy, and I wasn’t able to say anything about it,” Spencer
told the Los Angeles Times
in 1997.
Spencer did express regret for Scott’s death. And he would go on to say that the raid ruined his life. He told the LA Times that he developed a twitch in response to stress from the case, and that his children had to defend his reputation to their classmates. Still, Spencer continued to defend the raid, saying that he didn’t consider it botched because “that would say that it was a mistake to have gone in there in the first place, and I don’t believe that.”
Michael Bradbury deserves a lot of credit in this story. He was a rising star in Republican politics when the Scott raid went down. He saw a problem in law enforcement that had caused a tragedy, and he tried to do something about it.
Here’s Bradbury again speaking to “20/20.”
Michael Bradbury:
When you keep that information out of a warrant, you deprive the judge of making an informed decision. And in fact that can, and in this case did, in our opinion, invalidate the warrant.
Radley Balko:
When I first reached out to Bradbury, who is now in his 80s, he initially agreed to be interviewed for this podcast. But after consulting with his attorney, he told us that he would have to decline. It seems that Spencer is still around too, and Bradbury’s attorney feared that Spencer could still sue Bradbury for defaming him.
But in our initial phone conversation, Bradbury also told me something that hasn’t been widely reported about this case. In 2001, the George W. Bush administration contacted Bradbury and asked if he’d accept a nomination to be U.S. attorney for the district of Southern California. For a DA like Bradbury, this was a major promotion. Bradbury said he’d be honored, and he traveled to Washington to meet with White House officials. But when he arrived, he was told that the administration had changed its mind. According to Bradbury, the LA Sheriff’s Department had complained, citing the Scott case, and scuttled the nomination.
Bill Aylesworth:
Frances is the one who really became like a political activist and stayed on the property and armed herself, and they kept coming, doing harassment, raids, all kinds of crazy stuff.
Radley Balko:
Things would get worse for Frances Plante. After Donald Scott died, Plante inherited only a portion of Trail’s End. And she struggled to buy out the portion that went to his other family members. A little more than a year after the raid, the Malibu fires of 1993 then ravaged every manmade structure on the property. The fire also destroyed an urn containing Donald Scott’s ashes. Broke and heartbroken, Plante vowed to press on.
Bill Aylesworth:
They thought, well, she’s going to leave now for sure. And she didn’t. She bought a tipi from like a tribe up in Oregon or something. You can see pictures of her online in front of her tipi holding a shotgun in her wedding dress. And she really got into it — the whole political activism thing about the asset forfeiture. And she wanted to get it out there that this is happening and stop it. So she was on “20/20.”
Lynn Sherr:
Today, Frances takes little pleasure from this land. The memories of her husband and his love for these hills have now dissolved into the painful reality of one morning in October.
Frances Plante Scott:
I’m not sailing off into the sunset with Donald Scott, so I’m stuck here, and I’m going to stay here and keep the land just like Donald did all these years.
Radley Balko:
In 1993, Plante, Donald Scott’s estate, and his children filed a civil rights lawsuit against the various police agencies and deputies involved in the raid. The authorities dragged out the lawsuit for years, causing Plante to rack up massive legal debts.
Dan Alban:
And so while Donald Scott, the raid on his house and his ranch, was over 30 years ago. It’s something that we haven’t fixed. We haven’t really addressed, and that’s one of the reasons why there needs to be substantial reforms made at the federal level, made at the state level.
Radley Balko:
Alban’s organization, the Institute for Justice, launched an “
End Forfeiture Initiative
” in 2014. And since then, there have been significant changes. Three states: New Mexico, Nebraska, and Maine have abolished civil forfeiture completely. And that’s in addition to North Carolina’s ban which dates back to 1985.
Thirty-seven states, plus the District of Columbia, have reformed their civil forfeiture laws to some degree. One of the most popular changes include requiring a criminal conviction before seizing property — a measure that, arguably, should have been a foundational principle from the outset.
But many of these piecemeal changes have fallen short of fully protecting people’s money and property. According to the
Institute for Justice
, in 2018 alone the federal government and states have collected more than $3 billion in seized assets. Over the last roughly 20 years, that number jumps to about $68 billion. And that’s likely an undercount, since not all states fully report their forfeiture data. When it comes to changes at the federal level, the courts have been going back and forth on the issue.
PBS NewsHour
:
A unanimous decision today from the U.S. Supreme Court limits the ability of states to seize private property and impose excessive fines.
Radley Balko:
That was back in 2019, in a decision authored by former Justice Ruth Bader Ginsburg. But as the court’s ideological leanings have swung, so has its treatment of the issue. Here’s another case decided in May of 2024.
Fox News 10
:
The 6-3 ruling held that states aren’t required to hold a preliminary hearing shortly after police seize property or money. The case involved a Georgia woman who challenged the seizure of her vehicle by police …
Radley Balko:
Reform efforts have also stalled in Congress.
It would take seven years, but in April 2000, Los Angeles County finally settled with Donald Scott’s estate, paying out $4 million. The federal government also settled with the Scott estate for $1 million.
For most of this time, Frances Plante had been living in that tipi that she had put up at Trail’s End. Because she inherited her husband’s valuable land but not his wealth, she fell behind on property taxes.
And in the end, after paying attorneys’ fees and the shares to Scott’s children, Plante’s share of the $5 million settlement wasn’t enough to save Trail’s End. And after news of the settlement hit the press, the IRS came calling, claiming that Plante owed $1 million in inheritance taxes from when she obtained the ranch from Scott.
So in August 2001, almost nine years after an LA County tactical team had killed Donald Scott, a federal SWAT team — complete with two helicopters — descended upon Trail’s End Ranch to evict Frances Plante from the property.
They then did precisely what Donald Scott always feared the government would do: They seized his land, sold it at auction, and kept the proceeds for themselves.
That’s it for Collateral Damage.
Collateral Damage is a production of The Intercept.
It was written and reported by me, Radley Balko.
Additional writing by Andrew Stelzer, who also served as producer and editor.
Laura Flynn is our showrunner.
Ben Muessig is our editor-in-chief.
The executive producers are me and Sumi Aggarwal.
We had editing support from Maryam Saleh.
Truc Nguyen mixed our show.
Legal review by Shawn Musgrave and David Bralow.
Fact-checking by Kadal Jesuthasan.
Art direction by Fei Liu.
Illustrations by Tara Anand.
Copy editing by Nara Shin.
Social and video media by Chelsey B. Coombs.
Special thanks to Peter Beck for research assistance and to Ali Gharib for editorial feedback on this episode.
This series was made possible by a grant from the Vital Projects Fund.
If you want to send us a message, email us at podcasts@theintercept.com.
This is a follow up to the
previous post
about
#[inline]
in Rust specifically.
This post is a bit more general, and a bit more ranty. Reader, beware!
When inlining optimization is discussed, the following is almost
always mentioned: “inlining can also make code slower,
because
inlining increases the code size, blowing the
instruction cache size and causing cache misses”.
I myself have seen this repeated on various forms many times. I have
also seen a lot of benchmarks where judicious removal of inlining
annotations did increase performance. However, not once have I seen
the performance improvement being traced to ICache specifically. To me
at least, this explanation doesn’t seem to be grounded — people know
that ICache is to blame because other people say this, not because
there’s a benchmark everyone points to. It doesn’t mean that the
ICache explanation is wrong — just that I personally don’t have
evidence to believe it is better than any other explanation.
Anyway, I’ve decided to look at a specific case where I know
#[inline]
to cause an observable slow down, and understand why
it happens. Note that the goal here is not to explain real-world
impact of
#[inline]
, the benchmark is artificial. The
goal is, first and foremost, to learn more about the tools to use for
explaining results. The secondary goal is to either observe ICache
effects in practice, or else to provide an alternative hypothesis for
why removing inlining can speed the things up.
The benchmark is based on my
once_cell
Rust library. The library provides a primitive, similar
to
double-checked locking
. There’s a function that looks like this:
I know that performance improves significantly when the
initialize
function is not inlined. It’s somewhat obvious that
this is the case (that’s why the benchmark is synthetic — real world
examples are about cases where we don’t know if
inline
is
needed). But it is unclear why,
exactly
, inlining
initialize
leads to slower code.
For the experiment, I wrote a simple high-level benchmark calling
get_or_try_init
in a loop:
On Linux, the best tool to quickly access the performance of any
program is
perf stat
. It runs the program and shows a
bunch of CPU-level performance counters, which might explain what’s
going on. As we suspect that ICache might be to blame, let’s include
the counters for caches:
There is some difference in
L1-icache-load-misses
, but
there’s also a surprising difference in
instructions
.
What’s more, the
L1-icache-load-misses
difference is hard
to estimate, because it’s unclear what
L1-icache-loads
are. As a sanity check, statistics for
dcache
are the
same, just as we expect.
While
perf
takes the real data from the CPU, an
alternative approach is to run the program in a simulated environment.
That’s what
cachegrind
tool does. Fun fact: the primary
author of cachegrind is
@nnethercote
, whose
Rust Performance Book
we saw in the last post. Let’s see what
cachegrind
thinks about the benchmark.
$ valgrind --tool=cachegrind ./always10s I refs: 6,400,577,147 I1 misses: 1,560 LLi misses: 1,524 I1 miss rate: 0.00% LLi miss rate: 0.00% D refs: 1,600,196,336 D1 misses: 5,549 LLd misses: 4,024 D1 miss rate: 0.0% LLd miss rate: 0.0% LL refs: 7,109 LL misses: 5,548 LL miss rate: 0.0%$ valgrind --tool=cachegrind ./never9s I refs: 5,600,577,226 I1 misses: 1,572 LLi misses: 1,529 I1 miss rate: 0.00% LLi miss rate: 0.00% D refs: 1,600,196,330 D1 misses: 5,553 LLd misses: 4,024 D1 miss rate: 0.0% LLd miss rate: 0.0% LL refs: 7,125 LL misses: 5,553 LL miss rate: 0.0%
Note that, because
cachegrind
simulates the program, it
runs much slower. Here, we don’t see a big difference in ICache misses
(I1 — first level instruction cache, LLi — last level instruction
cache). We do see a difference in ICache references. Note that the
number of times CPU refers to ICache should correspond to the number
of instructions it executes. Cross-checking the number with
perf
, we see that both
perf
and
cachegrind
agree on the number of instructions executed. They
also agree that
inline_always
version executes more
instructions. It’s still hard to say what perf’s
sL1-icache-loads
means. Judging by the name, it should
correspond to
cachegrind
’s
I refs
, but it
doesn’t.
Anyway, it seems there’s one thing that bears further investigation —
why inlining changes the number of instructions executed? Inlining
doesn’t actually change the code the CPU runs, so the number of
instructions should stay the same. Let’s look at the asm then! The
right tool here is
cargo-asm
.
I’ve slightly edited the code and also highlighted the hot loop which
constitutes the bulk of the benchmark.
Looking at the assembly, we can see the following:
code is much larger — inlining happened!
function prologue is bigger, compiler pushes more callee-saved
registers to the stack
function epilogue is bigger, compiler needs to restore more
registers
stack frame is larger
compiler hoisted some of the
initialize
code to before
the loop
the core loop is very tight in both cases, just a handful of
instructions
the core loop counts upwards rather than downwards, adding an extra
cmp
instruction
Note that it’s highly unlikely that ICache affects the running code,
as it’s a small bunch of instructions next to each other in memory. On
the other hand, an extra
cmp
with a large immediate
precisely accounts for the amount of extra instructions we observe
(the loop is run 800_000_000 times).
It’s hard enough to come up with a benchmark which demonstrate the
difference between two alternatives. It’s even harder to explain the
difference — there might be many
readily available
explanations, but they are not necessary
true. Nonetheless, today we have a wealth of helpful tooling. Two
notable examples are
perf
and
valgrind
. Tools are not always correct — it’s a good idea to
sanity check different tools against each other and against
common-sense understanding of the problem.
For inlining in particular, we found the following reasons why
inlining
S
into
C
might cause a slow down:
Inlining might cause
C
to use more registers. This
means that prologue and epilogue grow additional push/pop
instructions, which also use stack memory. Without inlining, these
instructions are hidden in
S
and are only paid for
when
C
actually calls into
S
, as opposed
to every time
C
itself is called.
Generalizing from the first point, if
S
is called in
a loop or in an
if
, the compiler might hoist some
instructions of
S
to before the branch, moving them
from the cold path to the hot path.
With more local variables and control flow in the stack frame to
juggle, compiler might accidentally pessimize the hot loop.
If you are curious under which conditions ICache does become an
issue, there’s
this excellent article
about one such case.
Sleep Awake review – Gary Numan cameos in an overly straightforward sleep-deprivation horror
Guardian
www.theguardian.com
2025-12-03 10:00:29
PC, PlayStation 5, Xbox; Eyes Out/Blumhouse GamesPsychedelic visuals and a promising premise are let down by tired game design in this first-person horror with an appearance from the synthpop pioneer Video games have delivered a feast of singular and wondrous sights in 2025: ecological fantasias tee...
V
ideo games have delivered a feast of singular and wondrous sights in 2025:
ecological fantasias
teeming with magical beasts; stunning, historically obsessive recreations of
feudal Japan
. But here is an end-of-year curio: psychological horror game Sleep Awake serves us synth-rock pioneer Gary Numan stepping into what is perhaps the schlockiest role of his life – a gigantic floating head named Hypnos.
This late-stage cameo is not quite indicative of the game as a whole; the handful of hours prior to Numan’s arrival are more mournful than madcap. Mostly, you explore the dilapidated, tumbledown streets of what is thought to be the last city on Earth. This setting is a magnificent work of imagination. You see it through the eyes of a young woman named Katja, who moves along rooftops, gazing out upon a barren, lifeless hinterland, into labyrinthine streets whose darkness and arcane logic recall the stirring subterranean etchings of Italian artist Piranesi.
How has the planet become so inhospitable, so thoroughly wiped of life? That never really becomes clear. Rather, Katja must wrestle with a more pressing concern: should she fall asleep, our hero risks disappearing into a strange, inaccessible realm caused by a disease referred to as the Hush. Like every other perpetually exhausted person here, Katja plops a few drops of stay-awake serum into her eyes. Suddenly, she sees psychedelic visions and kaleidoscopic refractions of space. Katja seems to be losing the plot; everyone else certainly has. What remains of society has crumbled into the sleep-deprived paranoia of rival gangs.
Driven initially by a desire to care for an elderly relative, you direct Katja in first-person through the game’s many grotty, decaying spaces. At one point you’re on the turf of a gas-masked cult, so you try to sneak past them, crouching against walls and under tables to avoid detection. Yet there is hardly any tension: the enemies follow rote patrol paths; their field of vision is preposterously generous. This is a rather dull, easy game of hide and seek.
Sleep Awake betrays a further lack of gameplay imagination. You’re called on to short-circuit electricity breakers by rolling carts into them; you have to open doors by finding obviously placed keycards. Slowly, the lustre of the city also begins to dull: it becomes clear you are advancing along a lavishly art-directed tunnel – really, only a lightly interactive and not especially scary fairground ghost train.
This is a shame because Sleep Awake is visually daring. The exploratory action is intercut with bleary yet beautiful FMV sequences: the eerie silhouette of trees against a blood-red sky; bubbling liquids shown in extreme closeup. Sometimes these unsettling images are layered over the actual 3D space to gorgeously odd, arthouse effect. This surrealism extends to the death screen: should you get clubbed on the head by one of your dimwitted foes, you must walk out of the dark towards a spectacular light-filled door; as you do, the space mutates in hallucinatory real-time, spitting you out at your last autosave.
The death screen is a rare moment when Sleep Awake summons something between dream logic and the strange hazy moments between sleep states that can feel like dreaming. The rest of the time, this narcoleptic nightmare merely wears its psychedelic aesthetics – floating Numan included – without interrogating them interactively. It’s too straightforward, too legible, and not actually illogical enough where it matters. You may want to sleep on this one.
San Francisco-based
Anthropic
has asked Wilson Sonsini Goodrich & Rosati to begin work on an initial public offering (IPO) that could take place as early as 2026, the Financial Times
reported
this week.
The move positions the company, best known for its Claude chatbot, to reach the stock market ahead of rival OpenAI and would test investors’ willingness to fund large, loss-making artificial-intelligence labs. People familiar with the plans cautioned that discussions with investment banks remain informal and that no underwriting line-up has been chosen.
Wilson Sonsini
, which advised Anthropic on its multibillion-dollar investment agreements with Amazon and Google, has
previously
guided Google, LinkedIn and Lyft to market.
In a statement, an Anthropic spokesperson said: “We have not made any decisions about when, or even whether, to go public.”
The IPO preparations coincide with a private fundraising round that could value Anthropic above $300 billion. Microsoft and Nvidia have jointly committed $15 billion to that round, according to the Financial Times, while Anthropic has pledged to spend $30 billion on Microsoft’s cloud platform over the next four years. A previous funding round this autumn pegged the company at roughly $183 billion.
Chief executive Dario Amodei has told investors that annualised revenue could rise to $26 billion next year, triple this year’s run-rate, as the customer base expands beyond 300,000 businesses.
Internal changes aimed at satisfying public-market requirements are already under way. Last year Anthropic
hired
Airbnb’s former head of corporate finance,
Krishna Rao
, as chief financial officer and has since worked through governance, accounting and disclosure checklists, one source said.
OpenAI, valued at about $500 billion after a recent share sale, is
conducting
similar early-stage work but chief financial officer Sarah Friar said last month that a listing is “not in the near-term plan.”
Both companies face the challenge of forecasting profits while spending heavily on model training and infrastructure. Anthropic recently
announced
a $50 billion build-out of data centres in Texas and New York and plans to triple its global workforce.
Anthropic taps IPO lawyers as it races OpenAI to go public
Then
$75
per month.
Complete digital access to quality FT journalism on any device.
Cancel anytime during your trial.
Explore more offers.
FT Edit
$49
per year
Access to eight surprising articles a day, hand-picked by FT editors. For seamless reading, access content via the FT Edit page on FT.com and receive the FT Edit newsletter.
Standard Digital
$45
per month
Essential digital access to quality FT journalism on any device. Pay a year upfront and save 20%.
Premium Digital
$75
per month
Complete digital access to quality FT journalism with expert analysis from industry leaders. Pay a year upfront and save 20%.
Explore our full range of subscriptions.
For individuals
Discover all the plans currently available in your country
For multiple readers
Digital access for organisations. Includes exclusive features and content.
Why the FT?
See why over a million readers pay to read the Financial Times.
The following subscription-only content has been made available to you
by an LWN subscriber. Thousands of subscribers depend on LWN for the
best news from the Linux and free software communities. If you enjoy this
article, please consider
subscribing to LWN
. Thank you
for visiting LWN.net!
The designers of the
Zig programming language
have been working to find a
suitable design for asynchronous code for some time.
Zig is a carefully minimalist language, and its
initial design
for
asynchronous I/O did not fit well with its other
features. Now, the project has
announced
(in a Zig SHOWTIME video) a new approach to asynchronous I/O that
promises to solve the
function coloring
problem, and allows writing code that will execute
correctly using either synchronous or asynchronous I/O.
In many languages (including Python, JavaScript, and Rust), asynchronous code
uses special syntax. This can make it difficult to reuse code between
synchronous and asynchronous parts of a program, introducing a number of headaches for
library authors. Languages that don't make a syntactical distinction (such as
Haskell) essentially solve the problem by making everything asynchronous, which
typically requires the language's runtime to bake in ideas about how programs
are allowed to execute.
Neither of those options was deemed suitable for Zig. Its designers wanted to
find an approach that did not add too much complexity to the language, that
still permitted fine control over asynchronous operations, and that still made
it relatively painless to actually write high-performance event-driven I/O. The
new approach solves this by hiding asynchronous operations behind a new generic
interface,
Io
.
Any function that needs to perform an I/O operation will need to have access to
an instance of the interface. Typically, that is provided by passing the
instance to the function as a parameter, similar to Zig's
Allocator
interface for memory allocation. The standard library will include two built-in
implementations of the interface:
Io.Threaded
and
Io.Evented
.
The former uses synchronous
operations except where explicitly asked to run things in parallel (with a
special function; see below), in which
case it uses threads. The latter (which is still a work-in-progress) uses an
event loop and asynchronous I/O. Nothing in the design prevents a Zig programmer
from implementing their own version, however, so Zig's users retain their fine
control over how their programs execute.
Loris Cro, one of Zig's community organizers,
wrote
an explanation
of the new behavior to justify the approach.
Synchronous code is not much changed,
other than using the standard library functions that have moved under
Io
, he explained. Functions like the example below, which don't involve explicit
asynchronicity, will continue to work. This example creates a file, sets the
file to close at the end of the function, and then writes a buffer of data to
the file. It uses Zig's
try
keyword to handle errors, and
defer
to ensure the file is closed. The return type,
!void
,
indicates that it could return an error, but doesn't return any data:
If this function is given an instance of
Io.Threaded
, it will create
the file, write data to it, and then close it using ordinary system calls. If it
is given an instance of
Io.Evented
, it will instead use
io_uring
,
kqueue
, or some other asynchronous backend suitable to the target operating
system. In doing so, it might pause the current execution and go work on a
different asynchronous function.
Either way, the operation is guaranteed to be complete by the time
writeAll()
returns.
A library author writing a function that involves I/O doesn't need to
care about which of these things the ultimate user of the library chooses to do.
On the other hand, suppose that a program wanted to save two files. These
operations could profitably be done in parallel. If a library author wanted to
enable that, they could use the
Io
interface's
async()
function to express that it does not matter which order the two files are saved in:
When using an
Io.Threaded
instance, the
async()
function
doesn't actually
isn't actually required to do anything asynchronously [although the actual implementation may dispatch the function to a separate thread, depending on how it was configured] — it can just run the provided function
right away. So, with that version of the interface, the function first saves
file A and then file B. With an
Io.Evented
instance, the operations are
actually asynchronous, and the program can save both files at once.
The real advantage of this approach is that it turns asynchronous code into a
performance optimization. The first version of a program or library can write
normal straight-line code. Later, if asynchronicity proves to be useful for
performance, the author can come back and write it using asynchronous
operations. If the ultimate user of the function has not enabled asynchronous
execution, nothing changes. If they have, though, the function becomes faster
transparently — nothing about the function signature or how it interacts with
the rest of the code base changes.
One problem, however, is with programs where two parts are actually required to
execute simultaneously for correctness. For example, suppose that a program
wants to listen for connections on a port and simultaneously respond to user
input. In that scenario, it wouldn't be correct to wait for a connection and
only then ask for user input. For that use case, the
Io
interface
provides a separate function,
asyncConcurrent()
concurrent()
[this function was renamed during development;
concurrent()
is the most recent name] that explicitly asks for
the provided function to be run in parallel.
Io.Threaded
uses a thread
in a thread pool to accomplish this.
Io.Evented
treats it exactly the
same as a normal call to
async()
.
const socket = try openServerSocket(io);
var server = try io.concurrent(startAccepting, .{io, socket});
defer server.cancel(io) catch {};
try handleUserInput(io);
If the programmer uses
async()
where they should have used
concurrent()
, that is a bug. Zig's new model does not (and cannot)
prevent programmers from writing incorrect code, so there are still some
subtleties to keep in mind when adapting existing Zig code to use the new
interface.
The style of code that results from this design is a bit more verbose than
languages that give asynchronous functions special syntax, but Andrew Kelley,
creator of the language,
said
that "
it reads
like standard, idiomatic Zig code.
" In particular, he noted that this
approach lets the programmer use all of Zig's typical control-flow primitives,
such as
try
and
defer
; it doesn't introduce any new language
features specific to asynchronous code.
To demonstrate this,
Kelley gave an example of using the new interface to implement asynchronous DNS
resolution. The standard
getaddrinfo()
function for querying DNS information falls short because, although it makes
requests to multiple servers (for IPv4 and IPv6) in parallel, it waits for all of the queries to
complete before returning an answer. Kelley's example Zig code returns the first
successful answer, canceling the other inflight requests.
Asynchronous I/O in Zig is far from done, however.
Io.Evented
is still experimental, and
doesn't have implementations for all supported operating systems yet. A third
kind of
Io
, one that is compatible with WebAssembly, is
planned
(although, as
that issue details, implementing it depends on some other new language
features). The original
pull request for
Io
lists 24
planned follow-up items, most of which still need work.
Still, the overall design of asynchronous code in Zig appears to be set. Zig has
not yet had its 1.0 release, because the community is still experimenting with
the correct way to implement many features. Asynchronous I/O was one of the
larger remaining priorities (along with native code generation, which was also
enabled by default for debug builds on some architectures this year). Zig seems
to be steadily working its way toward a finished design — which should decrease
the number of times Zig programmers are asked to rewrite their I/O because the
interface has changed
again
.
Zig quits GitHub, says Microsoft's AI obsession has ruined the service
The Foundation that promotes the Zig programming language has quit GitHub due to what its leadership perceives as the code sharing site's decline.
The drama began in April 2025 when GitHub user AlekseiNikiforovIBM started a
thread
titled “safe_sleep.sh rarely hangs indefinitely.” GitHub addressed the problem in August, but didn’t reveal that in the thread, which remained open until Monday.
The code uses 100 percent CPU all the time, and will run forever
That timing appears notable. Last week, Andrew Kelly, president and lead developer of the Zig Software Foundation,
announced
that the Zig project is moving to Codeberg, a non-profit git hosting service, because GitHub no longer demonstrates commitment to engineering excellence.
One piece of evidence he offered for that assessment was the “safe_sleep.sh rarely hangs indefinitely” thread.
"Most importantly, Actions has
inexcusable bugs
while being
completely neglected
," Kelly wrote. "After the
CEO of GitHub said to 'embrace AI or get out'
, it seems the lackeys at Microsoft took the hint, because GitHub Actions started 'vibe-scheduling' – choosing jobs to run seemingly at random. Combined with other bugs and inability to manually intervene, this causes our CI system to get so backed up that not even master branch commits get checked."
Older and deeper
Kelly’s gripe seems justified, as the bug discussed in the thread appears to have popped up following
a code change
in February 2022 that users flagged in prior bug reports.
The code change replaced instances of the posix "sleep" command with a "safe_sleep" script that failed to work as advertised. It was supposed to allow the GitHub Actions runner – the application that runs a job from a GitHub Actions workflow – to pause execution safely.
"The bug in this 'safe sleep' script is obvious from looking at it: if the process is not scheduled for the one-second interval in which the loop would return (due to $SECONDS having the correct value), then it simply spins forever," wrote Zig core developer Matthew Lugg in
a comment
appended to the April bug thread.
"That can easily happen on a CI machine under extreme load. When this happens, it's pretty bad: it completely breaks a runner until manual intervention. On Zig's CI runner machines, we observed multiple of these processes which had been running for hundreds of hours, silently taking down two runner services for weeks."
Jeremy Howard, co-founder of Answer.AI and Fast.AI, said in a series of social media
posts
that users’ claims about GitHub Actions being in a poor state of repair appear to be justified.
"The bug,"
he wrote
, "was implemented in a way that, very obviously to nearly anyone at first glance, uses 100 percent CPU all the time, and will run forever unless the task happens to check the time during the correct second."
I can't see how such an extraordinary collection of outright face-palming events could be made
He
added
that the platform-independent fix for the CPU issue proposed last February lingered for a year without review and was
closed
by the GitHub bot in March 2025 before being revived and merged.
"Whilst one could say that this is just one isolated incident, I can't see how such an extraordinary collection of outright face-palming events could be made in any reasonably functioning organization," Howard
concluded
.
GitHub did not immediately respond to a request for comment.
While Kelly has gone on to
apologize
for the incendiary nature of his post, Zig is not the only software project publicly parting ways with GitHub.
Over the weekend, Rodrigo Arias Mallo, creator of the Dillo browser project,
said
he's planning to move away from GitHub owing to concerns about over-reliance on JavaScript, GitHub's ability to deny service, declining usability, inadequate moderation tools, and "over-focusing on LLMs and generative AI, which are destroying the open web (or what remains of it) among
other problems
."
Codeberg, for its part, has doubled its supporting membership since January, going from
more than 600 members
to
over 1,200
as of last week.
GitHub has not disclosed how many of its users pay for its services presently. The code hosting biz had "over 1.3 million paid GitHub Copilot subscribers, up 30 percent quarter-over-quarter," Microsoft CEO Satya Nadella said on the company's
Q2 2024 earnings call
.
In Q4 2024, when GitHub reported
an annual revenue run rate of $2 billion
, GitHub Copilot subscriptions accounted for about 40 percent of the company's annual revenue growth.
Nadella offered a different figure during Microsoft's
Q3 2025 earnings call
: "we now have over 15 million GitHub Copilot users, up over 4X year-over-year." It's not clear how many GitHub users pay for Copilot, or for runner scripts that burned CPU cycles when they should have been sleeping. ®
Accepting US car standards would risk European lives
EU officials must revisit the hastily agreed trade deal with the US, where the EU stated that it “intends to accept” lower US vehicle standards, say cities – including Paris, Brussels and Amsterdam, and more than 75 civil society organisations. In a letter to European lawmakers, the signatories warn that aligning European standards with laxer rules in the US would undermine the EU’s global leadership in road safety, public health, climate policy and competitiveness.
Road Safety
The deal agreed over summer states that
“with respect to automobiles, the United States and the European Union intend to accept and provide mutual recognition to each other’s standards.”
Yet, EU vehicle safety regulations have supported a
36% reduction
in European road deaths since 2010. By contrast, road deaths in the US over the same period
increased 30%
, with pedestrian deaths up 80% and cyclist deaths up 50%.
Europe currently has mandatory requirements for life-saving technologies, such as pedestrian protection, automated emergency braking and lane-keeping assistance. Some of the most basic pedestrian protection requirements which have long been in place in the EU, such as deformation zones in the front of vehicles to reduce crash severity and the prohibition of sharp edges have made cars like the Tesla Cybertruck illegal to sell in Europe.
“Europe built its reputation on pioneering robust vehicle standards.To accept lower US standards would undo decades of EU progress,”
say the signatories
.
According to the letter
“the consequences of such a move for European road safety would be profound.
“
European air quality & health at risk
The EU is set to apply limits to harmful pollution from brake and tyre wear from 2026 onwards, while at the same time the US is moving to weaken air pollution rules for vehicles. Accepting weaker US standards would increase European exposure to pollutants linked to asthma, cancer and numerous cardiovascular and neurological conditions, warn the signatories.
Jobs threat in Europe
Major EU brands such as BMW, Mercedes and Stellantis already build large numbers of vehicles in US automotive plants to EU standards – particularly larger SUVs. However, if the lower US vehicle standards are accepted in Europe, these production lines could produce vehicles to these US lower standards, before shipping these vehicles to the EU. Overall, vehicle production would shift from the EU to the US. To accept lower US car standards would risk large-scale job losses in EU car plants and across Europe’s automotive supply chain.
Existing import loopholes must be closed
The European Commission is already working to tighten Individual Vehicle Approval (IVA), which is being abused to put thousands of oversized US pick-up trucks on EU streets without complying with core EU safety, air pollution and climate standards. To now accept lower US vehicle standards across the board would open the floodgates to US pick-ups and large SUVs.
The signatories urge EU lawmakers to oppose the intention to accept lower US vehicle standards in the EU–US Joint Statement and affirm publicly that EU vehicle standards are non-negotiable.
Researchers Find Microbe Capable of Producing Oxygen from Martian Soil
When we talk about the possibility of humans living on Mars, one of the biggest challenges is not the rockets or the habitats, but something far more basic: how to breathe. Carrying oxygen tanks across space is not practical for long-term survival. This is where a tiny microbe might make a huge difference.
Scientists have been studying an
extremophile
, a type of microorganism that can survive in very harsh environments. This particular one is known as
Chroococcidiopsis
. It has shown the ability to grow on materials that are similar to Martian soil, and in the process, it produces oxygen. That means if it can be cultivated in future Mars colonies, it could support human breathing needs directly on the Red Planet.
Researchers tested this by using soil that mimics
Martian regolith
. The results were promising. The bacteria did not just survive, it actively thrived, pulling nutrients from the soil and releasing oxygen as part of its natural process. What makes it even more interesting is that it does not require rich Earth-like soil to function. Even in the limited resources available on Mars, it can manage to carry out its work.
The experiments also showed that these microbes can survive extreme conditions such as radiation and low pressure that would normally be deadly to most life. Even when their DNA was damaged by radiation, they were able to repair it after rehydration and continue functioning normally, with no lasting increase in mutations. This resilience is what defines them as extremophiles, organisms that have evolved to survive where most others cannot.
For space scientists and planners, this is a big step. If humans ever build bases on Mars, they will need systems that can provide oxygen without constant resupply from Earth. Carrying oxygen would be costly and dangerous, while producing it locally would make settlements more realistic. A living system using microbes might offer a natural and renewable source.
This does not mean the problem is solved. There are still challenges ahead. One is how to grow these organisms at scale in Martian conditions. Another is how to protect them and keep them productive in an environment that is far more unstable than Earth. But the fact that they can survive in laboratory simulations of Mars is an important first step.
There is also a wider question. If such microbes can survive on Mars-like conditions, does that mean life could exist elsewhere in the solar system? Extremophiles on Earth already show us that life can adapt to the most unlikely places — from boiling hot springs to the depths of ice. This experiment adds to the evidence that life is resilient and flexible.
For now, the practical focus remains on human needs. Space agencies and researchers are interested in creating closed-loop systems where food, water, and oxygen can all be recycled and produced on site. Using microbes for oxygen production could become one part of that system.
It is too early to say whether this specific cyanobacterium will be the final answer. But it shows a direction for research and gives hope that we may not need to carry every breath of oxygen from Earth. Instead, we may be able to “farm” our oxygen directly on another planet.
Quad9 will be discontinuing support within DNS-over-HTTPS (DOH) using HTTP/1.1 on December 15, 2025. This should have no impact on most users, but there are some older or non-compliant devices or software which may be unsupported after that time with DOH and which will have to revert to unencrypted DNS or shift to DNS-over-TLS.
Background
Quad9 was the first large-scale recursive resolver to offer standards-based encryption (DNS-over-TLS in 2017). We also provide DNS-over-HTTPS (DOH) as an encryption method, which has been slowly increasing as a percentage of our traffic since standardization and our inclusion of that protocol in 2018. Browsers have been the primary devices operating with DOH, which has some benefits: browsers are updated frequently and are typically kept up to date with newer standards.
The DOH standard recommends HTTP/2 as the lowest version of the protocol for use for DOH (
https://datatracker.ietf.org/doc/html/rfc8484#section-5.2
) but does not rule out using the older HTTP/1.1 standard. We have supported both HTTP/1.1 and HTTP/2 since our inclusion of DOH in our protocol stack seven years ago. However, we are reaching the end of life for the libraries and code that support HTTP/1.1 in our production environment and, therefore, will be sunsetting support for DOH over HTTP/1.1 on December 15, 2025.
Are you affected?
This sunsetting of HTTP/1.1 should not be noticed by the vast majority of our user community who are using Chrome (or any Chromium-based browser or stack), Firefox or Firefox forked projects, Safari (and to our knowledge all other Apple products/apps), or Android and iOS operating systems. They are all fully compliant with our existing and future DOH implementations and, to our knowledge, have always been compliant.
If your platform does not work without the older HTTP/1.1 protocol, then we would suggest you upgrade your system or shift to DNS-over-TLS which does not have an HTTP layer. There is always the possibility of moving to unencrypted DNS, but that decision should be closely considered as a downgrade of security and needs to be made carefully if you are in a network environment of higher risk.
The only platform that we are aware of directly that has ever used HTTP/1.1 and which will stop working after the sunset date are MikroTik devices that have been configured to use DNS-over-HTTPS, as those devices do not support the modern and recommended HTTP/2 transport protocol. We have communicated this to MikroTik on their support forum (
https://forum.mikrotik.com/t/quad9-to-drop-support-for-http-1-1/264174/4
), but there has not yet been an announcement by MikroTik as to when they will update their software to this more recent standard. Other than MikroTik, we have no specific knowledge of any other HTTP/1.1 devices or libraries with sizable user communities, though that does not mean there are no IOT devices or software libraries which are using that method.
From a geographic perspective, there is a community of users in Brazil who are on HTTP/1.1 which we believe to be MikroTik-based. Due to the fact that we cannot associate queries with users (or even one query with another) it is not easily possible for us to determine what types of devices these are, if not MikroTik, nor is it possible for us to inform those users about the impending change as by design we do not know who they are. We welcome any comments from our Brazilian community from knowledgeable users who can enlighten us as to the reasons for this geographic concentration (please contact
support@quad9.net
with details).
Our Reasoning
Despite our large geographic footprint and sizable user community, Quad9 remains a relatively small team. Our limited development efforts are better spent on bringing new features and core stability support to the Quad9 community, and we cannot justify the expense of integrating backwards compatibility for clients that are not meeting the recommended minimum version of protocols. HTTP/2 has been the recommended standard since the publication of the Request for Comments, and we believe this minimization of code is a reasonable step to take when compared with the costs and complexity of backwards compatibility development. In addition, HTTP/1.1 has significant speed and scale challenges, and as time progresses it may be the case that leaving it in our stack would introduce edge-case security or DOS attack vectors which would be difficult to discover and expensive to keep in our testing models.
The update allows us to move forward with additional, newer protocol support that we have been testing, which is ready for deployment and is part of a general refresh of our entire platform and system stack. We will have more flexibility and additional protocol support (keep watching this blog area for details), and the refresh also allows us to take better advantage of newer server hardware that we have been deploying worldwide to continue keeping pace with adoption rates.
We recognize this will cause inconvenience for some subset of users, and many users will not be aware of the change before it is applied as there is no assured direct method for us to communicate with our end users. This is the double-edged sword of not storing user data: we cannot directly notify everyone of changes.
If you know someone who will be impacted, please share and encourage them to take the necessary steps now to avoid interruption of service.
Anti-immigrant material among AI-generated content getting billions of views on TikTok
Guardian
www.theguardian.com
2025-12-03 06:00:57
Researchers uncovered 354 AI-focused accounts that had accumulated 4.5bn views in a month Hundreds of accounts on TikTok are garnering billions of views by pumping out AI-generated content, including anti-immigrant and sexualised material, according to a report. Researchers said they had uncovered 3...
Hundreds of accounts on
TikTok
are garnering billions of views by pumping out AI-generated content, including anti-immigrant and sexualised material, according to a report.
Researchers said they had uncovered 354 AI-focused accounts pushing 43,000 posts made with generative AI tools and accumulating 4.5bn views over a month-long period.
According to AI Forensics, a Paris-based non-profit, some of these accounts attempt to game TikTok’s algorithm – which decides what content users see – by posting large amounts of content in the hope that it goes viral.
One posted up to 70 times a day or at the same time of day, an indication of an automated account, and most of the accounts were launched at the beginning of the year.
Last month TikTok revealed there were at least 1.3bn AI-generated posts on the platform. More than 100m pieces of content are uploaded to the platform every day, indicating that labelled AI material is a small part of TikTok’s catalogue. TikTok is also giving users the option of reducing the
amount of AI content they see
.
Of the accounts that posted content most frequently, half focused on content related to the female body. “These AI women are always stereotypically attractive, with sexualised attire or cleavage,” the report said.
AI Forensics found the accounts did not label half of the content they posted and less than 2% carried the TikTok label for AI content – which the nonprofit warned could increase the material’s deceptive potential. Researchers added that the accounts sometimes escape TikTok’s moderation for months, despite posting content barred by its terms of service.
Dozens of the accounts revealed in the study have subsequently been deleted, researchers said, indicating that some had been taken down by moderators.
Some of the content took the form of fake broadcast news segments with
anti-immigrant narratives
and material sexualising female bodies, including girls that appeared to be underage. The female body category accounted for half of the top 10 most active accounts, said AI Forensics, while some of the fake news pieces featured known broadcasting brands such as Sky News and ABC.
Some of the posts have been taken down by TikTok after they were referred to the platform by the Guardian.
TikTok said the report’s claims were “unsubstantiated” and the researchers had singled it out for an issue that was affecting multiple platforms. In August the Guardian revealed that nearly one in 10 of the fastest growing YouTube channels globally were
showing only AI-generated content
.
“On TikTok, we remove harmful AIGC [artificial intelligence-generated content], block hundreds of millions of bot accounts from being created, invest in industry-leading AI-labelling technologies and empower people with tools and education to control how they experience this content on our platform,” a TikTok spokesperson said.
An example of AI ‘slop’, content that is nonsensical and designed to clutter social media feeds.
Photograph: TikTok
The most popular accounts highlighted by AI Forensics in terms of views had posted “slop”, the term for AI-made content that is nonsensical, bizarre and designed to clutter up people’s social media feeds – such as
animals competing in an Olympic diving contest
or
talking babies
. The researchers acknowledged that some of the slop content was “entertaining” and “cute”.
TikTok guidelines prohibit using AI to depict fake authoritative sources, the likeness of under-18s or the likeness of adults who are not public figures.
“This investigation of [automated accounts] shows how AI content is now integrated into platforms and a larger virality ecosystem,” the researchers said.
“The blurring line between authentic human and synthetic AI-generated content on the platform is signalling a new turn towards more AI-generated content on users’ feeds.”
The researchers analysed data from mid-August to mid-September. Some of the content attempts to make money from users, including pushing health supplements via fake influencers, promoting tools that help make viral AI content and seeking sponsorships for posts.
AI Forensics, which has also highlighted the prevalence of AI content on Instagram, said it welcomed TikTok’s decision to let users limit the amount of AI content they see, but that labelling had to improve.
“Given the structural and non-negligible amount of failure to identify such content, we remain sceptical regarding the success of this feature,” they said.
The researchers added that TikTok should consider creating an AI-only feature on the app in order to separate AI-made content from human-created posts. “Platforms must go beyond weak or optional ‘AI content’ labels and consider segregating generative content from human-created material, or finding a fair system that enforces systematic and visible labelling of AI content,” they said.
Tesla privately warned UK that weakening EV rules would hit sales
Guardian
www.theguardian.com
2025-12-03 06:00:56
Elon Musk-owned electric carmaker also called for support for the secondhand market, documents revealBusiness live – latest updatesTesla privately warned the UK government that weakening electric vehicle rules would hit battery car sales and risk the country missing its carbon dioxide targets, accor...
Tesla privately warned the UK government that weakening electric vehicle rules would hit battery car sales and risk the country missing its carbon dioxide targets, according to newly revealed documents.
The US electric carmaker, run by Elon Musk, also called for “support for the used-car market”, according to submissions to a government consultation earlier this year obtained by the
Fast Charge,
a newsletter covering electric cars.
The Labour government in April worried some electric carmakers by weakening rules, known as the zero-emission vehicle (ZEV) mandate. The mandate forces increased sales of EVs each year, but new loopholes allowed carmakers to sell more petrol and diesel cars.
New taxes on electric cars in
last week’s budget
could further undermine demand, critics have said.
Carmakers including
BMW, Jaguar Land Rover, Nissan and Toyota
– all of which have UK factories – claimed in their submissions to the consultation in spring that the mandate was damaging investment, because they were selling electric cars at a loss. However, environmental campaigners and brands that mainly manufacture electric vehicles said the rules were having the intended effect, and
no carmakers are thought to have faced fines
for sales in 2024.
Tesla argued it was “essential” for electric car sales that the government did not introduce new loopholes, known as “flexibilities”.
Changes “will suppress battery electric vehicle (BEV) supply, carry a significant emissions impact and risk the UK missing its carbon budgets”, Tesla said.
The chancellor, Rachel Reeves, alarmed carmakers further at the budget with the
promised imposition of a “pay-per-mile” charge
on electric cars from 2028, which is likely to reduce their attractiveness relative to much more polluting petrol and diesel models. At the same time, she announced the extension of grants for new electric cars, which the sector has welcomed.
Tom Riley, the author of the Fast Charge, said: “Just as the EV transition looked settled, the budget pulled it in two directions at once – effectively robbing Peter to pay Paul. If carmakers push again for a softer mandate, Labour only has itself to blame when climate targets slip.”
Tesla, Mercedes-Benz and Ford objected to their responses being shared, and were only obtained on appeal under freedom of information law. Several pages were heavily redacted, with one heading left showing Tesla called for “support for the used-car market”. Tesla declined to comment on whether that support would include grants.
In contrast, the US carmaker Ford and Germany’s Mercedes-Benz lobbied against more stringent rules after 2030 that would have forced them to cut average carbon dioxide emissions further – potentially allowing them to sell more-polluting vehicles for longer.
Ford strongly criticised European governments for pulling support for electric car sales, saying that “policymakers in many European jurisdictions have not delivered their side of the deal”.
Ford has U-turned
after previously backing stronger targets.
The US carmaker also pointed to the threat of being undercut by Chinese manufacturers that “do not have a UK footprint and benefit from a lower cost base”.
Mercedes-Benz argued that the UK should cut VAT on public charging from 20% to
5% to match home electricity
, and added that it should consider a price cap on public charging rates.
Tesla also called for a ban on sales of plug-in hybrid electric vehicles with a battery-only range of less than 100 miles after 2030 – a limit that would have ruled out many of the bestselling models in that category.
Ford, Mercedes-Benz and Tesla declined to comment further.
TIL: Dependency groups and uv run
Simon Willison
simonwillison.net
2025-12-03 05:55:23
TIL: Dependency groups and uv run
I wrote up the new pattern I'm using for my various Python project repos to make them as easy to hack on with uv as possible. The trick is to use a PEP 735 dependency group called dev, declared in pyproject.toml like this:
[dependency-groups]
dev = ["pytest"]
With ...
TIL: Dependency groups and uv run
. I wrote up the new pattern I'm using for my various Python project repos to make them as easy to hack on with
uv
as possible. The trick is to use a
PEP 735 dependency group
called
dev
, declared in
pyproject.toml
like this:
[dependency-groups]
dev = ["pytest"]
With that in place, running
uv run pytest
will automatically install that development dependency into a new virtual environment and use it to run your tests.
This means you can get started hacking on one of my projects (here
datasette-extract
) with just these steps:
git clone https://github.com/datasette/datasette-extract
cd datasette-extract
uv run pytest
YouTube says it will comply with Australia’s under-16s social media ban, with Lemon8 to also restrict access
Guardian
www.theguardian.com
2025-12-03 05:49:50
Australia’s under-16s social media ban might take weeks to work but all platforms are on notice, government saysFollow our Australia news live blog for latest updatesGet our breaking news email, free app or daily news podcastYouTube will comply with the federal government’s under-16s social media ba...
YouTube will comply with the federal government’s under-16s social media ban, but its parent company Google has warned the laws “won’t keep teens safer online” and “fundamentally misunderstands” how children use the internet.
But the communications minister, Anika Wells, said YouTube had a responsibility to keep its platform safe, calling its warnings “outright weird”.
Guardian Australia can also reveal that Lemon8, a newer social media app that has experienced a surge in interest recently because it is not included in the ban, will restrict its users to over-16s from next week. The eSafety Commissioner had previously warned it was closely monitoring the app for possible inclusion in the ban.
Ahead of Wells’ address to the National Press Club on Wednesday, Google said it will begin signing out underage users from its platform from 10 December, but warned it would mean children and their parents would lose access to safety features.
Rachel Lord, Google’s senior manager for public policy in Australia, said
in a blog post
that users under 16s will still be able to watch YouTube videos in a signed-out state, but that children would lose access to “features that only work when you are signed into an account, including subscriptions, playlists and likes, and default wellbeing settings such as “Take a Break” and Bedtime Reminders”.
She also warned that parents “will lose the ability to supervise their teen or tween’s account on YouTube”, such as content settings blocking specific channels.
Lord wrote: “This rushed regulation misunderstands our platform and the way young Australians use it. Most importantly, this law will not fulfill its promise to make kids safer online, and will, in fact, make Australian kids less safe on YouTube.”
While not flagging any legal options, Lord added: “We are committed to finding a better path forward to keep kids safe online.”
Speaking to the National Press Club, Wells said parents could set up control and safety settings on YouTube Kids, a separate platform not included in the ban.
“I find it outright weird that YouTube is always at pains to remind us all how unsafe their platform is in a logged-out state. If YouTube is reminding us all that it is not safe and there’s content not appropriate for age-restricted users on their website, that’s a problem that YouTube needs to fix,” she said.
Anika Wells speaks at the National Press Club on Wednesday.
Photograph: Mick Tsikas/AAP
But Wells also conceded the government’s plans to bar under-16s from social media might take “days or even weeks” to properly take effect.
“We know it won’t be perfect from day one but we won’t give up – and we won’t let the platforms off the hook,” Wells said.
Wells praised the advocacy of families of children who had ended their lives after online bullying and mental health issues, saying the changes would “protect generation Alpha from being sucked into purgatory by the predatory algorithms.” She claimed social media platforms deliberately targeted teenagers to maximise engagement and profits.
“These companies wield an incredible power we willingly hand over to them because of the benefits the platform bring to us. From 10 December, we start to take that power back for young Australians,” Wells said.
Meta has
told users of Facebook, Instagram and Threads what to expect
from next week,
as has Snapchat
. A Reddit spokesperson said the company had no updates to share when contacted by Guardian Australia on Tuesday, while X, TikTok, YouTube and Kick have also not publicly confirmed how they will comply with the legislation and did not respond to questions.
The government has said sending a signal to parents and children about not accessing social media is worthwhile, even if some children slip through the net.
Wells said it will take some time before tech companies are threatened with the $50m fines, explaining that the eSafety Commissioner will seek information from the platforms on 11 December about their efforts to purge underage users. It will then seek data monthly.
In a press conference in Adelaide on Tuesday, Wells foreshadowed more platforms being added to the under-16s ban if children migrated to sites not currently listed.
She told media to “stay tuned” for news about Lemon8, an Instagram-style app not included in the ban. Guardian Australia understands the eSafety Commission has written to Lemon8 – owned by TikTok’s parent company, ByteDance – to say the agency would monitor the platform for possible inclusion after the scheme begins.
Guardian Australia can reveal Lemon8 has decided to restrict its users to those aged over 16 from 10 December.
“If everybody ends up on LinkedIn, and LinkedIn becomes a place where there is online bullying, algorithms targeting 13-to-16-year-olds in a way that’s deteriorating their mental and physical health, then we will go after LinkedIn,” Wells said on Tuesday.
“That’s why all platforms are on notice. We have to be agile and dynamic.”
You're probably reading this page because you've attempted to
access some part of
my blog (Wandering
Thoughts)
or
CSpace
, the wiki thing it's
part of. Unfortunately whatever you're using to do so has a HTTP
User-Agent header value that is too generic or otherwise excessively
suspicious. Unfortunately, as of early 2025 there's a plague of
high volume crawlers (apparently in part to gather data for LLM
training) that behave like this. To reduce the load on
Wandering Thoughts
I'm experimenting with
(attempting to) block all of them, and you've run into this.
All HTTP User-Agent headers should clearly identify what they
are, and for non-browser user agents, they should identify not just
the software involved but also who specifically is using that software.
An extremely generic value such as "
Go-http-client/1.1
"
is not something that I consider acceptable any more.
Chris Siebenmann, 2025-02-17
Interview with RollerCoaster Tycoon's Creator, Chris Sawyer (2024)
In this article, we'll try to understand how
ECDSA
(Elliptic Curve Digital Signature Algorithm) works.
The version I have in mind is the one used by the
Ethereum
blockchain. Since my interest lies in security, we'll also explore the
signature malleability attack
.
I expect you to be familiar with
Public Key Cryptography
and how it can be used to sign messages, at least conceptually.
You'll only need to know basic math, so
abstract algebra
is
not
a requirement. I'll introduce the bare minimum as we go. My exposition will be deliberately
unsophisticated
, favoring
ease of understanding
over
conciseness
and
elegance
.
The reader I have in mind is someone dissatisfied with the many superficial, hand-wavy explanations of ECDSA often found in articles and books aimed at developers and auditors, but who doesn't have the time or interest to go all the way down the rabbit hole and learn cryptography in a thorough and systematic way.
If you, like me, work in a field where you need to have a working knowledge of multiple disciplines, you'll probably appreciate this kind of compromise.
Finally, this might also serve as an introduction to the topic before you turn to more serious and academic literature.
Not your typical article
You can think of this section as a kind of
disclaimer
.
This article is the result of an exercise where I start from a vague understanding of a topic and try to connect all the dots and fill in all the gaps on my own, without relying on any external sources of information. This means no books, no LLMs, and no internet.
For the exercise to be effective, it needs to be written with an audience in mind, forcing you to keep track of what you've already explained and what you can expect the reader to know. It also helps you do a better job because you feel more exposed.
Have you ever gone back to something you learned in the past and realized you forgot most of it? Your knowledge has become
sparse
and all you remember are some facts disconnected from each other.
Can you restore the original picture on your own?
If you succeed, your final understanding will be much deeper than the one you'd have if you relied on external help such as books and notes.
With this article, I go a step further and try to connect the dots with knowledge that I never had to begin with. The fact that it's possible is what makes mathematical topics so special.
That should explain why
I
wrote it, but why should
you
read it?
Well, you get to read something:
Constructive
in nature, since most of the formulas and derivations have to be recreated from scratch.
Insightful
, since I share some of my intuition and mental models, which is somewhat unusual in more rigorous settings.
Naive
, as I observe and notice some connections for the first time, possibly making my exposition more engaging but also less polished.
Non-authoritative
, demanding your full attention and critical thinking to spot inconsistencies.
Non-standard
, since some facts may be stated or named differently from official literature.
Your role is that of an
auditor
or
verifier
, constantly trying to find any inconsistencies and
non sequiturs
in what I wrote: I'm the
generator
and you the
discriminator
. In a (constructively)
adversarial
setting, this would be an
iterative
process.
It goes without saying that this article is meant to be read
linearly
, from the start.
Modular arithmetic
It's all around us:
Mon
Tue
Wed
Thu
Fri
Sat
Sun
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
If we're just interested in the day of the week, then the numbers in the same column are equivalent. What do they have in common? The fact that the difference between any two of them is always a multiple of
\(7\)
:
\(29-8 = 21 = 3\cdot 7\)
\(22-15 = 7\)
\(31-17 = 14 = 2\cdot 7\)
Since numbers in the same column are equivalent, we can represent all of them by the smallest one. Let's call it the
representative
of the column. If we do that, we end up with
\(7\)
numbers:
Mon
Tue
Wed
Thu
Fri
Sat
Sun
0
1
2
3
4
5
6
That's not ideal for a calendar, but it makes sense: we just add multiples of
\(7\)
to the starting numbers to recover the missing ones.
How do we get the representative from a number? For instance, what's the representative of
\(45\)
? Well,
\(45 = 3 + 7\cdot 6\)
, so the representative is
\(3\)
. Indeed, starting from
\(3\)
, we add a multiple of
\(7\)
to get
\(45\)
.
Now what's
\(3\)
with respect to
\(45\)
? It's the
remainder
of
\(45\)
divided by
\(7\)
. We can get that number by using the
mod(ulo)
operator:
\(45\ \mathrm{mod}\ 7 = 3\)
, or
45 % 7 == 3
, in many programming languages.
Beware:
In
JS
:
-45 % 7
is
\(-3\)
In
Solidity
:
-45 % 7
is
\(-3\)
In
Python
:
-45 % 7
is
\(4\)
In
Math
:
\(-45\ \mathrm{mod}\ 7\)
is
\(4\)
Both values make sense, since they're separated by
\(7\)
and, thus, in the same column, or
equivalence class
, i.e. the class of all equivalent elements. But we want the representative, so
\(4\)
is preferred. Observe that
\(-45 = -7\cdot 7 + 4\)
.
Basically, any time we're outside the window
\(\{0, \ldots, 6\}\)
, we add or subtract
\(7\)
as many times as we need to land in the window.
Note that
((n % 7) + 7) % 7
will give the representative in any language, since:
n % 7
is in
\(\{-6, -5, \ldots, 0, \ldots, 5, 6\}\)
(n % 7) + 7
is in
\(\{1, \ldots, 13\}\)
((n % 7) + 7) % 7
is in
\(\{0, \ldots, 6\}\)
Observe that:
adding
\(7\)
doesn't change the equivalence class
(x % 7) % 7
is just
x % 7
. This property is called
idempotency
(
same power
): reapplying the operation doesn't increase the extent of the effect, i.e. it gives the same result.
Instead of writing mod operators everywhere, we can say that we're computing mod
\(p\)
:
\[y^2 = x^3 + 7\ (\mathrm{mod}\ p)\]
That's equivalent to
\[y^2\ \mathrm{mod}\ p = (x^3 + 7) \ \mathrm{mod}\ p\]
which is a pain to write.
If we're only dealing with addition and multiplication, then we can insert as many "mod
\(p\)
" as we want wherever we want, so these two expressions are equivalent:
ECDSA doesn't rely on exponentiation, so we don't need to talk about it.
We still don't know how to
divide
mod
\(p\)
. That is, we don't know how to compute, say,
\(3/4\)
mod
\(p\)
, or whether it even exists.
What does dividing by
\(4\)
do? It does something that can be reversed by multiplying by
\(4\)
. So the two operations cancel out and are equivalent to multiplying by
\(1\)
, the
neutral element
. In other words, we must have
\[a / a = 1\ (\mathrm{mod}\ p)\]
That's usually written as
\[a\cdot a^{-1} = 1\ (\mathrm{mod}\ p)\]
where
\(a^{-1}\)
is called the
multiplicative inverse
of
\(a\)
.
As an aside, the
additive inverse
, or
opposite
, is simply
\(-n\)
, since
\(n + (-n) = 0\)
, where
\(0\)
is the neutral element of addition. Of course, we can compute
\(-n\ \mathrm{mod}\ p\)
to get the representative of
\(-n\)
.
Let's find
\(x\)
such that
\(4\cdot x = 1\ (\mathrm{mod}\ 7)\)
by using simple
brute force
:
\(4\cdot 0 = 0\)
\(4\cdot 1 = 4\)
\(4\cdot 2 = 1\)
\(4\cdot 3 = 5\)
\(4\cdot 4 = 2\)
\(4\cdot 5 = 6\)
\(4\cdot 6 = 3\)
I omitted "
\(\left(\mathrm{mod}\ 7\right)\)
" for convenience. I'll do that often from now on.
As we can see,
\(4^{-1} = 2\)
. That's because
\(4\cdot 2 = 8 = 8-7 = 1\)
.
Let's go back to our
\(3/4\)
:
\[3/4 = 3\cdot 4^{-1} = 3\cdot 2=6\]
Indeed:
\[6\cdot 4 = 24 = 3\]
so we get
\(3\)
back.
An important fact to know is that
a number
\(a\)
is always
invertible
mod
\(p\)
, as long as it's
coprime
with
\(p\)
, i.e. their
GCD
(greatest common divisor) is
\(1\)
.
Proof
(
safely skippable
)
Let's define
\(r_x = a\cdot x\ \mathrm{mod}\ p\)
. Let
\(r\)
be the sequence
\(r_0, \ldots, r_{p-1}\)
.
Again, I'll omit "mod
\(p\)
" for notational convenience.
If
\(r_x = r_y\)
, i.e.
\(a\cdot x = a\cdot y\)
, then
\(a(x-y) = 0\)
, which means that
\(a(x-y)\)
is divisible by
\(p\)
. If
\(a\)
and
\(p\)
are coprime, then
\(x-y\)
must be divisible by
\(p\)
, so
\(x-y = 0\)
, i.e.
\(x=y\)
. In other words,
\(x\neq y\)
implies that
\(r_x \neq r_y\)
.
This means that
\(r\)
has
\(p\)
distinct values in
\(\{0, \ldots, p-1\}\)
, i.e.
\(r\)
is a
permutation
of the sequence
\(0, \ldots, p-1\)
. In particular,
\(r\)
contains exactly one
\(1\)
, so there's exactly one
\(x\)
such that
\(a\cdot x = 1\)
.
End of proof!
As an example, let's look again at the brute-forcing we did above to find
\(4^{-1}\ \mathrm{mod}\ 7\)
and note that the results are a permutation of the numbers from
\(0\)
to
\(6\)
, so they contain exactly one
\(1\)
. That's expected since
\(4\)
and
\(7\)
are coprime.
Observe that when
\(p\)
is prime, all numbers from
\(1\)
to
\(p-1\)
are coprime with it, so they're all invertible.
Technically, the set of representatives
\(0, \ldots, p-1\)
is often denoted by
\(\mathbb{Z}_p\)
or
\(\mathbb{Z}/p\mathbb{Z}\)
. It's obtained by partitioning the integers into equivalence classes (our calendar columns, but extended to all integers) and representing each class by a representative in
\(\{0, \ldots, p-1\}\)
. That's what we did informally.
Extended Euclidean algorithm
Tip
You can safely skip this section
, if you already know or don't care about how the multiplicative inverse can be computed in practice. If you're interested in the method of
generating functions
you might still want to read the
Fibonacci numbers
subsection, though.
For a fast and practical way to compute the multiplicative inverse, we can use the
extended Euclidean algorithm
(EEA).
The Euclidean algorithm (EA) can be used to efficiently compute
\(\mathrm{GCD}(a, p)\)
, and its extended version returns two integers
\(x\)
and
\(y\)
such that
This means that
\(x\)
is the multiplicative inverse of
\(a\)
mod
\(p\)
.
How does the algorithm work? It's very simple.
The first observation is that
\[\mathrm{GCD}(a, b) = \mathrm{GCD}(a, b-a)\]
and, by symmetry,
\[\mathrm{GCD}(a, b) = \mathrm{GCD}(a-b, b)\]
We will prove this later.
Since we can subtract repeatedly, we can also use the mod operator:
\[\mathrm{GCD}(a, b) = \mathrm{GCD}(a\ \mathrm{mod}\ b, b\ \mathrm{mod}\ a)\]
This way, we can reduce the two arguments very quickly. Note that in a real implementation we only need one
mod
per step, since one of the two has clearly no effect.
Let's use it to compute
\(\mathrm{GCD}(784, 495)\)
:
The second column shows how we got the new values. Since we obtained
\(\mathrm{GCD}(3, 1)\)
, the GCD is
\(1\)
, i.e.
\(784\)
and
\(495\)
are coprime.
The extended version of the algorithm uses the second column in a simple way. To start, we notice that the equation at the bottom of the second column is already in the right form, i.e.
\[40\cdot 1 + 3(-13) = 1\]
However, we want the expression with respect to the initial values
\(784\)
and
\(495\)
.
The solution is easy: we just do substitutions as we go up the second column, starting from the bottom:
Let
\(d_1\)
be the GCD on the left and
\(d_2\)
the one on the right. It's clear that
\(d_1|u\)
and
\(d_1|v\)
, which implies that
\(d_1|(v+ku)\)
. Now we'd like to conclude that
\(d_1|d_2\)
.
Unfortunately, we only proved that
\(d_1\)
is a common divisor of
\(u\)
and
\(v+ku\)
so far.
Let's show that
if
\(d'\)
divides both
\(a\)
and
\(b\)
, then it also divides
\(d = \mathrm{GCD}(a,b)\)
.
Proof
(
safely skippable
)
We can always express
\(d\)
and
\(d'\)
as
\[
\begin{cases}
d' = u\cdot \mathrm{GCD}(d, d') \\
d = v\cdot \mathrm{GCD}(d, d') \\
1 = \mathrm{GCD}(u, v)
\end{cases}
\]
where, as indicated,
\(u\)
and
\(v\)
are coprime.
Observe that if
\(u\)
and
\(v\)
weren't coprime, their common divisor would be absorbed by
\(\mathrm{GCD}(d, d')\)
, so we'd have the same situation as above but for
\(u'=u/\mathrm{GCD}(u,v)\)
and
\(v'=v/\mathrm{GCD}(u,v)\)
.
Since
\(a\)
and
\(b\)
are divisible by both
\(d'\)
and
\(d\)
, then
\(a' = a/\mathrm{GCD}(d, d')\)
and
\(b' = b/\mathrm{GCD}(d, d')\)
must still be divisible by
\(u\)
and
\(v\)
. So:
\(u k_1 = a' = v k_2\)
\(u h_1 = b' = v h_2\)
for some integers
\(k_1\)
,
\(k_2\)
,
\(h_1\)
, and
\(h_2\)
.
Since
\(u\)
and
\(v\)
are coprime, then
\(u|k_2\)
and
\(u|h_2\)
, i.e.
\(k_2 = u k_3\)
and
\(h_2 = u h_3\)
for some integers
\(k_3\)
and
\(h_3\)
. Therefore:
\(a' = uv k_3\implies a = uv \mathrm{GCD}(d, d') k_3 = ud k_3\)
\(b' = uv h_3\implies b = uv \mathrm{GCD}(d, d') h_3 = ud h_3\)
This means that
\(ud\)
is a common divisor of
\(a\)
and
\(b\)
, and
\(u>1\)
would imply that we found a greater divisor than
\(d\)
, their GCD.
Since
\(u = 1\)
, then
\(d' = \mathrm{GCD}(d, d')\)
, i.e.
\(d'|d\)
.
End of proof!
I seem to recall that some people include this property in the definition of the GCD itself, but I think that's slightly redundant.
Anyway, we're done!
Wait! How fast is this algorithm? Let's look at the reduction again:
I can see two
super Fibonacci sequences
. Here's the green one:
Green Seq.
1
3
40
83
206
289
495
Green Mult.
0
13
2
2
1
1
1
Fibonacci numbers form a sequence
\(F_0, F_1, F_2, \ldots\)
where the
recurrence relation
is
\(F_i=F_{i-2}+F_{i-1}\)
, for
\(i=2, 3, \ldots\)
.
In our case, however, the
recurrence relation
is
\(F_i = F_{i-2}+F_{i-1}\cdot M_{i-1}\)
, where
\(F\)
is on the first row and
\(M\)
on the second row of the table.
As an example, I highlighted 4 elements in the table:
\(206 = 40 + 83\cdot 2\)
. I call this a
super
Fibonacci sequence because the multipliers make it grow faster than the regular one (corresponding to all
\(M_i=1\)
).
Fibonacci numbers grow exponentially, so the number of steps necessary to reach a number
\(n\)
is
\(\Theta(\log n)\)
.
Since our sequence is even faster, the number of steps is lower and all we can say for now is that the worst-case complexity of EEA is
\(O(\log n)\)
.
Note
Technically,
\(\Theta\)
denotes
exact growth
,
\(O\)
denotes an
upper bound
, and
\(\Omega\)
denotes a
lower bound
.
For instance,
\(n = O(n^2)\)
is correct, though in practice people often use
\(O\)
when they really mean
\(\Theta\)
.
Moreover,
\(n = O(n^2)\)
really means
\(n \in O(n^2)\)
, but the former notation is more common than the latter.
Can we think of a very slow sequence? But, of course! We can build it starting from the bottom and always choosing
\(M_i=1\)
:
Those are basically two Fibonacci sequences! This tells us that the worst case of the EEA is indeed logarithmic or, to be precise,
\(\Theta(\log (\min\{a, b\}))\)
. Why
min
? Because we have two sequences: the green and the red one. Since they start and end together, the faster one dominates the other and faster growth means shorter sequence, so the time complexity is
\(\Theta(\min\{\log a, \log b\})\)
, i.e.
\(\Theta(\log (\min\{a, b\}))\)
.
I had no idea that the EA had such a connection with the Fibonacci numbers before writing this section. As always, check my reasoning!
Fibonacci numbers
Tip
You can safely skip this section
. You don't need it for the rest of the article, but if you want to learn about
generating functions
, I think this is a good opportunity.
I want to find the
base
for the logarithm that appears in the time complexity of the EA and EEA algorithms.
If we assume that Fibonacci numbers grow
exponentially
, i.e.
\(F_i\sim b^i\)
, then:
We divide by
\(b^i\)
and get
\(b^2-b-1 = 0\)
, whose positive solution is
\[b_+ = \frac{1 + \sqrt{5}}{2} \approx 1.618\]
That's the well-known
golden ratio
.
We started from the assumption that the growth is exponential, but what's the exact expression for the
\(n\)
-th Fibonacci number, just to make sure we're correct?
Let
\(V_0\)
be the vector of Fibonacci numbers
\(F_i\)
:
\(V_0:\)
\(F_0\)
\(F_1\)
\(F_2\)
\(F_3\)
\(F_4\)
\(F_5\)
\(F_6\)
\(\ldots\)
Now let's introduce the two shifted versions
\(V_1\)
and
\(V_2\)
:
\(V_0:\)
\(F_0\)
\(F_1\)
\(F_2\)
\(F_3\)
\(F_4\)
\(F_5\)
\(F_6\)
\(\ldots\)
\(V_1:\)
\(F_0\)
\(F_1\)
\(F_2\)
\(F_3\)
\(F_4\)
\(F_5\)
\(\ldots\)
\(V_2:\)
\(F_0\)
\(F_1\)
\(F_2\)
\(F_3\)
\(F_4\)
\(\ldots\)
We can see that, from the third column onward,
\(V_0 = V_1 + V_2\)
, because of the relation
\(F_{i+2} = F_{i+1} + F_{i}\)
.
The advantage of the vector approach is that we don't have to deal with the index
\(i\)
anymore. In a sense, we
vectorized
the loop and abstracted away the annoying index. The drawback is that we lost some
algebraic power
because, unless we introduce other operations, we don't even know how to express the fact that
\(V_1\)
is a shifted version of
\(V_0\)
.
Instead of reinventing the wheel, why don't we use a
power series
instead of a simple vector? I'm thinking of something like this:
To reiterate, we expressed all the following relations at once:
\(F_2 = F_1 + F_0\)
\(F_3 = F_2 + F_1\)
\(F_4 = F_3 + F_2\)
\(\ldots\)
We can now express
\(P_1\)
and
\(P_2\)
in terms of
\(P_0\)
. For convenience, let's write
\(P\)
instead of
\(P_0(x)\)
:
\[
P - F_0 - F_1 x = x P - F_0 x + x^2 P
\]
Now we solve for
\(P\)
:
\[
P = \frac{(F_0 - F_1)x - F_0}{x^2 + x - 1}
\]
Since Fibonacci numbers start with
\(0\)
and
\(1\)
, let's substitute
\(F_0=0\)
and
\(F_1=1\)
:
\[
P = \frac{-x}{x^2 + x - 1}
\]
That's in
implicit
form. If we can put
\(P\)
in
explicit
form, then we can read the expression for the generic coefficient of
\(x^i\)
, which, by construction, is the
\(i\)
-th Fibonacci number! Here's the form we want:
\[
P(x) = \sum_{i=0}^\infty \alpha_i x^i
\]
The coefficient
\(\alpha_i\)
is bound to be a general expression for
\(F_i\)
.
How do we do that?
Let's take the simplest power series we can think of and find both the explicit and implicit forms for it:
We don't care about
convergence
, as we only want to read the coefficients of the
series
. As long as what we do is algebraically correct, we should be fine. We might say that we're repurposing some algebraic machinery to do "something else".
Note
We say a series
converges
if it evaluates to a real number. Otherwise, we say it
diverges
.
For instance, the following series clearly converges:
$$
\sum_{i=0}^\infty \left(\frac{1}{10}\right)^i = 1 + 0.1 + 0.01 + \ldots = 1.1111\ldots
$$
In the Fibonacci case, we want to find
\(\alpha_i\)
such that
Now that we've witnessed how the simple case works, we should have more confidence that this method might just work! It might still look like magic, though.
How do we close the gap between the simple case and the Fibonacci case?
First, we notice that the denominator is factorizable:
If we can convert each of the two parts into explicit form, then we're done, since explicit forms sum nicely: we just sum the corresponding coefficients.
Now we divide numerator and denominator of the left part by
\(c_1\)
and of the right part by
\(c_2\)
:
So,
\(F_i \sim \frac{\phi^i}{\sqrt5} = \Theta(\phi^i)\)
, where
\(\phi = \frac{1+\sqrt5}{2}\)
.
By the way,
\(P(x) = \sum_{i=0}^\infty F_i x^i\)
is called a
generating function
.
secp256k1
Ethereum's ECDSA uses the elliptic curve
secp256k1
, defined as
\[y^2 = x^3 + 7\ (\mathrm{mod}\ p)\]
where
\(p\)
is a very big prime number.
Here's what the continuous version (i.e. without mod) of the curve looks like:
The continuous elliptic curve is the set of all points
\((x, y)\)
in the plane such that
\(y^2 = x^3 + 7\)
. Because
\(y\)
is squared, the curve is symmetric about the X-axis, i.e.
\((x, y)\)
is on the curve if and only if
\((x, -y)\)
is.
When we switch to mod
\(p\)
, things get complicated:
To draw that picture I used
\(p = 97\)
, a small prime. The blue line is the continuous curve, while the dots are the solutions in
\(\mathbb{Z}_p\times\mathbb{Z}_p\)
. Note that those solutions must always be finitely many, since they lie on a
\(p\times p\)
grid.
This figure only shows the upper right part (1st quadrant) of the previous one, so we can't see the symmetry of the continuous curve. Yet, the points in
\(\mathbb{Z}_p\times\mathbb{Z}_p\)
show a new symmetry: they're reflected across the horizontal line
\(y = p/2\)
. That makes sense:
\((x, y)\)
lies on the continuous curve if and only if
\((x, -y)\)
also lies on it.
If
\(y\in\mathbb{Z}_p\)
, then
\(-y = p-y\)
.
This means that
\((x, y)\)
lies on the mod curve if and only if
\((x, p-y)\)
also lies on it.
\(y = p-y\)
gives us
\(y = p/2\)
. In the figure the axis of symmetry is
\(y = 97/2 = 48.5\)
.
Let's plot the negative solutions as well:
As we can see, the part above is identical to the part below, since adding
\(p\)
or
\(-p\)
to a coordinate doesn't change anything mod
\(p\)
.
Group
A group
\(G\)
is a set of elements equipped with a binary operation,
\(+\)
, such that:
For any elements
\(a, b\in G\)
, we must have
\(a+b \in G\)
.
There's a
neutral element
, or
identity
,
\(0\)
, so that
\(a+0 = 0+a = a\)
for every
\(a\in G\)
.
For every
\(a\in G\)
, there exists an
additive inverse
, or
opposite
, of
\(a\)
, denoted as
\(-a\)
, such that
\(a+(-a) = (-a)+a = 0\)
.
Note:
\(a+(-b)\)
can also be written as
\(a-b\)
.
We also want
associativity
, i.e., for all
\(a,b,c\in G\)
, we must have
\(a + (b+c) = (a+b) + c\)
. So, we can drop the parentheses and write
\(a+b+c\)
.
If for all
\(a, b\in G\)
we have
\(a+b = b+a\)
, then
\(G\)
is
abelian
or
commutative
.
Notice that we can't have two distinct identities
\(0_1 \neq 0_2\)
, since
\[0_1 = 0_1+0_2 = 0_2\]
Let's break it down:
\(0_1+0_2\)
can be simplified in two ways:
Since
\(0_1\)
is an identity, then it disappears, so
\(0_1+0_2 = 0_2\)
Since
\(0_2\)
is an identity, then it disappears, so
\(0_1+0_2 = 0_1\)
Therefore, the two identities must be equal.
The same goes for the additive inverse. If
\(x\)
and
\(y\)
are opposites of
\(a\)
then:
Given a subset
\(S\)
of elements from
\(G\)
, we can define the subgroup
\(G_S\)
of
\(G\)
generated by
\(S\)
as the smallest group that includes all the elements of
\(S\)
. We say that
\(S\)
is a
generator
of
\(G_S\)
.
In this article we'll only describe in detail the case where
\(S\)
has just one element. For simplicity, we'll use the same symbol for the subgroup and its generator, so a generator
\(G\)
generates the (sub)group
\(G\)
defined as follows:
\[G = \{0, G, G+G, G+G+G, G+G+G+G, \ldots\}\]
That's very cumbersome to read and write, so let's define
ECDSA defines a group over the points on the curve mod
\(p\)
. To do that, we first need to define an addition between points.
Here's how it's done on the continuous version of the elliptic curve:
If
\(P\)
and
\(Q\)
are two points on the curve, then
\(P+Q\)
is the reflection across the X-axis of the intersection between the curve and the line passing through
\(P\)
and
\(Q\)
.
The line through
\(P\)
and
\(Q\)
always intersects the curve at a third point, except when the line is perfectly vertical. In that case, the line is said to intersect the curve at
\(0\)
, called the
point at infinity
. The point
\(0\)
acts as the identity, but is not really part of the curve, so it needs to be handled as a special case.
Now, observe that the line through
\(R = (x, y)\)
and
\(-R = (x, -y)\)
is vertical, so
\(R+(-R)=0\)
, as suggested by the "
\(-\)
" sign.
Note
Usually, the
point at infinity
is denoted by
\(\mathcal{O}\)
and the origin by
\(0 = (0, 0)\)
. However, since we have no need for the origin in this context, we'll denote the point at infinity by
\(0\)
, stressing the fact that it's the
zero
of the group.
When
\(P = Q\)
, the line through
\(P\)
and
\(Q\)
is taken to be the
tangent
to the curve at
\(P\)
(or, equivalently,
\(Q\)
). It makes sense:
Just imagine fixing
\(P\)
and sliding
\(Q\)
along the curve from one side of
\(P\)
to the other.
If we want the animation of the line during the sliding to be continuous, the line must be the tangent when
\(P = Q\)
.
After all, the animation is continuous when the slope of the line is continuous, and the slope is continuous at a point when it's equal to its limit, i.e. the derivative, at that point.
Here's a figure with a fixed point
\(P\)
and secants through
\(P\)
and several
\(Q_i\)
points that converge to
\(P\)
. I chose a color map such that the closer a
\(Q_i\)
is to
\(P\)
, the
bluer
the secant through it becomes.
By the way, we still count the tangent line as intersecting the elliptic curve in three points, with two coinciding at the point of tangency.
But how is it possible that even an "almost vertical" line through two points on the curve always intersects the curve at a third point? Such a line intersects the curve either at a point towards
\((+\infty, +\infty)\)
or
\((+\infty, -\infty)\)
.
For
\(x\to+\infty\)
:
line:
\(y = mx + q \sim mx\)
curve:
\(y^2 = x^3 + 7 \sim x^3 \implies\)
\(y \sim x^{3/2}\)
(upper branch)
\(y \sim -x^{3/2}\)
(lower branch)
What we're saying is that when
\(x\)
becomes very big, additive terms such as
\(q\)
and
\(7\)
are dwarfed and can be ignored, so the line grows like
\(mx\)
, while the curve grows like
\(x^{3/2}\)
. The curve grows asymptotically faster, so, when
\(m > 0\)
, the upper branch of the curve will hit the line from below and cross it sooner or later. Similarly, when
\(m < 0\)
, the lower branch of the curve will hit the line from above and cross it.
Here's a visual example for
\(m>0\)
:
Finding the intersection point
The algebra is easy enough. We just compute the intersection between the curve and the line. Let's start from the two points
\(P = (x_P, y_P)\)
and
\(Q = (x_Q, y_Q)\)
.
If the line is vertical, the third intersection point is
\(0\)
. Otherwise, its equation has the form
\(y = mx + q\)
, where
\[
\begin{align*}
m &= \frac{y_Q-y_P}{x_Q-x_P} \\
q &= y_P - m x_P
\end{align*}
\]
Before giving in to despair, we remember that we already know two solutions,
\(x_P\)
and
\(x_Q\)
, and we're looking for the third point
\(-R = (x_{-R}, y_{-R})\)
. This means that the LHS of the equation in
\(x\)
must be factorizable as
\[(x - x_P)(x - x_Q)(x - x_{-R}) = 0\]
Let's expand it to get
\[x^3 + x^2(-x_P-x_Q-x_{-R}) + \ldots = 0\]
The second term is all we need: for the two LHS to be equal, we must have
As we can see, finding the sum of two points requires subtractions, multiplications, and one division to compute
\(m\)
. To sum two points on the curve mod
\(p\)
, we just need to do the calculations mod
\(p\)
. We know how to "divide" mod
\(p\)
, so we're good.
Let's briefly go through the case with
\(P=Q=(x,y)\)
. Recall that we need to consider the tangent to the curve at
\((x,y)\)
. Note that for
\(y=0\)
the tangent is perfectly vertical, so the result is
\(0\)
(look at the figure). For
\(y\neq 0\)
, we need to compute the derivative.
We start from the equation
\[y^2 = x^3 + 7\]
and derive both sides with respect to
\(x\)
:
\[2y \frac{dy}{dx} = 3x^2\]
We solve for the derivative:
\[\frac{dy}{dx} = \frac{3x^2}{2y}\]
That's our
\(m\)
.
A complete implementation will also handle the (trivial) edge cases with
\(P=0\)
or
\(Q=0\)
, of course.
Why reflect the intersection point
Having to reflect the intersection point might seem a little arbitrary at first, but if we think about it, not reflecting it is problematic. Indeed, since the three points lie on the same line, without reflection all the following equations would hold:
\(P + Q = R\)
\(P + R = Q\)
\(Q + R = P\)
By summing the first two equations, we'd get
\(2P+Q+R=R+Q\)
, i.e.
\(P=0\)
. Analogously, by summing the last two equations, we'd get
\(R=0\)
. Since
\(P=Q=R=0\)
, that rule would only work for
\(0\)
.
The correct rule will look less asymmetric if we think of it as
\(P+Q+R=0\)
, which gives
\(P+Q=-R\)
\(P+R=-Q\)
\(Q+R=-P\)
But what about this point at infinity? Where does it come from? All I know is that it has to do with the so-called
projective space
.
I got somewhat acquainted with that space when I was doing 3D graphics. In 3D, we may add a 4th coordinate, so that
\((x, y, z)\)
is represented by
\((wx, wy, wz, w)\)
and some computations become more regular (i.e. with fewer exceptions). At the end, we divide by
\(w\)
to go back to the usual coordinates.
There's also the 2D case when we project a 3D scene onto a 2D screen:
\(\pi(x, y, z) = (x/z, y/z)\)
, where I used
\(\pi\)
for
projection
. This has to do with how we perceive the world, so that the farther an object is from us, the smaller it looks (assuming, from our POV, that
\(z\)
is the distance of the object from us).
Projective space
Let's say we have some 3D space. We make it projective by imposing that, in general,
\((x, y, z) \sim (\lambda x, \lambda y, \lambda z)\)
for all
\(\lambda\neq 0\)
, and
\((x, y, z)\neq(0, 0, 0)\)
, where
\(\sim\)
means
equivalent
. In words,
all non-zero scalings of a non-zero point are equivalent
. Those are all the points, origin excluded, on the same line through the origin.
The classes partition the
punctured
(i.e. without the origin) 3D space. The origin, if included, would be in its own singleton class
\(\{0\}\)
anyway. If we instead allowed
\(\lambda = 0\)
, then two points
\(x\)
and
\(y\)
on different lines through the origin would violate the equivalence relation:
\(x\sim 0\)
and
\(y\sim 0\)
, but
\(x\nsim y\)
.
Indeed, an equivalence relation must follow three rules:
reflexivity:
\(x\sim x\)
symmetry:
\(x\sim y\iff y\sim x\)
transitivity:
\(x\sim y \wedge y\sim z\implies x\sim z\)
where "
\(\wedge\)
" means "and".
To remember the rules, we can just think of
equality
, which is also an equivalence relation:
\(a=a\)
\(a=b\iff b=a\)
\(a=b\wedge b=c\implies a=c\)
So, if
\(x\sim 0\)
and
\(0\sim y\)
, then we must have
\(x\sim y\)
. If
\(0\)
is equivalent to elements that belong to different classes, then it breaks transitivity.
Since the origin doesn't belong to the projective space, any generic point
\((x, y, z)\)
in it is to be considered non-zero.
Back to our elliptic equation. On the 2D plane, the equation is
\(y^2 = x^3 + 7\)
, but that won't work in the projective space. Since
\((x, y, z)\sim (\lambda x, \lambda y, \lambda z)\)
, with
\(\lambda\neq 0\)
, we'd like for
\((\lambda x, \lambda y, \lambda z)\)
to be on the curve whenever
\((x, y, z)\)
is.
Let's write the equation in the projective space as
\[Y^2 = X^3 + 7\]
and do the substitution
\((X, Y, Z) = (\lambda x, \lambda y, \lambda z)\)
:
\[\lambda^2 y^2 = \lambda^3 x^3 + 7\]
We want that to hold whenever
\((x, y, z)\)
is a solution, i.e. whenever
\(y^2 = x^3 + 7\)
. For that to happen, the equation must factorize as
\[f(\lambda)(y^2 - x^3 - 7) = 0\]
so that when the second factor is
\(0\)
, the equation holds regardless of the factor with
\(\lambda\)
.
We still have a
\(Z\)
to add, so why not use it to balance the degree of the terms? That is:
We did it, but what about that annoying extra
\(z\)
? If we want to recover the original equation, we need to set
\(z=1\)
, i.e. we need to restrict ourselves to
\((x, y, 1)\)
.
That's perfectly fine, though: the original curve lies on the
\(z=1\)
plane, while on each
\(z=\lambda\)
plane, with
\(\lambda\neq 0\)
, lies a
\(\lambda\)
-scaled version of the original curve:
With this setup, we can say that either all the elements of an equivalence class (a punctured line through the origin) are on the curve or none of them are.
There's actually an easier way to get the equation in
\(x\)
,
\(y\)
, and
\(z\)
coordinates. The original 2D curve is embedded in the 3D space by adding a
\(z = 1\)
coordinate, i.e.
\[(x, y)\mapsto (x, y, 1) \sim (\lambda x, \lambda y, \lambda) = (X, Y, Z)\]
Starting from a generic point
\((X, Y, Z)\)
with
\(Z\neq 0\)
, we can go back to the 2D case by just dividing by
\(Z\)
and dropping the third coordinate, i.e.
\[(x, y) = (X/Z, Y/Z)\]
Now, let's substitute
\(x = X/Z\)
and
\(y = Y/Z\)
into the starting equation and get rid of the denominators:
This is actually nicer and used to greatly speed up computations. It's called
Jacobian projection
.
We still haven't solved the mystery of the point at infinity. That was the main reason why we decided to explore projective spaces.
Point at infinity
We know that a vertical line intersects the planar elliptic curve either at no points at all or at the points
\(P\)
,
\(-P\)
, and
\(0\)
for some point
\(P\)
, where
\(0\)
is the so-called
point at infinity
. Let's try to make sense of it.
On the plane, a vertical line has equation
\(x = k\)
, but in our projective space that's the equation of a plane. Like with the elliptic curve, we want to upgrade the equation so that if
\((x, y, z)\)
is on the line, then so is
\((\lambda x, \lambda y, \lambda z)\)
. We use the same substitution as before:
\[
\begin{align*}
x &= k \\
\frac{X}{Z} &= k \\
X &= kZ
\end{align*}
\]
So the equation of the vertical plane becomes
\(X = kZ\)
, which represents a family of
\(Z\)
-scaled vertical lines. The equation
\(X = kZ\)
makes sense since, as
\(Z\)
gets closer to
\(0\)
, the line must also get closer to the origin because everything, curve included, gets scaled down.
Let's find the intersections between the curve and the line by solving
\[
\begin{cases}
Y^2 Z = X^3 + 7Z^3\\
X = kZ
\end{cases}
\]
We substitute the second equation into the first:
\[Y^2 Z = k^3 Z^3 + 7Z^3\]
That can be rewritten as
\[Z (Z^2 (k^3 + 7) - Y^2) = 0\]
For
\(Z\neq 0\)
, we can divide by
\(Z\)
, so we're left with
\[Z^2 (k^3 + 7) - Y^2 = 0\]
which gives two solutions for each
\(Z\neq 0\)
because of that
\(Y^2\)
. Those two solutions correspond to two points
\(P\)
and
\(-P\)
.
For
\(Z = 0\)
, we get
\(X = 0\)
from the second equation, i.e. the solutions are
\((0, \lambda, 0)\)
for
\(\lambda\neq 0\)
, which is the Y-axis without the origin. We can take
\((0, 1, 0)\)
as the
representative
of that class, which is exactly the point at infinity. As we can see, it doesn't live in any plane with the curve, so it doesn't exist in our original 2D space, but we already knew that.
This reminded me that we never designated representatives for the equivalence classes. Let's see:
Each class
\(C\)
is a punctured line through the origin.
If
\(C\)
intersects the plane
\(Z = 1\)
at some point
\(P = (X, Y, 1)\)
:
\(\mathrm{repr}(C) = P\)
Else
:
\(C\)
must lie on the plane
\(Z = 0.\)
If
\(C\)
intersects the line
\(\{Y=1; Z=0\}\)
at some point
\(P = (X, 1, 0)\)
:
\(\mathrm{repr}(C) = P\)
Else
:
\(C\)
lies on the X-axis, i.e.
\(\{Y = Z = 0\}\)
.
\(C\)
has the form
\((X, 0, 0)\)
.
\(\mathrm{repr}(C) = (1, 0, 0)\)
So, we have three groups of representatives:
\((X, Y, 1)\)
: on the curve if and only if
\(Y^2 = X^3 + 7\)
.
\((X, 1, 0)\)
: on the curve if and only if
\(X = 0\)
, which gives the point at infinity
\((0, 1, 0)\)
.
\((1, 0, 0)\)
: not on the curve.
Addition in projective space
Tip
You can safely skip this section!
Computing
\(m\)
, the slope of the line through the points
\((x_1, y_1)\)
and
\((x_2, y_2)\)
, requires a division, which, mod
\(p\)
, is a relatively expensive operation (compared to simple additions and multiplications).
Let's recall how to compute the point
\((x_3, y_3) = (x_1, y_1) + (x_2, y_2)\)
, assuming
\(x_1\neq x_2\)
:
To go from the plane to the 3D projective space, we proceed as we did before with the elliptic curve and the line equations, i.e. we make the substitution
\((x,y) = (X/Z, Y/Z)\)
.
\[
\begin{align*}
E &= B^2 Z_1 Z_2 - D^2 (D + 2C) \\
F &= D^3 Z_2
\end{align*}
\]
which results in
\[
\begin{align*}
X_3 &= DE \\
Y_3 &= B(D^2 C - E) - Y_1 F \\
Z_3 &= Z_1 F
\end{align*}
\]
Note that further micro-optimizations are possible. For instance, we shouldn't compute
\(D^2\)
and
\(D^3\)
separately.
I hope my calculations are correct, since this is my first time doing them. Either way, I'm satisfied with the result from a pedagogical point of view. I hope you are as well. As we can see, the common denominator was put in
\(Z_3\)
to avoid intermediate divisions. Now we can add many points together without any division and only do one single division when we want to go back to our 2D plane:
That's one slow (at least in
\(\mathbb{Z}_p\)
) division and 2 fast multiplications.
Note that the
\(P=Q\)
case is handled similarly. Moreover, the same approach will also work for the Jacobian projection, i.e. for
\((x, y) = (X/Z^2, Y/Z^3)\)
.
This is almost a philosophical observation. When we substitute
\(x\)
with
\(X/Z\)
, we're
not
promoting
\(x\)
to a fraction, but we're
reexpressing
it as a fraction, since they're assumed to be equal.
For example, if
\(x\)
is a simple integer in
\(\mathbb{Z}\)
(without mod) and we replace it with
\(X/Z\)
where
\(X\)
and
\(Z\)
are also in
\(\mathbb{Z}\)
, then we're ranking up from integers to rational numbers, which is a promotion. Indeed, unless
\(X\)
is divisible by
\(Z\)
or we're willing to lose some information, we won't be able to go back to
\(x\)
when the time comes.
In the ECDSA case, though,
\(x\)
is in
\(\mathbb{Z}_p\)
, with
\(p\)
prime, so
\(X/Z\)
is also in
\(\mathbb{Z}_p\)
: we're not promoting
\(x\)
to something more, but just reexpressing it.
Let's say we have a huge matrix that can be factorized into two small matrices because it's low-rank (i.e. many of its rows or columns are linear combinations of a selected few). Instead of carrying around the huge matrix during the computations, we may want to keep it in factorized form,
\((L, R)\)
, and then update the factorized form itself:
\[(L', R') = (f(L, R), g(L, R))\]
One way to find
\(f\)
and
\(g\)
is to do the substitution
\(M = LR\)
into whatever expression we want to evaluate involving
\(M\)
, and put the result back into factorized form. Note that if we end up with
\(L=I\)
or
\(R=I\)
(where
\(I\)
is the identity matrix), then the factorization is useless for our purposes.
Back to the group
ECDSA uses the addition operation we defined above to generate a group from a predetermined generator
\(G\)
. All the considered points are on the curve mod
\(p\)
, meaning that their coordinates are in
\(\mathbb{Z}_p\)
. Here's the group:
\[G = \{0, G, 2G, 3G, \ldots, (N-1)G\}\]
Notice that
\(G\)
has only a finite number of elements, which is to be expected since the points lie on a
\(p\times p\)
grid, which contains
\(p^2\)
distinct points at most.
We start from
\(0\)
and keep adding
\(G\)
until we loop, i.e. we get a point that we've already seen. Let's assume this is our current list:
\[0, G, 2G, 3G, \ldots, kG, \ldots, hG\]
We assume we've just looped, so the first
\(h\)
elements are all distinct, and
\(hG = kG\)
, with
\(k < h\)
.
We must have
\(0 = hG-kG = (h-k)G\)
. The only elements in the list that can be 0 are the first one and the last one. Since
\(h-k>0\)
,
\((h-k)G\)
must be the last element, so
\(h-k=h\)
, which gives
\(k=0\)
. This means that when we loop we restart from
\(0\)
.
So we end up with the following group:
\[G = \{0, G, 2G, 3G, \ldots, (N-1)G\}\]
\(G\)
has order
\(N\)
, i.e. it has
\(N\)
elements. This looping should remind you of
\(\mathbb{Z}_N\)
:
\[\mathbb{Z}_N = \{0, 1, 2, 3, \ldots, N-1\}\]
Indeed,
\(\mathbb{Z}_N\)
is also a (commutative) group:
If we couldn't inspect the single elements, and we just used the group laws, we'd actually be unable to tell
\(G\)
and
\(\mathbb{Z}_N\)
apart.
For instance, let's say we're given, as programmers, an opaque type
Element
together with an identity
zero
and an operation
add
. Would we be able to tell whether we're working with
\(G\)
or
\(\mathbb{Z}_N\)
without looking inside
Element
or at the implementation? No, we wouldn't. By defining a common API, we
abstracted away
the differences.
So, we've got ourselves an
isomorphism
between groups:
\[aG + bG = (a+b)G\]
More formally, let's define
\(f: a\mapsto aG\)
, which is a
bijection
, i.e. a 1-1 mapping between all the elements of
\(\mathbb{Z}_N\)
and all the elements of
\(G\)
. This means that we can invert
\(f\)
and use
\(f^{-1}\)
to go the other direction (however computationally expensive it is to do).
Then the equation above can be rewritten as
\[f(a) +_G f(b) = f(a+_{\mathbb{Z}_N}b)\]
That means that we can either
transform the addends into
\(G\)
's elements and then add them up using
\(G\)
's addition operation, or
sum the addends using
\(\mathbb{Z}_N\)
's addition operation and then transform the result into the corresponding element in
\(G\)
.
The final result will be the same.
Another way of saying this is that we can move back and forth between
\(G\)
and
\(\mathbb{Z}_N\)
without losing information
.
For example, for
\(N = 7\)
:
\(3G + 5G = 8G = 7G + G = 0 + G = G\)
because
\(7G = 0\)
\((3 + 5)G = (8)G = (1)G = G\)
because
\(8\ \mathrm{mod}\ 7 = 1\)
In the first case the looping comes from
\(G\)
, while in the second case it comes from
\(\mathbb{Z}_7\)
. In the second case we only worked inside the parentheses, i.e. with the numbers in
\(\mathbb{Z}_7\)
: we didn't touch
\(G\)
at all.
The idea is to work with numbers in
\(\mathbb{Z}_N\)
, which are easier to work with, and then transform them into points by multiplying them by
\(G\)
. While it's very easy to go from
\(k\)
to
\(kG\)
, it's computationally infeasible to go back from
\(kG\)
to
\(k\)
.
It shouldn't surprise that ECDSA represents
private keys
as numbers in
\(\mathbb{Z}_N\)
, and then multiplies them by
\(G\)
to get the associated
public keys
. This makes recovering the private key from a public key computationally infeasible.
Signing a message
I'll cheat a little and read my notes for the signing part of the algorithm:
* Message to sign:
z = 256-bit message digest
* Signature generation (in F_n, i.e. mod n):
k = rand_unif({1, ..., n-1}) # ephemeral nonce
R = k * G = (R_x, R_y)
r = R_x mod n # 1st component
if r = 0, restart and choose a new k
s = (k^{-1} * (z + r * d)) mod n # 2nd component [d = account private key]
if s = 0, restart and choose a new k
v = R_y mod 2 # AKA recid, recovery_id, or is_y_odd
signature = (r, s, v)
Let's see:
\(G\)
is both:
the group of points on the elliptic curve (with coordinates in
\(\mathbb{Z}_p\)
, with
\(p\)
prime)
the generator of that group
\(n\)
, a prime number, is the order of
\(G\)
\(z\)
is the message hash
\(d\)
is the private key of the account
\(k^{-1}\)
is the multiplicative inverse mod
\(n\)
, i.e.
\(k\cdot k^{-1} = 1\ (\mathrm{mod}\ n)\)
.
Note that I wrote
\(F_n\)
or, better,
\(\mathbb{F}_n\)
instead of
\(\mathbb{Z}_n\)
because, when
\(n\)
is prime, the latter is actually a field. The coordinates we've been working with for all this time are in
\(\mathbb{Z}_p\)
, which is also a field, since
\(p\)
is prime. That's why I wrote "
some
3D space" before: depending on which field we choose, we'll end up with a different 3D space.
We basically already observed that
\(\mathbb{Z}_n\)
, with
\(n\)
prime, is a
field
, but we never spelled it out.
That's actually why our math works both in the continuous case and mod
\(p\)
. The theory only requires that the coordinates are elements of a field. It doesn't matter which one.
A field
\(\mathbb{F}\)
is a set equipped with two binary operations, addition and multiplication, such that:
\(\mathbb{F}\)
is a commutative group under addition
\(\mathbb{F}\setminus\{0\}\)
is a commutative group under multiplication
A distributive law holds:
(a+b)c = ac + bc
Note that
\(\mathbb{F}\setminus\{0\}\)
means "
\(\mathbb{F}\)
minus
\(\{0\}\)
", i.e.
\(\mathbb{F}\)
without the element
\(0\)
.
\(\mathbb{Z}_n\setminus\{0\}\)
, with
\(n\)
prime, is a group under multiplication because:
Multiplication is associative:
\(a(bc) = (ab)c = abc\)
.
There's an identity:
\(1\)
Every (non-zero) element
\(x\)
has inverse, i.e. the famous
multiplicative inverse
mod
\(n\)
.
The
\(0\)
element has no multiplicative inverse since there's no
\(x\)
such that
\(0\cdot x = 1\)
. That would be against the very definition of
\(0\)
as the identity for the addition operation.
When
\(n\)
is not prime, we lose the group under multiplication because the inverse doesn't exist for all elements. For instance, let's consider mod
\(6\)
:
\(3\cdot 0 = 0\)
\(3\cdot 1 = 3\)
\(3\cdot 2 = 0\)
\(3\cdot 3 = 3\)
\(3\cdot 4 = 0\)
\(3\cdot 5 = 3\)
Since
\(3\)
and
\(6\)
are not coprime,
\(3\)
has no inverse.
When
\(n\)
is not a prime number,
\(\mathbb{Z}_n\)
is just a (commutative)
ring
, which has a weaker structure.
Well-known examples of fields are:
\(\mathbb{Q}\)
: the rational numbers
\(\mathbb{R}\)
: the real numbers
\(\mathbb{C}\)
: the complex numbers
The integers are clearly not a field since, for instance,
\(3x = 1\)
has no integer solution, so
\(3\)
has no multiplicative inverse. So, by adding a mod
\(p\)
, with
\(p\)
prime, we gain more structure, and we get ourselves a field!
Back to the algorithm. The first two lines are pretty easy:
We generate a random (uniformly distributed) temporary
nonce
\(k\)
(a number to be used only once
ever
)
We convert
\(k\)
into the associated point
\(R\)
on the curve
The point has coordinates
\((R_x, R_y)\)
(mod
\(p\)
)
It's computationally infeasible to go back from
\(R\)
to
\(k\)
.
Note that
\(n < p\)
, otherwise
\(r\)
would just be
\(R_x\)
.
Since
\(d\)
is the private key, then
\(dG\)
is the public key. We'll call it
\(Q\)
.
Verifying the signature
Given
\(r\)
,
\(s\)
,
\(Q\)
, and the message, we can verify the signature by:
hashing the message to get
\(z\)
recovering
\(R\)
checking that
\(R_x\ \mathrm{mod \ n} = r\)
We know that
\(R = kG\)
and that
\(s\)
contains
\(k\)
, but in inverse form. If we invert
\(s\)
and multiply it by
\(G\)
, we get
\[s^{-1}G = (z+rd)^{-1}kG = (z+rd)^{-1}R\]
Mhm... if we had
\(d\)
, we could compute
\((z+rd)\)
and use it to cancel
\((z+rd)^{-1}\)
and get
\(R\)
.
While we don't have
\(d\)
, we do have
\(Q = dG\)
, which means that although we can't compute
\((z + rd)\)
, we can compute
\((z + rd)G\)
:
In words, we factor out
\(G\)
from
\(zG + rQ\)
and form
\(s^{-1}G\)
, which is just
\((z+rd)^{-1}R\)
, as we saw at the beginning.
We did it! Now we check that
\(r = R_x\ \mathrm{mod}\ n\)
.
Here's the algorithm:
\(w = s^{-1}\ \mathrm{mod}\ n\)
\(u_1 = (z\cdot w)\ \mathrm{mod}\ n\)
\(u_2 = (r\cdot w)\ \mathrm{mod}\ n\)
\(R = u_1\cdot G + u_2\cdot Q\)
check that
\(r = R_x\ \mathrm{mod}\ n\)
Observe that
\(((zw)G + (rw)Q)\)
is more efficient than
\(s^{-1}(zG + rQ)\)
because, in the former,
\(w\)
multiplies two numbers, while, in the latter,
\(s^{-1}\)
multiplies a point.
Recovering
\(Q\)
Now the only problem is that in Ethereum the public key
\(Q\)
doesn't come with the signature. However, we can recover it from
\(r\)
,
\(s\)
, and
\(v\)
.
As we know,
\(Q = dG\)
and
\(s = k^{-1}(z+rd)\)
, so we should try multiplying
\(s\)
by
\(G\)
:
\[sG = k^{-1}(z+rd)G = k^{-1}(zG + rQ)\]
To solve for
\(Q\)
, we need to get rid of that
\(k^{-1}\)
. We can't just multiply
\(sG\)
by
\(k\)
because we don't know
\(k\)
... but wait:
\[(zG + rQ) = ksG = s(kG) = sR\]
Therefore:
\[Q = r^{-1}(sR - zG)\]
Unfortunately, we don't know
\(R\)
. But can we recover it? We know
\(r = R_x\)
, so we only need to recover
\(R_y\)
, since
\(R = (R_x, R_y)\)
. We're forgetting something, though:
\(r = R_x\ \mathrm{mod}\ n\)
. Recall that the coordinates of the points on the curve are in
\(\mathbb{Z}_p\)
, not
\(\mathbb{Z}_n\)
. We need to recover the original
\(R_x\)
from
\(r\)
.
We know that
\(R_x \in \{0, \ldots, p-1\}\)
, and, apparently,
\(n\)
is just a little smaller than
\(p\)
, which means that
\(R_x = r + jn\)
for some very small
\(j\)
. We start from
\(j = 0\)
and keep trying as long as
\(r + jn < p\)
. We say that
\(r + jn\)
is a
candidate
for
\(R_x\)
. Let's call the current candidate simply
\(x\)
.
Given
\(x\)
, we can recover
\(y\)
by using the equation
\(y^2 = x^3 + 7\ (\mathrm{mod}\ p)\)
itself and solve for
\(y\)
. There are fast algorithms to do that. If there's no solution, we try the next candidate. Otherwise, we get two possible solutions:
\(y\)
and
\(-y\)
. If you recall,
\(v = R_y\ \mathrm{mod}\ 2\)
, which tells us the solution to pick:
if
\(y\ \mathrm{mod}\ 2 = v\)
, we choose
\(y\)
otherwise, we choose
\(-y\)
One might wonder why we preserve the least significant bit of
\(R_y\)
to select the correct
\(y\)
. That's because if
\(y\)
is in
\(\{0, \ldots, p-1\}\)
, then
\(-y\ \mathrm{mod}\ p = p-y\)
. It's clear that
\(y + (p-y) = p\)
, which is odd (being a big prime), which implies that only one of
\(y\)
and
\(-y\)
can be odd (or even) mod
\(p\)
.
Anyway, once we have
\(R = (x, y)\)
, we compute
\(Q = r^{-1}(sR - zG)\)
.
Now we must check that
\(Q\)
is valid, i.e. that
\(Q\)
is on the curve. If it's not, then we try the next candidate.
We should actually check that
\(Q\)
is in
\(G\)
, but, apparently,
\(G\)
contains all the solutions of
\(y^2 = x^3 + 7\ \mathrm{mod}\ p\)
, so if
\(Q\)
is on the curve then it's also in
\(G\)
.
Signature malleability attack
We left this for last, but after all we've been through, this is disappointingly straightforward.
If
\((r, s, v)\)
is a signature created by signing a message
\(M\)
with a private key
\(d\)
, then so is
\((r, n-s, 1-v)\)
.
That's it. The problem arises when someone blacklists
\((r, s, v)\)
(once it's been used) believing that this will prevent double spending. An attacker will use the signature
\((r, n-s, 1-v)\)
to send the same message for a second time, bypassing the blacklist.
Instead, programs should use nonces contained directly in the messages and blacklist the nonces or the messages themselves.
Let's see why both signatures are valid.
Let's recall the signing algorithm:
* Message to sign:
z = 256-bit message digest
* Signature generation (in F_n, i.e. mod n):
k = rand_unif({1, ..., n-1}) # ephemeral nonce
R = k * G = (R_x, R_y)
r = R_x mod n # 1st component
if r = 0, restart and choose a new k
s = (k^{-1} * (z + r * d)) mod n # 2nd component [d = account private key]
if s = 0, restart and choose a new k
v = R_y mod 2 # AKA recid, recovery_id, or is_y_odd
signature = (r, s, v)
Now let's see what happens if we use
\(-k\)
instead of
\(k\)
:
\[
\begin{align*}
k' &= -k \\
R' &= k'G = -kG = -R = (R_x, p-R_y) \\
r' &= R'_x\ \mathrm{mod}\ n = R_x\ \mathrm{mod}\ n = r \\
s' &= (k'^{-1} (z + r'd))\ \mathrm{mod}\ n \\
&= -(k^{-1}(z + rd))\ \mathrm{mod}\ n \\
&= -(k^{-1}(z + rd)\ \mathrm{mod}\ n)\ \mathrm{mod}\ n \\
&= -s\ \mathrm{mod}\ n \\
&= n-s \\
v' &= R'_y\ \mathrm{mod}\ 2 = (p-R_y)\ \mathrm{mod}\ 2 = 1-v \\
(r', s', v') &= (r, n-s, 1-v) \\
\end{align*}
\]
That is, given the signature computed with
\(k\)
, we can trivially get the one computed with
\(-k\)
.
Basically, by using
\(-k\)
instead of
\(k\)
, we reflect
\(R\)
across the X-axis and flip
\(v\)
to signal that we switched the
\(y\)
coordinate.
The end
I hope you enjoyed the ride and deepened your understanding of ECDSA as much as I did.
If you spot any errors, you're welcome to open an issue or leave a comment below, but keep in mind that the article's nature will remain as stated in the
disclaimer
.
I won't be revisiting this years later unless something truly significant comes up.
Until next time!
Japanese game devs face font dilemma as license increases from $380 to $20k
"This is a little-known issue, but it's become a huge problem"
Image credit:
GamesIndustry.biz
Japanese game makers are struggling to locate affordable commercial fonts after one of the country's leading font licensing services raised the cost of its annual plan from around $380 to $20,500 (USD).
As reported by
Gamemakers
and
GameSpark
and translated by
Automaton
, Fontworks LETS discontinued its game licence plan at the end of November.
The expensive replacement plan – offered through Fontwork's parent company, Monotype – doesn't even provide local pricing for Japanese developers, and comes with a 25,000 user-cap, which is likely not workable for Japan's bigger studios.
The problem is further compounded by the difficulties and complexities of securing fonts that can accurately transcribe Kanji and Katakana characters.
"This is a little-known issue, but it's become a huge problem in some circles,"
wrote
CEO of development studio Indie-Us Games.
UI/UX designer Yamanaka
stressed
that this would be particularly problematic for live service games; even if studios moved quickly and switched to fonts available through an alternate licensee, they will have to re-test, re-validate, and re-QA check content already live and in active use.
The crisis could even eventually force some Japanese studios to rebrand entirely if their corporate identity is tied to a commercial font they can no longer afford to license.
Related topics
How large DOM sizes affect interactivity, and what you can do about it (2023)
Large DOM sizes have more of an effect on interactivity than you might think. This guide explains why, and what you can do.
There's no way around it: when you build a web page, that page is going to have a
Document Object Model (DOM)
. The DOM represents the structure of your page's HTML, and gives JavaScript and CSS access to a page's structure and contents.
The problem, however, is that the
size
of the DOM affects a browser's ability to render a page quickly and efficiently. Generally speaking, the larger a DOM is, the more expensive it is to initially render that page and update its rendering later on in the page lifecycle.
This becomes problematic in pages with very large DOMs when interactions that modify or update the DOM trigger expensive layout work that affects the ability of the page to respond quickly. Expensive layout work can affect a page's
Interaction to Next Paint (INP)
; If you want a page to respond quickly to user interactions, it's important to ensure your DOM sizes are only as large as necessary.
When is a page's DOM
too
large?
According to Lighthouse
, a page's DOM size is excessive when it exceeds 1,400 nodes. Lighthouse will begin to throw warnings when a page's DOM exceeds 800 nodes. Take the following HTML for example:
In the above code, there are four DOM elements: the
<ul>
element, and its three
<li>
child elements. Your web page will almost certainly have many more nodes than this, so it's important to understand what you can do to keep DOM sizes in check—as well as other strategies to optimize the rendering work once you've gotten a page's DOM as small as it can be.
How do large DOMs affect page performance?
Large DOMs affect page performance in a few ways:
During the page's initial render. When CSS is applied to a page, a structure similar to the DOM known as the
CSS Object Model (CSSOM)
is created. As CSS selectors increase in specificity, the CSSOM becomes more complex, and more time is needed to run the necessary layout, styling, compositing, and paint work necessary to draw the web page to the screen. This added work increases interaction latency for interactions that occur early on during page load.
When interactions modify the DOM, either through element insertion or deletion, or by modifying DOM contents and styles, the work necessary to render that update can result in very costly layout, styling, compositing, and paint work. As is the case with the page's initial render, an increase in CSS selector specificity can add to rendering work when HTML elements are inserted into the DOM as the result of an interaction.
When JavaScript queries the DOM, references to DOM elements are stored in memory. For example, if you call
document.querySelectorAll
to select all
<div>
elements on a page, the memory cost could be considerable if the result returns a large number of DOM elements.
A long task as shown in the performance profiler in Chrome DevTools. The long task shown is caused by inserting DOM elements into a large DOM via JavaScript.
All of these can affect interactivity, but the second item in the list above is of particular importance. If an interaction results in a change to the DOM, it can kick off a lot of work that can contribute to a poor INP on a page.
How do I measure DOM size?
You can measure DOM size in a couple of ways. The first method uses Lighthouse. When you run an audit, statistics on the current page's DOM will be in the "Avoid an excessive DOM size" audit under the "Diagnostics" heading. In this section, you can see the total number of DOM elements, the DOM element containing the most child elements, as well as the deepest DOM element.
A simpler method involves using the JavaScript console in the developer tools in any major browser. To get the total number of HTML elements in the DOM, you can use the following code in the console after the page has loaded:
document.querySelectorAll('*').length;
If you want to see the DOM size update in realtime, you can also use the
performance monitor tool
. Using this tool, you can correlate layout and styling operations (and other performance aspects) along with the current DOM size.
The performance monitor in Chrome DevTools. In this view, the page's current number of DOM nodes is charted along with layout operations and style recalculations performed per second.
If the DOM's size is approaching Lighthouse DOM size's warning threshold—or fails altogether—the next step is to figure out how to reduce the DOM's size to improve your page's ability to respond to user interactions so that your website's INP can improve.
How can I measure the number of DOM elements affected by an interaction?
If you're profiling a slow interaction in the lab that you suspect might have something to do with the size of the page's DOM, you can figure out how many DOM elements were affected by selecting any piece of activity in the profiler labeled "Recalculate Style" and observe the contextual data in the bottom panel.
Observing the number of affected elements in the DOM as the result of style recalculation work. Note that the shaded portion of the interaction in the interactions track represents the portion of the interaction duration that was over 200 milliseconds, which is
the designated "good" threshold for INP
.
In the above screenshot, observe that the style recalculation of the work—when selected—shows the number of affected elements. While the above screenshot shows an extreme case of the effect of DOM size on rendering work on a page with many DOM elements, this diagnostic info is useful in any case to determine if the size of the DOM is a limiting factor in how long it takes for the next frame to paint in response to an interaction.
How can I reduce DOM size?
Beyond auditing your website's HTML for unnecessary markup, the principal way to reduce DOM size is to reduce DOM depth. One signal that your DOM might be unnecessarily deep is if you're seeing markup that looks something like this in the
Elements
tab of your browser's developer tools:
When you see patterns like this, you can probably simplify them by flattening your DOM structure. Doing so will reduce the number of DOM elements, and likely give you an opportunity to simplify page styles.
DOM depth may also be a symptom of the frameworks you use. In particular, component-based frameworks—such as those that rely on
JSX
—require you to nest multiple components in a parent container.
However, many frameworks allow you to avoid nesting components by using what are known as fragments. Component-based frameworks that offer fragments as a feature include (but are not limited to) the following:
By using fragments in your framework of choice, you can reduce DOM depth. If you're concerned about the impact flattening DOM structure has on styling, you might benefit from using more modern (and faster) layout modes such as
flexbox
or
grid
.
Other strategies to consider
Even if you take pains to flatten your DOM tree and remove unnecessary HTML elements to keep your DOM as small as possible, it can still be quite large and kick off a lot of rendering work as it changes in response to user interactions. If you find yourself in this position, there are some other strategies you can consider to limit rendering work.
Consider an additive approach
You might be in a position where large parts of your page aren't initially visible to the user when it first renders. This could be an opportunity to lazy load HTML by omitting those parts of the DOM on startup, but add them in when the user interacts with the parts of the page that require the initially hidden aspects of the page.
This approach is useful both during the initial load and perhaps even afterwards. For the initial page load, you're taking on less rendering work up front, meaning that your initial HTML payload will be lighter, and will render more quickly. This will give interactions during that crucial period more opportunities to run with less competition for the main thread's attention.
If you have many parts of the page that are initially hidden on load, it could also speed up other interactions that trigger re-rendering work. However, as other interactions add more to the DOM, rendering work will increase as the DOM grows throughout the page lifecycle.
Adding to the DOM over time can be tricky, and it has its own tradeoffs. If you're going this route, you're likely making network requests to get data to populate the HTML you intend to add to the page in response to a user interaction. While in-flight network requests are not counted towards INP, it can increase perceived latency. If possible, show a loading spinner or other indicator that data is being fetched so that users understand that something is happening.
Limit CSS selector complexity
When the browser parses selectors in your CSS, it has to traverse the DOM tree to understand how—and if—those selectors apply to the current layout. The more complex these selectors are, the more work the browser has to do in order to perform both the initial rendering of the page, as well as increased style recalculations and layout work if the page changes as the result of an interaction.
Use the
content-visibility
property
CSS offers the
content-visibility
property, which is effectively a way to lazily render off-screen DOM elements. As the elements approach the viewport, they're rendered on demand. The benefits of
content-visibility
don't just cut out a significant amount of rendering work on the initial page render, but also skip rendering work for offscreen elements when the page DOM is changed as the result of a user interaction.
Conclusion
Reducing your DOM size to only what is strictly necessary is a good way to optimize your website's INP. By doing so, you can reduce the amount of time it takes for the browser to perform layout and rendering work when the DOM is updated. Even if you can't meaningfully reduce DOM size, there are some techniques you can use to isolate rendering work to a DOM subtree, such as CSS containment and the
content-visibility
CSS property.
However you go about it, creating an environment where rendering work is minimized—as well as reducing the amount of rendering work your page does in response to interactions—the result will be that your website will feel more responsive to users when they interact with them. That means you'll have a lower INP for your website, and that translates to a better user experience.
Luarrow - True pipeline operators and elegant Haskell-style function composition for Lua
"luarrow" is a portmanteau of "Lua" + "arrow", where "arrow" refers to the function arrow (→) commonly used in mathematics and functional programming to denote functions (
A → B
).
If you prefer left-to-right (
→
) data flow (like the
|>
operator in OCaml/Julia/F#/Elixir/Elm), use
arrow
,
%
, and
^
:
localarrow=require('luarrow').arrow-- Pipeline style: data flows left to rightlocal_=42%arrow(function(x) returnx-2end)
^arrow(function(x) returnx*10end)
^arrow(function(x) returnx+1end)
^arrow(print) -- 401-- Evaluation: minus_two(42) = 40-- times_ten(40) = 400-- add_one(400) = 401
Tip
Alternative styles:
You can also use these styles if you prefer:
-- Store the result and print separatelylocalresult=42%arrow(function(x) returnx-2end)
^arrow(function(x) returnx*10end)
^arrow(function(x) returnx+1end)
print(result) -- 401-- Or wrap the entire pipeline in print()print(
42%arrow(function(x) returnx-2end)
^arrow(function(x) returnx*10end)
^arrow(function(x) returnx+1end)
) -- 401
fun
: Mathematical style --
fun(f) * fun(g) % x
(compose right-to-left, apply at end)
So how should we use it differently?
Actually, Haskell-Style is not in vogue in languages other than Haskell.
So, 📝
"basically", we recommend Pipeline-Style
📝, which is popular in many languages.
However, Haskell-Style is still really useful.
For example, Point-Free-Style.
See below for more information on Point-Free-Style:
This definition style for
process_username
is what Haskell programmers call '
Point-Free Style
'!
In Haskell, this is a very common technique to reduce the amount of code and improve readability.
Inspired by Haskell's elegant function composition and the power of operator overloading in Lua.
💭 Philosophy
"The best code is code that reads like poetry."
luarrow brings functional programming elegance to Lua, making your code more expressive, composable, and maintainable.
Whether you're building data pipelines, processing lists, or creating complex transformations, luarrow makes your intent crystal clear.
Like this project?
Give it a ⭐ to show your support!
Happy programming!
🎯
To be precise, a pipeline operator RFC has been submitted for PHP 8.5.
Reference
↩
In Lua, expressions cannot stand alone at the top level - they must be part of a statement. The
local _ =
assigns the result to an unused variable (indicated by
_
, a common convention), allowing the pipeline expression to be valid Lua syntax.
↩
Are you a new comer for the pipeline operator? Alright! The pipeline operator is a very simple idea. For easy understanding, you can find a lot of documentations if you google it. Or for the detail, my recommended documentation is
'PHP RFC: Pipe operator v3'
.
↩