Reported: installed from the ISO, files present, but the kiosk setup
never appeared on first boot.
Cause: kiosk-firstboot.service had After=cloud-final.service, and on 26.04
cloud-final.service is After=multi-user.target - while multi-user.target,
which wants kiosk-firstboot, is implicitly After= it. An ordering cycle;
systemd breaks it at boot by deleting a job, and kiosk-firstboot's was
the one to go. systemd-analyze verify against the 26.04.1 ISO's own units
reproduces it:
Found ordering cycle: kiosk-firstboot.service/start after
cloud-final.service/start after multi-user.target/start - after
kiosk-firstboot.service
- Drop After=cloud-final.service. The script's `cloud-init status --wait`
goes too: cloud-final waits for multi-user.target, which waits for this
service - it would deadlock. DPkg::Lock::Timeout already covers what it
was guarding against (dpkg lock contention).
- Drop Conflicts=getty@tty1.service: getty.target pulls getty@tty1 into
the same boot transaction, and a conflict between two wanted jobs is
also resolved by dropping one. Before=getty@tty1 alone keeps tty1's
login prompt waiting until this oneshot finishes.
Verified: systemd-analyze verify of kiosk-firstboot + multi-user.target +
cloud-final + cloud-init.target + getty@tty1 + graphical.target in the
26.04.1 server filesystem - old unit reports the cycle, fixed unit is
clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
`iso/build-iso.sh --offline` (and the workflow's new `offline` box, on by
default) adds ~420 MB to the ISO and copies it to /opt/kiosk-offline:
- apt/: local repository with every package install.sh's provisioning
and the CUPS addon install ask for, NodeSource's nodejs, and all
dependencies a fresh server install doesn't already have. Resolved
against the source ISO's own package manifest via a synthesized dpkg
status (iso/offline/fake-dpkg-status.py), so apt downloads only what's
missing and makes the same virtual/alternative choices as the target.
- npm/: prebuilt kiosk-app (Electron 42, binary included - it no longer
downloads at npm install time) and Web UI node_modules.
On first boot, kiosk-firstboot installs online as before when there's
internet; with none it waits KIOSK_OFFLINE_WAIT (90s), then runs
`kiosk-offline-apt on` (apt.conf.d drop-in: bundle is the ONLY source)
and turns it off again afterwards. install.sh notices offline mode via
offline_mode_active (lib/config.sh) and installs nodejs from the bundle
and unpacks prebuilt node_modules instead of npm install.
Package lists moved into PROVISION_APT_PACKAGES /
PROVISION_INTEL_APT_PACKAGES / CUPS_APT_PACKAGES so the bundler reads
the same lists install.sh installs from.
Tested: built against the 26.04.1 ISO, then in a network-less chroot of
the ISO's own server filesystem installed every package set, ran
provision_create_kiosk_user/install_nodejs/install_app and
webui_install_app_files in offline mode (Electron runs, no missing
libraries, chrome-sandbox setuid), and ran kiosk-firstboot through the
offline switch with a stub install.sh.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm
iso/build-iso.sh remasters the official 26.04.x live-server ISO (downloaded
and checksum-verified, or --iso PATH) with xorriso, replaying its BIOS/UEFI
boot records unchanged. It adds:
- /autoinstall.yaml: Subiquity autoinstall that answers locale/keyboard/SSH
and by default leaves only network, storage and identity interactive
(--username/--auto-storage/--auto-network/--no-confirm/--fully-automatic
make those automatic too). late-commands copy the repo (a clone of the
current HEAD) to /opt/ubuntu-based-kiosk and enable a first-boot service.
- iso/firstboot/kiosk-firstboot(.service): on first boot, takes tty1, waits
for cloud-init and internet, copies the repo to the admin user's home and
runs ./install.sh as that user with temporary passwordless sudo (removed
on every exit path). Failed/interrupted installs re-run next boot.
install.sh hooks for this:
- KIOSK_UNATTENDED=1 makes ask_yes_no take each prompt's default and skips
the Core Settings menu (--unattended).
- KIOSK_PROVISION_DONE_MARKER is touched by provision_finish before the
reboot prompt, so the caller can tell a finished install from an
interrupted one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cjz8a58eB5P9sUweqd3NHm