OpenRiot v7.9.41 — The Backup That Knew Better
“The first clone worked. We were proud. We were fools. The construct had learned to write itself onto new silicon, but it hadn’t learned to lie about disk space. It hadn’t learned that a softraid chunk already attached is a chunk you don’t bioctl twice. It hadn’t learned that scanning dmesg for a kernel’s free-form prose is not a substitute for a syscall. So we taught it. We taught it that ‘not enough room’ is a sentence it gets to say. We taught it that the target drive is sacred — encrypted or not, it deserves a real mount, not a phantom. The Feds would have shipped the warnings and called it a feature. We shipped the fix and called it Tuesday.” — The OpenRiot Crew, somewhere in the Sprawl, v7.9.41
Release Overview
This is the release that followed the clone. Full Backup shipped
in v7.9.40 as a single-click rsync to /mnt/backup — and it
worked, mostly, until someone tried to back up onto a drive
whose softraid chunk was already attached, or tried to fit a
120 GB source into a 100 GB target, or tried to back up without
mounting the target at all. Three real bugs. Three real fixes.
One backup that now refuses to write to the wrong drive and
refuses to lie about space.
The Disk Manager TUI also got a small piece of honesty. The “Fetching drive list…” spinner used to leave the action line blank during a refresh, which looked like the menu had frozen. It now says what it’s doing. The Turing Police would call this “UX polish.” We call it not lying to the user.
💾 Full Backup: Three Guards, One Backup
The clone routine in clone.go is the kind of code that looks
finished until it isn’t. Three failures taught us three lessons.
The Space That Wasn’t
The space check used df -P and read the “used” column as
512-byte blocks. OpenBSD’s df -P reports 512-byte blocks on
some filesystems and 1K blocks on others, depending on the
kernel’s mood. The check passed when it shouldn’t have failed
and failed when it shouldn’t have passed. The clone would
happily start writing to a full drive and die halfway through
because the math was a lie.
df -kP now gives us a consistent 1K blocks everywhere. The
check uses used - target_used when the target already has
data — incremental clones don’t need to fit the whole source,
only the delta. The number on screen is the number on disk.
The Increment That Knew Itself
If /mnt/backup already held a previous clone, the old check
demanded enough free space to fit the entire source — even
though rsync would only transfer what changed. The first
incremental run after a 200 GB backup would refuse to start on
a 50 GB target because the math said “no,” not because the
disk said “no.”
The new check knows the difference. If the target has prior
data, it computes source_used - target_used and demands only
that. The status line reads like a sentence: “Source: ~120 GB,
Target: ~80 GB used, 40 GB free → ~40 GB needed.” The clone
runs. The clone fits.
The Drive That Wasn’t There
Click “Full Backup” with nothing mounted at /mnt/backup and
the clone would race ahead, fail on the first mount syscall,
and leave you with an empty progress screen and a confused
user. The Turing Police would call this “discoverable through
the error message.” We call it a guard rail.
GetDumpMenuAction now reads mount(8) output before allowing
the operation. If /mnt/backup isn’t in the list, the TUI
shows:
/mnt/backup is not mounted
Mount the backup drive first:
doas mount /dev/sdXi /mnt/backup
No race. No race-condition-shaped hole. The clone refuses to start until the mount exists. You can dismiss the error and try again once you’ve mounted the drive. The construct tells you what it needs, in the language you’d use yourself.
🛡️ softraid: The Chunk That Wasn’t Attached Twice
MountDrive and UmountDrive in source/disk/backend.go were
the original softraid-aware drive handling code. They did the
right thing for the common case: detect a softraid partition,
attach it with bioctl -c C -l <device>a softraid0, find the
virtual device, mount that.
The uncommon case was uglier. If the chunk was already
attached, bioctl failed (correctly), but the recovery path
relied on a substring search of dmesg(8) for the phrase
` at softraid` to figure out which virtual device to mount.
That’s parsing free-form kernel prose to drive privileged
mount operations. The Feds would call that “creative.” We
call it a five-alarm fire.
Round 1 deleted MountDrive and UmountDrive entirely —
104 lines of softraid mount/umount that duplicated logic now
handled correctly in the Disk Manager TUI. Round 2 removed the
dangerous dmesg fallback from UmountDrive. There is no
fallback anymore. If the softraid attach didn’t happen, the
detach doesn’t either. The Turing Police would have left the
fallback and called it “robustness.” We left the kernel in
charge and called it correct.
🔄 Disk Manager: The Spinner That Told the Truth
The Disk Manager’s “fetching drives” state used to render
nothing on the action line — just an empty string next to
the spinner. The menu looked frozen. The user clicked
“Refresh” again. The TUI started two fio queries at once
because there was no feedback.
renderRunning() now writes Fetching drive list... while
the spinner is up. The status bar says what the TUI is
doing. The user waits. No double-clicks. No racing queries.
The action handler also learned to trust the cache. If you
already fetched the drive list in this session and you’re
selecting a new action (mount, format, encrypt, benchmark),
the TUI uses the cached list instead of asking the kernel
for it again. Faster menus. Less fio traffic. The construct
remembers what it already knows.
📚 Documentation Audit
README.md got audited against the actual rofi launcher
configs in config/rofi/. Three icon codepoints were wrong
in the App Launcher table (Word Processor, System Monitor,
Discord). Two apps were missing from the table entirely
(Spreadsheet, Kate IDE). Five top-level entries were actually
in the Utilities sub-menu (Select WiFi, Monitor Resolution,
Settings, Benchmark, SolveSpace 3D).
The launcher table now matches apps.txt, blockchain.txt,
games.txt, and utilities.txt byte-for-byte. The Utilities
sub-menu has its own documented table. The README no longer
claims an app lives somewhere it doesn’t. The Turing Police
would have left the stale icons and called them “decorative.”
We call them misleading, and we fixed them.
📡 Gurk 0.9.3
The Signal messenger client got rebuilt against upstream
boxdot/gurk-rs tag v0.9.3. Faster database decryption on
debug builds. Stray previous() in select_next_channel gone.
The data directory gets created on first launch instead of
panicking when it’s missing. The pre-built binary shrunk about
900 KB (25.2 MB → 24.3 MB) after a fresh gurk.sh run — fewer
debug symbols, tighter codegen, same patched message.rs that
disables notify-rust so the construct doesn’t SIGSEGV when a
message lands.
The patch is unchanged. The patch was always going to be
unchanged — disabling notify-rust because /proc/self/exe
doesn’t exist on OpenBSD isn’t a workaround, it’s a fact. But
the upstream it patches against is now current. The Turing
Police would have left gurk at whatever commit it was on and
called it “stable.” We rebuilt it and called it Tuesday.
🔧 Monero Rebuild Path
The Monero GUI wallet build path got its own scripts/monero.sh
and scripts/monero-patch.diff, mirroring the pattern we
already use for Zed and gurk. These are the rare rebuild
scripts — the daily install path uses the pre-built
monero.tgz. The script clones monero-project/monero-gui,
checks out v0.18.5.0, applies the OpenBSD CMake patches
(C++17 and the CMAKE_SYSTEM_NAME MATCHES "kOpenBSD" branch),
configures with cmake, builds with make -j$(sysctl -n hw.ncpu),
and bundles the result into
~/.local/share/openriot/config/monero/monero.tgz so the
existing packages.yaml extract step can install it.
Two files, no behavior change for users who don’t rebuild. The Turing Police would call this “documentation.” We call it not having to remember how to compile Monero from scratch next time.
🧾 Files Changed
| File | Change |
|---|---|
source/migrate/clone.go |
FIX — df -kP 1K blocks; |
| incremental space check | |
| (delta only when target has data); | |
| honest status messages | |
source/migrate/model.go |
NEW — pre-flight error if |
/mnt/backup not mounted; uses |
|
errorRequiresManualDismissal |
|
source/migrate/screens/menus.go |
FIX — GetDumpMenuAction |
checks mount output before |
|
| allowing Full Backup; returns | |
| ScreenError on missing mount | |
source/disk/backend.go |
REMOVED — MountDrive, |
| UmountDrive (104 lines deleted); | |
| dmesg fallback gone | |
source/disk/update.go |
FIX — use cached drives |
| when selecting a new action; | |
| only fetch on refresh | |
source/disk/view.go |
FIX — renderRunning |
| shows “Fetching drive list…” | |
| instead of blank action line | |
config/bin/gurk |
REBUILT — upstream v0.9.3; |
| ~900 KB smaller; same | |
| notify-rust SIGSEGV fix | |
README.md |
FIX — 3 wrong icons, 2 |
| missing apps, Utilities sub-menu | |
| documented separately | |
scripts/monero.sh |
NEW — rebuild wrapper |
| mirroring gurk.sh / zed.sh | |
| pattern | |
scripts/monero-patch.diff |
NEW — OpenBSD CMake |
| patches for v0.18.5.0 | |
| (C++17 + kOpenBSD branch) |
🗣️ Final Words
“The clone worked. Then it lied about space. Then it forgot to ask if the drive was mounted. Then it scanned dmesg for a magic word to find a softraid device, and the kernel almost didn’t say it. The first backup is a miracle. The second backup is a habit. The third backup is a machine that knows what it doesn’t know. We fixed the lies. We removed the magic words. The construct now refuses to start a backup until the drive is mounted, refuses to write past the end of the disk, and refuses to bioctl a chunk that’s already attached. The Feds would have shipped the warnings and called it ‘compatibility.’ We shipped the guard rails and called it Tuesday.” — The OpenRiot Crew, after the third incremental backup finished without lying, v7.9.41