Six small files under /etc/scraplinux/, each with one job,
instead of one large file covering everything. Edit one, then run
scraplinux-rebuild to apply it.
| File | Covers |
|---|---|
pkgs.conf | a mirror of what's explicitly installed - never hand-edited, never reconciled against |
network.conf | saved wifi credentials, written by wifi-connect |
users.conf | declared accounts, additive only |
services.conf | the enabled service list |
hardware.conf | gpu, microcode, bootloader, boot timeout |
identity.conf | hostname, timezone, locale, keymap |
A bracketed block grammar, not shell - reading a value never
means . file-sourcing something a package or a service
wrote, which is a real quoting/injection risk once anything on the
machine writes into these automatically.
block.name[ value one per line another value ] another.block[ value ] ;;
Every file ends in a lone ;; line - a hard sentinel
a reader can check for, so a write interrupted partway through is
detectable rather than silently read as an empty, valid file.
conf.lib (sourced by wifi-connect,
scraps, and scraplinux-rebuild) provides
conf_get/conf_get1/conf_set/conf_set1
- the only supported way to read or write one of these files.
pkgs.scraplinux[] is rewritten by scraps itself
after every add/rem to match exactly what's
explicitly installed right now. Nothing ever reconciles the machine
to this file - editing it by hand changes nothing, the next
scraps add/rem just overwrites the edit with
reality again.
This is deliberate, and the reason is a real incident: an earlier design used a declared package list that a rebuild diffed against and enforced, and a machine that had a couple of packages installed by hand outside of that file had both silently removed the next time a system upgrade ran its own reconciliation. Making the ledger a pure mirror instead of a diff target removes that whole failure class structurally, rather than trying to get the diffing logic more careful.
pkg.save[] is the opposite - a hand-maintained
protect-list. Anything named there refuses removal outright
(scraps rem included, and any orphan sweep), regardless of
why it was installed, unless overridden with SCRAPS_FORCE=1.
wifi-connect writes wifi.ssid[],
wifi.psk[] and wifi.iface[] here on a
successful connect. rc.d/wifi reads it back on every
boot and reconnects in the background - see
networking.
These four are what scraplinux-rebuild actually
reconciles. users.declared[] holds one
name:shell:groups line per user - additive only, an
account that exists but isn't listed is left alone rather than
deleted. services.enabled[] is the full, authoritative
service list - a name removed from it gets disabled on the next
rebuild. hw.gpu[]/hw.microcode[] default to
auto, re-detecting from the PCI bus/cpuinfo every
rebuild rather than pinning whatever the machine had the day it was
set. hw.bootloader[] is reported on mismatch, never
switched automatically - that means reinstalling boot sectors, which
stays a manual step.
Every file that starts genuinely empty gets seeded from what the
machine already is on the first scraplinux-rebuild, rather
than being reconciled against as if empty meant "remove everything" -
an empty services.conf does not mean "disable every
service currently running."
scraplinux-rebuild make the system match the declared files scraplinux-rebuild --dry-run show what would change, do nothing
Also finishes what a chroot install couldn't: activates any kernel
package still marked -nomod, and writes
limine.conf/grub.cfg from the real
/etc/fstab//etc/crypttab - config only,
never the deploy command. Every successful run snapshots (on btrfs)
and records a generation, so a bad edit is one
scraplinux rollback away - see
generations.