A casper overlay layer, and a kiosk session that ships switched off
Booting an Ubuntu live audit stick straight into a kiosk by extending casper's own squashfs layer chain, and the three overlay faults - a 0700 layer root, a real /lib shadowing the symlink into /usr, and a destructive undo - that each cost boots.
Status
Shipped, 2–4 September 2026.
Twenty-one commits, mostly on one stubborn problem.
The problem
Two things the July fullscreen work could not solve:
The live session is amnesiac. Every apt install nvme-cli is discarded at
reboot — and without nvme-cli an NVMe drive cannot be secure-erased, which is
most of what comes through the door.
The kiosk does not start by itself. SystemRescue ran autorun at boot;
Ubuntu's casper has no such hook, so an operator had to type a command every
time.
What was built: an overlay layer
boot straight into the kiosk, via a casper overlay layer.
This was read out of the stick's own initrd, not from documentation, because most of what is written about casper online is wrong for 24.04.
Layer selection is driven by one variable:
LAYERFS_PATH=minimal.standard.live.squashfscasper builds the stack by repeatedly stripping the last dot-component off that
name, and the longest name ends up highest priority. So a layer named
minimal.standard.live.station.squashfs extends the chain and sits on top of
everything.
The naming trap, which the script now refuses to be talked out of: the
chain only walks up, by stripping components. Call the layer station.squashfs
and the chain is just "station" — casper would mount a few hundred kilobytes as
the entire root, find no /sbin/init, and panic. The name must extend a
chain whose every ancestor already exists.
Three failures that each cost boots
the layer's root was 0700. mktemp -d creates a directory at
0700, and mksquashfs faithfully preserved that as the layer's root.
Overlayfs takes a merged directory's mode from the topmost layer — ours — so
/ on the booted system became drwx------ root root. No non-root user could
traverse it, GDM never started, and the machine stopped at a text console with
nothing anywhere saying why. later extended the fix to /usr, /etc
and /var for the same reason.
the text-console boots were /lib, not the autostart. Days
were spent believing the autostart entry was at fault. /lib, /bin, /sbin
and /lib64 are symlinks into /usr; a real directory of that name in the
overlay shadows the symlink and the system cannot find its libraries. The
autostart was innocent the whole time.
undo reverted a setting it had nothing to do with. The tool
meant to make experiments safe was itself destructive.
How the investigation was actually made to work
separate the half that works from the half that keeps breaking. Packages (the safe half) split from autostart (the unstable half), so a build could ship the first without risking the second.
record that --with-autostart does not work, and what was ruled
out. A commit whose entire content is documenting a failure and the
eliminated hypotheses. later brought the header back in line once
more was known.
add a boot entry that proves whether the initrd conditional is the bug. Rather than guessing at the splash behaviour, ship an entry that answers the question. then "stop guessing at the splash" and drop the conditional entirely.
** — let the mode be set without carrying the stick to another machine**, and say plainly that a reboot wipes the test.
What shipped
a kiosk session, so GNOME never draws — and it ships switched off.
The contract in station-session.sh, every line of which serves it:
- Off by default. No
gui/kiosk.modeon the stick, or any word other thanon, and it hands straight to the stock Ubuntu session. The layer can be rebuilt, armed and booted with zero change in behaviour. - Every failure path ends in the stock session, with a message on screen saying why. There is no path that ends in a black screen.
- Only what is proven present: Xorg,
xsetroot, python3, and thefullscreen-x.pyalready on the stick. No gnome-kiosk, no cage, no xdotool.
a kiosk mode, now that the reason for avoiding it turned out to be false. A constraint that had been treated as fixed was re-tested and was not real.
What was deliberately not built
No custom initrd. The layer extends casper's existing chain rather than replacing its logic — the one piece of this that must never be fragile.
Open questions
the package gate refused a build for a reason that did not apply (dependency upgrades that this build never downloads). That gate is also why the layer cannot be rebuilt off the audit machine, which came up again on 23 September.
A published copy. Commit references and internal identifiers have been removed and the operator is not named; the engineering, the counts and the stated limits are unchanged.