// change history for linux
osreflog
A reflog for a Linux system: what changed, who changed it, and which change broke it.
osreflog records every change to a Fedora system together with its cause: the package transaction, service, command or app that made it. It rebuilds the system as it was at any point, bisects a regression down to a single cause by testing each step in a throwaway container or VM, and undoes that cause file by file. It is in active development on my Linux machine.
- year
- 2026
- type
- change history for linux
- group
- systems
- status
- in active development
the problem
//01the problem
When a Linux desktop breaks, you know one thing: it worked last week. Package history covers packages only, the journal shows events but not state, and snapshot tools show the whole system at a moment but not what changed between two snapshots, who changed it, or which change broke things. There is no git bisect for an operating system.
how it works
//02how it works
- 01
Two witnesses
A live witness records who changes each file as it happens, with a filesystem-wide fanotify mark and eBPF tracing every write back to its process and package transaction. A state witness proves what changed between read-only btrfs snapshots from the send stream. When they disagree, for example a change made while the daemon was stopped, the gap is reported instead of smoothed over.
- 02
Causes, not files
Changes are grouped into causes: a dnf transaction, a systemd service, a terminal command, an app writing a setting. Writes a daemon makes on someone's behalf over D-Bus, from tools like timedatectl or nmcli, are credited to the caller. osreflog why shows who changed a file and a readable diff of what it means.
- 03
Git's vocabulary on purpose
log, show, diff, blame, bisect and undo work on causes, and references read like git's reflog: c42, @{1}, @{boot-1}, @{yesterday}. Diffs are computed after each checkpoint and cached, so the diff after a real dnf transaction comes back in 6 ms.
- 04
Bisect down to one cause
Snapshots are coarse, so osreflog rebuilds the states between them by replaying causes into a writable clone. Each step is tested in a throwaway systemd-nspawn container in under a second, or in a QEMU VM that boots the clone's own kernel over virtiofs in about 14 seconds. Every replay says whether it was exact or approximate.
- 05
Undo without rolling back
osreflog undo reverts one cause path by path as a 3-way merge, leaving alone anything that changed since and refusing on conflict. Package causes are handed to dnf. Dry run is the default, a checkpoint is taken before every apply, and an undo can itself be undone. GNOME settings get the same treatment through dconf.
- 06
Observe before touching
Nothing wrote to the live root until a write gate passed: the full undo matrix in a contained scratch root, with symlink-swap races and magic links refused by openat2 resolution. Config parsers run in a sandboxed worker and were each fuzzed for an hour. Secrets are never stored and command lines are redacted.
- 07
Cheap enough to leave on
The idle daemon used 0.0% CPU and 26 MB of memory on the development laptop, and a checkpoint adds under a second to a dnf transaction. The ledger is local, root-only SQLite with a hash chain, so tampering is evident.
- 08
Metal bisect next
Some regressions only show on real hardware. The next phase reboots once into a disposable clone, with the default boot entry checked untouched after every probe, and it stays behind a boot gate tested in a nested VM first. A GTK and libadwaita app and COPR packaging, like drainscope's, come last.
measured
//03measured
- 5 of 5
- seeded regressions bisected
- each found within log2(n) + 1 probes
- 0.9 s
- slowest container probe
- nspawn; vm probes boot the snapshot's kernel in 14 s or less
- 87 of 87
- config changes attributed
- a scripted workload of about 22 real admin tools
- 0
- parser crashes under fuzzing
- 8 targets, an hour each, about 3.05 billion inputs
highlights
//04highlights
- Every change to /etc and system state recorded with its cause
- A live fanotify and eBPF witness reconciled with btrfs send-stream diffs
- Cause-level bisect in nspawn containers or VMs that boot the snapshot's kernel
- 3-way undo that refuses on conflict, dry run by default
- 5 of 5 seeded regressions found within the probe bound
- In active development on Fedora 44; metal bisect is next
built with
- Rust
- btrfs
- fanotify
- eBPF
- systemd
- D-Bus
- polkit
- SQLite
- QEMU
- KVM
- dnf5
//05contact
Say hello. I read everything.
Email is the fastest way to reach me. For anything that needs a conversation, book a call.
to
- name
- Khaled Saeed
- country
- Lebanon