From 92b46d1bba75eec512573874720a0dc92bdd5d10 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 12 Apr 2026 21:35:00 +0000
Subject: [PATCH 1/2] Initial plan
From 533c981a7019414f1d54c60215b5f8840cf8363a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 12 Apr 2026 21:38:18 +0000
Subject: [PATCH 2/2] fix(installer): generate diceware password during install
and display before reboot
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/ed1c266b-2f38-4831-9ba0-fa0f59cd162b
Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
---
iso/installer.py | 109 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 104 insertions(+), 5 deletions(-)
diff --git a/iso/installer.py b/iso/installer.py
index b82e8ec..b427e10 100644
--- a/iso/installer.py
+++ b/iso/installer.py
@@ -5,6 +5,7 @@ gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, GLib
import atexit
import os
+import secrets
import subprocess
import sys
import threading
@@ -36,6 +37,26 @@ DEPLOYED_FLAKE = """\
}
"""
+DICEWARE_WORDS = [
+ "apple", "barn", "brook", "cabin", "cedar", "cloud", "coral", "crane",
+ "delta", "eagle", "ember", "fern", "field", "flame", "flora", "flint",
+ "frost", "grove", "haven", "hedge", "holly", "heron", "jade", "juniper",
+ "kelp", "larch", "lemon", "lilac", "linden", "loch", "lotus", "maple",
+ "marsh", "meadow", "mist", "mossy", "mount", "oak", "ocean", "olive",
+ "petal", "pine", "pixel", "plum", "pond", "prism", "quartz", "raven",
+ "ridge", "river", "robin", "rocky", "rose", "rowan", "sage", "sand",
+ "sierra", "silver", "slate", "snow", "solar", "spark", "spruce", "stone",
+ "storm", "summit", "swift", "thorn", "tide", "timber", "torch", "trout",
+ "vale", "vault", "vine", "walnut", "wave", "willow", "wren", "amber",
+ "aspen", "birch", "blaze", "bloom", "bluff", "coast", "copper", "crest",
+ "dune", "elder", "fjord", "forge", "glade", "glen", "glow", "gulf",
+]
+
+def generate_diceware_password():
+ words = [secrets.choice(DICEWARE_WORDS) for _ in range(3)]
+ digit = secrets.randbelow(10)
+ return "-".join(words) + f"-{digit}"
+
try:
logfile = open(LOG, "a")
atexit.register(logfile.close)
@@ -135,6 +156,7 @@ class InstallerWindow(Adw.ApplicationWindow):
self.boot_size = None
self.data_disk = None
self.data_size = None
+ self.free_password = None
# Root navigation view
self.nav = Adw.NavigationView()
@@ -970,6 +992,46 @@ class InstallerWindow(Adw.ApplicationWindow):
run_stream(["sudo", "nix", "--extra-experimental-features", "nix-command flakes",
"flake", "lock", "/mnt/etc/nixos"], buf)
+ # Generate diceware passwords and write them to the installed system
+ GLib.idle_add(append_text, buf, "Setting up user passwords...\n")
+ self.free_password = generate_diceware_password()
+ root_password = generate_diceware_password()
+
+ run(["sudo", "mkdir", "-p", "/mnt/var/lib/secrets"])
+ run(["sudo", "chmod", "700", "/mnt/var/lib/secrets"])
+ proc = subprocess.run(
+ ["sudo", "tee", "/mnt/var/lib/secrets/free-password"],
+ input=self.free_password, capture_output=True, text=True
+ )
+ if proc.returncode != 0:
+ log(proc.stderr)
+ raise RuntimeError(proc.stderr.strip() or "Failed to write free-password")
+ run(["sudo", "chmod", "600", "/mnt/var/lib/secrets/free-password"])
+
+ proc = subprocess.run(
+ ["sudo", "tee", "/mnt/var/lib/secrets/root-password"],
+ input=root_password, capture_output=True, text=True
+ )
+ if proc.returncode != 0:
+ log(proc.stderr)
+ raise RuntimeError(proc.stderr.strip() or "Failed to write root-password")
+ run(["sudo", "chmod", "600", "/mnt/var/lib/secrets/root-password"])
+
+ proc = subprocess.run(
+ ["sudo", "chroot", "/mnt", "/run/current-system/sw/bin/chpasswd"],
+ input=f"free:{self.free_password}\nroot:{root_password}",
+ capture_output=True, text=True
+ )
+ if proc.returncode != 0:
+ proc = subprocess.run(
+ ["sudo", "chroot", "/mnt", "chpasswd"],
+ input=f"free:{self.free_password}\nroot:{root_password}",
+ capture_output=True, text=True
+ )
+ if proc.returncode != 0:
+ log(proc.stderr)
+ raise RuntimeError(proc.stderr.strip() or "Failed to set passwords via chpasswd")
+
GLib.idle_add(self.push_complete)
# ── Complete ───────────────────────────────────────────────────────────
@@ -979,14 +1041,51 @@ class InstallerWindow(Adw.ApplicationWindow):
status = Adw.StatusPage()
status.set_title("Installation Complete!")
- status.set_description("Rebooting…")
- status.set_vexpand(True)
-
+ status.set_description("Before rebooting, write down your login password.")
+ status.set_icon_name("dialog-password-symbolic")
outer.append(status)
- self.push_page("Complete", outer)
+ pw_frame = Gtk.Frame()
+ pw_frame.set_margin_start(60)
+ pw_frame.set_margin_end(60)
+ pw_label = Gtk.Label()
+ pw_label.set_markup(
+ f""
+ f"{GLib.markup_escape_text(self.free_password)}"
+ )
+ pw_label.set_selectable(True)
+ pw_label.set_margin_top(16)
+ pw_label.set_margin_bottom(16)
+ pw_frame.set_child(pw_label)
+ outer.append(pw_frame)
- GLib.timeout_add_seconds(3, lambda: subprocess.run(["sudo", "reboot"]))
+ warning = Gtk.Label()
+ warning.set_markup(
+ ""
+ "⚠ Write this password down now.\n"
+ "You will need it to log in to your computer and the Sovran Hub.\n"
+ "This password cannot be recovered."
+ )
+ warning.set_justify(Gtk.Justification.CENTER)
+ warning.set_wrap(True)
+ warning.set_margin_top(20)
+ warning.set_margin_start(48)
+ warning.set_margin_end(48)
+ outer.append(warning)
+
+ outer.append(Gtk.Label(label="", vexpand=True))
+
+ btn_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=0)
+ btn_box.set_halign(Gtk.Align.CENTER)
+ btn_box.set_margin_bottom(32)
+ reboot_btn = Gtk.Button(label="I Have Written Down My Password — Reboot Now")
+ reboot_btn.add_css_class("suggested-action")
+ reboot_btn.add_css_class("pill")
+ reboot_btn.connect("clicked", lambda b: subprocess.run(["sudo", "reboot"]))
+ btn_box.append(reboot_btn)
+ outer.append(btn_box)
+
+ self.push_page("Complete", outer)
return False
# ── Error screen ───────────────────────────────────────────────────────