Skip to content

Linux world

AffûtOS

A personal distribution derived from CachyOS handheld edition — 96 packages installed out of the box, 7 removed, every setting applied with no manual step.

0questions asked
165 MBon the USB stick
7automated steps

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 paquets.txt and the install scripts, then dated — the page is written by hand, so it can drift between two proofreads. Last checked: 18 August 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.

The install asks no questions

The graphical installer is preset before it even starts: partitioning, filesystem, bootloader, time zone, package selection. While it works, post-install follows on — no longer from a script waiting in the background, but called by the installer itself, like any other step: profile restore, packages, system and game-session configuration.

The guiding principle: a failure must never leave a broken system. Every step degrades gracefully — if something is missing, it warns and carries on instead of stopping midstream.

A 165 MB USB stick that installs everything

The stick holds nothing but the kernel and the bootloader. At boot it joins Wi-Fi from the initramfs — before the system even exists — then downloads the 2.8 GB system into RAM from my server, with an integrity check. The stick can be pulled out as soon as the download starts. System updates never require rewriting the stick again.

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.

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.

bashArchDeckyemulation
1script, not ten
92packages restored
~2 hmachine 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, a handful of commands

  • capture — backs up the profile and regenerates the list of packages actually added since install.
  • install — fully reinstalls a machine around the graphical installer, unattended.
  • restore — puts a profile and its configuration back in place.
  • usb — builds the network-boot USB stick.

The principle: whatever breaks once is fixed for good

Every failure I hit becomes a guard in the script. 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: 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 on the live desktop by an AffûtOS install launcher.
  • cachyos-wallpapersCachyOS wallpapers.
  • cachyos-vaporTheme shipping Steam Deck logos.
  • plymouthBoot animation. Removed from the initramfs HOOKS and disabled with plymouth.enable=0 — boot is faster and kernel messages are readable again.
  • cachyos-plymouth-bootanimationCachyOS-branded Plymouth animation.
  • cachyos-plymouth-themeCachyOS Plymouth theme.
  • cachy-updateUpdate notifier. Removed from the Calamares netinstall list.

Two installer groups are also unchecked by default:

  • CachyOS shell configurationfish and zsh preconfigured — bash is fine.
  • Firefox and language packageStock Firefox, replaced by the Developer Edition and its French language pack.

The 96 packages, by category

Gaming and emulation 16

  • beammp-launcher-gitMultiplayer Launcher/Client for BeamMP, providing multiplayer for BeamNG.drive
  • steam-boilr-guiSynchronize games from other platforms into your Steam library (bulk SteamGridDB artwork)
  • discordAll-in-one voice and text chat for gamers
  • discover-overlayYet another Discord overlay for Linux written in Python using GTK3
  • jre21-openjdkOpenJDK Java 21 full runtime environment (for MultiMC/Minecraft)
  • lib32-gamemodeA daemon/lib combo that allows games to request a set of optimisations be temporarily applied to the host OS
  • libva-utilsIntel VA-API Media Applications and Scripts for libva
  • lsfg-vkLossless Scaling Frame Generation on Linux
  • multimc-binFree, open source launcher and instance manager for Minecraft.
  • proton-cachyos-nativeA compatibility tool for Steam Play based on Wine and additional components, experimental branch with extra CachyOS flavour (native build)
  • proton-cachyos-slrA compatibility tool for Steam Play based on Wine and additional components, experimental branch with extra CachyOS flavour (Steam Linux Runtime build)
  • protonup-qtInstall and manage GE-Proton, Luxtorpeda & more for Steam, Lutris and Heroic
  • spicetify-marketplace-binSpicetify app that integrates a marketplace for extensions, themes and snippets
  • spirv-headersSPIR-V header files and registry
  • vulkan-validation-layersVulkan Validation Layers
  • wine-geckoWine's built-in replacement for Microsoft's Internet Explorer

Desktop and daily life 21

  • baobabA graphical directory tree analyzer
  • easyeffectsAudio Effects for Pipewire applications
  • filelightView disk usage information
  • filezillaFast and reliable FTP, FTPS and SFTP client
  • firefox-developer-editionFast, Private & Safe Web Browser (Developer Edition)
  • firefox-developer-edition-i18n-frFrench language pack for Firefox Developer Edition
  • flatpakLinux application sandboxing and distribution framework (formerly xdg-app)
  • freedownloadmanagerFDM is a powerful modern download accelerator and organizer.
  • google-chromeThe popular web browser by Google (Stable Channel)
  • gtk-layer-shellLibrary to create Wayland desktop components using the Layer Shell protocol
  • kcalcScientific Calculator
  • kclockClock app for Plasma Mobile
  • kolourpaintPaint Program
  • libappindicatorAllow applications to extend a menu via Ayatana indicators in Unity, KDE or Systray
  • libreoffice-fresh-frFrench language pack for LibreOffice Fresh
  • mypaintFast and easy painting application for digital painters, with brush dynamics
  • obs-studioFree, open source software for live streaming and recording
  • powerline-fontsPatched fonts for powerline
  • smplayer-skinsSkins for SMPlayer
  • smplayer-themesThemes for SMPlayer
  • woff2-font-awesomeIconic font designed for Bootstrap - woff2 format

Development 15

  • claude-codeAn agentic coding tool that lives in your terminal
  • claude-desktopOfficial Claude AI desktop app from Anthropic
  • ex-vi-compatThe ex and vi commands based on vim's compatibility modes
  • lldLinker from the LLVM project
  • moldA Modern Linker
  • neovimFork of Vim aiming to improve user experience, plugins, and GUIs
  • nodejsEvented I/O for V8 javascript ("Current" release)
  • npmJavaScript package manager
  • opencodeThe open source coding agent
  • opencode-desktop-binOpenCode desktop client
  • progressShows running coreutils basic commands and displays stats
  • rustSystems programming language focused on safety, speed and concurrency
  • treeA directory listing program displaying a depth indented list of files
  • uvAn extremely fast Python package installer and resolver written in Rust
  • vscodiumFree/Libre Open Source Software Binaries of VSCode (git build from latest release).

Hardware, disks and printing 23

  • 7zipFile archiver for extremely high compression
  • anydesk-binThe Fast Remote Desktop Application
  • arch-auditA utility like pkg-audit based on Arch Security Team data
  • cupsOpenPrinting CUPS - daemon package
  • cups-filtersOpenPrinting CUPS Filters
  • deskflowShare one mouse and keyboard between multiple computers
  • fwupdSimple daemon to allow session software to update firmware
  • gpartedA Partition Magic clone, frontend to GNU Parted
  • gsmartcontrolA graphical user interface for the smartctl hard disk drive health inspection tool.
  • gvfsVirtual filesystem implementation for GIO
  • gvfs-mtpVirtual filesystem implementation for GIO - MTP backend (Android, media player)
  • hddtempGives you the temperature of your hard drive by reading S.M.A.R.T. information
  • lactLinux GPU Configuration Tool
  • lib32-mesaOpen-source OpenGL drivers - 32-bit — pinned baseline, never mesa-git
  • lshwA small tool to provide detailed information on the hardware configuration of the machine.
  • mesaOpen-source OpenGL drivers — pinned baseline, never mesa-git
  • mtpfsA FUSE filesystem that supports reading and writing from any MTP device
  • ollama-rocmCreate, run and share large language models (LLMs) with ROCm
  • radeontopView GPU utilization for total activity percent and individual blocks
  • rocm-smi-libROCm System Management Interface Library
  • rog-control-centerApp to control asusctl
  • scrcpyDisplay and control your Android device
  • system-config-printerA CUPS printer configuration tool and status applet

Networking and virtualization 12

  • bridge-utilsUtilities for configuring the Linux ethernet bridge
  • dhcpcdDHCP/ IPv4LL/ IPv6RA/ DHCPv6 client
  • iptablesLinux kernel packet control tool (using nft interface)
  • net-toolsConfiguration tools for Linux networking
  • nmapUtility for network discovery and security auditing
  • openbsd-netcatTCP/IP swiss army knife. OpenBSD variant.
  • qemu-fullA full QEMU setup
  • qemu-imgQEMU tooling for manipulating disk images
  • qemu-system-x86QEMU system emulator for x86
  • sshpassFool ssh into accepting an interactive password non-interactively
  • virt-managerDesktop user interface for managing virtual machines
  • virt-viewerA lightweight interface for interacting with the graphical display of virtualized guest OS.

Audio processing (LV2) 5

  • 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

Every setting applied

Install (Calamares, preset)

  • Partitioning: erase disk, ext4
  • Boot: systemd-boot
  • Desktop: KDE Plasma
  • Time zone: Europe / Paris
  • Chaotic-AUR repository added before the packages are installed

Default files shipped by the ISO

  • /etc/mkinitcpio.conf.d/10-affutos.conf — initramfs without plymouth
  • /etc/sdboot-manage.conf.d/20-affutos.conf — kernel options
  • /etc/xdg/ksmserverrc and /etc/xdg/dolphinrc — KDE defaults for every account
  • /etc/skel/ — user folders, Dolphin settings, Steam bootstrap
  • /usr/share/libalpm/hooks/zz-affutos-branding.hook — the AffûtOS branding survives updates

System

  • Keyboard layout propagated to Wayland through XKB_DEFAULT_LAYOUT
  • amdgpu.ppfeaturemask=0xffffffff — full control of the AMD GPU (required by LACT)
  • UEFI boot entry “AffûtOS” created without duplicates, disk detected at run time

KDE desktop

  • Open on double-click rather than single click
  • Empty session at login — no windows restored
  • Dolphin: menu bar visible, hidden files shown, Details view applied to every folder
  • Shortcuts dropped straight into the home folder, no Desktop subfolder

Steam and Game Mode

  • Battery percentage always on screen
  • No screen dimming on AC power (IdleBacklightDimACSeconds = 0)
  • No suspend on AC power (IdleSuspendACSeconds = 0)
  • Interface in French
  • First-boot wizard pre-filled through the file Valve provides for it
  • Beta channel enabled

After the install, with no manual step

  • Decky Loader installed and enabled before the first boot
  • The 96 packages above installed from the repositories and the AUR
  • Eden (Switch emulator): latest build optimized for the detected CPU
  • No temporary privilege left behind — sudoers, units and markers cleaned up

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 reinstallable from a single script

Everyday tools

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

Gaming and emulation

  • Steam in Game Mode, settings applied from install time
  • Proton for Windows games
  • Eden (Switch), Cemu (Wii U), PCSX2 (PS2)
  • BoilR for cover art on non-Steam games

Technical journal

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

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.