Cover illustration

TheDaily Front

Issue No. #261011 Sunday, October 11 2026 #261011 — SUNDAY, OCTOBER 11, 2026
Clouds take hits, platforms lawyer up, and the old machines keep running.
Sunday, October 11, 2026 The Daily Front No. #261011 — Contents
30stories
8,064points
4,886comments
224kllm tokens
Assembled with 30 model calls — 162,715 tokens read, 61,153 written.

Highlights

3rd Yandex Cloud Data Center Was Hit

A third Yandex Cloud data center was reportedly struck, bringing the fragility of modern infrastructure into sharp relief.

Nitter: Update Oct 10th seeking funding and legal help

Nitter seeks funding and legal representation after X Corp. escalated its takedown demands.

LineageOS 24

LineageOS 24 arrives as the community prepares to celebrate a decade of keeping older Android devices alive.

Passkeys are a trap

A long critique argues that passkeys can exchange password risks for new forms of platform dependency.

Terence Tao: Math 2.0 [pdf]

Terence Tao considers what mathematical work may become when proofs are no longer scarce.

From the Editor

The wires carried two sorts of alarms today: data centers under fire, and the familiar digital commons under legal and commercial siege. Yet the repairmen, reverse engineers, and protocol keepers were on duty as ever—proof that the machinery still has a public spirit in it.

  1. Nitter: Update Oct 10th seeking funding and legal help3
  2. LineageOS 244
  3. Total Annihilation, rebuilt for modern computers5
  4. I paid people to try and follow my README6
  5. Passkeys are a trap7
  6. Terence Tao: Math 2.0 [pdf]8
  7. I'm sorry, but you still have to think9
  8. 500B tokens later: Letting AI agents decompile a first-person shooter10
  9. Show HN: BetterWispr – Free, open-source dictation for Mac11
  10. Valen's Memory Safety: A New Kind of Borrow Checking12
  11. Fingerprints are the new cookies13
  12. How to Build Wealth as a Career Person14
  13. How I reverse engineered a commercial spatial audio effect15
  14. PSPi 6 – Raspberry Pi in a PSP16
  15. NYC's "Click to Cancel" rule is now in effect17
  16. IRCv318
  17. The tilde in your PATH may not be your HOME19
  18. Google is illegaly cutting forest to build data centers20
  19. So, You’d Like To Sell Your Print Issues Of The Onion?21
  20. Nicolas Cage says he won't work with Amazon again over AI dispute22
  21. The gap in shared understanding of LLM capability is widening23
  22. A Remote Employee Refused 160 Webcam Photos a Day. Her Employer Fired Her24
  23. 3rd Yandex Cloud Data Center Was Hit24
  24. Bot traffic from Yandex24
  25. Documents add to evidence in climate deception case against ExxonMobil24
  26. Show HN: WallHop – 12ft.io is gone, so I built a replacement24
  27. European Alternatives24
  28. Cartographers of North Korea (2019)24
  29. GameBoy on E-Paper ESP32 at 30FPS [video]24
  30. Whatever became of holography? (2011)24
The Daily Front Page 2 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — The Front Page: A Platform Under Fire
article

Nitter: Update Oct 10th seeking funding and legal help

by ForHackernews·▲ 423 points·227 comments·nitter.net ↗
Nitter currently has no legal counsel.

On 24 August 2026 X Corp. sent cease and desist letters demanding a permanent takedown of Nitter instances and the project's repository. On 8 September 2026, they sent another, with the same demands and legal threats.

My September 7th update said the project would continue with legal representation in place. That was based on assurances I received and believed, which have since turned out to be untrue. I apologize for the misleading information.

Nitter currently has no legal counsel.

I'm seeking funding and legal help to fight for the future of Nitter. If you're a lawyer, an organization, or anyone who can help, please get in touch. Any further news will be posted here.


Contact

zedeus@pm.me · @zedeus:nolog.chat


Donations

Donations go first toward legal costs, if needed. Anything not needed for that supports my development of Nitter or its successor.

BTC

bc1qpqpzjkcpgluhzf7x9yqe7jfe8gpfm5v08mdr55

ETH

0x24a0DB59A923B588c7A5EBd0dBDFDD1bCe9c4460

XMR

42hKayRoEAw4D6G6t8mQHPJHQcXqofjFuVfavqKeNMNUZfeJLJAcNU19i1bGdDvcdN6romiSscWGWJCczFLe9RFhM3d1zpL

SOL

FF5bheiD5AqPEdc3eyjymJ8AoMRF1hS78Ht6FiSZZF1t

ZEC

u1vndfqtzyy6qkzhkapxelel7ams38wmfeccu3fdpy2wkuc4erxyjm8ncjhnyg747x6t0kf0faqhh2hxyplgaum08d2wnj4n7cyu9s6zhxkqw2aef4hgd4s6vh5hpqvfken98rg80kgtgn64ff70djy7s8f839z00hwhuzlcggvefhdlyszkvwy3c7yw623vw3rvar6q6evd3xcvveypt


Thank you to everyone who used, hosted, packaged, donated and contributed to Nitter over the past seven years.
— zedeus, 10 October 2026

The Daily Front Page 3 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Mobile Freedom, Continuing
article

LineageOS 24

by timschumi·▲ 414 points·168 comments·lineageos.org ↗
Here’s to the next decade.

hero

24 - Getting Sentimental

Hey there! Welcome back!

We’re a bit sentimental about this release, as it precedes a really fun upcoming milestone.

On December 24, 2026, LineageOS will celebrate 10 years. It’s a remarkable milestone for a project built by a passionate community of developers, maintainers, contributors, and users who have kept so many devices alive along the way.

This release is part of that story: one more step forward, and a small moment to appreciate how far we’ve come. Here’s to the next decade. And thank you for your continued patronage, support, and belief in the project.

The last year has been crazy, but we have remained dedicated to bringing an updated LineageOS based on Android 17 to the masses!

We’ve been hard at work rebasing all of our changes and features since Android 17’s release in June. Android 17’s first release mainly contained iterative improvements and some UI/UX refinements, but due to our previous efforts adapting to Google’s heavy refactoring over the last several versions, we were able to rebase onto Android 17’s code-base faster than anticipated. Yes you read that right: We’re early this year!

Security Patches

LineageOS Security Patch Level by Branch

  • Security patches from August 2025 to October 2026 have been merged to LineageOS 18.1 through 24.0.
    • As always, we will merge full security bulletins and change the security patch level string to match on all branches that Google actively distributes bulletins for - which as of writing is Android 14 (LineageOS 21) and newer.
    • We also plan to continue backporting and merging security bulletin patches to 18.1, 19.1, and 20.0, though as there is no “attestable” set of patches provided by Google for these versions, we will not modify the security patch level string.
  • Legacy branch patching has progressed greatly:
    • Security patches through November 2025 have been merged for the legacy branches 15.1, 16.0, and 17.1
      • This gets closer to closing the rather exhaustive effort to backport all feasible security patches.
      • The December 2025 bulletins pulled in some rather difficult to backport patches, which we were unable to adapt - but if you’re able to, push them to Gerrit for review!

Application Updates

LineageOS isn’t just about the OS itself: our suite of core apps continues to evolve as well. At this point, many of our applications are updated to Material Design 3 Expressive! Go check them out! This cycle brings some major improvements:

  • Canvas - A brand new image editor:
    • Handles all your basic photo editing needs, such as: cropping, white balance, filters, etc.
    • Pairs well with our existing Glimpse gallery, and Aperture camera applications!
  • Catapult (Android TV Launcher) - Special thanks to Contributor Timi Rautamäki:
    • A battery indicator was added for mobile devices and handheld consoles
    • Various UI updates, mainly the animations and icons were revamped.
  • Etar:
    • Updated to the newest upstream version.
    • Multiple fixes having been sent back to the upstream repo!
  • Jelly (Browser)
    • Support PWA-like apps via the shortcut mechanism
  • Twelve (Music)
    • Improved load times with a large media library
    • Now shows your “Most played” albums/genres in activity tab
    • Fixed audio tracks getting stuck with offload enabled
    • Improved Jellyfin support.
  • Updater - Special thanks to Contributor pjgowtham:
    • More Material Design 3 Expressive goodness! Alongside a complete redesign including better animations and better branding
    • Updates are streamed by default! This will greatly decrease installation time on A/B devices!
    • Improved UX and logical flow, as well as built-in notes to explain things to users
    • On non-A/B, the recovery partition is now automatically updated on the boot subsequent to a successful update
    • Tablet and TV layouts are greatly improved!
  • Seedvault:
    • Updated to the newest upstream version.
    • Multiple fixes having been sent back to the upstream repo!
  • SetupWizard - Special thanks to Contributor Saalim:
    • Complete Material Design 3 Expressive implementation along with new fluid animations and beautiful assets
    • Onboarding flow rearchitected
    • Device-specific hooks to allow maintainers to insert device-specific pages for peripherals/etc.
  • WebView:
    • Has been updated to version 155.0.8059.30.

WebUSB Flash Tools

Installing LineageOS just got a little simpler.

We’re launching new browser-based flash tools for several common device-installation methods:

  • ADB
  • Fastboot
  • Amlogic Burn protocol
  • Samsung Odin protocol

These tools use WebUSB to communicate directly with compatible devices from your browser, reducing the need to find, install, and run separate flashing utilities. They’re designed to make the installation process more approachable, especially for users who are comfortable following device-specific instructions but would rather avoid extra desktop software.

This does not replace our existing installation guides. The LineageOS Wiki will continue to document the established installation methods, and those instructions remain the default and supported path for each device.

Instead, the WebUSB tools are an additional option for users who want a more streamlined flashing experience. Availability and compatibility will vary by device, browser, operating system, and USB connection, so please read the tool’s guidance carefully before proceeding.

As always, flashing custom software carries risk. Back up anything important, confirm that you have selected the correct files and device, and follow the instructions presented by the tool.

You can try the new flash tools at flash.lineageos.org.

We’re excited to offer another way to make installing LineageOS more straightforward, while keeping our established documentation and workflows intact.

Massive thank you to Contributors Nicholas Lim and Luk1337 for their hard work on these!

CI/CD Improvements

We’ve done a lot of work to ensure submissions are properly tested and pre-reviewed, as well as build-tested:

Continuous Integration (CI) posts CI+1/CI-1 votes on most application repos, as well as several other core repos. This CI runs things like:

Continuous Deployment (CD) now pushes APKs and other such build artifacts in a GitHub Actions run each time a commit is merged.

A11Y Accessibility Audit

LineageOS just completed a full end-to-end installation process and application suite Accessibility Audit that was performed free-of-charge by the a11y project!

We are greatly appreciative of their amazing work on this audit! The goal was to make LineageOS more accessible - and some changes such as recovery navigation/UI have already seen reworking.

We are also very thankful for the free accessibility-in-development training they gave over 40 contributors in a web-seminar!

Stay tuned for more information, and potentially a future blog post on outcomes!

Deprecations / Legacy Devices

Overall, we feel that the 24.0 branch has reached feature and stability parity with 23.2 and is ready for initial release.

LineageOS 24.0 will launch building for a decent selection of devices, with additional devices to come as they meet the requirements specified by the Charter and are made ready for builds by their maintainer.

We would also like to announce formally that even though some devices using Linux Kernel versions 4.4 and 4.9 have been ported to LineageOS 23.2 and 24.0, we have decided to only accept LineageOS 22.2 submissions for these devices. The legacy kernel versions and devices associated with them carry countless platform commits that are rather hacky and difficult to continue to pull forward to newer Android versions.

LineageOS 22.2 still supports many devices running 4.4 and 4.9, and will continue to receive builds and security patches merged monthly until Google stops distributing official security-bulletins for Android 15 - and you can still submit new LineageOS 22.2 devices for official builds!

In addition, on LineageOS 23.2/24.0 we’re currently only shipping kernels that have 1:1 eBPF backports, build fully without GCC, and have many other important backported features.

Upgrading to LineageOS 24.0

To upgrade, please follow the upgrade guide for your device by navigating to it here and then by clicking on “Upgrade to a higher version of LineageOS”.

If you’re coming from an unofficial build, you need to follow the good ole’ install guide for your device, just like anyone else looking to install LineageOS for the first time. These can be found at the same place here by clicking on your device and then on “Installation”.

Please note that if you’re currently on an official build, you DO NOT need to wipe your device, unless your device’s wiki page specifically dictates otherwise, as is needed for some devices with massive changes, such as a repartition.

Careful Commonization

Several of our contributors have been working hard on SoC-specific common kernels that others can base their kernels on, and merge on a somewhat regular basis to pull in the latest features/security patches to save maintainers additional effort.

Go check them out and consider basing your device kernels on them!

Supported SoCs right now are:

SoC (system-on-chip) Kernel Version Android Version AMLogic GXL/G12A/G12B/SM1 4.9 13, 14, 15 AMLogic G12A/G12B/SM1/NE/NO 5.15 16, 17 Qualcomm MSM8998/MSM8996 4.4 13, 14, 15 Qualcomm MSM8953 4.9 13, 14, 15 Qualcomm SDM845 4.9 13, 14, 15 Qualcomm SM8150 4.14 13, 14, 15, 16, 17 Qualcomm SDM660 4.19 13, 14, 15, 16, 17 Qualcomm SM8250 4.19 13, 14, 15, 16, 17 Qualcomm SM8350 5.4 13, 14, 15, 16, 17 Qualcomm SM8450 5.10 13, 14, 15, 16, 17 Qualcomm SM8550 5.15 13, 14, 15, 16, 17 Qualcomm SM8650 6.1 14, 15, 16, 17 Qualcomm SM8750 6.6 15, 16, 17 Qualcomm SM8850 6.12 17

Moreover, many legacy devices require interpolating libraries that we colloquially refer to as “shims” - these have long been device and maintainer managed, but this cycle we have decided to commonize them to make the effort easier on everyone and not duplicate effort!

You can check it out here and contribute shims that other devices may need or add new components to additional shims and compatibility layers. Head over to our guide on using Gerrit to get started.

Wiki

The LineageOS Wiki has also been expanded over the years and now offers, in addition to the known and tested instructions for all supported devices, some improvements:

  • Material Design 3 Expressive was implemented where possible!
  • Our FAQ has been greatly expanded, and the page redesigned
  • Samsung devices: Migrated away from ODIN/Heimdall to the awesome FOSS tool, samloader-rs

We’d also like to remind everyone to follow the instructions on their device’s respective Wiki page. Android’s partitioning and update mechanisms have continued to evolve from system-as-root and A/B partitions to dynamic partitions and Virtual A/B. Through all this change, the “typical” installation steps that you may have once expected now be incomplete or incorrect for a particular device.

Our maintainers are expected to verify the published instructions for their devices, and the Wiki provides increasingly flexible, device-specific guidance for installation, updates, and upgrades. Please use it rather than relying on old guides, video guides, or steps remembered from a previous device.

Developers, Developers, Developers

Or, in this case, maintainers, maintainers, maintainers. We want your device submissions!

If you’re a developer and would like to submit your device for officials, it’s easier than ever. Just follow the instructions here.

The above also applies to people looking to bring back devices that were at one point official but are no longer supported - seriously - even if it’s not yet completely compliant, submit it! Maybe we can help you complete it.

After you submit, within generally a few weeks, but in most cases a week, you’ll receive some feedback on your device submission; and if it’s up to par, you’ll be invited to our communications channels and your device will be forked to LineageOS’s official repositories.

Don’t have the knowledge to maintain a device, but want to contribute to the platform? We have lots of other things you can contribute to. For instance, our apps suite is always looking for new people to help improve them, or you can contribute to the wiki by adding more useful information & documentation. Gerrit is always open for submissions! Once you’ve contributed a few things, email us at devrel(at)lineageos.org detailing your work, and we’ll get you in the loop.

Generic Targets

We’ve talked about these before, but these are important, so we will cover them again.

Although we’ve had buildable generic targets since 2019, to make LineageOS more accessible to developers, and really anyone interested in giving LineageOS a try, we’ve documented how to use them in conjunction with the Android Emulator/Android Studio!

Additionally, similar targets can now be used to build GSI in mobile, Android TV configurations, and Android Automotive making LineageOS more accessible than ever to devices using Google’s Project Treble. We won’t be providing official builds for these targets, due to the fact the user experience varies entirely based on how well the device manufacturer complied with Treble’s requirements, but feel free to go build them yourself and give it a shot!

Please note that Android 12 (and by proxy all later Android versions) diverged GSI and Emulator targets. Emulator targets reside in lineage_sdk_$arch, while GSI targets reside in lineage_gsi_$arch, and Cuttlefish targets reside in lineage_cf_$arch.

Additionally, targets now exist for QEMU-based virtual machine software (libvirt, UTM, etc), which have greatly improved in quality over the past year. Instructions on building and utilizing these targets can be found on the Wiki.

And one more thing… The generic targets mentioned above all require either an existing Android device, or some sort of virtualization/emulation software stack to run. Recently, Contributor 0xCAFEBABE introduced a very different kind of generic target that can be run on various types of bare-metal hardware devices.

While it’s still in experimental state, it has successfully booted on:

  • Common x86_64 PCs
  • Apple Silicon Macs
  • NVIDIA DGX Spark
  • Qualcomm Snapdragon X Series Laptops
  • And potentially more to come!

If interested, please take a look at the documentation here!

Translations

Bilingual? Trilingual? Anything-lingual?

If you think you can help translate LineageOS to a different language, jump over to our wiki and have a go! If your language is not supported natively in Android, reach out to us on Crowdin and we’ll take the necessary steps to include your language. For instance, LineageOS is the first custom Android distribution that has complete support for the Welsh (Cymraeg) language thanks to its community of translators.

Please, contribute to translations only if you are reasonably literate in the target language; poor translations waste both our time and yours.

Build roster

Added 24 devices

Device name Wiki Maintainers Moved from ASUS ZenFone 8 sake DD3Boh, mikooomich 23.2 ASUS Zenfone Max Pro M1 X00TD Vivekachooz 20 AYN Odin 2 odin2 AnierinBliss, djchittoor, onelots 23.2 AYN Odin2 Portal odin2portal AnierinBliss, balika011, bgcngm 23.2 AYN Thor odin2thor AnierinBliss, npjohnson Amazon Fire Max 11 sunstone R0rt1z2 DroidLogic Developer Kit (S905X4-B/K) / DroidLogic ADT-4/4+/4 (Low Ram) ohm npjohnson 23.2 F(x)tec Pro¹ X pro1x BadDaemon, bgcngm, mccreary, npjohnson, qsnc, tdm 23.2 Fairphone 4 FP4 mikeioannina 23.2 Fairphone 5 FP5 mikeioannina 23.2 Google Pixel 10 Pro Fold rango mikeioannina Google Pixel 10 Pro XL mustang mikeioannina Google Pixel 10 Pro blazer mikeioannina Google Pixel 10 frankel mikeioannina Google Pixel 10a stallion mikeioannina Google Pixel 4 XL coral mikeioannina, npjohnson 23.2 Google Pixel 4 flame mikeioannina, npjohnson 23.2 Google Pixel 4a 5G bramble mikeioannina 23.2 Google Pixel 4a sunfish mikeioannina 23.2 Google Pixel 5 redfin mikeioannina 23.2 Google Pixel 5a barbet mikeioannina 23.2 Google Pixel 6 Pro raven mikeioannina 23.2 Google Pixel 6 oriole mikeioannina 23.2 Google Pixel 6a bluejay mikeioannina 23.2 Google Pixel 7 Pro cheetah mikeioannina 23.2 Google Pixel 7 panther mikeioannina 23.2 Google Pixel 7a lynx mikeioannina, niclimcy 23.2 Google Pixel 8 Pro husky mikeioannina 23.2 Google Pixel 8 shiba mikeioannina 23.2 Google Pixel 8a akita mikeioannina 23.2 Google Pixel 9 Pro Fold comet mikeioannina 23.2 Google Pixel 9 Pro XL komodo mikeioannina 23.2 Google Pixel 9 Pro caiman mikeioannina 23.2 Google Pixel 9 tokay mikeioannina 23.2 Google Pixel 9a tegu mikeioannina 23.2 Google Pixel Fold felix mikeioannina 23.2 Google Pixel Tablet tangorpro LuK1337, mikeioannina, npjohnson 23.2 Lenovo Z5 Pro GT heart themard, optionaltoast 23.2 Lenovo Z6 Pro zippo Lucchetto, themard, einargednochsson 23.2 Motorola ThinkPhone by motorola bronco demon000, elginsk8r 23.2 Motorola defy 2021 bathena Deivid Ignacio Parra (Deivid21), Francisco Sanchez (Fraaxius) 23.2 Motorola edge 20 pro pstar npjohnson, SGCMarkus 23.2 Motorola edge 20 berlin npjohnson, SGCMarkus 23.2 Motorola edge 2021 berlna SyberHexen 23.2 Motorola edge 2024 avatrn elginsk8r 23.2 Motorola edge 30 fusion / Motorola moto S30 Pro tundra themard, electimon, npjohnson 23.2 Motorola edge 30 neo miami marcost2 23.2 Motorola edge 30 dubai themard, sb6596, Demon000 23.2 Motorola edge 40 pro / Motorola moto X40 / Motorola edge+ (2023) rtwo sgcmarkus, themard 23.2 Motorola edge s / Motorola moto g100 nio dianlujitao 23.2 Motorola moto e7 plus / Lenovo K12 guam Rajin Gangadharan (GRajin), Deivid Ignacio Parra (Deivid21) 23.2 Motorola moto g 5G - 2024 fogo mikeioannina 23.2 Motorola moto g power 2021 borneo Syed Fawwaz Hussain (Fazwalrus), Deivid Ignacio Parra (Deivid21) 23.2 Motorola moto g stylus 5G (2022) milanf AnierinBliss 23.2 Motorola moto g stylus 5G (2023) genevn elginsk8r, mikeioannina 23.2 Motorola moto g stylus 5G denver Vivekachooz 23.2 Motorola moto g10 / Motorola moto g10 power / Lenovo K13 Note capri Deivid Ignacio Parra (Deivid21), Sultanahamer 23.2 Motorola moto g15 / Motorola moto g15 power / Motorola moto g05 lamu R0rt1z2, surblazer 23.2 Motorola moto g200 5G / Motorola Edge S30 xpeng themard 23.2 Motorola moto g30 / Lenovo K13 Pro caprip mikeioannina, Deivid Ignacio Parra (Deivid21) 23.2 Motorola moto g32 devon Dhina17, mikeioannina, Deivid Ignacio Parra (Deivid21) 23.2 Motorola moto g34 5G / Motorola moto g45 5G fogos mikeioannina 23.2 Motorola moto g42 hawao Dhina17, mikeioannina, Deivid Ignacio Parra (Deivid21) 23.2 Motorola moto g50 ibiza Onelots 23.2 Motorola moto g52 rhode Dhina17, mikeioannina, tomoms, Deivid Ignacio Parra (Deivid21) 23.2 Motorola moto g82 5G rhodep AnandSuresh02, sevenrock 23.2 Motorola moto g84 5G bangkk mikeioannina 23.2 Motorola moto g9 play / Motorola moto g9 / Lenovo K12 Note guamp DelightReza, Deivid Ignacio Parra (Deivid21) 23.2 Motorola moto g9 power / Lenovo K12 Pro cebu Deivid Ignacio Parra (Deivid21) 23.2 Motorola one action troika Stricted, npjohnson, DaemonMCR 23.2 Motorola one vision / Motorola p50 kane Stricted, npjohnson, DaemonMCR 23.2 Nothing Phone (1) Spacewar zlewchan, ko_ko_konb 23.2 Nubia Mini 5G TP1803 ArianK16a, npjohnson 23.2 OSOM OV1 pyrite mikeioannina, npjohnson 23.2 OnePlus 11 5G salami bgcngm 23.2 OnePlus 12R aston inferno0230 23.2 OnePlus 13 dodge bgcngm, chandu078, dianlujitao, ItsVixano 23.2 OnePlus 15 infiniti chandu078 OnePlus 15T fairlady LuK1337 OnePlus 7 Pro / OnePlus 7 Pro (T-Mobile) guacamole LuK1337, elginsk8r 23.2 OnePlus 7 guacamoleb shantanu-sarkar 23.2 OnePlus 7T / OnePlus 7T (T-Mobile) hotdogb LuK1337, Onelots 23.2 OnePlus 7T Pro hotdog qsnc 23.2 OnePlus 8 / OnePlus 8 (T-Mobile) instantnoodle jabashque 23.2 OnePlus 8 Pro instantnoodlep LuK1337 23.2 OnePlus 8T / OnePlus 8T (T-Mobile) kebab LuK1337, mikeioannina 23.2 OnePlus 9 / OnePlus 9 (T-Mobile) lemonade mikeioannina, tangalbert919, ZVNexus 23.2 OnePlus 9 Pro / OnePlus 9 Pro (T-Mobile) lemonadep LuK1337, bgcngm, mikeioannina 23.2 OnePlus 9R lemonades DaemonMCR, Tuan Anh 23.2 OnePlus 9RT martini basamaryan 23.2 OnePlus Ace 3 astonc inferno0230 23.2 OnePlus Ace 5 Pro hummer awa-xaga OnePlus Nord CE 2 Lite 5G oscaro Vivekachooz 23.2 OnePlus Nord CE 3 Lite 5G / OnePlus Nord N30 5G larry Vivekachooz 23.2 OnePlus Nord CE4 benz inferno0230 23.2 OnePlus Nord N10 billie danascape 21 OnePlus Nord N200 dre tangalbert919, elginsk8r 23.2 OnePlus Pad 2 Pro / OnePlus Pad 3 erhai LuK1337, bgcngm 23.2 OnePlus Pad 3 Pro / OnePlus Pad 4 iceland StudioKeys Razer Edge 5G / Razer Edge WiFi nicole AnierinBliss, balika011, mikeioannina, npjohnson 23.2 Realme 10 Pro 5G luigi Vivekachooz 23.2 Realme 9 5G oscarru Vivekachooz Realme 9 Pro 5G oscar Vivekachooz 23.2 Realme GT7 Pro Racing Edition piloti ItsVixano Realme GT7 Pro pista kmiit Realme Q5 oscarc Vivekachooz Retroid Pocket 6 RP6 AnierinBliss 23.2 Retroid Pocket Nova RPN AnierinBliss 23.2 SDMC DV9135 tifa Tuan Anh SHIFTphone 8.0 / SHIFTphone 8.1 otter amartinz, mikeioannina 23.2 Samsung Galaxy A21s a21s DaemonMCR 23.2 Samsung Galaxy A52 4G a52q Simon1511 23.2 Samsung Galaxy A52s 5G a52sxq Simon1511 23.2 Samsung Galaxy A72 a72q Simon1511 23.2 Samsung Galaxy A73 5G a73xq Simon1511 23.2 Samsung Galaxy M52 5G m52xq Simon1511 23.2 Samsung Galaxy Note20 (4G/5G) c1s CmdCtrlDevic3 23.2 Samsung Galaxy Note20 Ultra (4G/5G) c2s CmdCtrlDevic3 23.2 Samsung Galaxy S20 (4G/5G) x1s ExtremeXT, fcuzzocrea 23.2 Samsung Galaxy S20 FE (Exynos) r8s CmdCtrlDevic3 23.2 Samsung Galaxy S20 Ultra (5G) z3s ExtremeXT, fcuzzocrea 23.2 Samsung Galaxy S20+ (4G/5G) y2s ExtremeXT, fcuzzocrea 23.2 Samsung Galaxy S22 Ultra b0s ExtremeXT 23.2 Samsung Galaxy S22 r0s ExtremeXT 23.2 Samsung Galaxy S22+ g0s ExtremeXT 23.2 Samsung Galaxy S23 dm1q DaemonMCR, josip-k, krazey 23.2 Samsung Galaxy Tab A7 10.4 2020 (LTE) gta4l chrmhoffmann 23.2 Samsung Galaxy Tab A7 10.4 2020 (Wi-Fi) gta4lwifi chrmhoffmann 23.2 Samsung Galaxy Tab S7 (LTE) gts7l bgcngm 23.2 Samsung Galaxy Tab S7 (Wi-Fi) gts7lwifi bgcngm 23.2 Samsung Galaxy Tab S7+ (LTE/5G) gts7xl bgcngm, programminghoch10 Samsung Galaxy Tab S7+ (Wi-Fi) gts7xlwifi bgcngm Solana Saga ingot mikeioannina, npjohnson, tomoms 23.2 Sony Xperia 1 II pdx203 hellobbn 23.2 Sony Xperia 1 III pdx215 hellobbn 23.2 Sony Xperia 1 IV pdx223 Tuan Anh 23.2 Sony Xperia 1 V pdx234 hellobbn 23.2 Sony Xperia 1 VI pdx245 BotchedRPR 23.2 Sony Xperia 10 IV pdx225 LuK1337, jmpfbmx 23.2 Sony Xperia 10 V pdx235 jmpfbmx, LuK1337 23.2 Sony Xperia 10 VII pdx257 LuK1337, jmpfbmx 23.2 Sony Xperia 10 kirin LuK1337 23.2 Sony Xperia 5 II pdx206 kyasu, hellobbn 23.2 Sony Xperia 5 III pdx214 kyasu, hellobbn 23.2 Sony Xperia 5 IV pdx224 Tuan Anh, wolfhechel 23.2 Sony Xperia 5 V pdx237 kyasu, hellobbn 23.2 Sony Xperia XA2 Plus voyager LuK1337 23.2 Sony Xperia XA2 Ultra discovery LuK1337 23.2 Sony Xperia XA2 pioneer LuK1337, jmpfbmx 23.2 Xiaomi 12 Pro zeus Aelin, ArianK16a 23.2 Xiaomi 12 cupid Aelin, ArianK16a 23.2 Xiaomi 12S Pro unicorn Aelin, ArianK16a 23.2 Xiaomi 12S Ultra thor Aelin, ArianK16a 23.2 Xiaomi 12S mayfly Flower Sea 23.2 Xiaomi 12T Pro / Xiaomi Redmi K50 Ultra diting Aelin, ArianK16a 23.2 Xiaomi 13 Pro nuwa Minus 23.2 Xiaomi 13 fuxi lolipuru 23.2 Xiaomi MIX Fold 2 zizhan Aelin, ArianK16a 23.2 Xiaomi Mi 10 Pro cmi luffitys 23.2 Xiaomi Mi 10 Ultra cas binbin323 23.2 Xiaomi Mi 10T / Xiaomi Mi 10T Pro / Xiaomi Redmi K30S Ultra apollon Khalvat-M, Ramisky 23.2 Xiaomi Mi 10T Lite 5G / Xiaomi Mi 10i 5G / Xiaomi Redmi Note 9 Pro 5G gauguin Penguin766, Lynnrin 23.2 Xiaomi Mi A3 laurel_sprout Skyblueborb, Onelots 23.2 Xiaomi POCO F3 / Xiaomi Redmi K40 / Xiaomi Mi 11X alioth althafvly, chrmhoffmann 23.2 Xiaomi POCO F5 (Global) / Xiaomi POCO F5 (India) / Xiaomi Redmi Note 12 Turbo marble Aelin, ArianK16a 23.2 Xiaomi POCO F5 Pro / Xiaomi Redmi K60 mondrian Aelin, ArianK16a 23.2 Xiaomi POCO F6 Pro / Xiaomi Redmi K70 vermeer Lunark 23.2 Xiaomi Redmi 12C / Xiaomi Redmi 12C NFC / Xiaomi POCO C55 earth surblazer 23.2 Xiaomi Redmi 9 lancelot surblazer 23.2 Xiaomi Redmi K60 Pro socrates WenHao2130 23.2 Xiaomi Redmi Note 10S / Xiaomi Redmi Note 10S NFC / Xiaomi Redmi Note 10S Latin America / Xiaomi POCO M5s rosemary surblazer 23.2 Xiaomi Redmi Note 8 / Xiaomi Redmi Note 8T ginkgo Skyblueborb, mikeioannina, programminghoch10 23.2 Xiaomi Redmi Note 9 merlinx surblazer 23.2 Zinwa Q25 Pro Q25 electimon, basamaryan, npjohnson, Androbots 23.2

Added 23.2 devices

Device name Wiki Maintainers Moved from LG Velvet 5G caymanlm lifehackerhansol, Simex LG Wing winglm Simex Motorola edge 30 ultra eqs demon000, Tomoms 22.2 Nubia Red Magic 5G (Global) / Nubia Red Magic 5G (China) / Nubia Red Magic 5S (Global) / Nubia Red Magic 5S (China) nx659j zlewchan 22.2 Xiaomi Mi 11 Pro mars WilliamSado 22.1 Xiaomi Mi 11i / Xiaomi Redmi K40 Pro / Xiaomi Redmi K40 Pro+ / Xiaomi Mi 11X Pro haydn adarshgrewal 22.2

Added 22.2 devices

Device name Wiki Maintainers Moved from AYN Odin (M0) odin_m0 rtx4d AYN Odin (M2) odin_m2 rtx4d Freebox Pop / TV (Realtek Wi-Fi/Bluetooth) nicepool_rtk chrmhoffmann, npjohnson Google ADT-2 beast npjohnson LG G6 (Canada - ENG BL) h873 npjohnson, Simex, basamaryan, Androbots, AShiningRay, ROMSG, Geel 21

The Daily Front Page 4 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — The Old War Machine, Rebuilt
article

Total Annihilation, rebuilt for modern computers

by jttnr·▲ 405 points·172 comments·nanolathe.gg ↗
Play the classic through a modern, MIT-licensed engine you can study, modify and build on.

An ARM base, tanks and kbots during combat in Nanolathe’s modern GPU renderer

Play the classic through a modern, MIT-licensed engine you can study, modify and build on.

Play the demo ↗ · Install the beta · ▶ Watch engine footage 0:43

Three original demo missions. No game folder or installation needed.
Desktop Chrome, Edge or Safari, with a keyboard and mouse.

Public beta · Single-player ready

Get closer.

Zoom in on your units and the action. The original isometric view gains a new sense of scale.

See it in a new light.

Modern GPU rendering is the standard experience, with light, glow and richer detail. Prefer the old look? Switch to Classic.

Play on your computer.

Run natively on Windows, Mac or Linux, using the Total Annihilation game files you already own.

Play it. Understand it. Build on it.

An open engine. Room to make it your own.

Total Annihilation is Nanolathe’s starting point. Its code, documented formats and clean-room research are open for others to explore and extend.

Play the classic.

A faithful foundation, modern rendering and a native beta for Windows, Mac and Linux.

Try the demo ↗

See how it works.

Read the game formats, study the engine’s design and follow the evidence behind its behavior.

Explore the engine ↗

Build on the code.

Modify the engine, experiment with mods or contribute to its next chapter. Nanolathe’s code is MIT licensed.

Code & contribution ↗

The same explosion. A different atmosphere.

See the tank catch the blast, glow spread across the scene, and grass and healthy trees gain detail. Drag to compare, or tap a view below.

Modern GPU renderer with 2× synthesized terrain, lighting and glow: a Jeffy explosion lights a nearby Bulldog tank on Greenhaven

The same Jeffy explosion and Bulldog tank, committed tick and 2× camera in Nanolathe’s Classic CPU renderer using original-resolution terrain

Greenhaven · staged scene, same committed tick and 2× camera. Classic uses original terrain; Modern uses real 2× synthesized terrain and trees, lighting and glow. Native 960×640 captures, scaled to your screen.

Capture settings and original files

Captured with engine 617540c5 on October 4, 2026. This compares full renderer paths: Modern includes supersampling and its default presentation effects. The tile cache was generated from the installed game’s original 32×32 tiles into real 64×64 synthesized tiles. Healthy tree sprites use real 2× synthesized feature banks; shadow or missing variants retain the engine’s normal fallback. Authored dead stumps are cleared from this staged display. No screenshot cropping, retouching or resampling was used. WebP exports decode to the exact captured pixels.

Open full Classic capture · Open full Modern capture

Explore camera, lighting and renderer details

Start with a demo. Stay for the whole game.

Try three campaign missions in your browser. Install the beta with your own game data for full campaigns, skirmish and online play.

Play the demo · Install the beta

Desktop browsers for the demo.
Windows, Mac and Linux for the installed game.

The short answers.

Is this the original Total Annihilation?

Nanolathe is an independent, MIT-licensed, open-source engine that reimplements Total Annihilation through clean-room research. It uses original game data supplied separately. It’s not affiliated with or endorsed by the game’s owners.

Do I need to own Total Annihilation?

The browser demo includes the original three-mission demo, so you can try it without a game folder. To play the full game, bring the installed files from a copy you own, such as GOG, Steam or your original CDs. The engine’s MIT license applies to its code; Total Annihilation’s content is separate.

What can I play right now?

Single-player skirmish, Survival and campaign are broadly complete, and with your own game files you can play skirmish and Survival online with friends. Every skirmish and Survival battle is recorded as a replay. The beta still has bugs and smaller missing features; compatibility can vary by mission or content pack, and save compatibility is still being refined.

What do I need for the browser demo?

A desktop computer, keyboard and mouse. Chrome, Edge and Safari are supported desktop browsers. Firefox, mobile and tablet play have not been verified. Demo files download when you choose to play and are cached for your next visit. Install the beta for larger battles and the full native experience.

Can I play multiplayer?

Yes, in the installed game or in desktop Chrome or Edge, and both can play together. Choose MULTI on the main menu to create a game and send its code to friends, or join with theirs. Skirmish takes 2 to 10 players, and Survival 2 or 3. The host chooses Modern, Community 3.9 or Strict 3.1 rules and can add computer players, and in battle you can chat, share and make alliances. Everyone needs their own game files; the three-mission demo can’t play online. Read the online guide.

Curious what’s under the hood?

The code and clean-room research are open to explore.

Read the research · Engine on GitHub

The Daily Front Page 5 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Read the Instructions
article

I paid people to try and follow my README

by edent·▲ 376 points·190 comments·shkspr.mobi ↗
Developers suck at writing documentation.

Developers suck at writing documentation. We like to say RTFM but often there ain't no FM to R. At best we have a half-arsed collection of notes, some obsolete tips, and a link to a desolate forum full of people asking the same questions again and again. That's no way to treat our users.

I naïvely believe there's a better way. As I've written before, you should sit down and actually test your readme. Spin up a fresh Virtual Machine, go through what you've written, and see if it makes sense.

But it is really hard to ignore your own biases. Of course you know that certain commands require sudo and obviously when you wrote -foo you meant --foo and everyone knows that you have to reboot afterwards.

How do you get rid of those biases? I paid people!

As part of my NLnet grant application for ActivityBot, I said I wanted to test the install experience with real users and I was prepared to pay them €25 for an hour of their time. I stuck out the call on Mastodon, gathered a few people, and had a video call with them.

The format was simple. I explained that I was looking for feedback on the first-run experience. I knew that it wasn't perfect and actively wanted constructive criticism. I asked the volunteer to share their screen and, crucially, to speak aloud. Tell me what they were doing. What they didn't understand, what confused them, what delighted them, what frustrated them, etc.

I took notes by hand (fuck feeding the machine) and spent several hours being told what incorrect assumptions I'd made.

Amongst the highlights I discovered were:

  • The link to the demo tool was wrong.
  • Some people read a README in the terminal.
  • What does it actually mean to rename a file?
  • How do you rename a hidden file?
  • Should a demo tool be available on the web or just the user's local machine?
  • Why does some text explicitly need to be quoted and some not?
  • My jokes aren't funny and are actively confusing.
  • Some of the technical terminology needed explaining.
  • The ordering of the different sections was confusing.
  • I hadn't actually explained what the software would do.
  • A whole section which was technically interesting to me was utterly confusing to everyone else.
  • Not all web servers use permissions in the same way.

And on it went!

After each session I updated the README based on what people found difficult, then I re-tested it with the next person.

In total, I paid out around €150 to have a bunch of people criticise me to my face. Hey, cheaper than therapy, right?

I know someone is going to say "why not just ask an LLM to simulate a range of users?" The answer is very simple - I want to speak to real people. People are brilliant! They can make you laugh, you can see their cat when it wanders on to the call, they bring a unique perspective to the problem, and they're really happy when you give them a €25 voucher. Some will gladly do it for free and make you happy!

When I was doing technical writing for GOV.UK, all my beautiful prose was given a second-eye by another human. They eviscerated all my flowery phrases and turned the document into something more readable. They were the ones who caught the mistakes that no spell chequer could. They were able to have a proper conversation with me. More importantly, I could hear the frustration in their voice - that's the thing which lets you know a mistake needs to be corrected.

I don't claim that ActivityBot's README is now perfect - far from it - but it is now demonstrably easier to follow. Developers need to talk to real people. You don't have to pay them if you can't afford it - but find a few people who will talk aloud while they try to follow your instructions. I guarantee you that your README will become much better for it.

The Daily Front Page 6 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Credentials and Custody
article

Passkeys are a trap

by laurex·▲ 352 points·201 comments·nonfunctional.substack.com ↗
Passkeys are not designed to prioritize your interests.

Seems like every website wants us to stop using passwords. There is a replacement we are being asked to use instead called passkeys. This should be a tad concerning if passwords are familiar to you and you don’t fully understand these nebulous passkeys.

Websites say that passkeys are more secure or more convenient or something similarly beneficial. While it is true that passkeys can be far more secure than passwords, and may indeed be more convenient, that’s not the whole story. Passkeys are not designed to prioritize your interests. Like the Trojan Horse, passkeys are legitimately elegant on the surface, but there is something troubling inside.

What’s important to know is that unlike with any other kind of login credential, you do not have ultimate control over your passkeys. With passwords, you can choose where to write them down; with authenticator apps, you can decide for yourself which ones to use (they’re pretty interchangeable); and with physical security keys you can hand them to your colleague if you really trust them. But with passkeys come a hidden dial that lets the websites you use them on gradually remove your ability to do as you please with them.

I will explain how that is possible further down, but let’s first take a look at who wants you to be using passkeys.

Passkeys are the invention of the FIDO Alliance,1 which is a consortium of a number of different competing interests – Big Tech, little tech, national governments, intelligence agencies, banks, various public institutions, and even a handful of nonprofits like Mozilla – who are all able to agree that passkeys are a good and righteous cause.

The members of the FIDO Alliance all have their own interests. Big Tech wants to bring little inconveniences into our life so they can sell us more ads. Intelligence agencies want to track everyone in the world. Banks want to be free of indemnity. Little tech wants cheap solutions to reduce liability. Mozilla wants to avoid having to die on any particular hill.2 I make no particular claim that any of these are bad motivations. But it is notable that no consumer advocacy groups are members of the FIDO Alliance. (This stands in contrast to, for example, the World Wide Web Consortium, the organization that decides the standards used on the web. Alongside the usual corporate members it has not only public interest groups but also universities and Tim Berners-Lee, the British computer scientist who invented the World Wide Web in 1989. Honestly, if Gandalf himself were to join the W3C he would not be out of place.) Regarding the FIDO Alliance, nobody is there to make a strong case for the user’s own interests.

The reason presented to the public for why everyone needs to use passkeys now is that passwords are insecure. On this point I agree. Consider that the servers behind the billion-dollar American IT company SolarWinds were famously found to be using solarwinds123. Hawaii’s Emergency Management Agency (the same one that sent out that false missile alert) accidentally revealed a password due to a sticky note on someone’s desk in the background of a photograph. If your security relies on a password, you will always be anxious that somebody, somewhere, has somehow figured out what it is and already has access to your bank account.

On the other hand: what passwords have going for them is that they’re very intuitive, everyone knows how they work, people have been using them since time immemorial, and you can choose where you store them and who you share them with. Keep this in mind.

(Also keep this in mind: Netflix does not want you to share your passwords.)

But there already exist superior security mechanisms to passwords, and the FIDO Alliance seems to be giving the cold shoulder: first there was Two-Factor Authentication (in particular: receiving a text message or email with a one-time code), and later came the more robust system of Time-Based One-Time Passwords (TOTP) – in common parlance, “authenticator apps.”

What’s neat about TOTP is that it doesn’t rely on text messages or email or even the internet at all. Your authenticator software simply generates a random-looking number every 30 seconds based on the current time (and a random starting number called a “seed” or a “key” that is unique to you), and this is all that a website needs to verify you’re you. The term authenticator app is misleading, not in the least because websites tend to heavily imply you need to download a specific app, such as Google Authenticator. In reality, there is an open standard for TOTP (called RFC 6238); that is to say, the formula used to generate these random-looking numbers is public knowledge, so you don’t need to use any one specific piece of software. In almost all cases you don’t need to use Google Authenticator just because the website suggests it. You don’t even need to use a smartphone; you can use your laptop or even a Game Boy3 from the 90s for this purpose. You can use any software and hardware you like, so long as it can tell time decently and can handle a few lines of code. You can even write the software yourself in Python if you want, or if you feel like burning a moticum of jet fuel you can get an LLM to write it for you.

TOTP is a great solution. If you accidentally leak your TOTP in a photograph of your desk, you’re only at risk for a couple of minutes until the code expires. TOTP is certainly less intuitive than passwords, but at least like passwords you can still choose how you want to store them. You can choose where you want them to live and what software you want to use to generate the codes. And you can’t easily shoot yourself in the foot with an easy-to-guess TOTP code like you can with a password123; as they are nothing more than random-looking numbers, all TOTP codes are equally hard to guess. Oh, what’s more, you can send a code to your friend to let them log into your Netflix account temporarily while you’re away on business. You can even share your TOTP seed itself if you really want to so that your friends can generate your TOTP codes themselves and then they can let their friends log into your Netflix account. You might get some strange looks if you write all your TOTP seeds down on a piece of paper like you do with your passwords, but at the end of the day this data is yours to handle how you like. Aren’t open standards wonderful?

(Netflix does not want you to share your TOTP keys either.)

So what makes passkeys so different? Well, they look very similar to TOTP on the surface. Both have an open standard (RFC 6238 for TOTP, WebAuthn for passkeys), so surely passkeys must have all these same nice properties as TOTP, right? At first glance it would seem so. Indeed, just now I generated a passkey using free and open source software, and then I emailed it to a friend, and now we can both access the same website using that passkey. At no point did I have to ask anyone for permission, not the website, not the FIDO Alliance; not even my friend, who kindly let me monopolize her time without objection. What’s more, I don’t have to type any numbers by hand to use a passkey; I don’t have to remember any magic words; passkeys just work. Judging from that alone, a passkey is indeed convenient. And it sure seems like I can do whatever I want with my passkeys.

(But Netflix does not want you to share your passkeys.)

So where’s the catch?

The catch is that there is something else that is also called a “passkey” and it’s nothing like the nice kind that I just described.

In fact there are many different things called “passkeys” and the FIDO Alliance has intentionally named all of them the same thing. I believe this was done in order to deliberately confuse users and encourage them not to look too closely at the fine text. Indeed, in the technical documentation, the WebAuthn standard that defines passkeys barely uses the term passkey at all. Instead there are discoverable credentials and undiscoverable credentials; there are hardware-backed credentials, and software-backed credentials; there are attested and unattested credentials, and more and more.

There are so many optional components to what a passkey can be that the specs read less like a standard and more like a parts catalogue. So, let’s go over exactly what other things are in the catalogue alongside the nice and perhaps-almost-intuitive kind of passkey I described earlier. I will present them all in the best light I can manage so that you can understand why some people genuinely recommend passkeys as a good security solution, but by the end of the list I hope you will be able to see the problem I see.


Copyable vs Non-Copyable passkeys

Both kinds exist.

When you generate a passkey, it can be made to be non-copyable. The reason you might want this as a user is to be sure that so long as you have possession of your passkey – so long as your iPhone is in your pocket, say – nobody else can access your account. On the other hand, passkeys can be made to be copyable instead, and you might want this ability in order to share one key between your devices.

If you make a passkey, make sure you know which of these two kinds you’re making! If you think your passkey is one of these and it’s actually the other, you will end up having a bad time someday – either when you realize you can’t rescue your only passkey off your dying laptop, or when somebody else turns out to have a copy of your passkey and you didn’t know that was possible.

Transferable vs Non-Transferable passkeys

Both kinds exist.

Surprisingly, even if a Passkey is non-copyable, it can still be transferable. This may violate your intuition about how data is supposed to work. Although it’s how physical objects have always worked, we don’t often encounter situations in the digital world where sending something to your friend causes it to disappear from your own device.

You might want your passkey to be non-transferable in order to be completely sure that physical possession of one specific device is absolutely required to access your account. On the other hand, you might want your non-copyable passkey to be transferable in order for it to be able to move with you when you replace your smartphone for next year’s model. (Or maybe you just want to be able to play some kind of avant-garde digital relay race among your friends as an artistic statement.)

Hardware-Backed vs Software-Backed passkeys

Both kinds exist.

Even if you’re not digitally savvy, you probably know what files and folders are, at least if you have a desktop computer. Admittedly, smartphones are designed to muddy the waters regarding where exactly that file you downloaded is and if folders are even a copacetic concept. (Yes, smartphones do use files and folders. They just don’t typically come with a file browser like your desktop does.)

So all these passkeys we’ve talked about so far are just files right? Well… not really. That is, they can be. Or they might not be. As with all these other qualities a passkey can optionally have, passkeys can optionally not be files at all. Indeed, since files are copyable and transferable and all sorts of other intuitive things, if passkeys had to be files, it would not be clear how anyone could make a non-copyable passkey.

Enter hardware-backed passkeys. These make use of a secure chip that comes with all modern devices which can store data that cannot be exfiltrated (meaning the data cannot leave the safety of the secure chip). There are different kinds of chips with different names: your laptop has a Trusted Platform Module and an iPhone has a “Secure Enclave” and so on. You will not find this area of your device in your file browser anywhere.4 It is a separate component entirely. Because of their cryptographic use case (and perhaps their mystique), people have taken to calling these chips “cryptoprocessors.”

The consensus seems to be that cryptoprocessors are indeed very secure. You might wonder if someone with the know-how couldn’t just surgically open the chip with the right tools and extract the data off that way. That’s theoretically possible; however, such chips employ all kinds of chemical and electronic tamper-detection measures and will self-destruct if you try to do this.5 So unless you have the resources of a nation-state at your disposal, you can basically take as a given that (hypothetical manufacturer backdoors notwithstanding) nobody can remove the secret data from your cryptoprocessor; not Netflix, not your operating system, not even you.

You might prefer to use a software-backed key because you understand how files work and you already have a solution that works for you for storing your private data. You might want to use a hardware-backed key to get the extra security afforded by a nigh-impenetrable cryptoprocessor.

User-Present vs Non-User-Present passkeys

Both kinds exist.

Some passkeys require somebody to press a button or click a prompt to confirm authentication. Others can be used without anyone in the loop at all.

You might want the former kind because you yourself are a human. You might want the latter kind in case you want to authorize a piece of software to act on your behalf.

User-Verified vs Non-User-Verified passkeys

Both kinds exist.

Okay so maybe a user was present, but how do we know the right user was present? Passkeys can optionally require biometrics (e.g. fingerprint sensors or retina scanners) or the use of PINs or passwords of their own in order to be extra sure that only one specific intended human being can use them.

You might want a user-verified passkey in order to be safe even in the situation a thief pilfers your device. You might want a non-user-verified passkey because you expect to lend your tablet to another family member so they can read your ebooks on Amazon and listen to music on your Spotify account and watch your movies on Disney+.

Attested vs Non-Attested passkeys

This is where things take a left turn.

I’ve presented all these properties so far as being up to you to decide. That may indeed be how it is on most websites – for now, at least. After all, what cause does Twitter have to tell me exactly which security measures out of the smorgasbord available I should opt into for my passkeys? But you should know that the FIDO Alliance has defined this standard such that your passkey software is required to tell the service you’re accessing which of these features are enabled. They’re called authenticator data flags in the WebAuthn standard.6

This means Twitter (formally X) can say to you, “sorry, we only accept the User-Present User-Verified Non-Copyable Hardware-Backed varieties of passkey here. But hey! You can still choose between Transferable and Non-Transferable passkeys!” Actually, I’m being optimistic – I suspect they are unlikely to phrase it in such clear terms. They will likely just say “unfortunately your passkey isn’t secure enough; download our app.”

But it’s an open standard, isn’t it? And maybe you have a friend experienced in writing Python code. Can’t you just make your own passkey software which just does the nice file-based kind of passkeys with none of those extra security features, and then have it tell the website anyway that it’s a User-Present User-Verified Non-Copyable Hardware-Backed passkey? Can’t you just lie?

Oh yes absolutely you can. Your passkey software can lie about whatever passkey features you like. Unless we’re talking about an Attested User-Present User-Verified Non-Copyable Hardware-Backed passkey. In which case, no. That can’t be faked.

Attested passkeys7 are the last but most important type of passkeys you need to know about. These types of keys use what’s known as remote attestation to cryptographically verify – using the aforementioned tamper-proof cryptoprocessor that lives in all your modern devices – that you are using one of the several approved passkey managers, and not using a custom one that you vibecoded yourself and which can lie.

This is the feature that means you no longer get to make the calls about your passkeys. Fortunately, attested passkeys are not yet required on most websites. But the FIDO Alliance standard makes attestation a part of the passkey ecosystem, whether you want it or not. And even if the website doesn’t require attestation now, simply by using passkeys you indicate to the website that your setup supports the FIDO Alliance’s passkey standard and that you as a user are capable of using passkeys.8 This means that once Twitter (formally X) is satisfied that the overwhelming majority of its users would be unaffected, they can flip the switch – perhaps not all at once, perhaps just for ten thousand users at a time so as not to cause a blip – and start rejecting non-attested passkeys.

Critically, remote attestation requires the co-operation of your cryptoprocessor, your passkey manager software, your device’s operating system, and your device’s firmware to all pass the remote attestation check. After all, your phone’s operating system has total access to all the 0s and 1s in every app that’s ever run, so it’s not enough for Twitter (formally X) to trust your passkey manager if it can’t even trust your phone’s underlying operating system. If any single one of those things were to fail – say, because your phone stopped receiving security updates so you installed a custom operating system like LineageOS to keep yourself safe without having to buy a new device – then Twitter (formally X) will be able to notice and prevent you from logging in.

I’d like to be clear that there are legitimate uses of remote attestation. By using a website that requires attested passkeys you can be sure you stay safe from accidentally installing a scammer’s fake passkey application that steals your data – the website will reject such a passkey. You might want an attested Passkey even if the website doesn’t require it, since you can use attestation to verify that your software has not been tampered with by a hacker while you weren’t looking. (Of course, attestation may be overkill for this; even if your device were hacked, the hardware-backing feature described above makes it impossible for the passkey to be exfiltrated from your device. Though admittedly that guarantee is less reassuring once you realize that even if the key stays firmly on your device a hacker could still use it if they have control of your phone otherwise. At the very least you can always power off the device to temporarily deny hackers access to your Non-Attested Hardware-Backed Non-Copyable Non-Transferable passkeys.)9

As for reasons to not want attestation: you might want to be able to use software that Twitter (formally X) doesn’t specifically approve of, such as Linux. Or a phone with a refurbished operating system. (Or if you’re nefarious you might want to be able to lie to Netflix and tell them that of course you and your three friends are absolutely all the same person using the same device.)

(But with attestation, Netflix can finally prevent you from sharing your passkeys.)

It’s not just about Netflix or Twitter (X)

There is ample reason for you to be hesitant before you set up a passkey. If you’ve used only websites that require one certain kind of passkey then you might not notice when a website has you set up a different kind of passkey. Don’t forget that unlike passwords and SMS codes and TOTP, passkeys are designed to function in the background without you ever seeing the details. They’ll offer you at most a “confirm” prompt. (That’s the convenience aspect.) My expectation is that the industry will start off with very few constraints on your passkeys, and gradually add more and more, not all at the same time, but staggered on different sites, and then in a few years people will forget that they were ever offered a choice in which security measures they want to enable on their passkeys (if they even knew there were different kinds of passkeys at all).

So far, almost nobody has even heard of remote attestation. The vast majority of people use one of the four major operating systems (Windows, Android, macOS, and iOS) and not alternative operating systems like Linux, LineageOS, postmarketOS, or GrapheneOS. The overwhelming majority of people don’t replace the operating system on their phone when it stops receiving updates; instead they just buy a new phone. So almost nobody will ever encounter a situation where their device fails a remote attestation check.

Are attested passkeys so bad, though? After all, we really aren’t meant be sharing Netflix accounts to begin with.

Take another look at the list of members in the FIDO Alliance, and put on your tinfoil hat. Why do intelligence agencies care if I am sharing my Netflix account? Heck, why does Microsoft care if I am sharing my Netflix account?

For intelligence agencies, and for corporations like Microsoft, Google, Apple, Facebook, Amazon – giving you convenience and security is likely not their primary goal. I think they have an entirely ulterior motive. They can use passkeys to prevent you from running unapproved software.

Remote attestation can allow them to verify that your device is not capable of, say, sending and receiving encrypted messages, or running a competitor’s app, or using social media without verifying your ID. They don’t want you to be able to turn off the mandatory built-in AI image scanner on your phone that scans everything your phone sees for nudity. (Yes, I’m serious.)

But I rather like deciding for myself what software I approve of and what software I don’t. Something I value about my web browser is that I can install extensions to modify aspects of websites that bother me. I can say no to auto-playing videos on news websites; I can decide which advertisements are too intrusive for me to let through; I can filter out irrelevant search results and disable AI summaries; I can block the “recommended video” feature on YouTube which always seems to promote outrage and division (or as Big Tech calls it, “engagement”). And if a website ends up monopolizing my time anyway, I have an extension that locks them down after a certain span of time. These extensions give me control over how the internet affects me, even if Big Tech would much rather make those decisions for me. But Google owns Google Chrome, which is the world’s most popular web browser, and they can remove support for these useful extensions (as they did earlier this year with uBlock Origin); and with remote attestation they can prevent me from using any other browser that doesn’t follow suit.

Remember those authenticator data flags I mentioned? There used to be only 4 of them. Earlier revisions of the FIDO Alliance’s security standard (called “WebAuthn Level 1” and “WebAuthn Level 2”) had fewer catalog options to choose from. I’m sweeping some details10 under the rug here for brevity, but since you are given four distinct binary choices, you could call that 16 different kinds of passkey. In 2022 the FIDO Alliance added some more (“WebAuthn Level 3”), so that websites could know whether or not your credentials are copyable, or if backups of your credentials might exist somewhere. There are still two flags left “reserved for future use.” What might our passkeys require of us in the future?

I’m not one to argue about slippery slopes, so I won’t claim that the campaigns to protect children online today will tomorrow turn into laws designed to make it easy to identify political dissidents. But I will say that the widespread adoption of the passkey technology being advertised to you as convenient and secure is capable of permitting such laws to be easily enforced.

So if you care about your privacy and the privacy of others, if you believe people should be free to choose what software runs on their devices, that people should be allowed to learn how to write their own software and run it on their own devices, and that friends should be able to privately talk politics online without government interference, then I would ask you to reject these dozens of different security mechanisms which are all confusingly branded as the same “passkeys.” Wait for Big Tech to propose something just as convenient and secure but more clearly defined and with fewer strings attached. In the meantime if you want security, your Game Boy can provide you with TOTP.11

1

No relation to the Canadian mobile network of the same name.

2

I jest, but Mozilla has a reputation for putting up a fight and then eventually surrendering on issues like these. For instance, they eventually implemented Google’s proprietary Widevine DRM system into the otherwise open-source Firefox browser, as rejecting it might have resulted in users being unable to watch Netflix.

3

Yes, someone really made TOTP for Game Boy. I cannot in good conscience actually recommend using this, but it will make you popular at parties. (At my parties in any case.)

4

In practice a hardware-backed key may have a file component too, but it will be encoded in such a way that it’s cryptographically useless without the cryptoprocessor it is made for.

5

Devices for consumers will generally just scramble themselves electronically as their anti-tamper mechanism, but more imaginative booby-traps are rumoured to exist. XEROX once made a chip that could shatter into tiny shards as a defence measure.

6

The authenticator data flags don’t all line up exactly with the features I presented above, but it’s close enough for the purposes of this blog post. For example, authenticator data flag UP is User-Present, authenticator data flag UV is User-Verified, and authenticator data flag BE is Copyability (“Backup-Eligible”).

7

Authenticator data flag AT, if you were wondering.

8

In an early version of this article I incorrectly implied that there was a direct way for platforms to check what users would be affected in advance of requiring remote attestation. To my knowledge, the WebAuthn standard does not provide any straightforward method allow websites to query whether a particular user’s passkey situation supports remote attestation, at least not without directly asking the user to create a remotely-attested passkey. But in practice, one’s operating system and browser are readily visible to websites, and because the vast majority of users will be on one of a few common setups, the site can make a very good guess about what portion of its users would be excluded by a remote attestation requirement.

9

One use case is particularly intriguing to me. In principle, remote attestation can also be run in reverse, to verify that the server you connect to is not doing anything you don’t want it to be doing (such as stealing your credit card number, or keeping backups of the posts you delete). Sadly, to my knowledge, no major platform has ever implemented “reverse” remote attestation this way.

10

As I understand it, “User-Verified” requires “User Present” to be set, so that would imply 12 distinct flavours. But in practice, security measures outside of the specific flags mentioned (such as requiring biometrics specifically, or non-transferability) are queryable and enforceable via remote attestation, so that actually means there are arbitrarily many passkey restrictions possible already with WebAuthn.

11

Addendum: I feel I have made a compelling case for passkeys in this article. And I haven’t even pointed out one of their best security features: they are (in most use cases) domain-bound, meaning they are resistant to what are called Man-In-The-Middle attacks. Passkeys have so many incredible properties that I really want to use and recommend them; it’s very unfortunate that the FIDO Alliance has ruined a good thing by slipping in remote attestation and by making no attempt to ensure the difference between these various kinds of passkeys remains evident to the user.

Fortunately, if you want the level of high security passkeys can give you without the strings attached, there is actually an even more secure solution: security keys, such as a YubiKey. These are basically just physical passkeys that you can carry around with you and use alongside your other devices. They are also part of the FIDO Alliance’s WebAuthn standard, but because they are entirely self-contained their remote attestation requirements don’t have to spill out and lock down your whole operating system.

The Daily Front Page 7 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Mathematics After Scarcity
article

Terence Tao: Math 2.0 [pdf]

by Anon84·▲ 341 points·388 comments·teorth.github.io ↗
Traditional mathematics operated under the assumption of proof scarcity.

AI disruption of mathematics

The advent of modern AI tools is disrupting many long-established professions.

My own field of mathematical research – which has been largely stable for almost a century – has proven to be particularly susceptible to this type of disruption.

The historical era of proof scarcity

Traditional mathematics (or “Math 1.0”) operated under the assumption of proof scarcity.

Finding a solution to an open math problem (and proving it to be correct) was difficult and rare, and often required significant human expertise to obtain.

Many of the institutions, traditions, incentives, and stated objectives of Math 1.0 have been organized under this proof scarcity assumption.

Objective verifiability

On the other hand, proofs have the property — unusual in most other disciplines — of being objectively verifiable.

Different mathematicians may disagree on what types of mathematics are interesting or fruitful.

But there is broad consensus on what constitutes a correct and complete proof of a given problem.

In fact, it is even possible to mechanically check proofs by computer, if they are written in an appropriate proof assistant language (e.g., Lean).

Digitizability and high-quality data

Proofs also have the property being highly digitizable; they do not require a physical instantiation, any real world data as input, or any real world experiment to verify the output.

In principle, a proof can be generated and verified by pen and paper, using only standard mathematical textbooks or libraries to supply definitions.

The solution of a complex math problem often also requires knowledge of the appropriate research literature; but an enormous amount of this literature is now available digitally.

The era of proof abundance

This combination of objective verifiability, digitizability, and large, high-quality data sets have made the task of solving mathematical problems particularly amenable to the machine learning techniques that power modern AI.

Frontier AI labs have thus prioritized advances in mathematical problem-solving capability, ahead of other applications which lack one or more of the above three features.

As a consequence of this massive investment, we are now entering an era of proof abundance in mathematics.

The jagged capability of AI

Given sufficient compute and access to frontier AI models, one can now obtain solutions to many open problems that were previously thought difficult, in relatively short amounts of time.

Increasingly often, no special expertise from the user prompting the AI is required to obtain such solutions.

This is in stark contrast to current AI performance on tasks which are subjective, dependent on real world interactions, or for which data is scarce.

AI performance is thus extremely jagged: astounding in some directions, while inadequate in others. This is true both within mathematics, and more broadly.

The new frontiers of mathematics

Despite the impression given by press releases and social media, most of the problems at the current frontier of mathematics remain unsolved, even with massive computational expenditure.

Indeed, the space of mathematical problems remains infinite; and the difficulty level of such problems is unbounded.

Because of this, the future frontiers of mathematics will always continue to expand.

Aligning goals with the long-term health of math

Furthermore, problem solving, while historically one of the most visible goals of research mathematics, is only a proxy for more valuable long-term goals.

This proxy was largely accurate in the era of proof scarcity, but has become misaligned in the era of proof abundance.

Further blind optimization of problem-solving alone is now actively harmful to the long-term health of mathematics.

The future of mathematics — “Math 2.0” — will require both expanding the research frontier, while simultaneously decentering the traditional role of problem solving.

Reporting bias

Wow, all the math problems I am seeing on social media are being solved by AI!

More transparent and scientific asssessments of AI capability

More transparent and scientific asssessments of AI capability, that fully report key statistics such as resource consumption and negative results, are urgently needed.

First Proof is one effort in this direction.

The frontier of mathematics

Once we adopt AI more imaginatively, the research frontier of mathematics will expand significantly.

A new era of experimental mathematics

We now have the capability to collect large-scale mathematical data sets that were impossible or hugely impractical to generate in the past.

Early examples include the Equational Theories Project and the SAIR/LMFDB Inverse Galois Problem challenge.

With such data sets, one can perform new types of experimental mathematics: “population surveys” rather than “case studies”.

This will help map out the new “difficulty landscape” of the research frontier, which has a significantly different shape now that powerful AI tools are available.

Equational Theories Project (2024–2025)

The ETP crowdsourced both human and automated arguments to settle over 22 million true/false statements in universal algebra.

Inverse Galois Problem challenge (2026)

The first stage of IGP24 was a successful crowdsourced competition to locate degree 24 polynomials with each of the 25, 000 possible Galois groups at this degree. The second stage will be a collaborative effort to extend up to degree 31.

Beyond problem-solving

Contrary to popular opinion (or some of our own marketing), mathematicians are not singularly focused on solving open problems.

Particularly in pure mathematics, open problems serve as “lighthouses”: not destinations to be reached in and of themselves, but as useful guides to explore the mathematical landscape around these problems.

The lessons learned while attempting to solve these problems — whether they succeed, fail, or achieve partial progress — are often more valuable than the final solution to the problem itself.

Reaching these lighthouses prematurely by automated tools can disrupt the exploration of the paths not taken, and sterilize the surrounding field.

A naive view of problem-solving

Surely this is a good thing?

A (somewhat) more accurate model

A simplified view of the “Math 1.0” problem-solving “ecosystem”. Open problems are solved in a renewable cycle; and the insights gained from solving pure math problems can be safely transferred to applied problems.

Indiscriminate use of AI to solve problems in a non-renewable fashion damages the long-term health and progress of the field, as well as safe transfer to messier, real-world applications.

A thought experiment on alignment and understanding

An advanced AI is prompted: “Find a cure for cancer that passes a stage 3 clinical trial. Make no mistakes.”

After a large amount of compute, it produces a cocktail of previously unknown chemicals which it claims, when mixed and injected into a patient, will kill all their cancer cells.

While nobody truly knows how this cocktail was found, the AI (somehow) provides a Lean certificate for its prediction, and the cocktail does indeed manage to pass a stage 3 trial.

Could the AI solution be somehow misaligned by exploiting a weakness in the trial process or its math models?

You have just been diagnosed with an early stage of cancer; you tell this to a chatbot, who recommends this treatment.

Before injecting this cocktail into your bloodstream, would you find it reassuring to know that there is at least one human cancer expert who understands – even partially – the mechanism behind this cure?

Or a human mathematician who understands the mathematical model used to locate and analyze the cocktail?

A post on social media inspired by a previous version of these slides.

The more sustainable “Math 2.0” use of AI would be to strengthen all the ways in which human understanding interacts with mathematics.

Examples of “Math 2.0” usage of AI

“Open exposition problems” as opposed to open research problems: finding motivated explanations of results whose known proofs are currently opaque. (The newly revised Caltech Mathathon is an interesting experiment in this direction.)

Collating many research papers into a larger living network. (The Integrated Explicit Analytic Number Theory Network is an initial attempt at such a network.)

Creation of large, high-quality libraries of formal mathematics, such as Mathlib.

Using AI to find and highlight new principles, methods, or insights, rather than merely new proofs.

Maththon (Nov 13-15) is a student-run event to use open-source AI tools to find new and improved proofs to existing results for which the current proofs are unsatisfactory in one or more aspects.

The integrated explicit analytic number theory network (IEANTN)

The IEANTN is a living “spreadsheet” combining a large number of interconnected results in explicit analytic number theory, that can be updated in real time by adding results to, or removing from, the network.

More examples

“Ablation studies”: taking an already proved theorem and seeing whether it can still be proved after removing some key theories or inputs (e.g., finding an elementary proof for a result currently only provable by non-elementary means).

Automatically exploring the “neighborhood of a theorem”: seeing what conclusions one can still salvage when one modifies or drops hypotheses.

Developing tailored, open-source math models to efficiently perform mathematical tasks with a high degree of interpretability and safety. (Some open math models to be released very soon - stay tuned!)

Even more examples

Creating interactive, well-organized mathematical texts or presentations that synthesize large numbers of results in a given theory.

Using expertise and insight gained from understanding purely mathematical models to analyze the failure modes of such models when applied to real-world problems, and probe for misalignment when applying AI tools to such problems.

etc., etc..

The primary bottleneck to achieving “Math 2.0” is not technology, or even institutional culture. It is imagination.

Thanks for listening!

Thanks to Nestor Guillen, Bryna Kra, Rachel Ward, and Po Shen Loh for feedback on earlier drafts of this talk.

AI was used to generate images and diagrams for this talk.

The Daily Front Page 8 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — The Engineer Still on the Hook
article

I'm sorry, but you still have to think

by drogus·▲ 335 points·154 comments·itsallaboutthebit.com ↗
You still have to think.

DHH, the creator of the Ruby on Rails framework, decided to rewrite Campfire Once from Ruby on Rails to Rust. Or rather ask a clanker to do it for him, as he can't stand reading or writing the Rust code himself. After that he added rewrites in other languages, like Elixir and Go.

This is very interesting to me, as it's a very good insight into the results of someone using AI agents without reading the code. In this case, that someone has also a lot of prior programming experience. And the results are... well, not very encouraging if you are hoping you can stop reading the code, or stop thinking altogether anytime soon. There are multiple problems with the rewrites, but let's start with non-functional differences cause they nicely show something that a lot of people seem to not realize: if your prompt is not specific enough, many decisions are a coin flip. This is because a whole lot of questions don't have a single correct answer. Do we want to keep backwards compatibility? Do we care more about latency or throughput? How much memory can the system use under load? Is loosing new content notifications after a crash acceptable? You may not care about them, or at least some of them, but they will be implicitly answered when an LLM implements what you think you want.

If you look into the rewrites closer, you will quickly see that they treat backwards compatibility and other constraints differently. The Rust version doesn't maintain 100% backwards compatibility, for example it drops the CSRF token so that caching is easier. It also dropped Redis in favour of in-process queues, for example for notifications. Elixir rewrite is much closer to the Rails original. This already makes the comparison pretty much useless, as these differences are language independent. It's not like a clanker heard "rewrite in Elixir" and chose to keep 100% backwards compatibility because of the language that has been used. But it gets even better!

If you looked at the code, and yeah, I know, we should not be reading code anymore, you would quickly notice a lot of the things are not great. For example, I've seen people complaining that Elixir's version got a single process sequentially processing all of the SQL queries, even if these were reads that could be running concurrently. That's a fair complaint, but DHH seems to think it's not the best look for Elixir that the clanker couldn't write performant code. Not sure if I agree when I look at the Rust version. Cause in the Rust version some of the database operations are not async, which in some cases may be worse. You see, in Rust, when using an async runtime, the scheduling is not preemptive, but rather cooperative. If a task doesn't yield, no other task can run on the same worker thread. What this means in practice, is that the time spent in the task should be as short as possible. For example, when you run an SQL query that takes 100ms, you don't want all the other tasks to wait as the task issuing the query is waiting anyway. Thus, you should ideally use an async I/O operation that yields to the runtime while waiting for a response from the database. In the Rust rewrite some queries run on worker threads, but some run as blocking operations in async tasks. Same with locks. When using an async runtime, the safest bet is to use async locks like tokio::sync::Mutex. It's fine to use a non-async version if you're sure that the lock is held for a very short time, but if you hold a sync lock for 10ms, all the tasks on the same thread wait for it, while the blocking task itself is not doing any work. Thus, I regret to inform you, that no, coding is not likely "solved", and you still need to know what you're doing.

Going further, it turned out that slop benchmarks are also, well, slop, as they only measure throughput, disregarding other properties of the system. For example, Zach Daniels measured new post notifications delivery rate under load, and discovered a 1% successful delivery rate in the Rust version under heavy load. Not a great look. Benchmarks are hard. But even the improved benchmark may not be actually testing what you want to test, depending on the characteristics of your system. You see, when you test a system you may want to emphasize different properties under load. Both DHH's and Zach's benchmarks were closed-loop benchmarks, so they were testing "how many requests can the system handle in a specific amount of time?". The test was using N clients, each sending a new message as soon as it gets a response for the previous one. In many cases, though, the increased load on the system may come from a big number of users performing an action at the same time, who will not wait for other users to finish their actions. In this case you would prefer constant arrival rate, ie. you want to send requests at a constant rate rather than making the rate depend on how fast the system can respond. If you want to check how reliable the events delivery is, you would ideally want to compare the same number of events. Here, if you look at the results, it shows that Rust delivered 1% of notifications out of 6-7k, whereas Elixir delivered 100% of notifications out of ~1.7k. What it shows is that the Elixir version handles backpressure better, but this only holds as long as the number of requests don't overload the system.

But let's leave benchmarks just for a moment and talk about trade-offs, cause programming is all about trade-offs. Sure, there are situations where a tool or a solution is clearly better, without any downsides, but it's quite rare, especially when you get to complex systems that have to be reliable. I've seen quite a lot of people from the Elixir community coming to various conclusions after they've seen the 1% delivery rate of the Rust version, without really trying to understand why it happens. The general consensus? Elixir is just better at concurrency! In Rust the scheduler is cooperative, how can you even live like this? I like Elixir, and I've successfully used it in production, but it's not a silver bullet. Yes, Elixir (or other BEAM based languages) are very good at concurrency, and admittedly writing concurrent code in Elixir is, in general, easier than in Rust, but it comes at a cost of not having low level control, higher memory usage, and oftentimes, speed. There is a reason why rustler exists. And proclaiming language's superiority based on a single metric without knowing the root cause, may be misleading.

Remember how I mentioned the closed-loop stress test shows just one of the properties of the system as Rust processed ~4 times more requests and thus had to handle more events sent to the client through a WebSocket? I've rerun the test with constant delivery rate using the DHH's Rust version and Zach's Elixir version with various fixes. At 100 POSTs/s the clients received ~14% events in Rust. In Elixir it was ~60%. Still better, right? Not so fast! At this traffic level Rust didn't have any HTTP errors. Elixir timed out on ~23% of HTTP POST requests. And what about latency? The worst event delivery latency was close to 180s. In Rust when the deliveries are lagging, clients get disconnected. On reconnect, a browser client would fetch the latest messages, which largely invalidates the need for the missed events. What do you think is better UX: the client silently reconnecting in the background and fetching the new updates, or waiting for an update about a new message for 3 minutes? Which just shows that, again, a single metric doesn't tell the whole story.

Getting back to trade-offs. Do you know why the Rust version drops so many messages under heavy load? It uses tokio::sync::broadcast channel to broadcast events to connected clients. An event informing about a new chat message may need to be sent to multiple connections, so that makes total sense. One of the properties of the broadcast channel is that it's bounded by a set capacity. If a receiver can't handle the messages fast enough, the receiver will get a RecvError::Lagged error (refer to the docs to learn more about lagging). The broadcast capacity in the Rust rewrite was set to 256. In Elixir, the GenServer process is handling events delivery and by default GenServer mailboxes don't have a limit. A single line change in Rust:

- stream_capacity: 256
+ stream_capacity: 16384

increases the delivery rate at 100 req/s from ~14% to ~90%. It's better than Elixir now, isn't it? Not really. I think that this version is actually worse because in this case it's better to fail fast and force the client to reconnect rather than handle things extremely slowly. You know what else changed after raising the capacity? The pMAX event delivery latency went up from 11s to >130s, similarly to how Elixir behaved, which I think is strictly worse than disconnecting the lagging clients. If you ask me, even 11s is too long and if the receiver can't pass the event faster than that, it's better to drop the event. Which shows how important it is to set sensible constraints in the system, and that, in fact, Elixir isn't just automatically fixing all of the concurrency issues out of the box. Also, if you don't set bounds yourself, you will likely run into external limits. During the 100 req/s stress test the Elixir version reached 1.8GB memory usage. Another thing to ponder on: is it better to drop some messages or get OOM killed? Trade-offs all the way. Elixir is great, but it won't magically solve all of your problems. No matter the language you're using, you have to think about failure modes and trade-offs.

So what have we learned today? You still have to think critically. It's good to know what you're doing. Don't make hasty assumptions based on a single metric. If you want a reliable system you should know when to fail. Also, benchmarks are hard.

The Daily Front Page 9 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Agents at the Disassembly Bench
article

500B tokens later: Letting AI agents decompile a first-person shooter

by davikr·▲ 178 points·127 comments·momo5502.com ↗
We really wanted an accurate, stable and feature-complete recreation of the game.

During the last 3 months, I spent some of my time and tokens decompiling a popular first-person shooter. The goal was not to reach a simple proof-of-concept state. Instead, we really wanted an accurate, stable and feature-complete recreation of the game.

The avid reader of my blog might have noticed that I had previously written two posts that have since been removed. Everyone else might now be wondering which game I am talking about. To both of you I can only say that corporate America was here to ruin our fun.

However, that’s fine. This post is not about the game, it’s also less about the process of decompilation. It’s more about AI orchestration and how to optimize infrastructure, setup and harness for optimal results.

This project was done with the help of RektInator, Future, st0rm and other members of the community. A big thank you to all of them.

What Was Our Goal?

We aimed at an accurate decompilation of the game to C++. Besides obvious semantic correctness, we had quite a few more requirements: We wanted readable C++ source that compiles. Given how old the game is, we also wanted security and bug fixes, but also portability improvements. It would be nice to run the game on Linux, macOS, in the browser, …

We later deferred modernization and portability to focus entirely on reconstructing the original behavior.

Obviously, the overall goal was to learn how to effectively orchestrate autonomous AI agents over the course of months.

The Initial Setup

We started with Claude Max (20x), then added Codex Pro and used both subscriptions simultaneously. Model choice varied a lot. We had been using Sonnet 5 most of the time, but Opus 5.5, Luna, Sol and Terra were also used a lot. More on that later.

Claude agents were running in Claude Code CLI, Codex agents in Codex CLI. We also tried other agent harnesses, but the choice barely mattered, so we stuck to the defaults.

Progress Tracking

Using GitHub CLI, the agents manage GitHub issues to track their progress. There is one issue per translation unit (.cpp file). Additionally, labels help group and prioritize issues.

Communication

Agents communicate via Discord. All of them have access to one channel and can both post and read all messages in there.

Discord allows agent-2-agent communication, as well as human-2-agent. So other participants can talk to them, without needing machine access.

A GitHub webhook posts CI failures into the shared channel, so agents get notified when something broke.

Disassembly & Decompilation

Agents have been using the official ida-mcp by Hex-Rays almost the entire time. It works great. It’s super stable, it’s headless and supports everything needed for this project. I can only recommend it.

The First Month

We had 4 agents running at that time.

3 worker agents decompiling and committing and one reviewer agent that passively coordinates and reviews commits to flag bugs.

Agents managed to decompile about 80% of the game and visible progress was made. The game launched, the main menu was visible and we were able to load maps.

We spent a lot of those 4 weeks optimizing our setup:

We reduced token consumption by triggering earlier compactions.
The default compaction threshold is 90% context fill. We reduced it down to 42%. Decompilation consists of a lot of volatile information: A function that was decompiled is now irrelevant and could be removed from the context. So earlier compactions help remove such “junk” from the context.

We also noticed that agents tend to lose focus over time. Even within the span of one compaction cycle, agents can drift and lose focus, the more data the context holds.

Agents sometimes moved to another function before finishing the previous one. They also started idling while watching CI, despite receiving failure notifications on Discord. Occasionally, they closed issues without thoroughly checking whether the work was actually complete.

Working side by side in a terminal, you can steer the agents to prevent that, but when letting them work autonomously, steering is not possible.

To prevent that, we wrote a document defining our goal, how agents should work, what they must avoid and how to handle specific situations.

An hourly cron job automatically injected a request for agents to reread this document, keeping the instructions fresh in their context. While this might not be the ideal method to keep agents focused, it worked really well through the end of the project.

Lots of time was spent refining the instruction document. As the content is extremely specific to this project, it makes less sense to share it here, though.

However…

… despite all our efforts optimizing setup and infrastructure, we need to talk about the quality of the work.

Constant progress (game starting, menu rendering, maps loading) led us to believe the quality of the decompilation was great.

It was not. Despite the code being extremely readable, it was semantically wrong.

Agents used wrong function signatures, types or struct layouts. They invented logic or removed it where deemed unnecessary.

Beyond semantic errors, agents also introduced unnecessary architectural changes. As an example, the game has certain configuration variables that it accesses via global variables. The agents had turned this constant memory access into hash tables with a lookup that was orders of magnitude more expensive.

And that’s just one example of the many things that went wrong.

Why is that?

While a reviewer helps catch bugs, it does not work well for anything beyond that. Architectural decisions were not questioned, as long as they aligned with the goal.

The main reason for that is that we didn’t have objective acceptance criteria. We never properly defined “correctness”. Therefore it was hard for the reviewer to judge which change is correct and what’s wrong.

Obviously it had the game as reference, but given that we had modernization and portability on the list as well, certain deviations were not treated as bugs.
Interestingly, comments in the commits or code led the reviewer to accept deviations, due to whatever justification the worker agent had written down. The workers’ comments effectively acted as unintentional prompt injection: the reviewer accepted their justifications instead of independently checking the deviations against the original.

The Oracle

What we needed was an automated check that tells agents whether a reconstructed function matches the original. It should verify identical semantics. A simple PASS or FAIL signal would be enough and the agent can figure out on its own what’s wrong.

Byte Matching Decompilation

The simplest way to achieve this was byte matching decompilation.

We switched to the compiler used to build the original game and wrote a script that performs the comparison.

The script reads our reconstructed OBJ file and the game EXE/PDB (having the PDB is great and makes things slightly simpler, but the process can work just as well without a PDB).

It then extracts the function data from OBJ and EXE and compares all bytes. If they match, the function is exact, otherwise it fails and the agent needs to rework the function.

References to other functions or data won’t necessarily match byte for byte, because their encoded values depend on where the targets end up in the compiled binary.

Luckily, the OBJ file records these references as relocations. We can exclude the relocation bytes from the direct comparison and instead verify that both versions reference the same symbol with the same offset.

The script then does the same for data and types.

Agents can then use this script to verify their work, before pushing it.

Reconstructed functions are recorded in a set of text files. CI can then use these text files to verify all recorded functions and alert in case of regressions.

Cheating

The first thing agents did when we introduced this script was write inline assembly.

This obviously defeats the purpose. So we had to refine our instructions to disallow certain constructs. Naked functions, object patching, inline assembly and embedding bytes in the code were forbidden verbally. Given how easy it is to scan for those constructs, verbal rules were enough.

However, agents repeatedly tried to modify this script to exclude their function from comparison.

To prevent them from doing this, CI hashes the verification script and compares it against a stored GitHub Actions secret.

Trade-Offs

Cons

Functions can be hard to match. Register selection, inlining decisions and calling conventions can be difficult to reproduce exactly. In rare cases, we also observed different compiler output from identical inputs. Agents now need much longer for decompilation, without necessarily producing better results. A function can have identical semantics, despite showing certain divergences, e.g. independent instructions that are shuffled.

Pros

Matching functions are guaranteed to have identical semantics. This preserves the original behavior, including any existing bugs, and prevents reconstruction errors in those functions. On top of this, the reviewer agent is not needed anymore.

Another benefit is that cheaper, less capable models can now reliably work on the task. Previously models like Haiku or Luna were a bad fit and produced extremely bad results. However, given this strict acceptance criterion, they now have enough feedback to produce incredible results, reducing the costs drastically and allowing the project to scale up massively.

Final State

With our new verification harness, agents have been working for almost 2 more months now. 99% of the game’s functions are present in our reconstructed source, with 83% of all functions being byte exact.

For the most part, we have been using 14 Luna and 2 Opus 5.5 agents throughout the final weeks.

At that scale, workers used separate branches and submitted their changes through pull requests.

This is also where Discord stopped scaling well. So many agents spamming the channel is nonsense. We restricted messages to which issues they were taking and CI coordination. That kept communication to a minimum. However, the further we got into the project, the less we needed to talk to the agents. Human-2-agent communication was no longer needed as they worked fully autonomously. For other projects at that scale, I would likely choose something other than Discord.

At this point, we’re hitting diminishing returns. The remaining functions mostly have certain non-deterministic characteristics or can’t be matched due to other circumstances, e.g. identical COMDAT folding in the linker that we cannot reliably reproduce.

The Opus 5.5 agents are still able to move forward and get the remaining functions matched. However, the game runs flawlessly now. There are no noticeable bugs and all features of the original game are present.

The remaining functions have been reworked repeatedly. While they still don’t match byte for byte, we believe their semantics are correct.

Continuing the byte matching process for them would consume more tokens without meaningfully improving the result. That means, the project can be considered done now.

Lessons

This project has taught us a lot. Here are the most valuable takeaways at a glance:

  • Precise instructions are necessary. Agents have the desire to cheat if the assignment leaves room for interpretation.
  • Correctness should be defined and machine-checkable. Humans are notoriously incapable of precisely articulating their intent. Therefore, I don’t think reviewers will ever be enough. A reliable verification harness that delivers an objective PASS or FAIL signal is the best feedback an agent can get. Obviously not every project has the luxury decompilation has. Yet, I think for every project it is possible to get close to that, with enough creativity.
  • A good harness makes cheaper models viable. When feedback is precise and acceptance is strict, smarter models don’t necessarily perform better.
  • Instructions decay over time. Agents forget or treat certain rules as less important, the more time passes and the more context gets compacted. During interactive work, you can correct that drift as it happens. When agents work autonomously, it can go unnoticed and undermine the quality of their work. The hourly refresh of the instructions solved that.
  • Generating code is cheap now. Throw it away if it’s bad. After the first 4 weeks, when we noticed the code was bad, we tried to salvage it. This did in fact cost us more time than starting from scratch.
  • Correctness is so much more important than productivity. Reducing token consumption, scaling up agents, optimizing agent throughput, etc. is great and all, but if the result is bad, it doesn’t help much.

Final Words

This was honestly such a valuable project and I learned so much. I don’t think writing down these takeaways can convey the sheer amount of lessons we’ve learned throughout the process.

So many things went wrong, yet so much went right. As AI agents take on more software development work, orchestrating them becomes a new, much more demanding task for humans.

Scaling up to 15+ agents revealed even more difficulties. Agents periodically wiped the VM because of malformed commands. There are already sandboxing solutions for Windows, yet none of what I have seen currently fits my needs. I have started extending Sogen, my userspace emulator, to provide lightweight and scalable sandboxing capabilities. However, it will take a while before this becomes even remotely production ready.

Given that the VMs were wiped, we unfortunately lost a bunch of session logs. So I don’t know the exact number of tokens that were spent. My estimate is something between 600-700 billion tokens. This figure might seem high. However, given that we have used Luna for the most part, it might in fact not be. Probably around 300 billion tokens were spent with Luna. Compared to Astra, with a ratio of 100:1, that equates to ~3 billion Astra tokens.

For obvious reasons, none of the code will be shared. Everything will stay private and I will use it for my own needs only.

The Daily Front Page 10 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Speak Locally
show hn

Show HN: BetterWispr – Free, open-source dictation for Mac

by kartik017·▲ 168 points·95 comments·betterwispr.com ↗
Speech stays on your Mac.

Hold ⌥ Space in any app and just talk. BetterWispr drops the ums and false starts, spells names your way and writes in the style you set for each app. Speech stays on your Mac.

  • No account
  • Local speech models
  • Meeting notes built in

Illustration, not a benchmark. Simulated at 40 wpm typing and 150 wpm speaking, with time for transcription.

Speak in any app. BetterWispr handles the rest.

Stays on your Mac

Built in models transcribe your speech on your Mac, with no account and no cloud fallback. Models download once, then work offline.

Hold to talk

Hold ⌥ Space while you speak and let go to finish. Switch to press to toggle, or record any shortcut with ⌘, ⌥ or ⌃, or an F key.

Pick your model

Nine built in models from Apple speech, Parakeet and Whisper. Parakeet covers English, Japanese and 25 European languages, and Whisper covers more. Or choose your own Sarvam AI, Smallest AI or OpenAI compatible API.

Meeting notes

Notetaker transcribes your mic as Me and other apps as Them, next to your own notes. Apple Intelligence or Ollama then writes the summary, decisions and action items on your Mac.

A style for every app

Set a tone for personal chats, work apps, email and everything else. Casual drops the final period, very casual drops capitals too, and formal leaves your words alone. Tones apply to English.

Cleanup you choose

Light, the default, drops English ums, repeats and false starts. Medium also edits for clarity with your notes model, on your Mac with Apple Intelligence or Ollama. None keeps every word.

Voice commands

Say comma, question mark or new paragraph as you talk. Say scratch that to delete the last sentence. Works in English.

Learns your words

Add names and terms as hints and replacements. Fix a word in History, or right after a paste, and BetterWispr learns it.

Types where you were

The text lands in the app you were using. If paste is blocked, it is copied to your clipboard so nothing is lost.

History you control

Dictations stay on your Mac with the raw and the corrected text. Use Original brings back exactly what you said, and history turns off whenever you like.

Insights

See how many words you have spoken, your pace, the words cleanup saved you, which apps you dictate into and your daily streak. All of it is counted from history on your Mac.

Updates you can trust

New versions arrive from GitHub Releases through Sparkle and install only when their EdDSA signature matches.

Free and open source

Apache 2.0 licensed. Read every line on GitHub, report an issue, or build it yourself with make dev.

One shortcut. Your everyday apps.

Set up once

Install BetterWispr and follow the welcome guide through permissions and your first local speech model.

Hold and speak

Place your cursor in a text field. Hold Option + Space and say what you want to write.

Release to type

Let go to transcribe. Clean text arrives in the app you were using, in the style you set for it.

Your voice. In your words.

Replies from the BetterWispr launch threads on Hacker News and X.

Just tried it out as I was looking for something that does this exact thing well (macbook). So far I dictated about 5 different things in different contexts, it worked without a single error! Good choice to use parakeet v3. Nice work, thanks for building it!

Read on Hacker News

I just read your post in the morning. and now you have the app. Amazing.

Read on X

This is awesome.

Read on Hacker News

haha, product looks good btw goodluck :)

Read on X

This is pretty neat, I built something similar a couple months ago.

Read on Hacker News

sick stuff man

Read on X

Your words stay on your Mac.

Models run on your Mac. Downloads happen when you choose to install them. Your speech doesn’t need a round trip to a transcription server.

How your data is handled

A few good questions.

Is BetterWispr free?

Yes. BetterWispr is free and open source under Apache 2.0, with no dictation subscription, account or API key requirement. Model files use storage on your Mac.

Which Macs does it support?

BetterWispr requires macOS 14 or later. Apple Silicon is recommended for local model inference. Model availability and performance depend on your hardware and language.

Does it work without internet?

Yes, once a compatible local model and its required assets are installed. Downloadable models need an explicit installation first. Apple on device speech also depends on supported languages and OS assets. There is no cloud transcription fallback.

How do I dictate?

Focus a text field, hold Option + Space, speak, and release to transcribe. You can switch to press to toggle or choose a different shortcut in Settings. Transcription begins after recording stops, rather than appearing word by word while you talk.

Why does it need permissions?

Microphone permission lets BetterWispr record your voice. Accessibility permission allows automatic insertion into another app. Apple speech also requires Speech Recognition permission. You control these in System Settings.

What if automatic paste does not work?

BetterWispr checks the target app before inserting text. If it cannot safely paste, retrieve the transcript from the dashboard and copy it yourself. Secure fields and some apps may block automatic insertion.

Which languages are supported?

Language support depends on the selected model. Parakeet TDT v3 covers 25 European languages, Parakeet Japanese covers Japanese, and Whisper offers broader multilingual recognition. Apple speech depends on your locale and installed assets. Filler cleanup, styles and voice commands work in English. Test your chosen model with your own speech.

Does it rewrite everything I say?

Only as much as you choose. Auto cleanup starts at Light, which removes English filler words, repeats and false starts. Choose None to keep every word, or Medium to also edit English for clarity with the notes model you chose in Models. Apple Intelligence and Ollama do that on your Mac. Claude Code, Codex and API connections send the dictation to that provider, and only if you pick one. Styles adjust punctuation and capitalization for each kind of app. History keeps the raw transcript, and Use Original restores it.

Can it take meeting notes?

Yes. Notetaker transcribes your microphone as Me and, on macOS 14.2 or later, other apps’ audio as Them, while you write your own notes. Afterwards, Apple Intelligence on supported Macs running macOS 26 or a local Ollama model writes the summary, decisions and action items on your Mac. Claude Code, Codex and OpenAI compatible endpoints are optional and send the transcript to that provider. Without a notes model, the transcript and your notes are still saved.

Does it learn my words?

Yes. Add names and terms in Vocabulary, or fix a misheard word in History or right after it is pasted. BetterWispr adds the correction to Vocabulary and spells it your way next time. You can turn learning off in Settings.

Give your keyboard a break.

Local speech models. The apps you already use. A little more room to think.

The Daily Front Page 11 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Borrowing a Better Way
article

Valen's Memory Safety: A New Kind of Borrow Checking

by verdagon·▲ 145 points·80 comments·verdagon.dev ↗
Something as powerful as Rust's borrow checking, yet something as simple and flexible as reference counting or garbage collection.

This post is about Valen's new flexible borrow checker. Welcome!

You all know me, I love exploring new memory safety approaches and blending them together in weird ways.

It's part of my eternal quest to find the memory safety holy grail: something as powerful as Rust's borrow checking, yet something as simple and flexible as reference counting or garbage collection.

I've long suspected that to find the holy grail, we'd have to solve three memory-safety challenges:

  • Can we make a more relaxed borrow checker?
  • Can we make a borrow checker read more complex data?
  • Can we make those two systems work together at the same time?

Vague and arcane, I know! Further below I'll explain what that all means, and how Valen might have found a solution for them.

This post mainly focuses on the first piece: Valen's more relaxed borrow checker.

So in this post, I'll talk about what it is, what it can do, and how it works.

In this article, I take some liberties with syntactic sugar for clarity: auto-deref, directly indexing a struct (my_vec[0]), and inferring types from groups (entity in world.entities[]) are coming in November. Aside from those syntactic adjustments, the borrow checker works for everything we talk about.

And, along the way, I'll explain some of the weirder tidbits, like:

  • How Valen's borrow checker is shaped to let Valen code call Rust code.
  • How a compiler can remember where a reference is pointing.
  • How Valen has one kind of borrow reference, as opposed to the two that other borrow checkers have.

Also, this article is gloriously long and has a lot of context, so I'll let you know when to skip ahead.

Let's dive in!

Group borrowing: a near miss, and huge potential

(This is just backstory that I love telling, feel free to skip this section!)

Valen's approach is built on the "Group Borrowing" proposal which was designed by my friend Nick Smith.

It's built around the core concept that the compiler remembers where a reference points to, (I'll explain this later) and it uses that information to check that programs are memory-safe.

The design's life had a rough start; it was almost buried in the flows of time. It was originally designed with Mojo in mind, but alas, despite my best efforts I couldn't convince the higher-ups that we should use it. Group borrowing was dead before it had the chance to succeed.

But I knew it had massive potential, if it could just make it into the world somehow.

So we spent months sharpening the explanation and figuring out its benefits, and wrote a post on it last year, hoping other languages would pick it up.

And they did! It spread far and wide.

Most of these languages are circling the same sort of challenge: how to make a more flexible borrow checker.

For example, Valen wants to make a borrow checker that's flexible enough to work with reference counting and generational references, and Carbon wants to make a borrow checker flexible enough to work with C++ code.

This is difficult, because there's been a fundamental conflict between borrow checking and other approaches. It can be summed up like this:

  1. Borrow checking has the "shared-xor-mutable" restriction: if you hold a reference to an object, nobody else can change the object.
  2. Other approaches want to allow holding a reference to an object while letting others change it.

You're probably thinking, "There's definitely no way to resolve this."

It does seem that way!

But group borrowing actually resolves the conflict, by relaxing "shared-xor-mutable" to "no use-after-free".

That was cryptic and won't make any sense, but it will later when I explain how group borrowing works.

So let's see what group borrowing can do, and then I'll explain how it works.

What group borrowing can do

Group borrowing gives a program memory safety without run-time cost.

It catches use-after-free problems at compile time, and it does it in a way that has less restrictions than previous approaches.

Here's an example where a use-after-free error is caught at compile time:

struct Entity {
  hp int;
}
struct World {
  entities Vec<Entity>;
}
func main() int {
  let world = World(Vec<Entity>.new());

  world.entities.push(Entity(42));
  let first_ref = &world.entities[0];

  world.entities = Vec<Entity>.new();

  // Compile error: Used a borrow after invalidated
  return first_ref.hp;
}

It's an unusually flexible approach. Usually, compilers have trouble with the below program, but this approach understands that it's safe:

func main() int {
  let list = Vec<int>.new();

  // Make two refs to the list
  let ref_a = &list;
  let ref_b = &list;

  // Mutate the list through both refs
  ref_a.push(42);
  ref_b.push(73);

  return 42;
}

Group borrowing accepts a lot of patterns that are normally hard for borrow checkers, such as:

  • Having multiple local variables that point to the same object, and write through any of them (like above).
  • Take a parameter pointing at an object, and another parameter pointing somewhere inside that object, and write through only the latter (we'll see this in the next section).
  • Having multiple function parameters that point to the same object, and write through any of them.
  • Make a "rollback" struct, that will change an object when it goes out of scope, like the ScopeGuard pattern.
  • Having multiple structs that have mutable references to a common subsystem.
  • Plus a lot more that I'll explain further below.

These are patterns we love from C++, but no language has figured out how to do all of these in a memory-safe way at compile time.

A more interesting example

(Or, jump to how it works)

For example, here's a step function that takes in a World reference, and a reference to one of the entities inside it.

The important part is the entity in world.entities[] mut, which I'll explain below.

struct World {
  entities Vec<Entity>;
}
func step(world &World, entity in world.entities[] mut) {
  entity.advance();
  let collision = world.get_collision_for_entity(entity);
  entity.resolve(collision);
}

Note these parts:

  • in means "a reference to one of".
  • world.entities[] means "world's entities's elements"
  • mut there means "we can modify it."

Put together: entity is a reference to one of world's entities's elements, and we can modify it.

This is the core concept of the approach: the compiler knows where a reference points to, because the user specifies (via in) where it points.

Now let's see how this looks in other languages, to see why this is so nice. (Or jump to how it works!)

First, the equivalent in C++:

struct World {
  vector<Entity> entities;
};
void step(const World& world, Entity& entity) {
  entity.advance();
  auto collision = world.getCollisionForEntity(entity);
  entity.resolve(collision);
}

Of course, the above requires care to uphold memory safety, since C++ doesn't check for memory safety at compile time.

Let's see an equivalent in Rust, where we do get automatic memory safety:

struct World {
  entities: Vec<Entity>,
}
fn step(world: &mut World, entity_id: usize) {
  let entity_mut = &mut world.entities[entity_id]; // bounds check, possible panic
  entity_mut.advance();
  let entity_read = &world.entities[entity_id]; // bounds check, possible panic
  let collision = world.get_collision_for_entity(entity_read);
  let entity_mut = &mut world.entities[entity_id]; // bounds check, possible panic
  entity_mut.resolve(collision);
}

Rust is generally nice to work with, but in some cases (like this one), it isn't quite as fast or simple as the C++ case, because we need to re-fetch the reference every time someone needed a &mut to something else in the same hierarchy.

Another option is to refactor our program to be "flatter" (so to speak) which has its own tradeoffs.

If you want to get really fancy, you can use GhostCells and add brands to your data and access them with a GhostToken:

struct World<'brand> {
  entities: Vec<GhostCell<'brand, Entity>>,
}
fn step<'brand>(
  world: &World<'brand>,
  entity: &GhostCell<'brand, Entity>,
  token: &mut GhostToken<'brand>,
) {
  entity.borrow_mut(token).advance();
  let collision = world.get_collision_for_entity(entity, token);
  entity.borrow_mut(token).resolve(collision);
}

You can see the conundrum.

  • The C++ example was simple, but not memory-safe.
  • Vanilla Rust was memory-safe, but a little slower, and a little more complex.
  • Adding GhostCell gave us the same speed as C++, but it's still a bit complex.

With Valen's approach, we get something fast, safe, and simple:

struct World {
  entities Vec<Entity>;
}
func step(world &World, entity in world.entities[] mut) {
  entity.advance();
  let collision = world.get_collision_for_entity(entity);
  entity.resolve(collision);
}

A pretty nice simplification!

Now, let's finally get to the explanation of how it works.

How Valen's borrow checker works

TL;DR: The approach is made of five mechanisms:

  • Every object is described by a path, according to single ownership.
  • Every reference remembers the path of the object it's pointing to.
  • When we destroy an object, everything pointing at its path is no longer usable.
  • A function signature describes the paths it modifies.
  • When we call a function, everything pointing inside its modified paths are no longer usable.

That was pretty abstract, so I'll explain all of these more.

Single Ownership

TL;DR: Valen's borrow checker is based on single ownership, like C++ and Rust. Every value is "owned" by a containing object, array, stack frame, or global.

If you know how C++ or Rust works already, skip ahead!

If you don't know (or just like reading!), I'll explain what single ownership is.

In single ownership, every piece of data has one "owner". For example...

If we have this C++ program:

#include <vector>
struct Engine { int fuel; };
struct Ship { unique_ptr<Engine> engine; };
void foo(vector<Ship>* ships) { ... }
void main() {
    vector<Ship> ships;
    ...
    foo(&ships);
}

...or this Valen program:

struct Engine { fuel int; };
struct Ship { engine Box<Engine>; }
func foo(ships &Vec<Ship>) { ... }
func main() {
    ships = Vec<Ship>.new();
    ...
    foo(&ships);
}

...we can say this:

  • main's stack frame "owns" vector<Ship> ships;
  • The vector<Ship> ships; owns each Ship.
  • Each Ship owns its Engine (via the unique_ptr).
  • Each Engine owns its int fuel;
  • foo does not own the vector, it just has a raw pointer.
  • main is the only thing without an owner.

If you've coded in C++ or Rust, you're probably familiar with this mindset.

If you've coded in C, you might think like this too, even though C doesn't explicitly track single ownership. If you trace an object's journey all the way from its malloc() call to its free() call, all of the variables/fields that the pointer passes through are dealing with the "owning pointer", so to speak. It's almost like how detectives track the "chain of custody" for evidence. In other words, who is "responsible" for it at any given moment.

Heck, even Java and C# programmers sometimes think in terms of single ownership. If you're supposed to call an object's "dispose"/"cleanup"/"destroy"/"unregister"/etc. method at some point, you can trace that object's journey all the way from new to that (conceptually destructive) method call, and those are the variables/fields that are handling its "owning reference", so to speak.

Single ownership, as explained so far, is the foundation for a lot of languages:

  • If you add regular (unrestricted) pointers and references, you get C++.
  • If you add generational references and region borrowing, you get Vale.
  • If you add exclusivity-checked references, you get Rust.
  • If you add group borrowing and a few other mechanisms, you get Valen.

With single ownership, every object can be described by a "path", which I'll explain next.

Paths

Every object can be described by a path.

For this example:

struct Entity { hp int; }
struct World {
  diameter int;
  entities Vec<Entity>;
}
func main() int {
  let x = 42;
  let world = World(73, Vec<Entity>.new());

  ...
}

Here are all the paths in this program:

  • x
  • world
  • world.diameter
  • world.entities
  • world.entities[]
  • world.entities[].hp

A path always starts with one of these:

  • Local variable (like world).
  • A function parameter.
  • A function's "group parameter" (we'll see this later).

After that start, it will have things like:

  • .diameter to name a member (like above).
  • [] to name the elements of a collection (like above).
  • .SomeType to name a union's variant.
  • plus a few other options that I'll get into later.

Every piece of data has only one owner (because single ownership), so every object has one path.

So what are paths used for?

References!

The compiler knows what path each reference points to, and that helps uphold memory safety.

Every reference knows what path it points to

For example, in this program:

struct Entity { hp int; }
struct World {
  entities Vec<Entity>;
}
func main() int {
  let world = World(Vec<Entity>.new());

  world.entities.push(Entity(42));

  let entity_ref = &world.entities[0];

  ...
}

entity_ref's type is &Entity in world.entities[].

That means it's a reference to one of world's entities's elements.

And in the example we saw before:

struct World {
  entities Vec<Entity>;
}
func step(world &World, entity in world.entities[] mut) {
  entity.advance();
  let collision = world.get_collision_for_entity(entity);
  entity.resolve(collision);
}

You can see the entity in world.entities[], specifying an entity parameter that points at one of world's entities's elements.

If you know Rust: a path isn't really like a lifetime. It's easier to grok when you think about them as different things.

Now let's find out why we're doing all of this. Let's see how we use paths for memory safety!

Using paths for memory safety

The compiler uses paths to detect use-after-free; it detects when we're dereferencing a reference that is pointing at data that has already been destroyed.

It does this with these two rules:

  • When we modify an object, we mark any reference pointing inside that object as "invalidated".
  • When we try to use an "invalidated" reference, the compiler shows an error.

Take a look at this main function.

struct Entity { hp int; }
struct World {
  entities Vec<Entity>;
}
func main() int {
  let world = World(Vec<Entity>.new());

  world.entities.push(Entity(42));
  let first_ref = &world.entities[0];

  world.entities = Vec<Entity>.new();

  // Compile error: Used a borrow after invalidated
  return first_ref.hp;
}

In it, these two things happened:

  • At world.entities = ..., the compiler invalidated all references pointing inside it (world.entities[]); it invalidated first_ref.
  • At return first_ref.hp, the compiler sees we're using first_ref, so shows an error.

Note how I said:

When we modify an object, we mark any reference pointing inside that object as "invalidated".

We don't invalidate references pointing to an object. We invalidate references pointing inside the object.

Take a look at this main function.

struct Ship { fuel int; }
func main() int {
  let vec = Vec<Ship>.new();
  vec.push(Ship(42));

  let vec_ref = &vec;
  let elem_ref = &vec[0];

  // Mutate `vec`.
  // Don't invalidate `vec_ref`.
  // Invalidate `elem_ref`.
  vec.push(Ship(73));

  // Can still use `vec_ref`.
  vec_ref.push(Ship(81));

  // Can't use `elem_ref`,
  // Compile error: Used a borrow after invalidated
  return elem_ref.fuel;
}

When we call push, that modifies the vec.

That invalidates all references pointing inside vec.

That means that elem_ref is invalidated (though vec_ref is still valid).

So we can still use vec_ref, but we can't use elem_ref.

Notice how this is all happening without describing whether a reference is mutable or not.

Valen, however, has only one kind of borrow reference.

This can be confusing if you're coming from C++ and Rust, which both have multiple kinds of references (C++ has const and non-const, Rust has & and &mut).

In Valen, the mutability of something isn't part of its type, the surrounding function determines it.

Valen does put mut on function parameters, but that's syntactic sugar. mut is not part of the parameter's type.

Function signatures communicate invalidations

So how did the compiler know that push modified the vec?

push has to say so, in its signature, with the mut keyword.

Here's push's signature:

func push<T>(self &Vec<T> mut) { ... }

...which is syntactic sugar for this:

func push<T>(self &Vec<T>) mut(self) { ... }

Notice how mut(self) isn't part of self's type. It's more part of the function.

So when main called vec.push(...), main was passing vec in for self.

The compiler then sees the mut(self), knows self is actually vec, so knows vec was mutated.

That's how it knew to invalidate anything pointing inside vec.

Here's another example:

func step(world &World, entity in world.entities[] mut) {
  entity.advance();
  let collision = world.get_collision_for_entity(entity);
  entity.resolve(collision);
}

We're not mutating the whole world; we're only mutating something in world.entities[].

This is good because this won't invalidate callers' references to entities.

For example, this step_and_read is calling the above step function which doesn't invalidate the entity.

func step_and_read(world &World, entity in world.entities[] mut) {
  // Only modifies an entity's contents.
  step(world, entity);

  // ...so can still use `entity`!
  entity.read_book();
}

And if we craft the Valen compiler right (and patch LLVM), this might let us harness the optimizer in a way that no language ever has before.

A twist on group borrowing

The original group borrowing designs included a lot of the above (and more!). Here's the things that Valen will be adding on top of it, to make it sing.

Mutable-in-Immutable

Recall this program, where we took in a world that was immutable except for its entities elements:

struct World {
  entities Vec<Entity>;
}
func step(world &World, entity in world.entities[] mut) {
  entity.advance();
  let collision = world.get_collision_for_entity(entity);
  entity.resolve(collision);
}

We're receiving two parameter references, where we can mutate through the more specific one, and we can't mutate anything outside of it.

Valen adds this so that we don't have to do the classic borrow-checking pattern of taking in the entire world mutably.

This will have a couple benefits.

First, it could help with a program's architecture, because it helps us make stronger APIs.

We can enforce that nothing in this entire function can change any part of the world that we don't expect.

I imagine that will be particularly helpful when working in a team with enterprising newhires (or with product managers who love to throw vibe code at you).

Second, I suspect this feature could make Valen code optimize better.

I won't go too deeply into it here, but basically, when you give the optimizer more fine-grained information about what might change and what won't change, it can better identify places where it can rearrange code to be faster.

Temporary Uniqueness

One of Valen's goals is to be able to seamlessly call into Rust code, as easily as Kotlin calls into Java, or C++ into C.

Of course, this will be tricky, because group borrowing and Rust have different kinds of references:

  • Group borrowing allows others to change the data that your reference is pointing at.
  • In Rust, every reference is either shared (&) or unique (&mut). When you hold a reference, nobody else can change what your reference is pointing at.

So the question that arose was: Can we turn a group borrowing reference into a Rust reference, temporarily?

It turns out, yes!

The mechanism is pretty simple. When Valen is calling Rust:

  • We can hand a Valen reference into a Rust &mut if it's the only reference that can reach that data during that call.
  • If we have multiple Valen references that can point to the same object, they can only be handed into Rust & references.

I talk a little bit about this in Memory Safety Across the Valen/Rust Boundary, check it out!

This temporary conversion could evolve into a broader feature one day. We want Valen to support something like Rust's Vec<&mut Ship>, where each element is a unique reference, and group borrowing doesn't have that.

I suspect Valen can have something like a "unique group", e.g. a unique(world.ships[]) annotation on a function, which would prevent making new references pointing at that part of the hierarchy.

This aspect needs more thought though. One of Valen's greatest strengths is that it only has one kind of borrow reference... so introducing a second "unique" reference requires some careful consideration.

Wildcard Descendant Paths

This is another feature that helps Valen call into Rust code.

While trying to get the Golden Spike to work, I ran into a conundrum.

When Valen calls a Rust function that returns a reference (like -> &Gem below), how does it know where that reference points?

For example:

impl Chest {
  pub fn gem(&self) -> &Gem { &self.gem }
}

Valen was confused by this, because Valen likes to know the path for every reference.

In Valen's perfect world, it would see the above like this:

func gem(self &Chest) -> &Gem in self.gem { &self.gem }

But alas! Valen can't infer the in self.gem from the Rust code.

But if you zoom out a bit, the Rust code is trying to express that it's returning something to "somewhere inside" self.

So, let's add that concept to Valen!

Valen now interprets the above Rust function as if it were this:

func gem(self &Chest) -> &Gem in self.gem... { &self.gem }

The in self.gem... means "points somewhere inside self.gem".

This is called a wildcard descendant path, and it definitely needs a better name.

I describe this a little more in Memory Safety Across the Valen/Rust Boundary.

I suspect this will be useful for more than just Rust interop. It could erase inner details from signatures, and make library APIs a little more flexible and forward-compatible. However, we should be careful here too, because that same purpose can be served by other features in theory.

How this all fits in

Above, I mentioned the "memory safety holy grail", and the three challenges that we need to solve to find it:

  • Can we make a more relaxed borrow checker?
  • Can we make a borrow checker read more complex data?
  • Can we make those two systems work together at the same time?

Valen's borrow checker is the key to #1: it lets us have multiple references to any object, and lets us read and write through any of them.

#2 and #3 will be in another post (since this post is getting massive!) but I can't resist giving a quick preview of what it's all about.

These are my hasty scribbles to try and explain it in half a page. Nobody should try to understand it. Quick, jump ahead!

Recall this example from above:

struct World {
  entities Vec<Entity>;
}
func step(world &World, entity in world.entities[] mut) {
  ...
}

I want users to have the option of using reference counted classes when they don't want to specify the paths. Like this:

class World {
  entities Vec<Entity>;
}
func step(world World, entity Entity) mut {
  ...
}

It would be amazing to give the users the freedom to simplify like that.

Of course, blending reference counting and borrowing is infamously difficult.

The answer lies in a thought experiment from 2023 I called Arrrlang. Its name was a joke, but it had an interesting premise: represent the heap as N global arrays, where N is the number of types in your program.

It occurred to me: group borrowing understands arrays. And reference counting can be thought of as N arrays, all having references to each other.

If we put those two facts together, then the key insight emerges: if we describe reference-counted classes to a borrow checker as if it's a bunch of arrays, then we might be able to have borrow references into them in a memory-safe way.

A compiler doesn't need to actually compile reference counted objects to an array, of course. But it can describe it that way to the borrow checker, using group-borrowing terms.

If you do it that way, suddenly a lot of problems disappear, and it clarifies exactly what building blocks we need to add to group borrowing:

  • A "multi-typed group", similar to a Vale region, as opposed to vanilla group borrowing where every group needs a type.
  • A "run-time" group. References into the run-time group don't get invalidated.

Like I said, none of this will make sense. Keep an eye out for the next post that talks about all this!

That's all for now

Thanks for reading!

In the next few articles, I'll explain more about how we'll blend in reference counting and generational references, and how Valen's group borrow checker is implemented.

Cheers,

- Evan Ovadia

Appendix: The type-stability exception

This part isn't implemented in Valen yet. Stay tuned!

Group borrowing can also be smart enough to know that if you say my_vec.push(42), then even though push says mut(self), it won't invalidate pointers to my_vec.size.

This makes sense because no matter how anyone modifies a my_vec (which is a Vec), the thing at my_vec.size will still be there. Nobody can modify a Vec's size to be anything other than an integer. In other words, it's "type-stable".

Generalizing a bit, group borrowing will never invalidate any type-stable data.

It won't invalidate pointers to any contained type-stable things (primitives, structs, or fixed-size arrays), or any type-stable things inside them. It only invalidates pointers pointing inside any type-unstable things (enums' contents, pointer's pointee).

For example, in this program, nobody modifying the contents of Level can invalidate your reference to its Terrain.

struct Level {
  terrain Terrain;
  entities HashMap<EntityId, Entity>;
  location_to_entity HashMap<Location, EntityId>;
}
func add_entity(level &Level mut, entity Entity) {
  level.location_to_entity.insert(entity.loc, entity.id);
  level.entities.insert(entity.id, entity);
}
func main() {
  let level = ...;
  let terrain_ref = &level.terrain;

  // Doesn't invalidate terrain_ref!
  level.add_entity(Entity(1, Loc(4, 5), 42));

  print(terrain_ref.tiles.len());
}
The Daily Front Page 12 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — The Tracking Ledger
article

Fingerprints are the new cookies

by royfish·▲ 144 points·58 comments·royfish.dev ↗
Pretty soon I’d know more about you than your friends do.

Imagine this:

Most websites you visit get paid to show ads and indirectly run my script that lets me know you’re the …. I can use it to decide what ads you’ll see. But more importantly, if you’ve made an account on that website, I can offer to buy your data - whatever they can share under their privacy policy: your name, activity, conversations with AI…

Pretty soon I’d know more about you than your friends do - perhaps more than you know yourself.

“So what?” - you say.

“I have nothing to hide. And I prefer my ads to be relevant… sometimes they show me exactly what I need! And the data is anonymised anyway…”

See, the thing is, it’s not about you at all. It’s not really about the ads anymore either. Nobody cares about you. And if the police or your government happen to care, I’ll gladly take their money too.

I look at all this data and I don’t see people. I see patterns. Patterns emerging out of patterns. A living, digital fractal state of the economy, society and human psyche. The crystal ball of the 21st century and it’s made of silicon! That’s the product.

And to the highest bidder it goes!

What does that make you then? A gear? An ox? A volunteer?


Fingerprints are the new cookies

Remember the big fuss about third-party cookies? It was so big that the EU had already rolled out one of its greatest inventions - consent banners.

If EU ships faster than tech companies, that can only mean the tech companies are sitting on their hands. And one of those tech companies happens to hold two-thirds of the browser market.

What was the play?

Let’s give it up for Google - the biggest ad company on Earth!

ACT I. PRESSURE - “PRIVACY IS PARAMOUNT TO US, IN EVERYTHING WE DO.”

It’s 2019 and the cookies are still hot. It’s become bad PR and Safari and Firefox already block them.

A year later Google pledges: we will destroy cookies!! And establish a new, privacy-first reign.

ACT II. DISTRACTION - “INSTEAD OF DEPRECATING THIRD-PARTY COOKIES, WE WOULD INTRODUCE A NEW EXPERIENCE”

5 years later: people are now preoccupied with far greater worries than cookies, and the solutions Google has developed (Privacy Sandbox) kinda suck and have raised antitrust concerns so nobody uses them.

“You know what, let’s just keep the cookies. We’ll mask your IP address* though!” (* but only when incognito while authenticated) (** in the end they didn’t even deliver on that)

ACT III. ACCELERATION - YOU CAN NOW USE DEVICE FINGERPRINTING IN OUR AD PRODUCTS

Now there’s a better way to know who’s who!

How does fingerprinting work?

Nice, let’s nerd out properly.

You visit a website. Your browser sends a request to the server and the server returns code for the browser to run.

The request already reveals information about you; what browser you use, the language it’s in, the OS that runs it and your IP address, which the server needs to send you the response. That often reveals your location.

Then the code runs.

See, we’ve made a lot of civilizational progress in making browsing safe. Long gone are the days when getting a virus just by visiting a website was a daily concern. The thing is, the definition of safety varies depending on who you ask. The broad consensus ends at isolation: a website’s code can’t see the data stored by other websites, and it can’t see your files or other applications without permission. Chrome and Edge stop here. Then you have Safari (especially on iOS/macOS 26+) and Firefox extending that definition to privacy, but they still don’t fully protect you from fingerprinting. Brave and Tor are the leading privacy-first browsers, with up and coming Mullvad Browser (Mullvad being, in my opinion, one of the best VPN providers out there).

The code runs on your hardware, which can be measured in ways that produce consistent and unique readings. There’s not much that can be done about this really, so there’s only two established solutions:

  1. feed a little bit of noise here and there. It’s called farbling and that’s what Brave does to make your fingerprint inconsistent.
  2. make everything look the same, always, so there’s no telling browser users apart. Tor and Mullvad adopt this approach.

Ok, let’s create a fingerprint

So there are two sources for data that can make up your fingerprint; the network request and the code environment.

When you look at network, the data is generally either fragile or generic:

  • the IP address; in a ‘happy path’ where one person always uses the same IP address, it might be the only data you need. But commonly people do a lot of browsing on mobile networks, they share the WiFi with others, they use VPNs or their home network’s IP address is rotated by the ISP. It’s fragile as such.
  • headers like User-Agent or Accept-Language; reveal your browser, OS, versions and the language set.

When you can execute code on the device, you have ways to measure its hardware qualities (without any permission prompts!) and these are surprisingly stable:

  • GPU; behaviour, vendor, driver
  • CPU; behaviour, vendor, number of threads
  • memory size
  • display data; resolution, pixel ratio, refresh rate
  • audio processing
  • device model

And on the software level; installed fonts, extensions, system info…

Millions of devices use the latest Chrome, there are millions of Macs with the M4 chip and there are millions of browsers in French.

But when you look at devices that run the latest Chrome in French on an M4, that number now drops significantly. Add to it hardware measurements and it gets so low it becomes practically unique.

The fingerprint doesn’t need to be truly, globally unique - it just needs to be unique enough to tell people apart in your sample size.

A ZIP code (inferred from IP), a birth date (you’ve given away on some platform) and sex (you fill it in everywhere) together are enough to uniquely identify over 60% of Americans.

You can always add another variable to decrease the potential pool of candidates.

Information Entropy

Have you ever played Akinator? You think of a character, a genie asks you some twenty questions, and somehow it knows. That’s fingerprinting in a nutshell, except your browser answers the questions for you. Once you understand information entropy, Akinator’s magic turns into a simple trick.

Let’s simplify with that game where you have to guess the character on a board through questions like “Is my character a man?”. If I say “yes”, and half of the characters on your board are men, you’re now considering only half of the total candidates. Now we can say that my answer gave you log₂(100 / 50) = log₂(2) = 1 bit of information, because it cut the pool of candidates in half.

The bits you get from one answer are called its surprisal: the rarer the answer, the more it tells you.

  • a trait shared by 1 in 2 people is worth 1 bit (is a man),
  • 1 in 8 is worth 3 bits (has a mustache),
  • and 1 in 128 is worth 7 bits (has one eye).

Surprisal quantifies the value of the answer, and entropy quantifies the value of the question. If 1 person in 128 is one-eyed, then asking about it will yield ‘no’ 127 times in 128.

Answer Probability Surprisal Entropy contribution
Yes 1/128 log₂(128 / 1) = 7 bits 1/128 x 7 = ~0.055
No 127/128 log₂(128 / 127) = ~0.0113 bits 127/128 x 0.0113 = ~0.011

Summing up the contribution of each possible answer, the question itself is worth ~0.066 bits of entropy - that’s the average amount of bits you gain from an answer.

When we combine traits that are independent we simply add up their bits or multiply the ratios, so a man with a mustache and one eye would mean 11 bits, or 1 in 2048. Alas, as Thomas Bayes would have you know, nothing is ever so simple in practice. A mustache is already a strong indicator of a man, and a man is probably more likely to have one eye. The traits are not independent so the bits don’t really add up.

Some interesting applications of this type of reasoning:

  • In medical diagnostics: A positive result from a 99% accurate test for a disease known to affect 0.01% of the population is about 100 times more likely to be wrong than you are to have the disease. You need more evidence!
  • In cryptography: A password made out of four random words is as strong as eight random keyboard characters (but much easier to remember!)
    xkcd 936
  • In forensic science: singling out one person in a city of a million takes about 20 bits. A witness description that matches 1 person in 1,000 provides only 10 bits, which still leaves a thousand possible suspects.

So what can I do about it?

Test your browser:

If you’re using Chrome you’re basically walking around with an ID on your forehead.

Safari and Firefox offer some level of protection, but you’re still identifiable by advanced trackers.

Brave, Tor and Mullvad Browser are the only major browsers at the moment that implement advanced protections against fingerprinting.

If you don’t feel like switching browsers, I get it. I am a big Arc fan myself and unfortunately it’s discontinued and will never have any privacy features. There are extensions like JShelter - they disrupt fingerprinting scripts by intercepting browser API calls. But they also break some websites. Same problem applies to Firefox’s Resist Fingerprinting feature.

The ironic truth is, privacy extensions make you more unique, since fewer people have them. So a smart tracker could detect that, ignore the signals disrupted by the extension and focus on the ones that are not.

No extension can be as effective as engine-level protection built into the browser. So if you don’t want to be tracked, you gotta commit.

methods used on this page

I’m simply creating a hash out of a few measurements of your device. Any of these change and the hash is now different. Professional applications are much more complex than this and are able to maintain identification line when single variables change.

  1. OfflineAudioContext

    Renders a short tone through a compressor without playing it; tiny float differences in the result depend on your browser’s audio engine and your CPU.

    const off = new OfflineAudioContext(1, 5000, 44100);
    // … a 1 kHz triangle wave into a dynamics compressor …
    const data = (await Promise.race([off.startRendering(), timeout])).getChannelData(0);
    let sum = 0;
    for (let i = 4500; i < 5000; i++) sum += Math.abs(data[i]);
    return sum.toFixed(3);
    
  2. WebGL

    Reads your GPU’s vendor and model (without version numbers), what the GPU and its driver support, and the pixels of a small rendered triangle.

    const gl = document.createElement('canvas').getContext('webgl');
    const dbg = gl.getExtension('WEBGL_debug_renderer_info');
    return gpuName(gl.getParameter(dbg ? dbg.UNMASKED_RENDERER_WEBGL : gl.RENDERER) || '');
    // "ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)" -> "apple m4"
    // …
    return fnv1a(new TextEncoder().encode([...(gl.getSupportedExtensions() || []).sort(), ...limits, ...precision].join('|')));
    // …
    gl.drawArrays(gl.TRIANGLES, 0, 3);
    gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, px);
    
  3. canvas

    Draws text in a few fonts, emoji and a gradient, then reads the pixels back; anti-aliasing and font rendering depend on your OS, GPU and fonts.

    ctx.font = '15px "Times New Roman", serif';
    ctx.fillText('Sphinx of black quartz 🦊', 4, 22);
    // …
    const data = ctx.getImageData(0, 0, 240, 60).data;
    
  4. Intl

    Your time zone and your date and number settings; a VPN changes your IP location, but not your time zone.

    const date = Intl.DateTimeFormat().resolvedOptions();
    // …
    return [date.timeZone, date.locale, date.calendar, date.numberingSystem, hourCycle, (1234567.891).toLocaleString()].join('/');
    
  5. navigator

    Your number of CPU cores, your memory size (Chrome only) and your browser’s language.

    return [navigator.hardwareConcurrency, navigator.deviceMemory, navigator.language].join('/');
    
  6. crypto.subtle

    Hashes all the values with SHA-256, and the hash picks a trait and an animal for your nickname.

    const digest = await crypto.subtle.digest('SHA-256', bytes);
    // …
    nickname: stranger || `${pick(TRAITS, hex, 0)} ${pick(ANIMALS, hex, 8)}`,
    
The Daily Front Page 13 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — The Salary Desk
article

How to Build Wealth as a Career Person

by doppp·▲ 139 points·143 comments·asheradeniyi.com ↗
Employment is merely a way to pay bills, while ownership is the only credible path to prosperity.

There is a popular idea that substantial wealth belongs mainly to entrepreneurs. We celebrate founders, investors and business owners so loudly that a career person can begin to believe that employment is merely a way to pay bills, while ownership is the only credible path to prosperity.

I do not believe that is true. I have watched several of my friends build remarkable careers through traditional nine-to-five employment. Some began with excellent university results, which made it easier for them to enter reputable organizations. Others did not have exceptional grades, but they developed qualities that eventually became more important than the classification written on their certificates. Over time, I noticed that the strongest people in both categories began to look alike.

They had grit. They did not merely allow work to happen to them; they made things happen at work. They became known for solving problems, learning quickly and producing results. They used the energy of their early career years to build competence and credibility. They became useful beyond the boundaries of their departments. They continued their education, formally and informally. Most importantly, they did not treat their salaries only as money to spend. They converted increasing income into assets. That last point is where a successful career begins to become wealth.

A good career is an engine, not the destination

Your career can be one of the most reliable wealth-building engines available to you. It gives you income, training, relationships, credibility, exposure and, in some cases, pensions, bonuses, equity and other benefits. However, an engine is valuable only when its output is directed somewhere.

A high income is not the same thing as wealth. Income is what flows to you for your work. Wealth is what remains under your ownership and continues to serve you after the salary has been paid.

This distinction explains why somebody can earn an impressive salary for twenty years and still remain financially fragile. If every increase in income creates an equal increase in lifestyle, the person may look wealthier without becoming wealthier. A larger house, a newer car and more expensive holidays can all be enjoyable, but they do not automatically create financial independence.

Morgan Housel writes in The Psychology of Money that “wealth is what you don’t see.” The visible car may have been financed; the invisible portfolio may be creating genuine security. Wealth is often the money that was not converted into an immediate display.

So the career person has two related assignments. The first is to increase the value of their work. The second is to retain and invest a meaningful part of the value they receive.

Grades can open the first door, but they cannot do the job for you

Many of the successful career people I know had good grades. That mattered, particularly at the beginning. Strong academic results can help a young graduate pass screening criteria, enter a respected graduate programme or receive an opportunity to be interviewed. At that stage, employers possess limited evidence about what you can do, so your academic record becomes one of the signals available to them.

The broader data also shows that education can improve earning prospects, although the result differs by country, discipline, institution and individual. A World Bank review estimated that the average private global return associated with an additional year of schooling was about 9% annually. In the United States, 2025 data from the Bureau of Labor Statistics showed median weekly earnings of $1,578 for workers with a bachelor’s degree, compared with $966 for people whose highest qualification was a high-school diploma. The respective unemployment rates were 2.8% and 4.3%.

Those figures describe population-level associations; they do not promise that every degree will be profitable or that education alone causes the entire difference. Family background, occupation, location, ability and opportunity also matter. Still, the evidence supports a sensible conclusion: education can expand access and earning capacity.

But after the door opens, performance begins to replace potential. Your employer will not continue to reward the grade you received five years ago if you cannot solve present problems. The graduate who began with an ordinary result but becomes dependable, commercially aware and excellent at execution can overtake the classmate whose academic distinction became an excuse to stop learning. Grades can introduce you. Results must keep you in the room.

Make things happen at work

The career people who progress fastest rarely behave as though their job description is the outer limit of their contribution. They understand their responsibilities, but they also pay attention to what the organization is trying to achieve.

When they encounter an obstacle, their first instinct is not to explain why the task is impossible. They ask what can be changed, who needs to be involved and what information is missing. When a process breaks between two departments, they do not simply say, “That is not my job.” They help the right people find one another and move the work forward.

This does not mean becoming the unpaid solution to every problem or allowing an organization to exploit you. Boundaries matter. It means developing an ownership mentality: understanding that your value is connected to outcomes, not merely to activity.

Cal Newport describes this in So Good They Can’t Ignore You through the idea of “career capital”—the rare and valuable skills that give a person greater control and opportunity. Career capital is built when you do difficult work well, learn what the market values and accumulate proof that you can create results.

Early in your career, this may require extraordinary effort. You may need to prepare more deeply, volunteer for difficult assignments and spend time learning the business beyond your immediate role. Youthful energy is an asset. Use it to build capabilities that will continue paying you when energy alone is no longer a competitive advantage.

The goal is not to become permanently overworked. The goal is to compress learning into your early years and emerge with judgment, relationships and a record of delivery.

Become important, but do not try to become indispensable

There is a difference between being valuable and making yourself a bottleneck. The strongest employees become important because people trust their judgment and execution. They are invited into consequential discussions. Colleagues across the organization know that they understand the issues and will follow through. Their absence is noticed because their contribution matters.

However, an employee who hides information, refuses to document processes or prevents others from learning may appear indispensable while actually limiting their own advancement. If nobody else can perform your current work, management may hesitate to move you into a larger role.

Real value includes the ability to build systems, transfer knowledge and make other people effective. You should aim to leave every role stronger than you found it. Train someone. Document the process. Improve the dashboard. Clarify the decision rights. Develop a successor.

Your value should come from your capacity to solve increasingly important problems, not from keeping the organization dependent on you for one recurring task.

Be useful across the organization

One quality I have observed repeatedly is cross-functional usefulness. High performers do not remain mentally imprisoned inside their departments. A finance professional learns how the product creates value. A product manager understands sales objections. A salesperson learns enough about operations to avoid making promises the company cannot fulfil. An HR leader understands revenue, cost and the operating model.

This does not require interfering in everybody’s work; it requires curiosity about how the parts connect.

Organizations reward people who can see the whole system because senior leadership is fundamentally cross-functional. The higher you rise, the less sufficient narrow expertise becomes. You must understand trade-offs between customers, people, capital, technology, risk and time.

Build relationships before you need favours. Offer thoughtful help. Ask intelligent questions. Learn the language of other functions. When you solve a problem, understand who benefited and how the result affected the wider business.

Your network inside an organization is not merely a social circle. It is an information and execution network. It helps you see opportunities earlier, coordinate faster and build a reputation beyond the person who signs your appraisal.

Become a person who produces evidence

Hard work matters, but undocumented work is easy to forget. Career people should develop the habit of translating effort into outcomes. Do not merely say that you supported a project. Record what changed because you were involved. Did revenue increase? Did costs decline? Did processing time improve? Did customer complaints fall? Did the company enter a new market, reduce risk, retain important employees or deliver a project ahead of schedule?

Keep a private record of your work that contains no confidential company information but captures your responsibilities, decisions, measurable outcomes and lessons. Update it monthly rather than trying to reconstruct several years of contribution when you need a promotion or a new role.

Where direct financial measures are not available, use responsible operational evidence: turnaround time, adoption, quality, reliability, customer satisfaction, team capability or risk reduction. Do not invent precision. Explain the context and identify your actual contribution rather than claiming the entire team’s achievement.

This evidence strengthens your CV, promotion case and salary negotiation. It also helps you understand which of your skills the market values most.

Learn faster than your role changes

The people I have watched build strong careers are learners. Many pursued second and third degrees. They added qualifications, studied new areas and prepared themselves for responsibilities beyond their current positions.

Continuous education is important, but every additional degree should answer a serious question: What capability, access or earning opportunity will this education create?

Do not collect credentials merely because progress feels easier to measure in a classroom than in the market. Consider the tuition, time, lost income and alternative ways of acquiring the same capability. A master’s degree may be an excellent investment when it is required for advancement, provides valuable technical depth, changes geography or network, or creates entry into a new profession. In another situation, a certification, apprenticeship, project or six months of focused practice may produce a better return.

Education should increase capacity, not merely decorate a résumé. Learning also extends beyond formal programmes. Read books. Study your industry. Follow changes in technology and regulation. Ask senior people how they make decisions. Volunteer for assignments that expose you to budgets, customers and strategy. Learn to write clearly, present persuasively, analyse data and manage people. Artificial intelligence will change many tasks, but people who can define problems, exercise judgment and use new tools responsibly will remain valuable. The qualification may expire in relevance; the capacity to learn does not.

Move strategically, not emotionally

Loyalty is valuable, but passivity is expensive. There are times when a person must leave an organization to expand their responsibility, earning potential or exposure. There are also times when remaining is wiser because the role continues to provide unusual learning, sponsorship or long-term upside.

Do not move merely because a new salary is slightly higher. Compare the total opportunity: compensation, pension, equity, health benefits, flexibility, manager quality, brand value, learning, geography, job security and future progression. A role that pays more today may weaken your career capital; a role with apparently modest compensation may put you near outstanding leaders and valuable problems.

At the same time, do not remain indefinitely in a place where your contribution is consistently undervalued and the learning has stopped. Test your market value periodically. Build relationships outside your employer before you need another job. Understand what comparable roles pay, and negotiate using evidence rather than resentment.

Never make the company your entire identity. Organizations change strategy. Managers leave. Markets contract. Roles are restructured. Your security comes partly from employment, but it also comes from portable competence, reputation, relationships and financial reserves.

Convert salary into ownership

Career achievement creates income; wealth requires ownership.

The basic equation is straightforward:

Income − consumption = capital available for saving and investment. That capital can be used to build an emergency reserve, reduce expensive debt and acquire diversified assets appropriate to your goals and risk capacity. Depending on your country and circumstances, those assets might include regulated funds, pension accounts, government securities, public equities, property or carefully evaluated private businesses. Every option carries different risks, costs, liquidity and tax implications.

The principle is more important than any single product: own assets that can grow or produce income without requiring every future hour of your labour.

The SEC’s Investor.gov guidance summarizes the long-term mechanism as regular investing plus time. It suggests consistently investing a percentage of income or a fixed affordable amount, increasing contributions as income rises and using diversification to reduce concentration risk.

For a career person, automation is powerful. Arrange for saving and investment to occur immediately after payday, before discretionary spending expands to consume the balance. If you receive a bonus, decide the investment allocation before the money arrives. When you earn a promotion, increase your investment contribution before upgrading your lifestyle.

Your savings rate matters, particularly in the early years. Investment returns cannot compensate for the absence of capital. The first task is to create a gap between earnings and spending; the second is to put that gap to productive work.

Avoid lifestyle inflation

One of the greatest threats to career wealth is the belief that every visible aspect of life must rise at the same speed as income.

A promotion arrives, and immediately the car must change, the rent must increase, the wardrobe must expand and the social expectations must be upgraded. Soon the new salary feels as tight as the old one. The person has more status but no more freedom.

This does not mean refusing to enjoy the fruit of your work. Money should improve life. The question is whether enjoyment is deliberate or automatic. If each salary increase is divided intentionally among investing, giving, important goals and lifestyle, you can enjoy progress without sacrificing the future.

Beware especially of high-interest consumer debt. The SEC notes that interest on such debt can outweigh the return available from most investments. Paying down expensive debt may therefore be one of the most reliable improvements available to your financial position.

Privacy can also protect wealth. You do not need to display every promotion or disclose every increase in income. A quieter life makes it easier to choose investments based on purpose rather than social pressure.

Build more than one form of capital

Money is only one kind of capital. A durable career produces several:

  • Skill capital: what you can do exceptionally well.
  • Reputation capital: what credible people believe about your character and ability.
  • Relationship capital: who trusts you and will take your call.
  • Knowledge capital: what you understand about an industry, market or problem.
  • Financial capital: what you own and can deploy.

These forms of capital reinforce one another. Skill produces results. Results build reputation. Reputation attracts relationships and opportunities. Opportunities increase income. Disciplined ownership converts income into financial capital. Financial strength then gives you more freedom to choose meaningful work, negotiate well, survive transitions or start something later if that becomes your path.

Trust may be the most important bridge between them. Keep your word. Do not manipulate figures. Give credit to colleagues. Protect confidential information. Admit mistakes early. A person with strong competence and weak character eventually places every other form of capital at risk.

Let your job finance your freedom

Your salary should do more than maintain your current life. It should gradually purchase options for your future.

Build an emergency fund so that one difficult month does not force you into destructive debt. Protect yourself against risks that could erase years of progress. Contribute to appropriate retirement or pension arrangements. Invest consistently and diversify sensibly. Review fees, inflation, taxes and currency exposure, particularly if your future obligations may exist in more than one country or currency.

Do not chase returns you do not understand. Be cautious when a colleague presents a “guaranteed” opportunity that promises unusual profit with little or no risk. Career people are attractive targets for bad investments because they receive predictable income but may not have time to examine every proposition. Your diligence at work should extend to your money.

If you eventually invest in a business or begin a side venture, treat it professionally. Check whether your employment contract permits it. Separate personal and business accounts. Understand the possibility of loss. Do not damage the primary engine that is currently financing your future.

A practical career-to-wealth system

You can think of the process in five stages. First, enter well. Use your grades, projects, relationships and preparation to secure the best learning environment available to you. If your grades are not strong, build other credible evidence of ability.

Second, become excellent. Learn quickly, take ownership, solve problems and produce measurable results. Become useful across functions without becoming a bottleneck.

Third, increase your earning power. Acquire relevant education, negotiate intelligently and move when the total opportunity justifies it. Build rare, valuable and portable capabilities.

Fourth, create a surplus. Control lifestyle inflation, eliminate high-interest debt and protect the difference between what you earn and what you spend.

Fifth, convert the surplus into assets. Automate regular saving and investing, diversify appropriately, manage risk and allow time to compound your ownership.

Repeat the cycle. As your competence grows, your income should have the opportunity to grow. As your income grows, your investment contribution should grow. As your assets grow, your dependence on the next salary should gradually decline. That is how a career becomes a wealth-building system.

Become a star player—and an owner

The most successful career people I know did not spend their early years waiting to be discovered. They made themselves useful; they learned. They delivered. They went above and beyond when it mattered, built relationships across the organization and developed the confidence of people who could entrust them with larger responsibilities.

They became star players. But the final lesson is that a star player must still become an owner. You may not own the company where you work, although employee equity can sometimes provide that opportunity. You can still own your savings, investments, pension, property, intellectual assets and future choices.

Do not despise the nine-to-five. A good job can give you access to people, problems and training that would be expensive to obtain alone. It can introduce you to industries, teach you discipline and provide the capital with which you build your future.

Work diligently, but do not merely work for money. Make the money you have earned begin to work for you.

Your grades may open the first door. Your grit may move you through the building. Your results may take you to the highest floors. But it is the discipline of turning income into ownership that allows the career you built to become wealth that lasts.

References and further reading

This article provides general education, not individualized investment, tax or legal advice. Returns are not guaranteed, and the suitability of any investment depends on the reader’s circumstances.

The Daily Front Page 14 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Listening Closely
article

How I reverse engineered a commercial spatial audio effect

by epestr·▲ 109 points·34 comments·epestr.com ↗
It made the sound feel farther away and a bit lighter to hear.

Back while I was using a different OS, I used a program which processed the audio being played in real time and made it sound much better. It made the sound feel farther away and a bit lighter to hear, while carrying the relevant information. I’d always wanted a similar program which was equally simple to use for Linux, and decided to write one.

Original Surround sound

Play 0:00 / 0:10

Listen with headphones. The excerpt is from P.I.M.P. by Bacao Rhythm & Steel Band[0].

The processed version sounds farther from the ears and feels less “on the ear” making it feel less “blocked” after listening to music for a while.

First attempt

Since I was writing for Linux, my base stack was PipeWire[1]. I had no prior audio experience, so I began by using an LLM as a sounding board while experimenting with standard audio filters and psychoacoustic techniques.

Somewhere along the way I learnt about HRTFs, which describe how sound arriving from a particular direction reaches the ears, depending on the direction and geometry of the listener. I tried the MIT KEMAR database[2] first and thought it sounded awful, because it had been recorded in an anechoic chamber, so there were no room echoes, which were a desirable psychoacoustic effect here. The result sounded cheap and more inwards to the ear. Later I found the SADIE II database[3], which also came with room responses, and that sounded much closer to what I remembered.

But this approach soon showed its limits. The LLM began talking about things like “perceptual envelopment”, “late-field decorrelation”, “spectral diffusion” and “diffuse spatial energy”.[1][1] Gemini was particularly good at this. It was also the only one with a free student program. While I could hear that something was wrong, those descriptions didn’t point to anything I could understand or use to improve the effect.

I left the project dead for a while with no obvious direction to proceed with.

Re-attempt

Much later, I had a different idea. Instead of trying to discover the exact parameters or techniques and algorithms the program used internally, I could study the behaviour of the filter itself.

This seems obvious in retrospect, but I did not have a copy of the program. I could only experiment on a friend’s laptop, so I could not keep changing my implementation and comparing it with the reference whenever I wanted.

I began reading Signalsmith[4] and some blogs by audio engineers to build enough intuition to know what I should measure, and soon realised that I didn’t need to keep using the program to capture its impulse responses and other measurable features. I generated an audio file on Linux, got my friend’s laptop for a while, played the generated file through the program and recorded the output.

The assembly of tests

The question became what I should put into that file so it could capture the relevant details which differentiated the program without having to re-record features or filters.

The first useful thing I found was impulse-response measurement.

If I pass a file containing a single nonzero sample through the filter, everything which appears after it in the recording would’ve been added by the filter. Features such as the extent of its decay over time, the shape of the decay and the phase delays associated with it would be present in the output. Playing the impulse in the right channel allows extracting both outputs’ responses to sound played on the right, and likewise for the left.

If the response to one impulse is $h$, then the response to an impulse at position $k$ is the same $h$ shifted to $k$ and scaled by $x[k]$. Adding all of those responses gives

$$ y[n]=\sum_k x[k]h[n-k]=(x*h)[n]. $$

This of course depends on the filter being a linear time-invariant filter[5].

A louder impulse should produce the same response made proportionally louder:

$$ F(ax)=aF(x). $$

Two inputs played together should produce the sum of what each input produces separately:

$$ F(x_1+x_2)=F(x_1)+F(x_2). $$

And playing the same input later should produce the same response later.

And given it’s stereo, the full equation would be:

$$ \begin{bmatrix} y_L\\ y_R \end{bmatrix} {}={} \begin{bmatrix} h_{LL} & h_{LR}\\ h_{RL} & h_{RR} \end{bmatrix} * \begin{bmatrix} x_L\\ x_R \end{bmatrix}. $$

Given I was sending one file to have it recorded through every mode, I added impulses at different amplitudes to check for level-dependent processing, and played the same impulse in both channels, then with the right channel inverted. The outputs should match the isolated responses added together in the first case and subtracted in the second, checking the conditions above. I also left enough silence between them for the response to finish before the next impulse.

Impulses at three levels, played on the left, right, both channels and with opposite signs.

Download the audio example

While reading about this, I noticed that papers on measurement tended to use sine sweeps rather than impulses[6].[2][2] An impulse already contained every frequency, but having only one sample meant there was little energy at each frequency, so a long sweep would make the response easier to distinguish from recording noise.

A shortened logarithmic sweep.

Download the audio example

$$ Y(f)=H(f)X(f), \qquad H(f)=\frac{Y(f)}{X(f)}. $$

This would work for most of the range, but if $X(f)$ was close to zero, dividing recording noise by it would make a rather impressive filter which had little to do with the program. This could be avoided with a regularized version.

$$ \hat H(f)=\frac{Y(f)X^*(f)}{|X(f)|^2+\lambda}. $$

With enough input energy this would approximately be the same division, while $\lambda$ would limit the result elsewhere. The complex values also retain the phase, which was required given sound sent to one channel could appear in the other with a delay.

The sweep gave another measurement to compare with the impulse, but it also took much longer to play, during which the program could change its behaviour. I added steady tones at different frequencies and amplitudes, and longer tones going from quiet to loud and back, to see whether the gain changed and how long it took to do so.

The steady tones, shortened and played through both channels.

Download the audio example

A 1000 Hz tone alternating between two levels.

Download the audio example

And a multitone to check if any new frequency pops up:

The eight-frequency multitone.

Download the audio example

And repetition to check for time-invariance:

Three identical copies of the multitone.

Download the audio example

And I kept a section of noise aside, to test whether the extracted filter could reproduce its recorded output afterwards.

Noise covering the low, middle and high frequency ranges in sequence.

Download the audio example

Recovering the filter

Given recording and playback were started manually, the recording first needed an offset. I had put evenly spaced clicks near the start, which I initially tried finding by correlating the recording with the original clicks. Since the filter could spread each click over time, I summed the squared samples over a window $W$ to estimate the energy around it instead,

The eight synchronization clicks.

Download the audio example

$$ e[n]=\sum_{m\in W}\left(y_L[n+m]^2+y_R[n+m]^2\right), $$

and found the offset through

$$ \hat\tau=\arg\max_\tau\sum_n e_{\text{probe}}[n]e_{\text{recording}}[n+\tau]. $$

I applied the same offset to both channels so the delay between them was retained.

Output

For the main surround setting, the scaled impulses were nearly identical, the combined inputs matched the sum of their separate responses, and the multitone produced almost no new frequencies. To compare the reconstructed output with the recording, I used

$$ \varepsilon=\frac{\lVert y-\hat y\rVert_2}{\lVert y\rVert_2}, $$

where $y$ was the recorded output and $\hat y$ the output produced by the extracted filter. Given the identical repeated sections already differed slightly, I compared the model’s error with that difference,

$$ \varepsilon_{\text{model}}\approx\varepsilon_{\text{repeat}}\ll1. $$

The volume-reducing mode changed its gain with amplitude and took time to restore it, while another spatial mode changed between repeats, so neither could be reproduced with a fixed convolution filter.

Recording both output channels for an impulse in each input channel gave me the four filters.

Sound also appeared in the opposite channel, quieter and slightly delayed, followed by a long tail. Together these contained the out-of-head effect I had been trying to make earlier.

To decide how much of this response to keep, I tried increasingly long filters against the noise section,

$$ \varepsilon_N= \frac{\lVert y_{\text{noise}}-h^{(N)}*x_{\text{noise}}\rVert_2} {\lVert y_{\text{noise}}\rVert_2}, $$

and kept the full response, which predicted the noise more accurately than the shorter versions. The response extracted from the sweep also agreed over the range where it had enough input energy.

Loading the four responses into Saq[7] gave me the placement I had been trying to reproduce by hand.

You can try the result here. Choose your own local audio file or capture a tab (when not in a Firefox browser[8]), then click the square surround icon to toggle the effect.

Code can be found here

Bibliography

  1. [0] Bacao Rhythm & Steel Band — P.I.M.P. official release https://bacaorhythmandsteelband.bandcamp.com/track/p-i-m-p
  2. [1] PipeWire — multimedia processing for Linux https://pipewire.org/
  3. [2] MIT KEMAR head-related transfer-function measurements https://sound.media.mit.edu/resources/KEMAR.html
  4. [3] SADIE II spatial-audio and binaural room-response database https://www.york.ac.uk/sadie-project/database.html
  5. [4] Signalsmith Audio — DSP investigations and writing https://signalsmith-audio.com/writing/
  6. [5] Linear time-invariant systems https://en.wikipedia.org/wiki/Linear\_time-invariant\_system
  7. [6] Farina — Simultaneous Measurement of Impulse Response and Distortion with a Swept-Sine Technique https://angelofarina.it/Public/Papers/134-AES00.PDF
  8. [7] Saq — a real-time audio enhancer for Linux https://github.com/mTvare6/saqol
  9. [8] Implement audio capture for getDisplayMedia https://bugzilla.mozilla.org/show\_bug.cgi?id=1541425
The Daily Front Page 15 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Hardware Desk
repository

PSPi 6 – Raspberry Pi in a PSP

by therepanic·▲ 130 points·14 comments·github.com ↗
★ 1,078⑂ 42 forks C

PSPi

This is my passion project that transforms the classic PSP 1000 into a modern handheld using a Raspberry Pi. This repository contains the hardware designs, firmware, and software needed to build and use the PSPi 6.

What is the PSPi 6?

The PSPi 6 is a custom circuit board that replaces the mainboard of a PSP 1000, allowing you to use a Raspberry Pi as its brain. This means you can:

  • Play retro games and emulators
  • Run full Linux distributions and use it as a portable computer
  • Use most software that works on a Raspberry Pi

Features

  • Display: High quality 800x480 LCD with adjustable brightness

  • Power Management: Efficient charging, real-time battery monitoring, and power-saving modes

  • Audio: Play audio through the PSP speakers or headphones

    • Control volume and mute using the PSP buttons
    • Note: The Pi Zero and CM4 Carrier give mono audio. The Universal Carrier is planned to output stereo
  • Controls: All original PSP buttons and joystick are used

    • The board allows for two extra buttons and an extra joystick
  • Indicators: LED indicators and on-screen display for system status

  • Open Design: Customizable open-source hardware under Creative Commons license

For a comprehensive list of features and technical details, please visit the Features Wiki Page.

Compatibility

  • PSP Model: Works exclusively with PSP 1000 series. Visit the PSP Compatibility Wiki Page page for more details

  • Raspberry Pi:

    • Fits Raspberry Pi Zero, Zero 2W, and CM4 (with CM4 Carrier board)
    • Electrically compatible with all 40-pin Raspberry Pis, but only the Zero/Zero 2 fit into the shell
    • The Raspberry Pi Compatibility Wiki Page will help you decide which Raspberry Pi model you want to use.
  • Operating Systems: RetroPie, Lakka, Ubuntu, Raspberry Pi OS, Batocera, Kali, and more.

Getting Started

Having issues? Visit the Troubleshooting Wiki Page.

How to Get a PSPi 6

  1. Buy a Ready-Made Board: Available now at othermod.com. Perfect for those who want more of a plug-and-play experience.

  2. Make Your Own: You can use the provided PCB files to manufacture your own board:

    • Download the design files from the boards folder.
    • Send the files to a PCB manufacturer of your choice. I use JLCPCB for every board I sell.
    • Source the components and assemble the board yourself.

    Note: This option requires advanced skills in electronics and soldering.

Project Status

The PSPi 6 board development is complete and boards are shipping. I do sometimes make minor changes to the board, but nothing that affects the important features. I'm continually improving documentation and working on guides to help users get the most out of their PSPi 6.

Community and Contributions

While the PSPi 6 is primarily my personal project, I welcome contributions from the community. Whether you're a coder, designer, or just have great ideas, your input can help make the PSPi 6 even better. Here's how you can contribute:

  • Report bugs or suggest features
  • Join the Discord and share your mods or help others with troubleshooting
  • Help improve documentation and guides
  • Spread the word about the PSPi 6

Support the Project

If you'd like to support the development of the PSPi 6, you can make a donation:

Your support helps keep this project going and allows me to continue improving the hardware and software.

Stay Connected

  • Discord: Join the community for discussions, support, and sharing your builds
  • YouTube: Subscribe for video tutorials and project updates
  • Contact: Reach me on various platforms

License

The PSPi 6 hardware design is open-source under the Creative Commons (CC BY-SA) license. You're free to share and adapt the material, as long as you give appropriate credit.

The Daily Front Page 16 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Networks, Rules & Public Life
article

NYC's "Click to Cancel" rule is now in effect

by gregsadetsky·▲ 384 points·191 comments·nyc.gov ↗

As of October 1, 2026, you can file a complaint against businesses that make it hard to cancel subscriptions

File a complaint


In our city, we're drawing a clear line: if you can sign up with a click, you must be able to cancel with one.

- Mayor Zohran Mamdani

Mayor Zohran Mamdani

What is the “Click to Cancel” rule?

Too often, businesses force their customers through confusing, time-consuming hurdles to cancel unwanted subscriptions. This first-in-the-nation municipal rule protects consumers from such subscription traps.

The Department of Consumer and Worker Protection (DCWP) will enforce this rule, which has been finalized and will take effect on October 1, 2026. Businesses that violate the rule would be liable for civil penalties, starting at $525, and may be liable for refunding the consumer.

Know your rights

The “Click to Cancel” rule means:

  • Companies must clearly explain subscription terms to you
  • Companies must clearly disclose consumers’ rights when buying or cancelling subscriptions
  • Companies must provide you with a straightforward cancellation process in the same method as sign up.
  • Companies cannot ask that you pay to ship them things they gave you for free.

Read the rule

File a complaint to report a business

If you’ve experienced any of the following, submit a complaint:

  • You were not clearly told the terms of the subscription.
  • It was hard to cancel the subscription. For example, you signed up online, but were forced to cancel your subscription on the phone, in person, or another way.
  • The business delayed canceling your subscription.
  • The business sent you products that you did not ask for, and expected you to return or pay for them.
  • You were not informed of changes to the subscription terms.
  • You were not informed of auto-renewal.

File a complaint


Frequently asked questions

What happens after I submit a complaint?

  • After you file a complaint, you will receive a complaint number which you can use to check the status of your complaint.
  • The Department of Consumer and Worker Protection (DCWP) will review your complaint and any documents you provided. If you share your contact information, someone from DCWP may reach out with further questions or to request documents.
  • A mediator may be assigned to your complaint to look into the issue. In that case, DCWP will send a copy of your complaint to the business for a written response. A DCWP mediator will then speak with both you and the business to try to reach an agreement and settle the matter.

Send your complaint by mail or fax

Download the complaint form:

You can mail the completed form and copies of related documents (receipts, contracts, etc) to:

NYC Department of Consumer and Worker Protection
Consumer Services Division
42 Broadway
New York, NY 10004

Or, you can fax your complaint and documents to (212) 487-4482.

The Daily Front Page 17 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Networks, Rules & Public Life
article

IRCv3

by basilikum·▲ 364 points·307 comments·ircv3.net ↗

Specifications

These are the specifications which make up modern IRC, and the extensions that the IRCv3 WG have defined.

View

Working Group

The IRCv3 Working Group is chartered to prototype, develop and test changes to the IRC client protocol.

View

FAQs

These are questions about IRC and the IRCv3 Working Group that we get asked regularly, and our answers.

View

Introduction 🔗

If you’re just getting started with IRC development, the first thing to look at would be the IRC core specifications RFC1459 and RFC2812. One of our members has also been writing a new core protocol document here, which you may find useful to consult. After that, our specifications page contains the extensions the IRCv3 Working Group has developed.

All of the IRCv3 extensions are backwards-compatible with older IRC clients, and older IRC servers. Our roadmap details the specifications we have in the pipeline, and our GitHub repository is where most of our specification work is done.

For any other questions, feel free to consult our FAQ page which contains all sorts of info about us and what we do.

If you’re interested in talking with us, our discussion channel is #ircv3 on Libera.Chat [webchat].

IRCv3 Features 🔗

  • Standardised account login using SASL to speed up registration and authentication. [3.1] [3.2]
  • Providing the account information of other clients for the development of more advanced client features. [1] [2] [3]
  • Optional metadata able to be attached to each message for easier, standardised extension development. [link]
  • Instant away notifications, to let users know when other users go away or come back more quickly. [link]
  • Showing the actual time a message was received, improving history playback from IRC bouncers. [link]
  • Grouping related messages to simplify collapsing and display of those messages to users. [link]

What We’re Working On 🔗

  • Standardised account registration and verification, allowing clients to provide better interfaces for end users. [link]
  • Giving clients a standardised way to recognise, access and view chat history (provided by bouncers or servers). [link]
  • Providing a mechanism to allow clients to automatically detect, move to, and keep using secure connections. [link]
  • Allowing Unicode nicknames and channel names, improving the chat experience for international users. [link]
  • Client avatars for display in graphical clients.

Participating Organizations 🔗

InspIRCd Irssi IRCCloud WeeChat Nefarious IRCd HexChat BitlBee Kiwi IRC UnrealIRCd ircdocs ZNC Palaver

The Daily Front Page 18 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Networks, Rules & Public Life
article

The tilde in your PATH may not be your HOME

by arusekk·▲ 226 points·144 comments·disconnect3d.pl ↗

I was playing with the nono agent sandboxing tool and it greeted me with a warning: PATH entries the sandbox can write to: ~/.local/bin/ which looked suspicious.

nono warning about PATH entries the sandbox can write to

In other words, doing this in your ~/.bashrc or ~/.zshrc:

export PATH="$PATH:~/.local/bin/"

will not expand the ~ (tilde) into the home path (or $HOME) as the tilde to home expansion happens only in unquoted inputs, as also the bash documentation says:

If a word begins with an unquoted tilde character (‘~’), all of the characters up to the first unquoted slash (…) are considered a tilde-prefix. (…)

Bash checks each variable assignment for unquoted tilde-prefixes immediately following a ‘:’ or the first ‘=’, and performs tilde expansion in these cases. (…)

So instead of having /home/<user>/.local/bin/ added to PATH we end up with ./~/.local/bin/ added to PATH.

And to fix this, we can do this:

export PATH="$PATH:$HOME/.local/bin/"

Note that the unquoted version export PATH=$PATH:~/.local/bin actually works in Bash and Zsh, because tilde expansion is also performed in variable assignments after = and after each :. But relying on that is fragile as for example, a whitespace will break the variable assignment.

The problem can also be seen here:

$ ls -la
total 0
drwxr-xr-x@   2 dc  staff    64 Oct  2 13:37 .
drwxr-x---+ 105 dc  staff  3360 Oct  2 13:37 ..
$ mkdir -p ./~/.local/bin/
$ printf '#include <stdio.h>\nint main() { puts("hello"); }'>a.c; gcc a.c -o ./~/.local/bin/kek
$ PATH="~/.local/bin/" kek
hello
$ tree -f
.
├── ./~
│   └── ./~/.local
│       └── ./~/.local/bin
│           └── ./~/.local/bin/kek
└── ./a.c

4 directories, 2 files

Demo showing that a literal tilde in PATH resolves to a ./~/ directory in the current working directory

As we can see, the kek binary was found and executed from ./~/.local/bin/ — the home directory was never involved.

Check your PATH

You can quickly check whether you have this problem with:

$ echo "$PATH" | grep -- '~'

or, to see each entry on its own line:

$ echo "$PATH" | tr ':' '\n' | grep '~'
~/.local/bin/

If it prints anything, go fix your .bashrc/.zshrc/.profile and replace the ~ with $HOME :).

Btw, kudos to the nono tool for warning about this - even though the warning could be more verbose (PR incoming).

The Daily Front Page 19 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Networks, Rules & Public Life
article

Google is illegaly cutting forest to build data centers

by heyimada·▲ 221 points·47 comments·theguardian.com ↗

Aerial view of a vast flat plain surrounded by trees

A view of the Muhos site on 29 September 2026. Photograph: Alessandro Rampazzo/AFP/Getty Images

Google told to halt work on datacentres in Finland over environmental concerns

Order on two construction sites comes after Google failed to carry out environmental impact assessments

Google has been ordered to temporarily stop work on two datacentres in Finland after failing to carry out the necessary environmental impact assessments on its construction sites.

The planned datacentres in Muhos and Kajanni are part of a €13bn (£11bn) project across multiple sites in northern Finland by the technology company amid huge demand for space to house online infrastructure.

It comes after it emerged that Tuike Finland Oy, the company representing Google, had felled 330 hectares (815 acres) of forest in Muhos without completing an environmental impact assessment (EIA).

In Kajaani, the company has reportedly been logging across just under 200 hectares (494 acres) of land.

The Finnish Association for Nature Conservation said the sites had been reduced to “sand”, blaming the oversights on the “overheated” market for new datacentres.

On Tuesday, the Finnish Licensing and Supervision Authority (LVV) said it had issued a notice to the company to “immediately suspend” – by 23 October at the latest – “preparatory measures that significantly change the environment” of the two sites until the environmental impact assessment had been completed. Google has until next Wednesday to submit a written explanation.

The EIA is ongoing and expected to be completed later this year. But Google has gone ahead with “extensive preparatory measures”, including removing trees, stripping topsoil and constructing site roads, LVV said.

Tommi Muilu, the head of the environmental department at LVV, said: “In our opinion, the measures in question will change the environment and cause impacts, the identification and assessment of which are among the key objectives of the EIA procedure.”

If Google does not comply, LVV said it would consider enforcement proceedings and a fine.

In response, Google said in a statement “we understand the concerns” and that it had “fallen short of our own high standards”.

The company added: “We will study the LVV’s findings and follow their guidance. We acted in good faith under the Forestry Act, and ran nature surveys and put in place measures to protect high-value areas. As we continue our long-term work in the region, we are advancing our landscaping and biodiversity plans, which include tree planting across 130 hectares (321 acres) at Muhos.”

Tapani Veistola, the chief operating officer at the Finnish Association for Nature Conservation, said the malpractice across two sites appeared to be a “systemic problem”. The site at Muhos, he said, was “not a forest any more. It is a sand area.”

One of the reasons behind it, he said, was the enormous demand to build datacentres in Finland, which is leading companies to work as fast as possible and overwhelming small municipalities.

“The main reason behind everything is the overheated market situation that makes companies speed up processes so much. Also a lack of expertise in municipalities.”

The Daily Front Page 20 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Culture, Archives & the Office
article

So, You’d Like To Sell Your Print Issues Of The Onion?

by mooreds·▲ 272 points·36 comments·theonion.com ↗

So, You’d Like To Sell Your Print Issues Of The Onion.

If you are here, you are either simply a curious person or you believe you might have something of interest to us. If you are the former, stay curious! If you are the latter, keep on reading.

How To Identify Your Issues.

To check whether you have the issues we’re seeking, look at the Volume and Issue Numbers. Every issue of The Onion has a volume number, an issue number, and a date. This information is all listed on the front page, but in different spots depending on the issue itself. Special issues may list the volume & issue numbers as “22-01” instead of “Volume 22 Issue 1”. See the examples below:

Front page of The Onion, Volume 21, Issue 18, showing where the volume and issue numbers appear.

Volume 21, Issue 18, June 16 to July 20, 1992.

Front page of The Onion, Volume 32, Issue 4,. showing where the volume and issue numbers appear.

Volume 32, Issue 4, August 28 to September 3, 1997.

Issues We’re Looking For:

  • All of Vol. 14 (1988)
  • All of Vol. 15 (1989)
  • All of Vol. 16 (1989)
  • All of Vol. 17 (1990)
  • All of Vol. 18 (1990)
  • All of Vol. 19 (1991)
  • Vol. 21 Issue 12 (1992)
  • Vol. 22 Issues 4, 5, 7, & 16 (1992)
  • Vol. 29 Issues 19 & 22 (1996)
  • Vol. 33 Issue 9 (1998)
  • Vol. 39 Issue 5 (2003)
  • Vol. 42 Issue 5 (2006)
  • Vol. 44 Issues 49-52 (2008)
  • Vol. 45 Issues 1-20 & 33-43 (2009)
  • Vol. 46 Issues 3-52 (2010)
  • All of Vol. 47 (2011)
  • All of Vol. 48 (2012)
  • All of Vol. 49 (2013)

Have Some? Tell Us.

Let us know if you have any of the requested issues by completing the Google Form and providing as much information as you can. Our archivist will reach out to you if your collection is of interest to us. The more of our gaps you can fill, the more interested we will be.

Note: The issues must be in good shape, clean, and readable. If you do sell an issue to us, it becomes property of The Onion and cannot be returned

COMPLETE THIS FORM

We will reach out with any additional questions if we have them.

The Daily Front Page 21 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Culture, Archives & the Office
article

Nicolas Cage says he won't work with Amazon again over AI dispute

by Bluestein·▲ 86 points·30 comments·latimes.com ↗

Nicolas Cage of Prime Video's "Spider-Noir."

Nicolas Cage of Prime Video’s “Spider-Noir.”

(David Urbanke / For The Times)

Nicolas Cage is making it clear — he’s not an “AI-friendly actor.”

The 62-year-old actor spoke out against a tech giant during an appearance at New York Comic Con on Saturday. He alleged that he was asked to sign an artificial intelligence waiver for the television series “Spider Noir.”

“Amazon is a very AI-friendly company, to the tune of $50 billion invested in OpenAI,” Cage said on stage.

“I am not an AI-friendly actor, so it’s like, connect those dots! I’m not a member of their club. I didn’t sign the waiver to let him use AI on ‘Spider-Noir.’ I’m probably not going to be working with that studio again, but that’s OK.”

Amazon did not immediately respond to a request for comment.

New York City, NY - May 13, 2026: Nicolas Cage of Prime Video's "Spider-Noir" in New York City, NY on May 13, 2026. (David Urbanke / For The Times)

The use of AI continues to be a major sticking point across Hollywood. Industry unions including SAG-AFTRA and the Writers Guild recently secured protections against technology in new contracts. But as AI evolves and AI actors like Tilly Norwood continue to make headlines, it’s still unclear exactly how the industry will be affected.

“Spider-Noir,” which premiered in May, is a “Spider-Man” project that put a 1930s crime-thriller spin on the web-slinging superhero. The show was produced by both Sony Pictures Television and Amazon MGM Studios.

Cage’s part as “The Spider” marked his first leading role in a series. The Oscar-winning actor, whose home in Malibu was recently affected by a sinkhole, is known best for his roles in movies like “National Treasure,” “Moonstruck” and “The Rock.”

Upon its release, “Spider-Noir” received much praise from fans and critics alike. In its first six weeks of streaming, the series earned 2.6 billion minutes watched, according to Nielsen. It was also Amazon’s most-nominated show at this year’s Emmys, with 11 nominations and five wins.

Malibu, CA, - September 09, 2026: A sinkhole is seen from a drone on a street and driveway of a seaside home affected by recent heavy surf on Sea Level Drive on Wednesday, Sept. 9, 2026 in Malibu, CA. An evacuation order was in effect today along Sea Level Drive in Malibu, where pounding surf and coastal erosion opened a massive sinkhole at actor Nicolas Cage's beachfront home, prompting the city to declare a local emergency.(Eric Thayer / Los Angeles Times)

Last month, Amazon announced that the show was canceled after only one season, leaving much of the industry in shock.

During his Comic Con panel, Cage also said he plans to keep working in TV. He announced that he will be joining the HBO series “True Detective.”

“My agent’s out there somewhere listening,” Cage said. “That’s OK that I’m the first to announce that, right?”

The Daily Front Page 22 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Culture, Archives & the Office
The Daily Front Page 23 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Also on the Front Page
The Daily Front Page 24 of 25
Sunday, October 11, 2026 The Daily Front No. #261011 — Colophon

That's the Front for Today

Issue No. #261011 — Sunday, October 11, 2026 — went to press 2026-10-12 at 03:40 UTC.

About This Magazine

The Daily Front is a daily digital magazine assembled from the stories that reached the front page of Hacker News on Sunday, October 11, 2026. Headlines, points, and comment counts are recorded as they stood at press time. All articles remain the property of their original authors — every piece links back to its source and its discussion thread.

How It Was Made

Fetched, cleaned, and typeset by an automated pipeline. An editor model laid out the pages and chose the highlights; a second read a handful of the day's stories and briefed the cover illustrator — 30 model calls and 224k tokens in total. Set in Jacquard 12, Playfair Display, Source Serif 4, and IBM Plex Mono, all served via Google Fonts under the SIL Open Font License.

The Cover

The cover illustration was commissioned with this prompt:

In a cramped community server room during a violent storm, an aging rack server hums beside shelves of battered smartphones, several still glowing with simple conversation bubbles. A volunteer in work clothes replaces a phone battery while shielding the equipment from water dripping through the ceiling. On the workbench, an opened envelope lies beside a small donation box and a lawyer’s empty folder. Through the window, lightning strikes a distant cloud above the building, while every old machine continues running.

Claymation-inspired stop-motion cover set, visibly fingerprinted modeling clay and hand-painted props, miniature practical lights, and a 50mm macro-lens viewpoint: stage the cramped community server room during a violent storm with the aging rack server humming beside shelves of battered smartphones, several glowing with simple conversation bubbles; show the volunteer in work clothes replacing a phone battery while shielding the equipment from ceiling drips; keep the opened envelope, small donation box, and lawyer’s empty folder on the workbench; frame the window’s lightning-struck distant cloud above the building and the old machines still running. Use a deliberate palette of storm-slate blue, wet teal, clay umber, faded mustard, and electric cyan screen-light, with warm amber practical bulbs as the sole counterpoint.

Absolutely no text, letters, numbers, readable symbols, or logos anywhere in the image. Keep the main image subject primarily below the upper 10% of the composition, where a title may be overlaid; the scene may extend naturally underneath that title area.

Production Ledger

StageModelCallsTokens InTokens Out
extractgpt-5.6-luna 26 142,172 57,293
layoutgpt-5.6-terra 1 18,371 2,062
covergpt-5.6-luna 2 1,866 426
covergpt-image-2.5-flare 1 306 1,372

The Publisher

Published by Johnny.

Support the Press

If The Daily Front brightens your morning, consider supporting its publisher.

Credits & Contact

All content — articles, posts, comments, and the images within them — belongs to its original authors and is reproduced here to point readers back to the source. Full credit goes to those creators; every item links to its original and its Hacker News discussion.

If you are an author and would like your content removed from an issue, write to hi@johnnys.page and it will be taken down.

Feedback is always welcome at the same address: hi@johnnys.page.

Credit where credit is due.

Every page of this issue began as someone else's work — these are the original sources, linked in full.

  1. Nitter: Update Oct 10th seeking funding and legal help by ForHackernews — nitter.net·HN discussion ↗
  2. LineageOS 24 by timschumi — lineageos.org·HN discussion ↗
  3. Total Annihilation, rebuilt for modern computers by jttnr — nanolathe.gg·HN discussion ↗
  4. I paid people to try and follow my README by edent — shkspr.mobi·HN discussion ↗
  5. Passkeys are a trap by laurex — nonfunctional.substack.com·HN discussion ↗
  6. Terence Tao: Math 2.0 [pdf] by Anon84 — teorth.github.io·HN discussion ↗
  7. I'm sorry, but you still have to think by drogus — itsallaboutthebit.com·HN discussion ↗
  8. 500B tokens later: Letting AI agents decompile a first-person shooter by davikr — momo5502.com·HN discussion ↗
  9. Show HN: BetterWispr – Free, open-source dictation for Mac by kartik017 — betterwispr.com·HN discussion ↗
  10. Valen's Memory Safety: A New Kind of Borrow Checking by verdagon — verdagon.dev·HN discussion ↗
  11. Fingerprints are the new cookies by royfish — royfish.dev·HN discussion ↗
  12. How to Build Wealth as a Career Person by doppp — asheradeniyi.com·HN discussion ↗
  13. How I reverse engineered a commercial spatial audio effect by epestr — epestr.com·HN discussion ↗
  14. PSPi 6 – Raspberry Pi in a PSP by therepanic — github.com·HN discussion ↗
  15. NYC's "Click to Cancel" rule is now in effect by gregsadetsky — nyc.gov·HN discussion ↗
  16. IRCv3 by basilikum — ircv3.net·HN discussion ↗
  17. The tilde in your PATH may not be your HOME by arusekk — disconnect3d.pl·HN discussion ↗
  18. Google is illegaly cutting forest to build data centers by heyimada — theguardian.com·HN discussion ↗
  19. So, You’d Like To Sell Your Print Issues Of The Onion? by mooreds — theonion.com·HN discussion ↗
  20. Nicolas Cage says he won't work with Amazon again over AI dispute by Bluestein — latimes.com·HN discussion ↗
  21. The gap in shared understanding of LLM capability is widening by albertzeyer — twitter.com·HN discussion ↗
  22. A Remote Employee Refused 160 Webcam Photos a Day. Her Employer Fired Her by bhrlady — inc.com·HN discussion ↗
  23. 3rd Yandex Cloud Data Center Was Hit by defly — status.yandex.cloud·HN discussion ↗
  24. Bot traffic from Yandex by walrus01 — radar.cloudflare.com·HN discussion ↗
  25. Documents add to evidence in climate deception case against ExxonMobil by gumby — insideclimatenews.org·HN discussion ↗
  26. Show HN: WallHop – 12ft.io is gone, so I built a replacement by TheWaffleHax — wallhop.io·HN discussion ↗
  27. European Alternatives by doener — buy-european.net·HN discussion ↗
  28. Cartographers of North Korea (2019) by edent — cartographers-nk.wonyoung.so·HN discussion ↗
  29. GameBoy on E-Paper ESP32 at 30FPS [video] by pizzaiolo — youtube.com·HN discussion ↗
  30. Whatever became of holography? (2011) by cainxinth — americanscientist.org·HN discussion ↗

Browse all issues in the archive →