feat: walking-skeleton live ISO that boots into the Flask wizard
iso/build.sh runs mkarchiso inside a privileged archlinux container, overlays our customizations onto Arch's stock releng profile (systemd unit that launches Flask on 0.0.0.0:5000, the webinstaller under /opt/furtka, extra packages for python/flask/avahi), and drops a hybrid BIOS/UEFI ISO in iso/out/. Verified end to end: Proxmox VM (OVMF, Secure Boot off) boots the ISO, DHCP's onto the LAN, and serves screens 1-3 of the existing wizard at http://<vm-ip>:5000/install/step1. This is the first point at which Furtka is something you can run instead of something you can read about. Two known drive-list bugs surfaced while testing (/dev/loop0 and /dev/sr0 appear as install targets) — captured in the README roadmap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
03b2b7d451
commit
a535debf2e
9 changed files with 150 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ __pycache__/
|
|||
|
||||
# Real credentials must never be committed — use the .example files
|
||||
archinstall/user_credentials.json
|
||||
iso/out/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ This project uses calendar versioning: `YY.N-stage` (e.g. `26.0-alpha` = 2026, r
|
|||
### Added
|
||||
|
||||
- **Forgejo Actions runner** live on Proxmox VM (`forge-runner-01`, Ubuntu 24.04) with DinD sidecar — CI green end-to-end. Setup scripts in `ops/forgejo-runner/`.
|
||||
- **Walking-skeleton live ISO** (`iso/build.sh`). Overlays an Arch `releng` profile with Flask + the webinstaller, bakes a systemd unit that auto-starts the wizard on boot, produces a hybrid BIOS/UEFI ISO via `mkarchiso` in a privileged `archlinux:latest` container. Tested booting under OVMF in Proxmox — wizard screens 1–3 respond at `http://<vm-ip>:5000`.
|
||||
|
||||
## [26.0-alpha] - 2026-04-13
|
||||
|
||||
|
|
|
|||
|
|
@ -106,8 +106,12 @@ None of these nail the "your dad can set this up" experience. The installer wiza
|
|||
- [x] Release process + CI — CalVer tags, conventional commits, Forgejo Actions (ruff, pytest, JSON, link checks), `26.0-alpha` tagged
|
||||
- [x] Forgejo runner live on Proxmox VM (`forge-runner-01`, Ubuntu 24.04, Docker + DinD sidecar) — setup captured in [docs/runner-setup.md](docs/runner-setup.md) + [ops/forgejo-runner/](ops/forgejo-runner/)
|
||||
- [ ] **Publish `26.0-alpha` Forgejo Release** — [releases/new](https://forgejo.sourcegate.online/daniel/furtka/releases/new), paste CHANGELOG section, tick Pre-release *(Daniel, next session)*
|
||||
- [ ] **Base OS bootable image** — Robert gets a minimal Arch image that boots, runs Docker, serves the installer webapp at `https://proksi.local` *(next blocker)*
|
||||
- [ ] Installer wizard screens S5–S8 (domain, SSL, diagnostic, confirm)
|
||||
- [x] **Walking-skeleton live ISO** — `iso/build.sh` produces a hybrid BIOS/UEFI Arch-based ISO that boots in a Proxmox VM, DHCP's onto the LAN, and serves the Flask webinstaller on `:5000`. Screens 1–3 work end-to-end. Build infra in [`iso/`](iso/).
|
||||
- [ ] **Drop /dev/loop0 + /dev/sr0 from drive list** — the live ISO's own squashfs and the CD-ROM both show up as install targets. Simple filter in `webinstaller/drives.py`.
|
||||
- [ ] **Rebrand GRUB menu** — the ISO still boots as "Arch Linux install medium". Cosmetic, fix when we start caring about end-user-facing polish.
|
||||
- [ ] **Base OS post-install** — what Furtka actually looks like *after* the wizard writes config + reboots: Caddy + Authentik + app store. Robert's area.
|
||||
- [ ] Installer wizard screens S4–S8 (user/password, domain, SSL, diagnostic, confirm) + actually invoking `archinstall` on the chosen disk
|
||||
- [ ] `https://proksi.local` via mDNS + local CA (currently only raw-IP HTTP)
|
||||
- [ ] Caddy + Authentik wired into first-boot bootstrap
|
||||
- [ ] Managed gateway infrastructure — `ns1/ns2.furtka.org` + DNS-01 wildcard automation
|
||||
- [ ] First containerized service (Nextcloud?) with auto-SSO + auto-subdomain
|
||||
|
|
|
|||
52
iso/README.md
Normal file
52
iso/README.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# 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.
|
||||
|
||||
```bash
|
||||
./iso/build.sh
|
||||
```
|
||||
|
||||
Output ISO ends up in `iso/out/furtka-<date>-x86_64.iso`. Around 3–10 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/`)
|
||||
3. Create a VM with:
|
||||
- 2 vCPU, 4 GB RAM, 20 GB disk (empty)
|
||||
- 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 4–8.
|
||||
- **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.
|
||||
62
iso/build.sh
Executable file
62
iso/build.sh
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build a Furtka live ISO.
|
||||
#
|
||||
# From the repo root or from iso/ on any host with Docker:
|
||||
# ./iso/build.sh
|
||||
#
|
||||
# The build runs inside a privileged `archlinux:latest` container because
|
||||
# mkarchiso needs root + loop mounts + an Arch package manager, which
|
||||
# Ubuntu doesn't provide natively. Output ISO goes to iso/out/.
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
OUT_DIR="$SCRIPT_DIR/out"
|
||||
|
||||
if [[ "${FURTKA_ISO_INNER:-0}" != "1" ]]; then
|
||||
mkdir -p "$OUT_DIR"
|
||||
echo "==> Launching build container"
|
||||
exec docker run --rm --privileged \
|
||||
-v "$REPO_ROOT:/work" \
|
||||
-w /work \
|
||||
-e FURTKA_ISO_INNER=1 \
|
||||
archlinux:latest \
|
||||
bash /work/iso/build.sh
|
||||
fi
|
||||
|
||||
# ---- inside the container from here on ----
|
||||
|
||||
echo "==> Syncing pacman, installing archiso"
|
||||
pacman -Syu --noconfirm --needed archiso
|
||||
|
||||
PROFILE_SRC="/usr/share/archiso/configs/releng"
|
||||
PROFILE_WORK="/tmp/furtka-profile"
|
||||
BUILD_WORK="/tmp/furtka-build"
|
||||
OUT_IN_CONTAINER="/work/iso/out"
|
||||
|
||||
rm -rf "$PROFILE_WORK" "$BUILD_WORK"
|
||||
cp -a "$PROFILE_SRC" "$PROFILE_WORK"
|
||||
|
||||
echo "==> Overlaying Furtka customizations"
|
||||
|
||||
cat "$SCRIPT_DIR/overlay/packages.extra" >> "$PROFILE_WORK/packages.x86_64"
|
||||
|
||||
cat "$SCRIPT_DIR/overlay/profiledef.sh" >> "$PROFILE_WORK/profiledef.sh"
|
||||
|
||||
cp -a "$SCRIPT_DIR/overlay/airootfs/." "$PROFILE_WORK/airootfs/"
|
||||
|
||||
mkdir -p "$PROFILE_WORK/airootfs/opt/furtka"
|
||||
cp -a "$REPO_ROOT/webinstaller/." "$PROFILE_WORK/airootfs/opt/furtka/"
|
||||
rm -rf "$PROFILE_WORK/airootfs/opt/furtka/__pycache__"
|
||||
|
||||
mkdir -p "$PROFILE_WORK/airootfs/etc/systemd/system/avahi-daemon.service.d"
|
||||
ln -sf /usr/lib/systemd/system/avahi-daemon.service \
|
||||
"$PROFILE_WORK/airootfs/etc/systemd/system/multi-user.target.wants/avahi-daemon.service"
|
||||
|
||||
echo "==> Building ISO (mkarchiso)"
|
||||
mkdir -p "$OUT_IN_CONTAINER"
|
||||
mkarchiso -v -w "$BUILD_WORK" -o "$OUT_IN_CONTAINER" "$PROFILE_WORK"
|
||||
|
||||
echo
|
||||
echo "==> Done. ISO(s) in $OUT_IN_CONTAINER (on host: iso/out/):"
|
||||
ls -lh "$OUT_IN_CONTAINER"
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
[Unit]
|
||||
Description=Furtka Live Installer (Flask)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/furtka
|
||||
ExecStart=/usr/bin/python -m flask --app app run --host 0.0.0.0 --port 5000
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
@ -0,0 +1 @@
|
|||
../furtka-webinstaller.service
|
||||
4
iso/overlay/packages.extra
Normal file
4
iso/overlay/packages.extra
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
python
|
||||
python-flask
|
||||
avahi
|
||||
nss-mdns
|
||||
9
iso/overlay/profiledef.sh
Normal file
9
iso/overlay/profiledef.sh
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
# Overrides for releng's profiledef.sh — only the fields we want to change.
|
||||
# build.sh sources releng's original first, then this file, so these win.
|
||||
|
||||
iso_name="furtka"
|
||||
iso_label="FURTKA_$(date +%Y%m)"
|
||||
iso_publisher="Furtka <https://furtka.org>"
|
||||
iso_application="Furtka Live Installer"
|
||||
iso_version="$(date +%Y.%m.%d)"
|
||||
Loading…
Add table
Reference in a new issue