Skip to content

Linux world

AffûtOS

A personal distribution derived from CachyOS handheld edition — installed entirely from a preset installer: 97 hand-picked packages, my settings and my profile restored before the first boot.

97packages picked
177 MBon the USB stick
1.3 to 2.5 ssaved at boot by booster

The project

AffûtOS is my personal distribution, derived from CachyOS handheld edition. It installs what I use, removes what I don't, and applies my settings before the first boot — so that a reinstall no longer costs an evening of reconfiguration.

The figures on this page are read from the installer configuration, paquets.txt and the measurements, then dated — the page is written by hand, so it can drift between two proofreads. Last checked: 8 October 2026.

AffûtOS is built for gaming on the move, with a single requirement: being able to fully reinstall a machine without having to remember anything. Everything I configure once is written down somewhere, and replays itself.

Everything happens in the installer

The graphical installer opens already filled in: language, keyboard, bootloader, partitioning, desktop, package themes. All that is left is to create the account and pick a backup on the Restore page — the profile archives found on the connected disks are listed there. No terminal window, no script waiting for the first login: profile restore, Decky Loader, the Steam client and the boot entry are all done before the reboot, as installer steps like any other.

While it works, the slideshow is replaced by the list of steps: the one in progress, how long each one took, and the time left — estimated from the medians of real installs, about a quarter of an hour. The final page already has “Restart now” ticked, and the installer log is kept in the installed system, even after a failure.

The guiding principle: a failure must never leave a broken system. The steps specific to AffûtOS — restore, post-install — degrade gracefully: if something is missing, they warn and carry on instead of stopping midstream. A restore that fails leaves a note on the desktop; the system itself is complete.

As of 7 October, my settings live in packages: seven affutos-* packages carry the settings for the system, boot, the AMD GPU, KDE, Firefox, Steam and Decky. Installing them adds the feature, removing them takes it away, and their updates also fix machines that are already installed — except what is copied into an account, which keeps its copy. The installer now only handles what happens once, at install time: the choices made on screen, the restored backup, the pacman configuration, the firmware boot entry and the pre-fetched Steam client.

A 177 MB USB stick that installs everything

The stick holds nothing but the bootloader, the kernel and its initramfs. Its menu offers three ways onto the network: interactive Wi-Fi — scan, pick the network, type the password at boot, from the initramfs, before the system even exists — a Wi-Fi network saved on the stick, or Ethernet. It then downloads the 2.5 GB system into RAM from my server, with an integrity check. The stick can be pulled out as soon as the kernel is loaded, and stays valid from one version to the next as long as the kernel does not change.

What it taught me

  • A non-executable initramfs hook is ignored silently — three hours to figure that out.
  • The initramfs console cannot display accented characters: my diagnostic messages were unreadable.
  • Steam stores some settings in one specific block of a file that contains two identical ones; pick the wrong block and you are writing into the void.
  • A server without ECC memory can flip a bit in the middle of a 2.8 GB image. It happened. The integrity check caught it.
  • A one-second boot menu cost 1.2 seconds on every boot; the initramfs “optimization” that looked best on paper saved nothing at all.
  • Since pacman 7.1, package hooks run with no network by default. That was the real cause of the failed downloads during installation; I had first blamed them on DNS.
  • An explanation that holds together is only a hypothesis until it has been compared with the original: one of mine had been running in production for weeks.

Each of these lessons is told in full in the technical journal, further down this page.

Linux day to day: a single script

The method that makes AffûtOS possible — and the machine reinstallable without a second thought.

bashArchCalamaresrsync
1script, not ten
4commands
~15 minmachine like new

It started as an annoyance: reinstalling a machine means two days spent hunting down your settings, and forgetting half of them. I turned that know-how into code. Nothing is done by hand, so nothing gets lost — and whatever breaks once is fixed for good.

One script, four commands

  • capture — updates the package list, then archives the profile's data, not its configuration: the configuration is the installer's job. User folders are stored under their role, not their name, and land in the right folders of the new account.
  • envoi — puts the capture away from the machine, as a delta against the previous one. An interrupted upload resumes where it stopped, and sleep is blocked meanwhile.
  • diff-paquets — keeps the reference list up to date. It is cumulative: a capture can only add to it, and any removal is shown by name and requires typing OUI.
  • usb — builds the network-boot USB stick.

Installing and restoring are no longer commands: they have moved into the installer, which does both in one go.

The principle: whatever breaks once is fixed for good

Every failure I hit becomes a guard in the code. A stale package list overwriting the fresh one, restored files owned by the wrong user, a setting written to the wrong place in a config file, a home folder left readable by everyone: traps hit once, then closed for good.

The pleasant side effect is that a reinstall is no longer an event. It has become routine, something I kick off without thinking about it.

What is removed from CachyOS

  • cachyos-helloCachyOS welcome screen. Replaced in the live system's menu by an “Install AffûtOS” launcher.
  • cachy-updateUpdate notifier.
  • plymouthBoot animation, along with plymouth-kcm, which the KDE group pulled in: nothing to load or draw before the desktop.
  • kwallet-pamWallet unlocked at login; the KDE wallet is disabled.
  • realtime-privilegesReal-time privileges group, unused here.
  • zfs-utilsZFS, its kernel and its tools: neither offered by the installer nor shipped on the live system.
  • nvidia-utilsNVIDIA kernel, drivers and boot entries of the live system: these are AMD machines.

Groups the installer no longer offers:

  • CachyOS desktop editions19 groups meant for desktop PCs, and 3 hidden groups.
  • CachyOS shell configurationfish and zsh preconfigured — bash is fine.
  • FirefoxStock Firefox, replaced by Firefox Pure and the language pack of the system.

And what is replaced:

  • btrfs → ext4Default filesystem, first in the installer's list.
  • Limine → systemd-bootUEFI only, menu hidden: holding a key shows it.
  • mkinitcpio → boosterAn initramfs built for the machine, 1.3 to 2.5 seconds faster at boot.

The 97 packages, by installer theme

The installer offers them as themed checkboxes, all ticked except accessibility; the rest are installed out of the box, with no checkbox.

Games 10

Native Proton CachyOS, outside Steam (1.4 GB)

  • proton-cachyos-nativeA compatibility tool for Steam Play based on Wine and additional components, experimental branch with extra CachyOS flavour (native build)

Wine and its Gecko web engine (800 MB)

  • wine-geckoWine's built-in replacement for Microsoft's Internet Explorer

Steam tools (110 MB)

  • protonup-qtInstall and manage GE-Proton, Luxtorpeda & more for Steam, Lutris and Heroic
  • lsfg-vkLossless Scaling Frame Generation on Linux
  • affutos-deckyDecky Loader ready for Game Mode plugins: folder in Documents, restarted on every update

Switch and PS2 emulators (125 MB)

  • eden-*Nintendo Switch emulator — official PGO-built nightly, variant picked for the CPU
  • pcsx2-gitPlayStation 2 emulator

Java 21 for Minecraft (150 MB)

  • jre21-openjdkOpenJDK Java 21 full runtime environment — for MultiMC and Minecraft

Minecraft (MultiMC) and BeamMP (89 MB)

  • multimc-binFree, open source launcher and instance manager for Minecraft.
  • beammp-launcher-gitMultiplayer Launcher/Client for BeamMP, providing multiplayer for BeamNG.drive

Internet and communication 13

Google Chrome (440 MB)

  • google-chromeThe popular web browser by Google (Stable Channel)

Firefox Pure (290 MB)

  • firefox-pureFast, Private & Safe Web Browser
  • cachyos-firefox-settingsCachyOS settings for Firefox browser
  • xdg-desktop-portal-kdeA backend implementation for xdg-desktop-portal using Qt/KF5
  • profile-sync-daemonSymlinks and syncs browser profile dirs to RAM
  • affutos-firefoxFirefox Pure settings: system language pack put back after every update, default browser, profile kept in RAM

Discord and its in-game overlay (23 MB)

  • discordAll-in-one voice and text chat for gamers
  • discover-overlayYet another Discord overlay for Linux written in Python using GTK3
  • gtk-layer-shellLibrary to create Wayland desktop components using the Layer Shell protocol

Remote desktop and transfers (270 MB)

  • anydesk-binThe Fast Remote Desktop Application
  • deskflowShare one mouse and keyboard between multiple computers
  • filezillaFast and reliable FTP, FTPS and SFTP client
  • freedownloadmanagerFDM is a powerful modern download accelerator and organizer.

Sound, image and video 12

EasyEffects and audio plugins (130 MB)

  • calfLV2 plug-in suite
  • lsp-plugins-lv2Collection of open-source plugins - LV2
  • mda.lv2A port of the MDA VST plugins to LV2
  • x42-plugins-lv2Professional audio processing units available as LV2-plugin and JACK-application - LV2 plugins
  • zam-plugins-lv2Collection of audio plugins for high-quality processing - LV2 plugins
  • easyeffectsAudio Effects for Pipewire applications

OBS, drawing, SMPlayer and Spotify (290 MB)

  • obs-studioFree, open source software for live streaming and recording
  • kolourpaintPaint Program
  • mypaintFast and easy painting application for digital painters, with brush dynamics
  • smplayer-skinsSkins for SMPlayer
  • smplayer-themesThemes for SMPlayer
  • spicetify-marketplace-binSpicetify app that integrates a marketplace for extensions, themes and snippets

Office and printing 5

LibreOffice in French, and a clock (600 MB)

  • libreoffice-fresh-frFrench language pack for LibreOffice Fresh
  • kclockClock app for Plasma Mobile

Printing (27 MB)

  • cupsOpenPrinting CUPS - daemon package
  • cups-filtersOpenPrinting CUPS Filters
  • system-config-printerA CUPS printer configuration tool and status applet

Development and AI assistants 14

VSCodium (1.0 GB)

  • vscodiumFree/Libre Open Source Software Binaries of VSCode (git build from latest release).

Claude Desktop (660 MB)

  • claude-desktopOfficial Claude AI desktop app from Anthropic

Claude Code and OpenCode (1.0 GB)

  • claude-codeAn agentic coding tool that lives in your terminal
  • opencodeThe open source coding agent
  • opencode-desktop-binOpenCode desktop client

Rust, mold and lld (390 MB)

  • rustSystems programming language focused on safety, speed and concurrency
  • lldLinker from the LLVM project
  • moldA Modern Linker

Node.js, npm and uv (130 MB)

  • nodejsEvented I/O for V8 javascript ("Current" release)
  • npmJavaScript package manager
  • uvAn extremely fast Python package installer and resolver written in Rust

Neovim and Powerline fonts (35 MB)

  • neovimFork of Vim aiming to improve user experience, plugins, and GUIs
  • ex-vi-compatThe ex and vi commands based on vim's compatibility modes
  • powerline-fontsPatched fonts for powerline

Hardware: AMD GPU, ASUS ROG, disks 17

AMD GPU: LACT, monitoring, Vulkan (86 MB)

  • lactLinux GPU Configuration Tool
  • affutos-amdgpuFull AMD GPU control for LACT (ppfeaturemask), lactd service enabled
  • radeontopView GPU utilization for total activity percent and individual blocks
  • rocm-smi-libROCm System Management Interface Library
  • libva-utilsIntel VA-API Media Applications and Scripts for libva
  • vulkan-validation-layersVulkan Validation Layers
  • spirv-headersSPIR-V header files and registry

ASUS ROG Control Center (42 MB)

  • rog-control-centerGraphical user interface for asusctl

Disks and phones (53 MB)

  • gpartedA Partition Magic clone, frontend to GNU Parted
  • gsmartcontrolA graphical user interface for the smartctl hard disk drive health inspection tool.
  • hddtempGives you the temperature of your hard drive by reading S.M.A.R.T. information
  • baobabA graphical directory tree analyzer
  • gvfsVirtual filesystem implementation for GIO
  • gvfs-mtpVirtual filesystem implementation for GIO - MTP backend (Android, media player)
  • mtpfsA FUSE filesystem that supports reading and writing from any MTP device
  • scrcpyDisplay and control your Android device
  • lshwA small tool to provide detailed information on the hardware configuration of the machine.

Console network diagnostics (33 MB) 6

  • nmapUtility for network discovery and security auditing
  • net-toolsConfiguration tools for Linux networking
  • openbsd-netcatTCP/IP swiss army knife. OpenBSD variant.
  • sshpassFool ssh into accepting an interactive password non-interactively
  • dhcpcdDHCP/ IPv4LL/ IPv6RA/ DHCPv6 client
  • arch-auditA utility like pkg-audit based on Arch Security Team data

Accessibility: screen reader (110 MB) — unchecked 3

  • espeak-ngMulti-lingual software speech synthesizer
  • mousetweaksMouse accessibility enhancements
  • orcaScreen reader for individuals who are blind or visually impaired

Installed out of the box, no checkbox 17

  • xbindkeysLaunch shell commands with your keyboard or your mouse under X — required by Game Mode's key-binding service
  • flatpakLinux application sandboxing and distribution framework (formerly xdg-app)
  • libappindicatorAllow applications to extend a menu via Ayatana indicators in Unity, KDE or Systray
  • woff2-font-awesomeIconic font designed for Bootstrap - woff2 format
  • 7zipFile archiver for extremely high compression
  • treeA directory listing program displaying a depth indented list of files
  • progressShows running coreutils basic commands and displays stats
  • affutos-keyringAffûtOS repository public key: its packages are signed
  • affutos-scriptsThe affutos script: capture, envoi, diff-paquets, usb
  • affutos-systemeSystem settings: AffûtOS identity, I/O schedulers, account lockout disabled, Navi 14 GPU fix, Flathub remote
  • affutos-boostersystemd-boot entries using the booster initramfs, microcode first
  • affutos-steamSteam and Game Mode: settings from install time, last mode resumed at boot, missing Game Mode services skipped instead of failing
  • mesaOpen-source OpenGL drivers — pinned baseline, never mesa-git
  • lib32-mesaOpen-source OpenGL drivers - 32-bit — pinned baseline, never mesa-git
  • vulkan-radeonOpen-source Vulkan driver for AMD GPUs — pinned baseline, never mesa-git
  • lib32-vulkan-radeonOpen-source Vulkan driver for AMD GPUs - 32-bit — pinned baseline, never mesa-git
  • proton-cachyos-slrA compatibility tool for Steam Play based on Wine and additional components, experimental branch with extra CachyOS flavour (Steam Linux Runtime build) — installed out of the box, used for every Steam game

Every setting applied

On the installed system, most come from the affutos-* packages: removing a package takes its system settings away, but an account keeps what was copied into it.

Install (Calamares, preset)

  • Partitioning: erase disk preselected, ext4 by default; neither ZFS nor encryption offered
  • USB disks and disks under 16 GB set read-only: the installer does not offer them
  • Boot: systemd-boot in UEFI, menu hidden (timeout 0); initramfs by booster
  • Chaotic-AUR and AffûtOS repositories added after the official ones; AffûtOS packages are signed
  • Restore page: backups from the connected disks, checksum verified before extraction
  • Step list with durations and time left; log copied into the installed system, even after a failure
  • “Restart now” ticked on the final page

Live system

  • In French: language, console and desktop keyboard, Europe/Paris time zone
  • Common packages preloaded into RAM from boot, so the install does not download them again (skipped below 3 GB of free memory)
  • Wi-Fi joined from the initramfs for network boot; sleep disabled during the install

System

  • amdgpu.ppfeaturemask=0xffffffff — full control of the AMD GPU (required by LACT); amdgpu.runpm=0 only when a Navi 14 GPU is present
  • AffûtOS identity (/etc/os-release) replayed after every update that would overwrite it
  • A single “AffutOS” UEFI boot entry for the disk, created without duplicates
  • Firefox Pure: the language pack matching the system, fetched at the exact version, checked against Mozilla's checksums and put back after every update; profile kept in RAM
  • I/O schedulers: BFQ for SATA and SD cards, none for NVMe
  • Two parallel pacman downloads instead of ten; package cache emptied after the install, then trimmed automatically

KDE desktop

  • The desktop is the home folder: shortcuts land there directly
  • Empty session at login — no windows restored
  • Never any automatic sleep; screen off after 10 minutes, on battery only
  • KDE wallet disabled
  • Variable refresh rate set to “Always” on the screens of my machines
  • Dolphin: menu bar, panels, hidden files shown, Details view
  • CPU, memory and disk monitors in the taskbar, right after the task list
  • A “Restart Plasma” launcher; boot resumes the last mode used, gaming or desktop

Steam and Game Mode

  • Full brightness, never dimmed and never asleep, on AC as on battery
  • Proton CachyOS for every game; variable refresh rate enabled in Game Mode
  • First-launch wizard skipped, interface in French, stable Steam Deck branch
  • Steam client downloaded during the install: nothing left to fetch at first launch
  • Steam no longer starts with the Plasma session
  • Game Mode: the notification and input services the session starts are skipped while their program is missing, instead of failing

Before the first boot, with no manual step

  • The chosen profile restored, the reference package list put back in its place
  • Decky Loader installed and enabled; the plugins of the backup reinstalled from the Decky store, up to date
  • Eden (Switch emulator): the official PGO-built nightly, republished every night, in the variant optimized for the detected CPU

Around AffûtOS, day to day

System

  • AffûtOS, my distribution derived from CachyOS
  • KDE Plasma on the desktop, gamescope in Game Mode
  • Decky Loader for Game Mode plugins
  • Everything is reinstalled from the installer, profile included

Everyday tools

  • Konsole and bash — where most of the work happens
  • Dolphin, menu bar visible and hidden files shown
  • Firefox Pure, profile in RAM
  • Claude Code for automation and debugging

Gaming and emulation

  • Steam in Game Mode, settings applied from install time
  • Proton CachyOS for Windows games
  • Eden (Switch), Cemu (Wii U), PCSX2 (PS2)
  • lsfg-vk for frame generation

Technical journal

Failures that taught me something — and the path to understanding them. RSS feed

8 October 2026

Every setting in a package

Until now, the installer itself copied my settings into the system: desktop configuration, the template for new accounts, system rules. It worked on install day and never again: a fixed setting never reached machines already installed, and no setting could ever be removed.

Seven packages, three rules

Settings now belong to affutos-* packages in the AffûtOS repository: system, boot, AMD GPU, KDE, Firefox, Steam, Decky. Installing a package applies its settings, removing it takes them away, updating it fixes them on every machine — with one exception, forced by the second rule. The image build rejects any setting copied outside a package; the installer keeps only what happens once, such as the restored backup or the firmware boot entry.

  • A setting KDE reads as a global default goes into /etc/xdg: changed for everyone on update, and the account can always override it;
  • a per-account setting — Steam, Firefox, files Plasma rewrites — goes into the template for new accounts, /etc/skel, as a file owned by the package. The downside: an existing account keeps its copy, which neither an update nor a removal touches;
  • a service is enabled by a link shipped in the package, not by a command: removing the package is enough to disable it. The one exception is Decky: its service is named after the account, which the package cannot know in advance, so it enables it for the first account and disables it when removed.

The CPU, memory and disk monitors in the taskbar go through a Plasma update script instead, run once per account at login: they land right after the task list, as on my main machine.

The culprit was pacman

The affutos-firefox package installs the system language pack when it is installed, then puts it back after every browser update. On an already installed machine with a flawless network, reinstalling it failed at the download: curl: (7) Failed to connect to archive.mozilla.org:443 after 1 ms. One millisecond is not a slow DNS: the connection never left the machine.

The answer was in the pacman.conf manual: since pacman 7.1, hooks and package install scripts run by default in a fresh network namespace, with no interface but loopback. No download gets through. That was the cause of the “could not resolve host” errors seen during installs since late September — which I had first blamed on the dangling DNS link suspected in mid-August, then filed as “cause unknown” on 4 October.

A hook can ask for the network (NetworkAccess = allowed), a package install script cannot — and the pack is also installed when the package itself is. So both the hook and the script ask systemd to start a service, which downloads outside the isolated namespace: systemd's sockets are files, reachable even without a network. During installation, where systemd is not yet running in the target system, a post-install step replays the download.

Three fewer errors in the system journal

Three errors cluttered the system journal. Two came from the Game Mode session, which starts a Steam notification daemon and an input method whose programs are not installed: their services are now skipped while their program is missing. The third came from the service that keeps the Firefox profile in RAM: it also started for root, which has no profile, and only runs for regular accounts now.

Checked both ways

On a test machine, every package was removed and reinstalled: its system files and services disappeared and came back identical. After that, two full installs in a virtual machine with the new images: none of the three errors in the system journal, and Decky running from the first boot.

One trap along the way: on a fresh install, Decky refused to start for lack of a folder my test account already had. A service checked on an existing account proves nothing for a new one: every service is now also tested on an account created for the occasion.

The lesson: as long as the failure only happened in the installer, I searched the installer. It took seeing it on an ordinary machine for me to finally look at pacman.

4 October 2026

Holding it up against the original

For two months AffûtOS has drifted away from CachyOS one fix at a time, each with its reason written in a comment. What I had never done was install both side by side and look at the difference, file by file.

The bench

Two virtual machines on the same computer, each on its own disk, both fed by the same local package cache: CachyOS handheld with its defaults — except the filesystem and the bootloader, set to ext4 and systemd-boot like mine — and my working version of AffûtOS. Then an offline survey of both installed systems — packages, files owned by no package, enabled services — and their boot timings.

  • Boot: 4.56 s against 6.78 s, even though my working version starts a few more services — the initramfs phase takes 0.07 s with booster against 1.65 s;
  • and five seconds of boot menu on top for CachyOS, against none for AffûtOS.

A wrong explanation, in production for weeks

In mid-August I had blamed an installer step that, I thought, replaced the DNS configuration with a dangling link, and I had added a step to repair it. In September, a Firefox language pack that failed to download during an install — “could not resolve host” — seemed to prove me right. The comparison sank that story: the live system has had no such link since 2022, the step copies an ordinary file identical to the one already in place — and CachyOS, with no repair step at all, ends up with exactly the same file.

The repair step is gone. The Firefox symptom is real, and its cause is still unknown; what no longer depends on it is that downloads outside pacman — language pack, Decky, Steam client — already happen in the post-install steps, where the network works.

Update, 8 October: the cause has been found — since pacman 7.1, package hooks have no network by default. See “Every setting in a package”.

A clean-up you can check

Every difference then had to justify itself against the original. Result: 1,224 lines removed and 391 added, over ten commits — network-boot entries nobody used, up to three minutes of waiting before the installer opened, for a mirror ranking it only reads once installing starts, a copy of my script that nothing read anymore, duration estimates exceeded at nearly every install. Reinstalled after the clean-up, AffûtOS had the same packages, the same services, and only the file differences that were intended: same behaviour, less code.

Along the way, a fix CachyOS had published on its main branch — a mirror-ranking timer to mask on the live system — was ported too.

The lesson echoes August's: reading your own code convinces you, putting it next to the original does not lie.

30 September 2026

Booting faster: what mattered, and what did nothing

The goal: shave seconds off the time from power button to desktop on my main machine. The first question came before any change: how to measure. systemd only sees what happens after the firmware; to compare a classic bootloader with a kernel launched directly by the firmware, you need the firmware's own clock — an ACPI table records the moment it hands over. Each configuration was booted three to six times.

  • Starting point, mkinitcpio: 12.83 s from power-on to desktop.
  • An “optimized” mkinitcpio — fewer hooks, uncompressed modules, maximum compression: 12.81 s. Nothing.
  • booster, which only packs what this machine needs (under 10 MB instead of 16.7): 11.52 s, 1.3 s saved. On my other laptop, 2.5 s.
  • A unified kernel image launched by the firmware, with no bootloader at all: 11.47 s against 11.45 s. Nothing either: the bootloader's 0.3 s is mostly the firmware reading the kernel from disk, which it has to do anyway.

The second nobody sees

The boot menu, set to one second so I could still pick an entry, actually cost 1.2 seconds per boot; on my other laptop the bootloader alone took up to 1.8 seconds. The timeout is now zero, and holding a key brings the menu back.

Two traps

  • booster's strip option, which slims the modules down, also strips their signatures: the kernel then marks itself as tainted. It stays off — the size difference is under a megabyte.
  • Creating or deleting a UEFI boot entry makes my machine's firmware run a full initialization on the next boot: 7.3 s instead of 4.4. Any measurement taken right after touching the boot entries is wrong; it takes one warm-up boot first.

Where to stop looking

Sampling the rest of the boot showed that the login screen, at 6.8 s, is held back by two drivers: the AMD GPU, which only starts probing at 5.75 s, and the Wi-Fi card applying its radio regulations. Whatever is left on systemd's side is worth about 0.2 s. The measurements say where to stop digging as much as where to dig.

18 August 2026

The right lead was not the one I thought

A read through the code had named a perfect culprit: packages preloaded at boot did not land where the installer looked for them, and would therefore be downloaded all over again. Two minutes to claw back, on paper.

The fix was a regression

Twice over. The installer does not call pacman --root but pacman --sysroot — and that option makes it read the configuration and the cache inside the system being installed, not on the live medium. The original code was already pointing at the right place. My “fix” was diverting 746 MB into a cache nobody reads, under filenames the installer never looks for.

Twelve packages picked at random among the preloaded ones: twelve downloaded again. The live cache had not grown by a single byte over the whole install. Only a real run showed it — reading the code had convinced two successive analyses.

The real win was elsewhere, and it is huge

A package cache on the local network, on the other hand, changes everything. Measured on the living-room machine, full install:

  • 5 minutes instead of 12, for 1271 packages installed;
  • 341 MB pulled from the Internet instead of 4.2 GB;
  • 3347 files served by the local cache against 177 fetched from outside, or 95% of the requests.

The lesson fits on one line: an assumption about a cache is checked against file timestamps after the fact, never by reading command-line options.

18 August 2026

Taking the configuration out of the script and putting it in the image

Until now, preparing the installer meant patching it on the fly: over a thousand lines of substitutions applied to its configuration file, on every boot of the live system. It worked, and it was untenable — no way to tell what differed from upstream without walking the script by hand.

The configuration now lives in the image, in plain text, as an overlay the installer reads before its own. It can be read back, diffed, versioned. The launcher checks in the log which configuration was actually opened and stops if it is the wrong one: a silent fallback would install the upstream distribution without warning.

In the same pass, post-install left its background script to become a step called by the installer. And a full review dropped 925 lines: package groups a console edition cannot display, a bootloader nothing knew how to configure, startup files nothing ever read.

A detail measured along the way: with no explicit setting, the kernel tooling rebuilt an initramfs and a boot entry already produced by the previous step — 64 MB wasted on a 196 MB boot partition.

10 August 2026

A single flipped bit in a 2.8 GB image

AffûtOS network boot started refusing the system image: checksum error. The usual reflex: regenerate the checksum. It would have been the worst possible call.

The disk was telling the truth, memory was lying

Comparing the published image with the one in the working directory, a single byte differed out of 2,823,114,752 — and it differed by one bit: 0xA2 had become 0xAA. The rest of the image, kernel and initramfs included, was identical down to the bit.

The timeline placed the accident in a window of a second and a half, while the image was being copied into the ISO file. The source file on disk, read back later bypassing the page cache, was perfectly sound.

The proof, a few hours later

The same day, a package in the cache started failing its signature check. Read normally — so from memory — its signature was invalid. Read straight from the disk, it was perfect. And the faulty bit was the same one as in the image: bit 3.

The machine has non-ECC memory, four sticks pushed past the frequencies the vendor validated. It has no way to detect, let alone correct, that kind of accident.

What I took from it

  • A checksum that fails is good news: it did its job. Regenerating it amounts to officially certifying corrupted data.
  • A check that reads a file back right after writing it only checks memory. You have to drop the cache before reading back, otherwise you validate the suspect copy against itself.
  • Nothing gets published now without a disk read-back of every file, not just the biggest one.

The server side of this incident

9 August 2026

Three hours lost to a permission bit

The goal: join Wi-Fi from the initramfs, before the system exists, so a machine can be installed with no network cable. The script was written, injected into the image, and never ran. No message. No error. Nothing.

Everything was right, except one thing

The file was there in the image — checked. Its syntax was valid — checked with the exact interpreter the initramfs uses. Sourcing it did define the expected function — checked. It was listed among the scripts to run — checked. And still it never ran.

The answer was in the boot system's own code, a single line:

[ -x "/hooks/$hook" ] || continue

A non-executable script is ignored, without so much as a message. It is even the official way to disable one: the function meant to switch a script off simply strips its execute bit.

Rebuilding the image by hand, I had recreated the file as 0644 instead of 0755. I had been disabling my own script without knowing it, on every single attempt.

Two lessons in method

  • I added a version number printed on screen by the script. Without it there is no way to know which version is actually running — I spent an hour testing fixes that were never loaded.
  • The initramfs console does not render accented characters: every one of my French diagnostic messages showed up as unreadable symbols. They had been there from the start, I simply could not read them.

9 August 2026

Hunting a setting through minified JavaScript

I wanted three Game Mode settings applied from install time, with no manual step: never dim the screen on AC power, never suspend, and always show the battery percentage. The key names looked obvious. They did not work.

The right key in the wrong place

The configuration file contains two blocks with the same name. The software reads only one: the deeper one. I was writing into the other. The key was right, the value was right, the location was wrong — so nothing happened, without the slightest warning.

Same trap for the battery percentage a few hours later: the key had to live in one specific sub-block, alongside three sibling keys written by the same component. At the root it stayed a dead letter.

The method that worked

Two complementary approaches. First the empirical one: snapshot the files, change the setting by hand in the UI, compare. The file then confesses exactly what to write, and where.

Then reading the sources. Steam's interface is no longer a compiled library but minified JavaScript: a hundred-odd megabytes of unreadable yet perfectly searchable code. In it you find the exact key name, the function that writes it, and the one that reads it back at startup — hence the certainty that a value set in advance will really be picked up.

One trap to avoid at all costs

A trick going around for disabling the first-boot wizard: create a certain configuration file with a magic line. When the launcher meets that line, it runs an rm -rf on the entire application folder — on every startup. The correct solution uses a bootstrap file the vendor provides, which nobody seems to use.