From 7442dbe47e11f5b7ede2bcc1122b0998c60388a0 Mon Sep 17 00:00:00 2001 From: Daniel Maksymilian Syrnicki Date: Tue, 14 Apr 2026 18:08:59 +0200 Subject: [PATCH] feat: console welcome with proksi.local + post-install reboot flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two user-visible polish passes on top of the walking-skeleton install: - Console welcome: live ISO's getty no longer shows the bare Arch prompt. `/etc/hostname` is now `proksi` so avahi advertises `proksi.local`; a systemd oneshot (`furtka-issue.service`, runs after network-online.target) regenerates `/etc/issue` via `/usr/local/bin/furtka-update-issue` to show both `http://proksi.local:5000` (preferred, via mDNS — avahi and nss-mdns are already in `packages.extra`) and the raw IP as a fallback for networks where mDNS is flaky. `agetty --reload` nudges the already- running login prompt to redraw. - /install/log now polls a JSON endpoint (`/install/log.json`) every 3 s instead of meta-refresh, so expanding the collapsed log `
` doesn't get eaten by the refresh. Noscript fallback keeps the meta-refresh for JS-off users. When the install finishes, the Done state shows a Reboot-now button that POSTs to `/install/reboot` (guarded server-side to only reboot once status is "done", so a panicked click mid-pacstrap can't brick the box). A confirm() reminds the user to pull the USB / eject the ISO first. End-to-end tested on a Proxmox VM 2026-04-14: boot → wizard → archinstall → Done state → Reboot now → VM came back up → login as created user → `docker ps` worked. Co-Authored-By: Claude Opus 4.6 (1M context) --- iso/overlay/airootfs/etc/hostname | 1 + iso/overlay/airootfs/etc/issue | 6 ++ .../etc/systemd/system/furtka-issue.service | 12 +++ .../furtka-issue.service | 1 + .../usr/local/bin/furtka-update-issue | 23 +++++ webinstaller/app.py | 22 ++++- webinstaller/templates/install/log.html | 89 ++++++++++++++++--- webinstaller/templates/install/rebooting.html | 10 +++ 8 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 iso/overlay/airootfs/etc/hostname create mode 100644 iso/overlay/airootfs/etc/issue create mode 100644 iso/overlay/airootfs/etc/systemd/system/furtka-issue.service create mode 120000 iso/overlay/airootfs/etc/systemd/system/multi-user.target.wants/furtka-issue.service create mode 100755 iso/overlay/airootfs/usr/local/bin/furtka-update-issue create mode 100644 webinstaller/templates/install/rebooting.html diff --git a/iso/overlay/airootfs/etc/hostname b/iso/overlay/airootfs/etc/hostname new file mode 100644 index 0000000..76051cd --- /dev/null +++ b/iso/overlay/airootfs/etc/hostname @@ -0,0 +1 @@ +proksi diff --git a/iso/overlay/airootfs/etc/issue b/iso/overlay/airootfs/etc/issue new file mode 100644 index 0000000..42ea414 --- /dev/null +++ b/iso/overlay/airootfs/etc/issue @@ -0,0 +1,6 @@ + + Furtka Live Installer starting… + + Once ready, open http://proksi.local:5000 on another device + on your network. The exact URL will appear below. + diff --git a/iso/overlay/airootfs/etc/systemd/system/furtka-issue.service b/iso/overlay/airootfs/etc/systemd/system/furtka-issue.service new file mode 100644 index 0000000..d1768ed --- /dev/null +++ b/iso/overlay/airootfs/etc/systemd/system/furtka-issue.service @@ -0,0 +1,12 @@ +[Unit] +Description=Write Furtka /etc/issue with current IP for the console welcome +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/furtka-update-issue +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/iso/overlay/airootfs/etc/systemd/system/multi-user.target.wants/furtka-issue.service b/iso/overlay/airootfs/etc/systemd/system/multi-user.target.wants/furtka-issue.service new file mode 120000 index 0000000..1b8a2e3 --- /dev/null +++ b/iso/overlay/airootfs/etc/systemd/system/multi-user.target.wants/furtka-issue.service @@ -0,0 +1 @@ +../furtka-issue.service \ No newline at end of file diff --git a/iso/overlay/airootfs/usr/local/bin/furtka-update-issue b/iso/overlay/airootfs/usr/local/bin/furtka-update-issue new file mode 100755 index 0000000..e0b649f --- /dev/null +++ b/iso/overlay/airootfs/usr/local/bin/furtka-update-issue @@ -0,0 +1,23 @@ +#!/bin/bash +# Regenerates /etc/issue so the live-ISO console tells the user which URL +# to open in their browser. Shows proksi.local (via avahi/mDNS) as the +# preferred URL and the raw IP as a fallback for networks where mDNS +# doesn't work. Reload at the end nudges agetty to redraw. +set -e + +ip=$(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1) + +{ + echo + echo " Open Furtka in a browser on another device on your network:" + echo + echo " http://proksi.local:5000 (easy — try this first)" + if [ -n "$ip" ]; then + echo " http://${ip}:5000 (fallback if the first doesn't work)" + fi + echo + echo " Then follow the wizard to install Furtka on this machine." + echo +} > /etc/issue + +agetty --reload 2>/dev/null || true diff --git a/webinstaller/app.py b/webinstaller/app.py index e7e3dde..69bf035 100644 --- a/webinstaller/app.py +++ b/webinstaller/app.py @@ -5,7 +5,7 @@ import subprocess from pathlib import Path from drives import list_scored_devices -from flask import Flask, redirect, render_template, request, url_for +from flask import Flask, jsonify, redirect, render_template, request, url_for app = Flask(__name__) @@ -269,5 +269,25 @@ def install_log_view(): ) +@app.route("/install/log.json") +def install_log_json(): + log = INSTALL_LOG.read_text() if INSTALL_LOG.exists() else "" + return jsonify(log=log, progress=parse_install_progress(log)) + + +@app.route("/install/reboot", methods=["POST"]) +def install_reboot(): + # Only allow rebooting once the install has actually finished — we don't + # want a panicked click during install to reboot mid-pacstrap. + log = INSTALL_LOG.read_text() if INSTALL_LOG.exists() else "" + if parse_install_progress(log)["status"] != "done": + return redirect(url_for("install_log_view")) + subprocess.Popen( + ["/usr/bin/systemctl", "reboot"], + start_new_session=True, + ) + return render_template("install/rebooting.html") + + if __name__ == "__main__": app.run(debug=True, port=5000) diff --git a/webinstaller/templates/install/log.html b/webinstaller/templates/install/log.html index 392ca3a..00e530d 100644 --- a/webinstaller/templates/install/log.html +++ b/webinstaller/templates/install/log.html @@ -2,29 +2,96 @@ {% block title %}Installing… · Furtka Installer{% endblock %} {% block head_extra %} +{# Fallback for users with JS disabled — otherwise the JS below takes over + and updates in-place so the log
doesn't re-collapse. #} + {% endblock %} {% block step_indicator %}Installing{% endblock %} {% block content %} +

+{% if progress.status == "done" %}Furtka is ready +{% elif progress.status == "error" %}Installation hit a snag +{% else %}Installing Furtka{% endif %} +

+

+{% if progress.status == "done" %}Installation finished. Remove the installer USB / eject the ISO, then click Reboot. +{% elif progress.status == "error" %}Something went wrong. Open the details below and share them so we can help. +{% else %}This takes a few minutes. Don't close this page or power off the machine.{% endif %} +

+ {% if progress.status == "done" %} -

Furtka is ready

-

Installation finished. Remove the USB / eject the installer image, then reboot.

-{% elif progress.status == "error" %} -

Installation hit a snag

-

Something went wrong. Open the details below and share them so we can help.

-{% else %} -

Installing Furtka

-

This takes a few minutes. Don't close this page or power off the machine.

+
+
+ +
+
{% endif %}
-
+
-

{{ progress.phase }} · {{ progress.percent }}%

+

{{ progress.phase }} · {{ progress.percent }}%

Show details -
{{ log or "(waiting for install to start)" }}
+
{{ log or "(waiting for install to start)" }}
+ + {% endblock %} diff --git a/webinstaller/templates/install/rebooting.html b/webinstaller/templates/install/rebooting.html new file mode 100644 index 0000000..a9754f8 --- /dev/null +++ b/webinstaller/templates/install/rebooting.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block title %}Rebooting · Furtka Installer{% endblock %} +{% block step_indicator %}Done{% endblock %} + +{% block content %} +

Rebooting…

+

The machine is restarting. This page will stop responding in a moment — that's expected.

+

When the machine comes back up, log in with the username and password you set during the install.

+{% endblock %}