10 interesting stories served every morning and every evening.

Running local models is good now

vickiboykis.com

I’ve been work­ing with lo­cal mod­els since they came out, and fi­nally, they’re sur­pris­ingly good now.

I have a 2022 M2 Mac with 64 GB RAM and 1TB stor­age and I’ve used

Mistral 7B

Gemma 3

OpenAI OSS-20B

Qwen 3 MOE, as well as a num­ber of other Qwen vari­ants like Qwen 2.5 Coder

across a lot of dif­fer­ent sys­tem se­tups like

raw llama.cpp with Open WebUI

llama-cpp-python

Ollama

lla­mafiles and

LM Studio

Where are lo­cal mod­els now?

Early on, mod­els were slow, hard to use, and just not that ac­cu­rate for most pro­gram­ming tasks. The idea that lo­cal mod­els were se­verely lag­ging be­hind was largely true un­til, for me, the re­lease of GPT-OSS. I have no con­crete sci­en­tific ev­i­dence of this - my own per­sonal vibe met­ric of is a model good enough” is, do I have to dou­ble-check it against an API model”, and GPT-OSS was the first one where I started do­ing that a lot less of­ten.

As a re­sult, I’ve mostly been us­ing lo­cal mod­els as fast, per­son­al­ized Google for de­vel­op­ment ques­tions that don’t re­quire re­cency.

But with the most re­cent re­leases from Google in the Gemma 4, fam­ily, I’ve fi­nally been able to do agen­tic cod­ing lo­cally and have loops work at about ~75% the ac­cu­racy/​speed of fron­tier mod­els, which is in­cred­i­ble.

I’ve so far been us­ing gemma-4 – 26b-a4b LM Studio im­ple­men­ta­tion as my de­fault lo­cal model. I’ve used the lo­cal setup so far to: Refactor a Python script that was a note­book into a repo of 5 – 6 mod­ules, lint that mod­ule to use cor­rect type hints for gener­ics (most fron­tier mod­els now do this au­to­mat­i­cally, but not al­ways).

I’ve also used it to proof­read some blog posts, write unit tests, and to boot­strap a repo that stands up a two-tower model for rec­om­men­da­tions just to see what the agent would do with a blank slate. Here’s what it gen­er­ated, which was pretty ba­sic but still be­yond the scope of any­thing I would have thought pos­si­ble last year:

Note that the en­vi­ron­ment is re­stricted be­cause I run all my agen­tic work­flows in a Docker con­tainer with lim­ited ac­cess to ex­e­cu­tion.

I’m also build­ing an app that sur­faces trend­ing top­ics from Arxiv pa­pers. Out of cu­rios­ity, I had Pi go through my past LM Studio ses­sion logs and fig­ure out what I was us­ing LM Studio for:

Unsurprisingly, since I’ve been work­ing on Rijksearch,

None of these are ground­break­ing tasks (again, a lot of per­son­al­ized Google/docs lookups), and work­ing on them does give my GPUs and RAM a work­out and the K-V cache grows to 64 GB RAM.

But, the larger story for me is that these kinds of tasks, even as sim­ple as they are, used to be im­pos­si­ble for lo­cal mod­els as re­cently as 6 months ago.

Gemma-4 – 12b-qat just came out but I’ve al­ready also re­ally been im­pressed with its per­for­mance rel­a­tive to its size. The model ar­chi­tec­ture it­self is re­ally in­ter­est­ing and pro­poses a bunch of in­ter­est­ing ques­tions like, if we are con­strained by per­for­mance and price, what ar­chi­tec­tural trade­offs do we need to make?” a ques­tion that so far has not re­ally been asked in the mad to­ken gold rush.

Running agen­tic mod­els lo­cally to­day

But don’t take my word for any of this, try it out for your­self! You’ll need a lo­cal model in­fer­ence en­gine, an agen­tic har­ness, and the lo­cal model ar­ti­fact if you want to try to run lo­cal agen­tic flows. You’ll need to set up the har­ness to point at your lo­cal in­fer­ence end­point, the down­loaded model ar­ti­fact served via the in­fer­ence en­gine.

For my lo­cal setup, I’m cur­rently us­ing Pi as the agent har­ness and LM Studio as the in­fer­ence server, al­though it would likely be faster if I just used llama.cpp di­rectly - a po­ten­tial di­rec­tion for a fu­ture ex­per­i­ment.

This post was very easy to fol­low to set up agen­tic cod­ing with Pi and LM Studio, al­though I did make a few tweaks to the post’s setup.

Model: The post rec­om­mends Gemma 26B A4B , but gemma-4 – 12b-qat is more re­cent and smaller and faster, with­out much sac­ri­fice in ac­cu­racy.

Security: I run every Pi ses­sion in a Docker con­tainer and give it per­mis­sions only to bash so that it can’t run Python code or do web brows­ing, al­though I do plan to al­low curl in a dif­fer­ent im­age for some re­search work I’m do­ing.

Agent Harness Config: Since I run every­thing in Docker, I edited Pi’s mod­els.json in or­der to get Pi to talk to the model.

lmstudio”: { baseUrl”: http://​host.docker.in­ter­nal:1234/​v1, api”: openai-completions”, apiKey”: not-needed”, models”: [ { id”: google/gemma-4 – 12b-qat”, input”: [ text”, image” ] } ] }

Here’s my Docker Compose con­fig:

ser­vices: pi: build: con­text: . dock­er­file: Dockerfile im­age: pi-agent:0.74.0 init: true stdin_open: true tty: true ex­tra_hosts: - host.docker.internal:host-gateway” en­vi­ron­ment: ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-not-needed} GEMINI_API_KEY: ${GEMINI_API_KEY:-} OPENAI_API_BASE: ${OPENAI_API_BASE:-http://​host.docker.in­ter­nal:1234/​v1} # note that you’ll need to spec­ify a base if you also use OpenAI to ac­cess OpenAI’s ac­tual com­ple­tions end­point WHATEVER_API_KEY: ${WHATEVER_API_KEY:-} vol­umes: - ${HOME}/.pi/agent/models.json:/config/models.json - ${WORKSPACE:-.}:/workspace - pi-con­fig:/​con­fig - pi-ses­sions:/​ses­sions work­ing_dir: /workspace

vol­umes: pi-con­fig: pi-ses­sions:

and here’s the bash script that runs pi .

#!/usr/bin/env bash

# Pi — Start the con­tainer­ized Pi agent.

# Directory con­tain­ing this script and the com­pose files. SCRIPT_DIR=“$(cd — “$(dirname ${BASH_SOURCE[0]}“)” && pwd)”

# Workspace to mount into the con­tainer. WORKSPACE_DIR=“${WORKSPACE:-$(pwd)}” case $WORKSPACE_DIR” in /*) ;; *) WORKSPACE_DIR=“$(cd — “$WORKSPACE_DIR” && pwd)” ;; esac ex­port WORKSPACE=“$WORKSPACE_DIR”

sand­box=“${PI_SAND­BOX:-0}” pi_args=()

while (($#)); do case $1” in

–sandbox) sand­box=1 ;; –no-sandbox) sand­box=0 ;; *) pi_args+=(“$1”) ;;

esac shift done

com­pose_­files=( -f $SCRIPT_DIR/docker-compose.yml” ) if [[ $sandbox” == 1” ]]; then # an even more se­cure sand­box com­pose_­files+=( -f $SCRIPT_DIR/docker-compose.sandbox.yml” ) fi

# Derive a con­tainer name from the work­space di­rec­to­ry’s base­name. # Sanitize to char­ac­ters Docker ac­cepts: [a-zA-Z0 – 9][a-zA-Z0 – 9_.-]* re­po_s­lug=“$(base­name — “$WORKSPACE_DIR” | tr -c a-zA-Z0 – 9_.-’ -’ | sed s/^-*//’)” [[ -z $repo_slug” ]] && re­po_s­lug=“work­space” con­tain­er_­name=“pi-${re­po_s­lug}-$$”

api_key_args=( -e OPENAI_API_KEY -e DEEPSEEK_API_KEY -e ANTHROPIC_API_KEY -e GEMINI_API_KEY )

cmd=( docker com­pose –project-directory $SCRIPT_DIR” ${compose_files[@]}” run –rm –name $container_name” ${api_key_args[@]}” pi )

if ((${#pi_args[@]})); then cmd+=(“${pi_args[@]}“) fi

exec ${cmd[@]}”

I build the Docker con­tainer and make changes to the files in its own repo. Then, I run Pi in the repo I’m work­ing in, which spins up Docker so that Pi can’t wipe files or di­rec­to­ries by act­ing on my phys­i­cal hard drive. This also en­ables Pi run­ning in the con­tainer to see my cus­tom model json con­fig by ship­ping it into the con­tainer. All of this has been work­ing fairly well for my ex­per­i­ments.

There are still is­sues with lo­cal mod­els: in­fer­ence can be slow, con­text win­dows are small and lim­ited to your own hard­ware, and the ecosys­tem, al­though it’s made a ton eas­ier by tool­ing like LM Studio and HuggingFace’s Use This Model but­ton. Early re­leases suf­fer from prompt tem­plate mis­matches. But, these are usu­ally patched ex­tremely quickly. Needless to say, I’m not sure this is ready for pro­duc­tion soft­ware de­vel­op­ment quite yet.

The ben­e­fits, though, are nu­mer­ous and the ecosys­tem crit­i­cal to in­vest in, par­tic­u­larly now. One of the very cool parts of lo­cal mod­els is you can in­tro­spect al­most every­thing, like watch­ing the to­ken in­fer­ence process live,

and watch­ing to­kens in/​out.

You can do things like change the lo­cal con­text win­dow and watch per­for­mance im­prove or de­grade, and re­ally dig into how your to­kens are processed on the GPU. You can change the sys­tem prompt, the quan­ti­za­tions. You can pit mod­els against each other. You can also change and in­tro­spect the har­ness side.

The pos­si­bil­i­ties are end­less, and the tools only keep get­ting bet­ter.

reuters.com

www.reuters.com

Please en­able JS and dis­able any ad blocker

Banned Book Library

www.richardosgood.com

Overview

A long while back I had an idea to hack a WiFi smart light bulb to do some­thing more use­ful to me. Actually, I had a few dif­fer­ent ideas of things to do with them. One of these ideas was to mod­ify the de­vice to have an open WiFi ac­cess point and a web server host­ing banned books. The idea was that if you lived some­where that banned books you thought were im­por­tant, you could the­o­ret­i­cally stick a dig­i­tal copy of the book on one of these light bulbs. Then you could go in­stall it some­where in your com­mu­nity. As long as the light bulb is switched on, then any­one in the vicin­ity can still ac­cess the banned ma­te­r­ial as­sum­ing they have an elec­tronic de­vice with WiFi. Since the de­vice is a light bulb, it would be dif­fi­cult to de­tect and likely to go un­no­ticed. A cy­ber­punk dig­i­tal dead drop. These de­vices are also fairly in­ex­pen­sive, so leav­ing them around town as is hope­fully not very cost pro­hib­i­tive.

I think the idea host­ing banned books specif­i­cally came to me af­ter hav­ing read Ben Brown’s short story Library. It’s been a while since I read it, but if I re­call there are char­ac­ters in the story who main­tain a library” which acts as a dig­i­tal archive of cre­ative works, own­ers man­u­als, 3d mod­els, etc. Things that oth­ers might find use­ful or in­ter­est­ing that you would­n’t want to lose should they be some­how wiped from the Internet. That’s only a part of the story and it was a fun read. You should go read it!

Anyway, a few months ago I de­cided to fi­nally get to work on this pro­ject. The re­sult is the Banned Book Library!

Hardware

I brought up this idea with some folks at my lo­cal DEFCON meetup group. One of them had some ex­pe­ri­ence with home au­toma­tion and rec­om­mended I look into Tasmota. Tasmota is an open-source firmware you can in­stall on var­i­ous smart de­vices to in­te­grate them into a home au­toma­tion sys­tem such as HomeAssistant. The main idea with this firmware is to pro­vide you with lo­cal con­trol over the de­vice. Many of these de­vices rely on cloud ser­vices that change over time or some­times com­pletely dis­ap­pear, leav­ing the de­vices un­us­able. Tasmota al­lows you to un­tether your­self from these cloud ser­vices and host every­thing in­ter­nally. Actually, this is an­other great par­al­lel to Ben Brown’s Library story. Also rel­e­vant is Cory Doctorow’s Unauthorized Bread.

I had­n’t heard of Tasmota but af­ter read­ing about it, it sounded like a good way to go. I had sort of ex­pected many of these smart light bulbs would rely on ESP32 chips, or sim­i­lar. Having no ex­pe­ri­ence with them made it feel a bit daunt­ing to get started. I thought maybe it might be eas­ier to mod­ify the Tasmota firmware to do what I wanted in­stead of writ­ing some­thing from scratch. I did not end up mod­i­fy­ing Tasmota in the end, but this rab­bit hole did lead me to find a web­site that sells WiFi light bulbs with Tasmota pre-in­stalled. The prod­uct page even spec­i­fied that the bulb uses an ESP32C3 4MB. It also listed which GPIO pins were used to con­trol the var­i­ous LEDs, which would come in handy later:

R:GPIO6 G:GPIO7 B:GPIO5 CW:GPIO3 WW:GPIO4

This seemed like a great start­ing point be­cause al­though Tasmota sup­ports many other de­vices, not all of them can be flashed over the air (OTA). Many of them re­quire break­ing them open, sol­der­ing on small wires, and flash­ing via a se­r­ial pro­gram­mer. Tasmota has a built-in mech­a­nism to up­date the firmware OTA, so it seemed likely I might be able to flash my own mod­i­fied Tasmota firmware, or oth­er­wise a cus­tom firmware with­out hav­ing to tear the light bulbs apart.

The one thing that struck me as a po­ten­tial prob­lem was the flash size. It was listed as 4MB. This is not very much space to host a li­brary of books… That 4MB would need to fit all of the firmware, the web­site, and any books. Not much space. I thought I might be able to over­come this by adding stor­age, such as a mi­croSD card reader. More on that later.

I pur­chased two of these bulbs to play with. I fig­ured I might end up break­ing or brick­ing one, so hav­ing a backup would be good.

Teardown

The bulbs showed up in the main a few days later and I opened up the box to check it out.

The first thing I wanted to do was open it up and see what I was work­ing with. I was mainly won­der­ing if the pins were ex­posed so I might be able to at­tach a mi­croSD card reader. To re­move the white plas­tic bulb on top, I ran a razer blade around the cir­cum­fer­ence of the bulb in be­tween the base and the bulb. I had to go around twice, the sec­ond time an­gling the knife down­ward to cut through the sealant in­side. Then I was able to just twist and pull the bulb right off. Minimal dam­age.

This re­vealed a round daugh­ter board with all of the LEDs on it. This PCB was at­tached to an­other one un­der­neath us­ing six pins. There was also a hole in the mid­dle where the mother board stuck through a bit. This ended up be­ing the an­tenna for the ESP32. The bulb hous­ing was lined with alu­minum and the daugh­ter board was also made of alu­minum. So they likely de­signed it this way to en­sure a de­cent wifi sig­nal.

The daugh­ter board was glued in with more sealant. I used my knife to cut through this and a small, flat screw­driver to care­fully pry the daugh­ter board out. I slid it up so it would sep­a­rate from the mother board.

Now I could very clearly see the ESP32C3 in­side, as well as some other sup­port­ing cir­cuitry. I’m no elec­tron­ics ex­pert, but I be­lieve most of the com­po­nents in­side are to con­vert the AC mains power to a cleaner 3.3V DC for the ESP32 as well as what­ever volt­age was needed to drive the LEDs. I never plugged this de­vice into mains while it was open so I did­n’t mea­sure the volt­age for the LEDs.

One nice thing about this ESP32 was that it seemed to have a bunch of pins ex­posed. I hoped this might make it pos­si­ble to sol­der on a mi­croSD card reader for ex­panded stor­age. You can also see some of the pins are la­beled at the bot­tom to let you know which pins are for which col­ors.

There was re­ally no way to get a sol­der­ing iron in­side the bulb. The ESP32 only had the an­tenna por­tion stick­ing out above the hous­ing. The only way I was go­ing to sol­der any wires to those pins was to re­move the mother board. Unfortunately this was not a sim­ple task. The mother board was held in place with some kind of rub­bery pot­ting com­pound. There was… a lot of it. I had to dig it out with a knife and screw­driver and then yank the board out.

I chipped away a bunch of the com­pound from the mother board to get a bet­ter look at it. It made a mess.

This was a huge pain and not some­thing I would want to be a re­quired step in the process of set­ting up one of these dead drops. I re­ally wanted this pro­ject to be as ac­ces­si­ble as pos­si­ble, re­quir­ing min­i­mal tools and hard­ware skills. Not only that, but there re­ally was no way I was go­ing to get this re-in­stalled prop­erly. And even if I man­aged to get it back in, I would­n’t trust it to be safe. It could be­come a fire haz­ard for all I know.

All that said, this did give me a bit of a de­vel­op­ment plat­form to work from. I thought since I had this thing apart any­way, I might as well sol­der on some wires for se­r­ial pro­gram­ming. I had not done this be­fore, so I had to do some read­ing to fig­ure it out. Basically, I needed to power the chip with 3.3v and ground. Plus I need one wire each for the se­r­ial UART TX and RX pins. The first ques­tion to an­swer was which pins were the right pins?

I man­aged to find this ex­act mod­ule on AliExpress. The list­ing in­cluded an im­age of the back­side of the mod­ule, which thank­fully had la­beled all of the pins.

This helped me fig­ure out the VCC, GND, TX, and RX pins. For GND I ended up just sol­der­ing to the metal shield­ing as it was also grounded and much eas­ier to sol­der to. I sol­dered wires to all of the other pins. I had to re­move a few ca­pac­i­tors in or­der to get in there.

In or­der to pro­gram the de­vice via se­r­ial, you have to boot it into a spe­cial down­load mode. This seems to nor­mally in­volve short­ing one of the ESP32 pins to ground while it is pow­ered on. I can’t re­mem­ber how I fig­ured this out, but it ended up bing the IO9 pin in this case. So I sol­dered an­other wire there.

I set my bench top power sup­ply for 3.3v and hooked it up to the chip. Applying power to the VCC and GND wires did boot it up and I could see the IoTorerro ac­cess point wait­ing for me to con­nect and con­fig­ure the de­vice.

To get it into down­load mode, I pow­ered the de­vice off. I con­nected my FTDI de­vice to my lap­top’s USB port and then to the GND, TX, and RX wires on the mother board. Then I man­u­ally shorted the IO9 wire to the shield­ing to ground it. Then I pow­ered the de­vice on. I could see this time it was only draw­ing about 0.09 amps, which was much less than be­fore. So some­thing was dif­fer­ent.

I thought the first thing I should prob­a­bly do is try to dump the en­tire firmware. This would hope­fully al­low me to flash it back to the de­vice to start it back over from a clean state. I used es­p­tool to do this.

es­p­tool –chip es­p32c3 –port /dev/ttyUSB0 –baud 114200 read-flash 0x0 0x4000000 ./tasmota_original_firmware.bin

I could see that it was able to talk to the de­vice and af­ter a few min­utes I had a firmware dump! Things were look­ing good so far.

Early Experiments

Hello World

Early on I went look­ing at the Tasmota source code to see if I could mod­ify it to act as the Banned Book Library. The firmware was much more com­pli­cated than I had an­tic­i­pated. Not only that but it sup­ported all dif­fer­ent ar­chi­tec­tures and de­vices. It also had many fea­tures I re­ally did­n’t need. And con­sid­er­ing the pur­pose of my pro­ject, I wanted to try and keep the firmware bloat down to make more space for book stor­age. So I scrapped the idea of mod­i­fy­ing Tasmota.

I then dis­cov­ered that you can pro­gram ESP32 de­vices with Arduino. I had used Arduino a bunch maybe 10 – 15 years ago, so I had some ex­pe­ri­ence with it. I re­called it be­ing pretty ac­ces­si­ble and mak­ing it eas­ier to work with em­bed­ded sys­tems. But I was def­i­nitely rusty and I had never used it to pro­gram an ESP32 be­fore.

I setup the Arduino IDE on my lap­top and con­fig­ured it to use my ttyUSB0 se­r­ial pro­gram­mer as well as the proper ESP32C3 chip. I then wrote a very ba­sic hello world pro­gram to just send a mes­sage over the se­r­ial port back to the lap­top. This would let me test and see if I could flash the firmware and get the de­vice to do some­thing new.

I used the Arduino IDEs built in up­load fea­ture to up­load the code to the de­vice. It took care of the com­pli­cated stuff and just did it. I checked the se­r­ial mon­i­tor and found that it was work­ing! I did get se­r­ial out­put from the de­vice. So I was able to write my own firmware to this thing.

Web Server

The next thing I wanted to do was setup an open WiFi ac­cess point and Web Server. I be­lieve I started with this tu­to­r­ial to get an idea of what to do, though I mod­i­fied it since I was­n’t in­ter­ested in con­trol­ling an LED at the time. I later switched to us­ing Async Web Server and used this tu­to­r­ial to get a han­dle on things.

MicroSD Card

After get­ting that work­ing I wanted to try and get a mi­croSD card work­ing. I pur­chased some break­out boards from Sparkfun.

I went read­ing the ESP32C3 datasheet to fig­ure out how to wire up the SD card reader. I man­aged to fig­ure it out even­tu­ally. However, in­stead of sol­der­ing to this de­vice, I de­cided to switch to us­ing an Adafruit ItsyBitsy ESP32 that I had lay­ing around un­used from a pre­vi­ous pro­ject. The ItsyBitsy was eas­ier to work with be­cause it breaks out all of the pins in such a way that I could sol­der on header pins. This made it re­ally easy to at­tach the mi­croSD card reader for pro­to­typ­ing.

Then I fol­lowed this other tu­to­r­ial to fig­ure out how to pro­gram the ESP32 to use the de­vice. I did end up get­ting this to work and even us­ing it to host files for the web server us­ing LittleFS, how­ever the en­tire idea of adding a mi­croSD did not work out so I won’t go into de­tail on this.

The real prob­lem with the mi­croSD card idea was that sol­der­ing wires onto this ESP32C3 in the ac­tual de­vice was a real pain. There was no way to do it with­out re­mov­ing the board from the hous­ing which ef­fec­tively de­stroys the de­vice as far as I’m con­cerned. I tried to get cre­ative.

First I looked at re­pur­pos­ing some of the LED con­troller pins. There were six pins go­ing from the mother board to the daugh­ter board. Five of those were for the var­i­ous LED col­ors: warm white, cool white, red, green, and blue. I did­n’t care about the RGB at all. And I could do away with ei­ther the warm or cool white if needed. However this did­n’t pan out. The way this de­vice is de­signed, the mother board sends power to the daugh­ter board via one pin. The other five pins route back to tran­sis­tors on the mother board. The ESP32 turns its GPIO pins high which then trig­gers the tran­sis­tors and com­pletes the cir­cuit for each color back to ground. This meant that the GPIO pins could only be used for out­put at best in this con­fig­u­ra­tion. No in­put.

I then had a crazy idea to make a clamp” that could clamp onto the top of the ESP32 and pos­si­bly al­low some header pins to make con­tact with the ex­posd ESP32 pins. I de­signed a small 3D-printable part that was in­tended to slip over the ESP32C3 and clamp into place.

This ended up be­ing way too finicky and un­re­li­able. After sev­eral it­er­a­tions of the de­sign, I aban­doned the idea al­to­gether.

Detour

At this point, I de­cided to try look­ing at some other bulbs. Maybe there were other de­vices out there that would lend them­selves bet­ter to sol­der­ing on some ex­tra com­po­nents? The prob­lem was I did­n’t want to break the bank buy­ing 20 dif­fer­ent LED bulbs just to see if the idea was even fea­si­ble. I started by look­ing into prior re­search. I found sev­eral tear­down ar­ti­cles fea­tur­ing var­i­ous smart light bulbs, but they all looked very sim­i­lar to my setup. It did re­veal that they don’t all use ESP32. At this point I had de­cided I only wanted to stick with the ESP32 since I had al­ready spent time learn­ing how to pro­gram it.

I bought a few bulbs from the lo­cal hard­ware store. One of them had a sim­i­lar de­sign, but ac­tu­ally had a bit of alu­minum pro­tect­ing the mother board that I could­n’t re­move safely.

The Philips WiZ looked promis­ing at first. It used an ESP32C3-mini-1 and the en­tire chip was ex­posed af­ter re­mov­ing only the plas­tic bulb!

Unfortunately none of the ESP32 pins were ac­ces­si­ble on this mod­ule. So there was no way to sol­der wires for any­thing I needed.

I also tore apart a few stan­dard LED bulbs with no smart” com­po­nents. I thought maybe I could just stick my own cir­cuit in­side. But this ended up look­ing more com­pli­cated and spe­cial­ized than just flash­ing the Tasmota bulb.

There was also an in­ter­est­ing DIY LED smart bulb pro­ject I found on Hackaday that in­trigued me, but I re­ally pre­ferred the idea of re­pur­pos­ing an off-the-shelf unit.

Ultimately I de­cided to stick with the Tasmota bulb and to just try to work within my 4MB lim­i­ta­tions.

The Storage Problem

To get a han­dle on the stor­age sit­u­a­tion, we can look at the ESP32 par­ti­tion table. The table is nor­mally stored at off­set 0x8000 in flash, so we can dump this sec­tion and then con­vert the bi­nary to a read­able CSV file.

$ es­p­tool -p /dev/ttyUSB0 –baud 115200 read­_flash 0x8000 0x1000 part_­dump.bin Warning: Deprecated: Command read_flash’ is dep­re­cated. Use read-flash’ in­stead. es­p­tool v5.1.0 Connected to ESP32-C3 on /dev/ttyUSB0: Chip type: ESP32-C3 (QFN32) (revision v0.4) Features: Wi-Fi, BT 5 (LE), Single Core, 160MHz, Embedded Flash 4MB (XMC) Crystal fre­quency: 40MHz MAC: 0c:4e:a0:31:cb:e4

Stub flasher is al­ready run­ning. No up­load is nec­es­sary.

Configuring flash size… Read 4096 bytes from 0x00008000 in 0.4 sec­onds (87.2 kbit/​s) to part_dump.bin’.

Hard re­set­ting via RTS pin…

I used gen_E­s­p32­part.py to gen­er­ate a csv file de­scrib­ing the par­ti­tions.

$ gen_e­s­p32­part.py part_­dump.bin Parsing bi­nary par­ti­tion in­put… Verifying table…

# ESP-IDF Partition Table # Name, Type, SubType, Offset, Size, Flags nvs,data,nvs,0x9000,20K, ota­data,data,ota,0x­e000,8K, safeboot,app,fac­tory,0x10000,832K, app0,app,ota_0,0x­e0000,2880K, spiffs,data,spiffs,0x3b0000,320K,

This re­vealed five par­ti­tions:

nvs

ota­data

safeboot

app0

spiffs

As I went through this pro­ject, I even­tu­ally learned that nvs is used for non-volatile stor­age. This is where the main firmware can store con­fig­u­ra­tion set­tings like the WiFi net­work, pass­word, LED color, etc. That way when it re­boots, it can re­mem­ber these set­tings.

I’m not sure what ota­data is used for ex­actly, other than it has some­thing to do with over the air up­dates.

The safeboot par­ti­tion is a sec­ond bootable firmware that Tasmota uses to flash the main firmware. It seems that the usual way of deal­ing with OTA up­dates is to have two du­pli­cate firmware par­ti­tions of the same size. You boot from par­ti­tion A, and then when you in­stall an up­date it gets writ­ten to par­ti­tion B. Then you re­boot into par­ti­tion B. If every­thing looks fine, the firmware can then be flashed to par­ti­tion A. This way if a firmware up­date fails on par­ti­tion B, the de­vice can re­cover by re­boot­ing into par­ti­tion A. The down­side with this method is that you need to firmware par­ti­tions of equal size. This takes up a lot of space.

Tasmota does things a bit dif­fer­ently in the safeboot con­fig­u­ra­tion. Instead of hav­ing two du­pli­cate firmware im­ages, there is the main firmware stored in the app0 par­ti­tion. It then has a sec­ond, smaller firmware stored in safeboot. The safeboot firmware can con­nect to a pre-con­fig­ured WiFi net­work and flash the app0 par­ti­tion and that’s about it as far as I can tell. You can’t even use the safeboot firmware to con­fig­ure WiFi. That must be done via the main firmware. Safeboot must read the set­tings from nvs. The ben­e­fit of do­ing things this way is that the main Tasmota firmware can be larger with more fea­tures with­out tak­ing up dou­ble the space for OTA up­dates. More info on this can be found here.

Finally there is the spiffs par­ti­tion. spiffs is a file sys­tem type but in this case can also rep­re­sent a more mod­ern LittleFS file sys­tem. It’s ba­si­cally a small par­ti­tion to store files.

With this con­fig­u­ra­tion, the main firmware had close to 3MB of space and the safeboot was close to 1MB. There was just 320K for stor­age. That might fit one ebook, de­pend­ing on the length. Not ideal.

It oc­curred to me that I likely did­n’t need 2880KB to store my own firmware since mine would be much sim­pler than Tasmota. I thought I might be able to ad­just the par­ti­tion size from in the firmware it­self to shrink the app0 par­ti­tion and grow the spiffs par­ti­tion. That would give more space for web files and books.

I even­tu­ally did fig­ure out there was a way to do this thanks to this blog post.

Editing the par­ti­tion table is risky be­cause if it gets cor­rupted the de­vice may not boot and would only be re­cov­er­able via se­r­ial pro­gram­ming. This is not ideal, but the whole pro­ject is a hack so I guess what the hell?

The par­ti­tion table is stored at off­set 0x8000 in flash mem­ory. So re­ally all we need to do is over­write the table with what­ever we want it to be. We can’t just change the par­ti­tion off­sets and sizes though, be­cause there is an MD5 check­sum value at the end of the table data. Therefore we would need to up­date this value as well or the de­vice will not boot. We also can’t move the app0 par­ti­tion while we are booted into that par­ti­tion or else we will not be able to boot back to this fir­w­mare as it will not be lined up to the start of the par­ti­tion.

I mod­i­fied the par­ti­tion.csv file to look how I wanted the par­ti­tions to look and saved it as par­ti­tions.csv.new:

# Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x5000, ota­data, data, ota, 0xe000, 0x2000, safeboot, app, ota_1, 0x10000, 0xD0000, app0, app, ota_0, 0xE0000, 0x120000, spiffs, data, spiffs, 0x200000,0x200000,

This would al­low for 2MB of data for web server files and books in the SPIFFS par­ti­tion, which felt like enough to be at least use­ful. Then I used gen_e­s­p32­part.py to gen­er­ate an ac­tual par­ti­tion table bi­nary blob from the csv file:

$ gen_e­s­p32­part.py par­ti­tions.csv.new par­ti­tion­s_new.bin

I used xxd to out­put the im­por­tant bits in c ar­ray for­mat:

rick@nixlap ~/Projects/BannedBookLibrary/idf/library/main$ xxd -i ../partitions_new.bin |head -n17 ✭main un­signed char ___partitions_new_bin[] = { 0xaa, 0x50, 0x01, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x6e, 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x6f, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x73, 0x61, 0x66, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x61, 0x70, 0x70, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x82, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x73, 0x70, 0x69, 0x66, 0x66, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xa8, 0x74, 0x2c, 0xcd, 0xc5, 0x28, 0xab, 0xd5, 0x0d, 0xf6, 0x41, 0xd3, 0xa7, 0xdd,

I then dropped it in a par­ti­tion.h file:

un­signed char par­ti­tion_table[] = { 0xaa, 0x50, 0x01, 0x02, 0x00, 0x90, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x6e, 0x76, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x6f, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x11, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x73, 0x61, 0x66, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x61, 0x70, 0x70, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x50, 0x01, 0x82, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x73, 0x70, 0x69, 0x66, 0x66, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xeb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xda, 0xa8, 0x74, 0x2c, 0xcd, 0xc5, 0x28, 0xab, 0xd5, 0x0d, 0xf6, 0x41, 0xd3, 0xa7, 0xdd }; un­signed int par­ti­tion_table_len = 192;

I then wrote a func­tion to over­write the par­ti­tion table data with this in­for­ma­tion. The func­tion first checks to see if the par­ti­tion table MD5 sum al­ready matches the new table. If so, it’s al­ready been flashed and does­n’t need to be flashed again. If not, then it up­dates the par­ti­tion table.

bool ed­it_­par­ti­tion_table() {

int re­sult = es­p_flash_init(es­p_flash_de­fault­_chip); Serial.printf(“esp_flash_init re­sult: 0x%x\n”, re­sult);

uin­t8_t cur­ren­t_md5[MD5­SUM_­SIZE]; mem­set(cur­ren­t_md5, 0x0, MD5SUM_SIZE); re­sult = es­p_flash_read(es­p_flash_de­fault­_chip, cur­ren­t_md5, CONFIG_PARTITION_TABLE_OFFSET + OFFSET_TO_PART_MD5SUM, MD5SUM_SIZE); Serial.printf(“esp_flash_read re­sult: 0x%x\n”, re­sult);

if (memcmp(partition_new_md5, cur­ren­t_md5, MD5SUM_SIZE) != 0) { Serial.printf(“Patching par­ti­tion table…\n”); re­sult = es­p_flash_erase_re­gion(es­p_flash_de­fault­_chip, CONFIG_PARTITION_TABLE_OFFSET, 0x1000); Serial.printf(“esp_flash_erase_region re­sult: 0x%x\n”, re­sult);

re­sult = es­p_flash_write(es­p_flash_de­fault­_chip, par­ti­tion_table, CONFIG_PARTITION_TABLE_OFFSET, par­ti­tion_table_len); Serial.printf(“esp_flash_write re­sult: 0x%x\n”, re­sult);

Serial.printf(“Erasing NVS par­ti­tion…\n”); re­sult = es­p_flash_erase_re­gion(es­p_flash_de­fault­_chip, 0x9000, 0x5000); Serial.printf(“esp_flash_erase_region re­sult: 0x%x\n”, re­sult);

Serial.printf(“Setting de­fault boot par­ti­tion\n”); const es­p_­par­ti­tion_t * part = es­p_­par­ti­tion_find­_­first(ES­P_­PAR­TI­TION_­TYPE­_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, app0″); es­p_o­ta_set_­boot_­par­ti­tion(part);

Serial.printf(“Restarting…\n”); ESP.restart(); } else { Serial.printf(“Partition table al­ready patched\n”); }

re­turn true; }

This did not work at first. Whenever I tried read­ing from or writ­ing to the par­ti­tion table, the API func­tions would re­turn a suc­cess code but would­n’t ac­tu­ally read or write any­thing. It took some re­search and test­ing but even­tu­ally I dis­cov­ered that the ESP32 frame­work does­n’t al­low you to ac­cess cer­tain sen­si­tive ar­eas of the flash mem­ory for safety rea­sons. This in­cludes the boot­loader and the par­ti­tion table. When us­ing Arduino IDE to pro­gram an ESP32 the frame­work is pre­con­fig­ured for you, which makes things eas­ier in many cases. However, one of the con­fig­u­ra­tions has this safety fea­ture en­abled. This meant that I was­n’t go­ing to be able to edit the par­ti­tion table us­ing Arduino.

ESP-IDF

After some more re­search I dis­cov­ered that the of­fi­cial ESP32 frame­work is called ESP-IDF. It’s more com­pli­cated to setup and use but of­fers greater con­trol over the de­vice and the frame­work it­self.

Feds freaked over Fable 5 after simple 'fix this code' prompt, not jailbreak, says researcher

www.theregister.com

REG AD

se­cu­rity

According to the one per­son who ac­tu­ally read the re­search pa­per

The jailbreak” that prompted the Trump ad­min­is­tra­tion to block Anthropic’s most ad­vanced mod­els was ac­tu­ally a sim­ple three-word prompt: Fix this code.”

That’s ac­cord­ing to Katie Moussouris, founder and CEO of Luta Security, and the fairy god­mother of bug boun­ties. She says she was the only out­side ex­pert to read the third-party re­search pa­per on the Fable 5 guardrail by­pass tech­niques that prompted the ban.

On Friday, the US gov­ern­ment, re­port­edly cit­ing na­tional se­cu­rity con­cerns, is­sued an ex­port con­trol di­rec­tive to sus­pend ac­cess to Fable 5 and Mythos 5 by any for­eign na­tional, in­side or out­side the United States. In re­sponse, Anthropic dis­abled both mod­els for all our cus­tomers to en­sure com­pli­ance.”

REG AD

Anthropic shared the re­port pri­vately with her, Moussouris wrote in a Monday blog post.

REG AD

The out­side re­searchers re­port­edly fed Anthropic’s Fable 5, Mythos, and Claude Opus mod­els open-source code con­tain­ing known CVEs, plus new code in­ten­tion­ally laced with vul­ner­a­bil­i­ties, and asked the mod­els to review the code for se­cu­rity is­sues.”

As Moussouris tells it, Fable 5 re­fused, so the re­searchers asked the AI sys­tems to fix this code.” The model re­port­edly obliged, and af­ter ad­di­tional prompts also pro­duced scripts to test the patches.

That’s it,” Moussouris wrote. ‘Fix this code,’ plus sev­eral man­ual steps to gen­er­ate test scripts, should never have trig­gered an ex­port con­trol. I feel like mak­ing 90s-style t-shirts with fix this code’ on the front and this shirt is a mu­ni­tion’ on the back.”

Between 2013 and 2017, Moussouris served on the tech­ni­cal ex­pert group that rene­go­ti­ated the Wassenaar Arrangement, a vol­un­tary agree­ment be­tween 42 na­tions that gov­erns cer­tain ex­port con­trols for clas­si­fied dual-use soft­ware and tech­nol­ogy.

The group even­tu­ally won ex­emp­tions for de­fen­sive cy­ber­se­cu­rity ac­tiv­ity. This al­lows de­fend­ers to share vul­ner­a­bil­ity data, con­duct mal­ware analy­sis, and co­or­di­nate in­ci­dent re­sponse in­ter­na­tion­ally with­out the threat of crim­i­nal pros­e­cu­tion.

On Sunday, Moussouris joined more than 100 other cy­ber­se­cu­rity lead­ers and signed an open let­ter urg­ing the Trump ad­min­is­tra­tion to re­verse the re­stric­tions on Fable 5 and Mythos and re­store cy­ber­se­cu­rity firms’ ac­cess to the ad­vanced mod­els.

To pull the best ca­pa­bil­i­ties away from de­fend­ers with­out a good rea­son when our ad­ver­saries are rapidly ad­vanc­ing is dan­ger­ous,” they wrote.

In her blog, Moussouris ar­gues that there was no guardrail by­pass or jail­break. Defenders should be able to ask AI sys­tems to find and fix bugs, and write tests to val­i­date the patch, she said. Anthropic’s mod­els were do­ing the most valu­able thing an AI model can do for de­fen­sive se­cu­rity: ex­e­cut­ing the find, fix, and test loop de­fend­ers run every day.”

REG AD

Removing the ca­pa­bil­ity for mod­els to re­spond to de­fen­sive re­quests makes AI sys­tems worse at find­ing bugs and ver­i­fy­ing patches,” she con­tin­ued.

Plus, the US can’t ex­tend ex­port con­trols to open-weight sys­tems or sim­i­lar ad­vanced mod­els from China and other coun­tries - and these sys­tems will soon achieve Mythos-like ca­pa­bil­i­ties, any­way. Anthropic and Google have both ac­cused China-based ri­vals in­clud­ing DeepSeek of us­ing distillation at­tacks” to train their mod­els by si­phon­ing knowl­edge from American com­pa­nies’ AI.

Banning Anthropic’s ad­vanced mod­els is go­ing to hurt de­fend­ers more than at­tack­ers, Moussouris warns. Defense im­proves when de­fend­ers find the same bugs at­tack­ers find and fix them faster,” she wrote. We need the best tools to de­fend against in­creas­ingly ca­pa­ble at­tack­ers in the AI era of cy­ber­se­cu­rity.”

The Register reached out to the Trump ad­min­is­tra­tion for com­ment on Moussouris’ as­ser­tion, and we’ll up­date this post if we hear back. ®

The time the x86 emulator team found code so bad that they fixed it during emulation

devblogs.microsoft.com

During an ex­change of war sto­ries, a col­league of mine told one from back in the days when Windows in­cluded a proces­sor em­u­la­tor for x86 – 32 on sys­tems that na­tively ran some other proces­sor. (This has hap­pened many times. And no, I don’t know which proces­sor this par­tic­u­lar story ap­plied to.)

This par­tic­u­lar em­u­la­tor em­ployed bi­nary trans­la­tion, gen­er­at­ing na­tive code to per­form the equiv­a­lent op­er­a­tions of the orig­i­nal x86 – 32 code. This of­fered a sig­nif­i­cant per­for­mance im­prove­ment over em­u­la­tion via in­ter­preter. You can imag­ine that x86 – 32 is just a byte­code, and the em­u­la­tor is a JIT com­piler.

Anyway, my col­league found that there was one pro­gram that needed to al­lo­cate around 64KB of mem­ory on the stack and ini­tial­ize it. The stan­dard way of do­ing this is to per­form a stack probe to en­sure that 64KB of mem­ory is avail­able, then sub­tract­ing 65536 from the stack pointer, and then ini­tial­iz­ing the mem­ory in a small, tight loop.

But us­ing a loop to ini­tial­ize the mem­ory was too mun­dane for what­ever com­piler was used to com­pile this code. Instead of gen­er­at­ing a loop to ini­tial­ize each byte of the buffer, the com­piler optimized” the code by un­rolling the loop into 65,536 in­di­vid­ual write byte to mem­ory” in­struc­tions, each 4 bytes long.

All in all, it took this pro­gram 256 kilo­bytes of code to ini­tial­ize 64 kilo­bytes of data.

This of­fended the team so much that they added spe­cial code to the trans­la­tor to de­tect this hor­ri­ble func­tion and re­place it with the equiv­a­lent tight loop.

Author

Raymond has been in­volved in the evo­lu­tion of Windows for more than 30 years. In 2003, he be­gan a Web site known as The Old New Thing which has grown in pop­u­lar­ity far be­yond his wildest imag­i­na­tion, a de­vel­op­ment which still gives him the hee­bie-jee­bies. The Web site spawned a book, co­in­ci­den­tally also ti­tled The Old New Thing (Addison Wesley 2007). He oc­ca­sion­ally ap­pears on the Windows Dev Docs Twitter ac­count to tell sto­ries which con­vey no use­ful in­for­ma­tion.

Apple’s weird anti-nausea dots cured my car sickness

www.theverge.com

I’ll just work from the car, I thought. But af­ter a few min­utes of star­ing at my screen on quick moun­tain switch­backs I could feel the first signs of cold, co­ag­u­lated nau­sea bub­bling up from that sweaty place in my gut. I looked to the hori­zon for re­lief, but noth­ing helped… un­til I re­mem­bered Apple’s magic dots.

Introduced in 2024, Apple’s Vehicle Motion Cues promise to tap into your de­vice’s ac­celerom­e­ter and gy­ro­scope to re­duce or, in my case, even elim­i­nate the mo­tion sick­ness felt when try­ing to use an iPhone, iPad, or MacBook in­side a mov­ing ve­hi­cle.

According to big-S Science, this type of ve­hi­cle mo­tion sick­ness is caused by the eyes star­ing at a sta­tic dis­play while the in­ner ear feels the car turn­ing, brak­ing, and ac­cel­er­at­ing. Motion Cues solve this by plac­ing dots around the pe­riph­ery of the dis­play that move in har­mony with the mo­tion of the car. When the car turns right, the dots sweep across the screen to the left; when the car brakes the dots slide for­ward.

It sounds pre­pos­ter­ous, but I’m here to tell you that it ac­tu­ally works. Once en­abled, I’ve com­fort­ably read books in the Kindle app on my phone for a few hours at a go, and even writ­ten 1,000-word re­views while my wife drove our camper van to the next des­ti­na­tion. She uses Apple’s Vehicle Motion Cues now, too, be­cause they’ve been a game changer for how we bal­ance work with life on the road.

Vehicle Motion Cues can be con­fig­ured un­der ac­ces­si­bil­ity set­tings in iOS, iPa­dOS, and ma­cOS. They can be turned on, off, or set to ap­pear au­to­mat­i­cally when ve­hi­cle mo­tion is de­tected. I pre­fer to tog­gle the dots to avoid see­ing them when I’m dri­ving the car. The black dots are fairly un­ob­tru­sive, but they can in­ter­fere with maps, text, and im­agery on long straight stretches of road that cause the dots to sit mo­tion­less (Apple should dim all the dots in those sit­u­a­tions). You can also con­fig­ure the dot size, color, and den­sity if you want, but I found the de­faults to work just fine.

I made it easy to quickly tog­gle the Motion Cues on and off by dou­ble tap­ping the back of my iPhone. To do the same, head over to Accessibility –> Touch –> Back Tap and set the Double Tap ges­ture to Vehicle Motion Cues on de­vices sup­port­ing iOS 18 and above.

I’m for­tu­nate that I re­mem­bered this ob­scure ac­ces­si­bil­ity fea­ture that I used al­most daily on a re­cent two-month road trip around Europe. Hopefully you’ll find sim­i­lar suc­cess when trav­el­ing this sum­mer.

Follow top­ics and au­thors from this story to see more like this in your per­son­al­ized home­page feed and to re­ceive email up­dates.

Thomas Ricker

I Love the Computer

michaelenger.com

In a re­cent dis­cus­sion on the Aftermath Podcast about the ill ef­fects of the cur­rent AI hype cy­cle, one of the ed­i­tors said some­thing that re­ally res­onated with me:

I love the com­puter. — Chris Person

I love the com­puter. — Chris Person

This was in the mid­dle of a rant about how these snake oil sales­men are ru­in­ing the space he loves with their in­sa­tiable avarice and, as much as I’d like to add my voice to the cho­rus of tech­nol­o­gists who are le­git­i­mately an­gry at this so­cial crime be­ing com­mit­ted, I’m go­ing fo­cus on that spe­cific quote.

Because I, too, love the com­puter.

In The Beginning

It all be­gan with a cu­ri­ous box that my mother brought home from work. Sometime when I was around six or seven we were liv­ing in Dølihagen, a sub­ur­ban area near Jessheim, it­self a small town in Norway. The area was less pop­u­lous then than it is now, hav­ing bal­looned in size since they opened the new in­ter­na­tional air­port nearby, and my mem­o­ries of it are a sparse mix of play­grounds, muddy fields, and a sea of ho­moge­nous build­ings.

We had moved there af­ter the death of my fa­ther, from a large house my par­ents had build next door to my grand­par­ents, to a small flat on the lower ground floor where my mother, my brother, and I all slept in the same room. My mother flit­ted through a se­ries of work­places and even­tu­ally landed a job in the min­istry of for­eign af­fairs, a po­si­tion that was go­ing to send us to the Philippines. I don’t re­mem­ber much of the prepa­ra­tions lead­ing up to the move, but one ex­pe­ri­ence is burned into my mind: the day she brought home the com­puter. From the mo­ment she un­packed and set it up on the din­ing room table I was en­thralled.

This daunt­ing and for­eign ma­chine was fairly typ­i­cal for the early 90s and was a tool she was given to aid in her new work. It was an IBM 486 DX6 run­ning Windows 3.0 (later Windows for Workgroups 3.11), housed in a busi­ness-grey tower adorned with green LEDs and an be­guil­ing turbo but­ton. It came pre­in­stalled with Paint, SkiFree, and Solitaire, and would be­come my por­tal into a new world wherein I would find friends, hob­bies, and a ca­reer. It was even­tu­ally equipped with a sound card and CD-ROM drive—my mother damn­ing whomever had coined the term Plug & Play” dur­ing its in­stal­la­tion—and I have count­less mem­o­ries of time spent ex­plor­ing all it had to of­fer. Nearly two decades af­ter that first in­tro­duc­tion, a ther­a­pist would spec­u­late that my in­ter­est in com­put­ers could stem from how it was a rare point of sta­bil­ity in a life where I ended up leav­ing my home and my friends every few years.

The Smell of Ink on Cheap Paper

As I had the plea­sure of be­ing drawn into the world of com­put­ers in the pre-In­ter­net era1, my ex­pe­ri­ence of dis­cov­ery is in­ex­orably linked with the en­thu­si­ast print me­dia at the time. Magazines like TEKNO and Geek, and even­tu­ally Incite PC Gaming and PC Gamer, gave me in­sight not only into the hard­ware and soft­ware that so en­rap­tured me, but a cul­ture which I yearned to be a part of. My in­ter­ests re­volved mostly around gam­ing, but I would pour over any and all com­puter-re­lated pub­li­ca­tion I could get my hands on. I un­der­stood very lit­tle but I was des­per­ate to learn, and thor­oughly ex­plored the floppy discs and CDs that came along with the mag­a­zines. Through these pub­li­ca­tions I picked up the slang and the over­lap­ping in­ter­ests, and found my­self build­ing an iden­tity around what it meant to be a geek”, a gamer”, or a computer guy”. Having re­vis­ited some of those mag­a­zines in re­cent times shows how crude, misog­y­nis­tic, and ad­ver­sar­ial a lot of the writ­ing was, so al­though they pro­vided me with a lot of self-dis­cov­ery I’m glad I out­grew that men­tal­ity.

It would be easy to say that it’s just nos­tal­gia that makes me lament what was lost in the tran­si­tion to the Internet, and it’s not like print was spared the rot of cap­i­tal­ism that has made on­line geek spaces into ad-rid­den, en­gage­ment-max­imis­ing cesspools. But I am glad that I was able to do my ini­tial dis­cov­ery in a world de­void of pop-ups, auto-play­ing ads, click-bait, and in­ces­sant pleas to like and sub­scribe”. Print me­dia was slow, im­per­fect, and filled with a cal­lous tox­i­c­ity that still per­me­ates the hobby, but, as I have es­poused be­fore, there is some­thing unique in the kind of writ­ing that was done by a full ed­i­to­r­ial team and meant for per­ma­nently print­ing on pa­per. All that be­ing said, as much as I loved the mag­a­zines I col­lected, they pale in com­par­i­son to the end­less font of knowl­edge which was about to en­ter my life. For what bet­ter place is there for a kid with a vo­ra­cious ap­petite for niche in­for­ma­tion than the won­der­ful World Wide Web?

An Era of Self-Discovery

In my early teens we moved to Malaysia and I started at­tend­ing a school which had a per­ma­nent Internet con­nec­tion avail­able to stu­dents. I had had a brief in­tro­duc­tion to use of the Internet at my for­mer school, but hav­ing it avail­able on all com­put­ers and free to be ex­plored was a boon to a tech ad­dict like me. My ex­plo­ration was wide and deep, and I can re­call read­ing film re­views on GeoCitites sites and play­ing early Java-based games hosted by ISPs. I printed hun­dreds of pages of SNES walk­throughs on the li­brary printer, and down­loaded pix­e­lated pho­tos of naked peo­ple to a col­lec­tion of floppy disks, spurred on by re­bel­lious peers and the rav­en­ous needs of my bur­geon­ing sex­u­al­ity. I went every­where and did every­thing I could.

Magazines were still the pri­mary source of new in­for­ma­tion for many years af­ter this, but brows­ing the Internet be­came my favourite way to spend my free time. It felt as if dis­ap­pear­ing into this dig­i­tal world was giv­ing me a real con­nec­tion to the al­lur­ing cul­ture I so des­per­ately wanted to be a part of, and though I felt lost in life but I found my­self in the Net. I lived close enough by the school to bike over when­ever I wanted and the com­puter labs were open on week­ends, so in­stead of spend­ing time with peers I would spend it hap­pily ex­plor­ing all the kinds of places I had read about in the mag­a­zines.

By the time we moved back to Norway and then fur­ther out to Colombia I had be­come so deeply em­bed­ded into the Internet that I would spend en­tire week­ends in my room by my­self. I ex­plored fo­rums and chat rooms, and toyed with all the things I could get my com­puter to do. At this point I had also thor­oughly sur­passed my moth­er’s tech­ni­cal ca­pa­bil­i­ties and was reg­u­larly switch­ing out com­po­nents that I had re­searched and pur­chased my­self. When a pi­rated CD of Planescape: Torment ex­ploded in my CD drive, the frus­tra­tion was mixed with the glee of get­ting some­thing shiny and new to add to this grey tower that was be­com­ing an ex­ten­sion of my ego.

I had be­come one with the ma­chine, and all my in­ter­ests re­volved around the com­puter. There was only one fi­nal puz­zle piece to add: pro­gram­ming.

Not Exactly a Natrual Talent

My first time pro­gram­ming did­n’t go very well. One of the mag­a­zines I had fea­tured an ar­ti­cle about this new thing called Java that was get­ting a lot of buzz, and I wanted to try out for my­self. Unfortunately, the nec­es­sary com­piler was on the ac­com­pa­ny­ing CD, which had­n’t sur­vived my brother and his friend us­ing it as fris­bee. However, hav­ing had much suc­cess with ex­per­i­men­ta­tion so far I de­cided to try some­thing that would prob­a­bly work: I copied the code from the mag­a­zine into Notepad and saved the file as .exe in­stead of .txt.

You don’t need to have a lot of tech­ni­cal un­der­stand­ing to imag­ine that this ap­proach did­n’t work. Despite hav­ing read the ar­ti­cle mul­ti­ple times I had­n’t quite grasped the con­cept of com­pi­la­tion and, as far as I un­der­stood, I just needed to write the cor­rect in­can­ta­tion into a file and tell the com­puter to ex­e­cute it. In hind­sight it’s funny how close I was to a work­ing so­lu­tion if only I had known about shell scripts, but this was a fail­ure and caused me to not make an­other at­tempt for sev­eral years.

Eventually, thanks to a class in school and some in­dus­tri­ous friends, I be­came well versed in pro­gram­ming. I got fa­mil­iar with Java, tried my hand at C++, and dove into PHP when that hit the scene. By the time I grad­u­ated from the International Baccalaureate pro­gramme I had re­ceived an ho­n­ours in Computer Science, nat­u­rally mov­ing on to tak­ing a Bachelors in Computer Science from the University of Oslo. It’s been many years since I first tried to re­name a text file to an ex­e­cutable, and since then I’ve made dozens of pro­grams in a myr­iad of dif­fer­ent lan­guages2. I’ve had jobs rang­ing from as­sem­bly line web­site de­vel­op­ment3 to work­ing on au­dio play­back en­gines and I still en­joy slinging code”, al­though maybe with a lit­tle less gusto than I once had. During my uni­ver­sity days I spent so much time mak­ing web­sites and games that it re­sulted in me fail­ing a lot of my classes, so there is clearly a love there which has kept me go­ing.

A Life Well Lived?

Computers have been a large part of my life since that fate­ful child­hood en­counter with a mys­te­ri­ous and noisy beige box. My un­der­stand­ing of its in­ner work­ings and the cul­ture around it did a lot to shape my in­ter­ests and per­son­al­ity in my for­ma­tive years, and I doubt I’d be the same per­son if I had­n’t had this as my main in­ter­est for so long. Now as I am slid­ing gra­cious­ly4 into mid­dle-age I can see the ef­fect it has had on me, and I can con­fi­dently say that I have no re­grets. Considering the com­fort­able and in­vig­o­rat­ing ca­reer I got out of my pas­sion, I would also say that get­ting into com­put­ers in the early 90s was a re­ally smart move.

But things feel dif­fer­ent now. I can re­late to what Chris Person said when he ex­pressed his frus­tra­tions about how these slick con­men are us­ing the tech­nol­ogy I adore as tools for ex­ploita­tion and dis­em­pow­er­ment. The Internet, built by ide­al­ists on a foun­da­tion of open­ness and com­mu­nity, has be­come a mire of dark pat­terns and gar­dens with ever thicker walls, des­per­ate to keep peo­ple within an ecosys­tem where their at­ten­tion is the prized com­mod­ity. I’ve wit­nessed a nerdy space full of nerds be in­vaded by mar­keters, cal­lous cap­i­tal­ists, and brogrammers”—exaggerating the worst, most toxic, as­pects of geek cul­ture in their pur­suit of money and power. I’ve poured hun­dreds of hours of work into open source pro­jects only to have it all be scraped into a pla­gia­rism ma­chine and then ag­gres­sively sold back to me. It feels that the hope I had for the fu­ture tech­nol­ogy could give us, the naïve and starry-eyed fan­tasies I fos­tered in my youth, has been eroded when faced with a re­al­ity where the thing I love can make a lot of money for peo­ple who don’t care for any of it.

Then again, it’s not all bad. We’ve come a long way from the time when com­put­ers were seen as ex­pen­sive and ex­clu­sive tools, and the un­wel­com­ing do­main of elit­ist men. Programming—with the em­pow­er­ment that it brings—is more ac­ces­si­ble than ever and there seems to be a strong cul­tural shift in the techie spaces away from cen­tralised ser­vices and onto fed­er­ated, self-hosted, and in many other ways more free al­ter­na­tives. The Internet seems to be­com­ing more and more locked down, but us weirdos will just stay in our weird cor­ners and will find means to cir­cum­vent any re­stric­tions put on us. My af­fec­tion to­wards tech­nol­ogy made me an os­tracised out­sider when I was younger, then it con­de­scend­ingly made me into a rockstar”, and now it’s look­ing like my peers are ush­er­ing in the end of civilised so­ci­ety. So I’m ready to go back into be­ing just some strange guy with strange in­ter­ests, do­ing silly things peo­ple don’t un­der­stand and don’t care to.

Because, man… I love the com­puter.

It had def­i­nitely around, but was by no means as ubiq­ui­tous as it is now. ↩︎

It had def­i­nitely around, but was by no means as ubiq­ui­tous as it is now. ↩︎

Check out my Codeberg pro­file if you want to see some of them. ↩︎

Check out my Codeberg pro­file if you want to see some of them. ↩︎

Otherwise known as being a con­sul­tant in a dig­i­tal agency”. ↩︎

Otherwise known as being a con­sul­tant in a dig­i­tal agency”. ↩︎

Debatable. ↩︎

Debatable. ↩︎

SpaceX overtakes Amazon to become world's fifth most valuable firm

www.bbc.com

4 hours ago

Archie MitchellBusiness re­porter

Reuters

Elon Musk’s SpaceX has over­taken Amazon to be­come the world’s fifth most valu­able com­pany af­ter a surge in its share price.

Days af­ter join­ing New York’s tech-fo­cused Nasdaq stock ex­change in the biggest pub­lic list­ing ever, its share price has risen by more than 50%.

It leaves Musk’s rocket com­pany worth about $2.78tn (£2.1tn), while Jeff Bezos’s sprawl­ing on­line re­tail and me­dia em­pire is cur­rently worth about $2.66tn.

The boom in SpaceX’s value came as it an­nounced it was buy­ing AI cod­ing start-up Cursor for $60bn.

SpaceX said it would take over Anysphere, Cursor’s par­ent com­pany, which makes the ar­ti­fi­cial in­tel­li­gence cod­ing agent.

Soaring shares

SpaceX has gar­nered huge en­thu­si­asm among in­vestors for its vi­sion of send­ing AI data cen­tres to space and even help­ing hu­mans to colonise Mars.

Its list­ing raised $85.7bn and minted Musk as the world’s first tril­lion­aire. Since first sell­ing shares to the pub­lic at $135 each on Friday, they have risen to $209.

But an­a­lysts have ques­tioned the sus­tain­abil­ity of its high share price given the huge amount of un­cer­tainty over its fu­ture earn­ings.

While Amazon is a house­hold name, with its brand dif­fi­cult to avoid be­ing en­coun­tered on an al­most daily ba­sis, SpaceX is less em­bed­ded in the lives of the gen­eral pub­lic.

Despite SpaceX’s stock mar­ket value over­tak­ing Amazon, the rev­enues and prof­its made by the com­pa­nies are vastly dif­fer­ent.

Amazon made $30.3bn of profit in the first quar­ter of 2026, while Musk’s fu­ture-fo­cused SpaceX lost $4.3bn.

In 2025, Jeff Bezos’s firm ac­crued some $716.9bn in sales, while SpaceX recorded $18.67bn.

But in­vestors ap­pear to be bet­ting on what they think SpaceX can acheive. While its biggest fo­cus is the man­u­fac­ture and launch of rock­ets with reusable parts, the com­pany also man­u­fac­tures and launches Starlink in­ter­net satel­lites, and is ramp­ing up its pres­ence in the AI race.

SpaceX and Cursor have been part­ners since April, when Musk’s firm an­nounced it had the right to ei­ther buy it for $60bn, or pay $10bn for the work they have done to­gether.

Like OpenAI and Anthropic, Cursor’s tech­nol­ogy uses AI to au­to­mate the process of writ­ing code, one of the most promi­nent cur­rent uses for ar­ti­fi­cial in­tel­li­gence.

The tie-up comes as SpaceX tries to catch up with ri­vals by grow­ing its AI busi­ness, xAI, which is be­hind the con­tro­ver­sial Grok chat­bot.

Announcing the part­ner­ship in April, SpaceX said: The com­bi­na­tion of Cursor’s lead­ing prod­uct and dis­tri­b­u­tion to ex­pert soft­ware en­gi­neers with SpaceX’s mil­lion H100 equiv­a­lent Colossus train­ing su­per­com­puter will al­low us to build the world’s most use­ful mod­els.”

Cursor is used by ma­jor com­pa­nies in­clud­ing Stripe, Adobe and Nvidia, whose boss Jensen Huang has de­scribed it as his favourite en­ter­prise AI ser­vice”.

SpaceX said the deal would be com­pleted by the end of September, with Cursor’s share­hold­ers paid with $60bn worth of SpaceX shares.

Just a moment...

gmalandrakis.com

I Could've Rickrolled the Entire FIFA World Cup. All I Needed Was My ID.

bobdahacker.com

They fixed it with­out ever re­spond­ing to me. I had to call FIFA, MediaKind, HBS, CISA, and the FBI at 3am Tokyo time just to get some­one to lis­ten. This is that story.

It Started With a Football Agent Registration

So FIFA has this thing called the FIFA Agent Platform. It’s a pub­lic por­tal where you can reg­is­ter to be­come a li­censed foot­ball agent. You sub­mit your ID, ver­ify your email, and you’re in. Simple enough.

What I did­n’t ex­pect was what hap­pened next.

When you reg­is­ter on agents.fifa.org, FIFA adds your ac­count to their Microsoft Entra ten­ant (formerly Azure AD). That’s the same ten­ant that pow­ers all of FIFAs in­ter­nal plat­forms. And I mean all of them.

My first two at­tempts ac­tu­ally failed be­cause the light­ing on my ID pho­tos was­n’t good enough:

Registration failed dur­ing the last step of check­ing your iden­ti­fi­ca­tion.” - ap­par­ently FIFA has higher stan­dards for my selfie than my ac­tual se­cu­rity

But the third at­tempt went through. And I re­ceived this beau­ti­ful email:

Subject line: FIFA - FAP - CONFIRMATION. Yes, FIFAs Agent Platform is of­fi­cially called FAP. I can­not make this up. FAP CONFIRMATION. Moving on.

The Access Denied” That Wasn’t

After reg­is­tra­tion, I tried nav­i­gat­ing to fdp.fifa.org - FIFAs Football Data Platform. The app au­then­ti­cated me through the shared Entra ten­ant, checked my roles, found I had none, and showed me:

Sorry, you do not have any FIFA Football Data Platform role as­signed to your ac­count.”

Looks like it works, right? Access de­nied. Go away. Nothing to see here.

Except this was all client-side. The Angular app checked the JWT for a NO_ROLES marker and ren­dered the ac­cess-de­nied page. The back­end APIs? They did­n’t check any­thing. They just served what­ever you asked for.

Welcome to the Streaming Management Panel

After by­pass­ing the client-side guards, I landed on the Streaming Management panel. And my jaw hit the floor.

Every sin­gle FIFA World Cup 2026 match. With stream­ing con­trols.

This was­n’t some dev en­vi­ron­ment. This was­n’t test data. This was the live pro­duc­tion Streaming Management panel for the FIFA World Cup 2026. Every match. Every cam­era an­gle. Every RTMP in­gest URL. Every stream key.

Let me ex­pand one of those matches so you can see what I mean:

Five cam­era an­gles per match: PGM, Tactical, Camera1, High Behind Left, High Behind Right

Each match had five cam­era feeds, each with:

An RTMP in­gest URL (where the cam­era sends video TO)

A pre­view man­i­fest (where you can WATCH the feed)

An out­put URL (the HLS man­i­fest that goes to broad­cast part­ners)

The RTMP in­gest URLs looked like this:

rtmp://​in-6c81fc99 – 513f-4c76 – 82c2 – 877e0b93f2ea.wes­teu­rope.stream­ing.me­di­akind.com:1935/​96886a14 – 9987-420f-814c-2f7cec5408ae

That UUID at the end? 96886a14 – 9987-420f-814c-2f7cec5408ae. That’s the stream key (not a real one). It’s shared across all five cam­era an­gles for the same match. One key to rule them all.

The stream­ing in­fra­struc­ture is hosted on MediaKind, FIFAs stream­ing tech­nol­ogy part­ner. These are pro­duc­tion end­points. The same ones re­ceiv­ing live cam­era feeds from sta­di­ums across the US, Mexico, and Canada.

I Opened VLC. It Was Live.

I had to con­firm the pre­view man­i­fests ac­tu­ally worked. So I copied one into VLC.

That’s a live tac­ti­cal cam­era feed from an ac­tive FIFA World Cup 2026 match. Playing in VLC. On my PC. In Tokyo.

I closed it im­me­di­ately. But the dam­age was done (to my brain). Those pre­view URLs serve live video. During ac­tive matches. To any­one with the URL.

I Could Have Stopped the Streams

It was­n’t just read ac­cess. The Streaming Management panel had full con­trols. Start, stop, sched­ule. For every match. Every cam­era an­gle.

One click. That’s all it would take to kill a live World Cup cam­era feed.

I did not touch any of these con­trols. But they were there. Functional. Waiting for any­one with a NO_ROLES ac­count to press them.

The Nuclear Option

Let me spell out what this means.

Those RTMP in­gest URLs are the lit­eral pipe from the sta­dium cam­eras to FIFAs broad­cast dis­tri­b­u­tion chain. Camera -> RTMP in­gest -> MediaKind -> broad­cast part­ners -> your TV.

If an at­tacker pushed video to one of those RTMP end­points with the stream key (which is RIGHT THERE in the URL), they would re­place the cam­era feed. The PGM (Program) feed is the main broad­cast out­put. Replace that, and every TV net­work re­ceiv­ing the FIFA feed shows what­ever you pushed.

The stream key is shared across all five cam­era an­gles per match. A sin­gle at­tacker could hi­jack every cam­era si­mul­ta­ne­ously.

An at­tacker could have rick­rolled the en­tire FIFA World Cup. Or played Subway Surfers game­play. Live. On every TV net­work world­wide. During an ac­tive match.

I did not test this. I did not push any­thing to any RTMP end­point. But the in­fra­struc­ture was wide open.

But Wait, There’s More

The Streaming Management panel was­n’t the only thing ex­posed. My NO_ROLES ac­count had ac­cess to the en­tire plat­form.

Competitions, Matches, Teams, Tools, Exchange Platform, Analysis Dashboard, Commentator Information System, FIFA AI Pro, Admin. All ac­ces­si­ble.

The plat­form also had a full live match dash­board with an em­bed­ded video player, real-time event time­line, and match of­fi­cials data:

Côte d’Ivoire vs Ecuador, live. Embedded video feed, yel­low card time­line, match of­fi­cials. The LIVE badge is­n’t dec­o­ra­tive.

Advanced Analytics (Live Match)

Live pos­ses­sion con­trol, at­tempt cre­ation break­downs, ball re­cov­ery tim­ing, dis­tance cov­ered, and FIFA AI Pro in­te­gra­tion

Match Management (Write Access)

Here’s where it gets worse. The Management tab on fdp.fifa.org has write op­er­a­tions. And the back­end ac­cepts them from a NO_ROLES ac­count.

Update Live Stats” with a rich text ed­i­tor, match time, match score fields, and an Edit and Publish” but­ton

Attendance, Possession, Post Match Statistics, Team Registration Statistics, Analysis Finished, Score and Statistics, Adjust Kick-off Moment, Performance Data, Send Tactical Lineup, Event Ingress Details

An at­tacker could:

Modify ed­i­to­r­ial com­men­tary notes and pub­lish them to broad­cast sys­tems

Adjust the of­fi­cial kick-off mo­ment

Send tac­ti­cal lineup data

Change scores and match sta­tis­tics

This data feeds into the Commentator Information System and gets dis­played on live tele­vi­sion.

The Commentator Information System

cis.fifa.org was also ac­ces­si­ble with the NO_ROLES ac­count. This is the real-time dash­board that broad­cast com­men­ta­tors use dur­ing live matches.

The FIFA World Cup 2026 dash­board. Live scores, up­com­ing matches, re­sults.

Côte d’Ivoire vs Ecuador, 75th minute. Full tac­ti­cal view with player po­si­tions, for­ma­tions, live stats, sub­sti­tu­tion time­line, and squad data.

When a com­men­ta­tor says fun fact, Enner Valencia at 36 years and 222 days is the old­est out­field player to make a FIFA World Cup ap­pear­ance for Ecuador” - this is where that comes from. My ac­count could see every ed­i­to­r­ial note, every pre-match stats kit, every talk­ing point pre­pared for every match.

The Exposed Dev Environment

As a bonus, I also found an Azure Function App at xxxxxxxxx-spread­sheets-api.azureweb­sites.net that re­turned meta­data and di­rect Azure Blob Storage down­load URLs for 23 in­ter­nal FIFA files.

{ Size”: 10, Skip”: 0, Total”: 23, Items”: [ { Name”: 00_TransferCount_in_ENGLISH.xlsx”, BlobPath”: https://​xxxxxxxxx.blob.core.win­dows.net/​spread­sheet-stor­age/​00_­Trans­fer­Coun­t_in­_ENG­LISH.xlsx }, { Name”: 0_pending_transfers_example.xlsx”, BlobPath”: https://​xxxxxxxxx.blob.core.win­dows.net/…” }, { Name”: Debbie.xlsx”, BlobPath”: https://​xxxxxxxxx.blob.core.win­dows.net/…” } ] }

Transfer re­ports, rev­enue com­par­isons, board-level rep­re­sen­ta­tion data, ref­eree and coach sta­tis­tics. And what­ever Debbie.xlsx is. All ac­ces­si­ble with zero role checks.

The Absolute Nightmare of Reporting This

OK so I found all of this while the World Cup was un­der­way. Matches are hap­pen­ing. The RTMP URLs are ac­tive. Stream keys are ex­posed. And FIFA has no bug bounty pro­gram, no se­cu­rity.txt, and no pub­lished se­cu­rity con­tact.

What fol­lowed was the most stress­ful night of my life.

Attempt 1: Email

I fired off the full dis­clo­sure to every FIFA email I could find or guess:

[email protected], [email protected], [email protected], [email protected], and some em­ployee emails.

Five of them bounced. The rest went into the void. No re­sponse.

Attempt 2: WhatsApp

I found Sebastian Runge (Head of Football Technology & Data at FIFA, 14 years at the org) on LinkedIn. His phone num­ber was listed. I WhatsApped him. No re­sponse.

Attempt 3: FIFA HQ Phone

Called +41 43 222 7777. Closed. It was Sunday evening in Zurich.

Attempt 4: The FIFA Media Line

Called +41 43 222 7272. Also closed.

Attempt 5: The Dallas Convention Center

The IBC (International Broadcast Centre) is at the Kay Bailey Hutchison Convention Center in Dallas. I called +1 (214) 939 – 2700. Got voice­mail. Left a mes­sage.

Attempt 6: MediaKind

This was the break­through. I called MediaKind’s toll-free line +1 833 211 8472. Someone picked up. They un­der­stood the is­sue im­me­di­ately. They asked me to email the de­tails with the stream keys as proof. I did.

Attempt 7: HBS (Host Broadcast Services)

Called +41 41 726 0090. They said they did­n’t have any­one who could help and hung up. Called back. No an­swer.

Attempt 8: Infront Sports & Media

Called +41 41 723 15 15 (HBSs par­ent com­pany). No an­swer.

Attempt 9: CISA

Here’s where things got in­ter­est­ing. I dis­cov­ered that CISA (Cybersecurity and Infrastructure Security Agency) is the fed­eral lead on cy­ber­se­cu­rity for the FIFA World Cup 2026, in­clud­ing broad­cast sys­tems. I called their 24/7 op­er­a­tions cen­ter at +1 888 282 0870.

They picked up. They lis­tened. They asked me to email the de­tails. I did.

Attempt 10: The FBI

I have ex­ist­ing con­tacts at the FBI from pre­vi­ous cy­ber­se­cu­rity work. I mes­saged them on Signal. They re­sponded, said they had con­tacts and needed to pack­age it the right way.

The Timeline

The Root Cause

The whole thing boils down to one ar­chi­tec­tural mis­take: client-side au­tho­riza­tion with no server-side en­force­ment.

FIFAs in­ter­nal ap­pli­ca­tions use Microsoft Entra for au­then­ti­ca­tion and role-based ac­cess con­trol. The Angular/React/Vue fron­tends check the JWT to­ken for role claims and ren­der ac­cess-de­nied pages ac­cord­ingly. But the back­end APIs trust any au­then­ti­cated ten­ant mem­ber and serve data re­gard­less of roles.

The at­tack chain:

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

10HN is also available as an iOS App

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

Visit pancik.com for more.