The rootfs mounted read-write, so a slot ran with its own files open to
change, and the factory-slot mounts rode along on the release image.
Both images now carry the read-only-rootfs feature: the ro root line and
the rcS default, the volatile links made at rootfs time, a writable copy
of /var/lib at boot, a build failure for a post-install that needs the
machine, and the removal of shadow, base-passwd, update-rc.d and
update-alternatives.
What must last or change at run time is handled file by file:
- forgefirm-users renders the four account files from the record into
/run/forgefirm/accounts and bind-mounts each copy over its /etc file
(useradd and the rest are gone with shadow); a render writes through
the mount, and the image's own files apply until the first render.
- forgefirm-banner bind-mounts a copy of /etc/issue and writes the
address block through it.
- sshd keeps its host keys under /data/forgefirm/ssh, so the fingerprint
survives updates; both sshd configs carry the same HostKey lines.
- forgefirm-logging passes logrotate a state file under /var/run
(logrotate refuses to run without one).
- forgefirm-persist points the boot timestamp and the random seed at
/data/forgefirm.
The dev image appends the /factory slot mounts, without nofail (busybox
mount hands it to the kernel, which rejects it). The rootfs command
entries lose their semicolons: on scarthgap the value is the task's
vardeps, split on whitespace, so "name;" left the function body out of
the signature and a changed body did not remake the rootfs; with the
bodies tracked, the dev image's DATETIME string needs a vardepsexclude.
release.sh gains the read-only gate (root ro, no /factory line,
ROOTFS_READ_ONLY=yes, host keys on /data). image.health checks the
mounts, the account binds, the banner bind, the host keys and the
dev-only /factory mounts.
Proven on the bench reference (dev image 20260909140901): / ro, /data
rw, /var/lib a tmpfs copy, the four account files and /etc/issue bound
from tmpfs, the host keys in /data/forgefirm/ssh, no "Read-only file
system" line in any log; forgectrl.auth and commission.account-login (a
temporary account rendered, logged in over HTTPS and removed again),
kernel.latch-locked-idle and motion.liveness-probe PASS; logrotate runs
with the volatile state. forgetest unit tests 335 OK; both images build
clean, and debugfs on the built rootfs shows every setting above.
The daemons log to /dev/log with non-blocking datagrams and drop what
the socket will not take. The kernel's default queue for a unix datagram
socket is 10; forgectrl's arm-time burst alone was about 20, so the
lines around every job start were lost, and the daemon reported 18 to
46 dropped per job on the bench. The logging init sets
net.unix.max_dgram_qlen to 512 before rsyslog and the daemons start;
nothing on the image applied sysctl files before.
Bench: set at runtime on 20260902144848 for the rest of the session.
Catalog consequence: a layer change; the meta-forgefirm content hash
moves and nothing inherits on the image that ships it.
rsyslog flagged the fallback action after the include as unreachable (the rendered rules end in stop). The forgefirm-logging init script now writes a default rules file when the render leaves none, and the shipped rsyslog.conf carries only the include.
rsyslog replaces busybox syslogd/klogd (VIRTUAL-RUNTIME_base-utils-syslog,
trimmed PACKAGECONFIG) and becomes the only log writer: the appended
/etc/rsyslog.conf sets the inputs and the ff_line format and includes
the per-logger rules that `forgectrl --render-syslog` renders from the
machine settings at boot. forgefirm-logrotate becomes forgefirm-logging:
render before rsyslog starts (S19), sweep the pre-syslog log files into
/data/forgefirm/legacy-logs once, and rotate the tree at boot and hourly
by rename + HUP instead of copytruncate. Pins bumped to the pushed
forgectrl (syslog emitter, Logs tab, export), grblHAL-glowforge (syslog
emitter) and python3-gfhardware apps (syslog handlers, capture dir);
the CI harnesses set FFLOG_STDERR=1 so failure diagnostics keep the
controller's log lines. BRINGUP carries the bench validation checklist
(Next work item 14); this is an image change and rides the next flash.