furtka/iso
Daniel Maksymilian Syrnicki 15b876c70a
Some checks failed
CI / lint (push) Failing after 25s
CI / test (push) Successful in 31s
CI / validate-json (push) Successful in 23s
CI / markdown-links (push) Failing after 2s
feat: webinstaller writes archinstall config + execs install, styled
Wires the live-ISO wizard from "shows three screens" to "actually invokes
archinstall on the chosen disk", plus first-pass styling so it stops looking
like raw <h1>/<form>.

Webinstaller flow:
- S1 form gains username/password/password2/language with server-side
  validation (hostname/username regex, ≥8 char password, match check).
- /install/run writes user_configuration.json + user_credentials.json
  (creds 0600) to FURTKA_STATE_DIR (default /tmp/furtka), then execs
  `archinstall --config … --creds … --silent` as a backgrounded subprocess.
- /install/log renders the subprocess output via meta-refresh polling.
- FURTKA_DRY_RUN=1 short-circuits the exec for testing.
- archinstall flag names verified against `archinstall --help` in an
  archlinux container before committing.

Drive list:
- drives.py now filters via `lsblk … -o NAME,SIZE,TYPE` keeping TYPE=disk,
  so the live ISO's own squashfs (loop) and CD-ROM (rom) stop appearing
  as install targets.

Boot menu:
- iso/build.sh sed-rebrands "Arch Linux install medium" →
  "Furtka Live Installer" across grub/, syslinux/, and efiboot/loader/
  entries. Verified zero leftovers against the current releng profile.

Styling:
- static/style.css adopts the website's design tokens (palette,
  typography, gate-mark accent), with light + dark via prefers-color-scheme.
- New base.html with header (gate SVG + FURTKA·INSTALLER wordmark + step
  indicator) and footer; all install templates extend it.
- Drive picker uses radio cards with score chip; overview uses a summary
  table and a destructive "wipe drive" button.

Tests: 17 pass (4 new in test_app.py covering validation + config builders,
2 new in test_drives.py covering the lsblk filter). Ruff clean.

README roadmap updated to mark these done and explicitly defer the
26.0-alpha release until archinstall actually completes end-to-end on a VM.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:54:49 +02:00
..
overlay feat: walking-skeleton live ISO that boots into the Flask wizard 2026-04-13 23:55:58 +02:00
build.sh feat: webinstaller writes archinstall config + execs install, styled 2026-04-14 10:54:49 +02:00
README.md docs: capture UEFI + Secure Boot gotchas in iso/README.md 2026-04-13 23:57:54 +02:00

Live ISO build

Builds a bootable Arch-based live ISO that auto-starts the Flask webinstaller from ../webinstaller/ on boot. User plugs in a USB, boots, and the installer wizard comes up on http://<vm-ip>:5000.

Directly runnable; CI integration comes later once the build is stable.

Run a build

Needs a host with Docker. Disk space required: ~15 GB scratch during the build, ~1.5 GB for the final ISO.

./iso/build.sh

Output ISO ends up in iso/out/furtka-<date>-x86_64.iso. Around 310 min on a 4-core VM. First run is slower because it pulls archlinux:latest and all packages from upstream.

The script re-execs itself inside a privileged archlinux:latest container. That's so mkarchiso has root + loop-mount access without polluting the host — Ubuntu hosts don't ship archiso natively anyway.

What gets baked in

The build starts from Arch's stock releng profile (the same one used to build the official Arch ISO), then overlays our customizations from overlay/:

Overlay file Effect
overlay/packages.extra Appended to the package list. Adds python, python-flask, avahi, nss-mdns
overlay/profiledef.sh Appended to profiledef.sh. Renames the ISO to furtka-* with a dated version
overlay/airootfs/opt/furtka/ Directory where webinstaller/ is copied at build time
overlay/airootfs/etc/systemd/system/ Contains furtka-webinstaller.service + a symlink into multi-user.target.wants/ so it auto-starts on boot

The systemd service runs flask --app app run --host 0.0.0.0 --port 5000 under /opt/furtka. The 0.0.0.0 binding is important — the Flask default is localhost-only, which wouldn't be reachable from another machine on the LAN.

mDNS (proksi.local) via avahi is installed but not yet wired. First milestone is just "boot → browser → wizard at raw IP". Naming comes next.

Test flow

  1. Build: ./iso/build.sh
  2. Copy the ISO to your Proxmox host's ISO storage (typically /var/lib/vz/template/iso/). Browser uploads of 1.5 GB truncate silently — prefer scp over the Proxmox WebUI.
  3. Create a VM with:
    • 2 vCPU, 4 GB RAM, 20 GB disk (empty)
    • BIOS: OVMF (UEFI), add EFI Disk on local-lvm. SeaBIOS fails to load ldlinux.c32 from our ISO; only the UEFI path works reliably.
    • Secure Boot disabled. Our GRUB isn't signed, so Secure Boot rejects it with Access Denied. Either boot into OVMF setup (Esc during boot) → Device Manager → Secure Boot Configuration → Attempt Secure Boot [ ] → F10 → reboot. Or remove the EFI Disk and re-add it with "Pre-Enroll keys" unchecked.
    • CD-ROM attached with the Furtka ISO
    • Boot order: CD before disk
    • Network: same bridge as your LAN, DHCP
  4. Start the VM. Wait ~30 s for boot.
  5. Find its IP in Proxmox's VM summary (or your router's DHCP table)
  6. Open http://<vm-ip>:5000 — the existing 3-screen wizard should be there

Known rough edges

  • Disk space: the first time you build on a fresh host, the squashfs/xorriso steps need ~15 GB free. If the host's LVM-root is smaller, xorriso silently dies at the very end with "Image size exceeds free space on media".
  • Flask / route returns "Hello World" instead of redirecting to /install/step1. Harmless but surprising; will be cleaned up when we wire up screens 48.
  • No HTTPS yet. The Furtka plan is "local CA + green padlock on https://proksi.local" — that's a later milestone. For now, plain HTTP.
  • archinstall is not invoked. The wizard collects input but doesn't write to disk yet. Still a walking skeleton, not an installer.
  • Drive list includes /dev/loop0 and /dev/sr0. /dev/loop0 is the live ISO's own squashfs mounted in RAM; /dev/sr0 is the CD-ROM itself. Both appear as install targets, which is wrong. Filter lives in webinstaller/drives.py and hasn't been added yet.
  • GRUB menu still says "Arch Linux install medium". We inherit releng's bootloader config. Cosmetic, fix when we care about end-user polish.