3 Commits

Author SHA1 Message Date
Sovran_Systems 3a59944967 Merge pull request #212 from naturallaw777/copilot/update-installer-password-prompt
[WIP] Update installer to show generated login password
2026-04-12 16:38:34 -05:00
copilot-swe-agent[bot] 533c981a70 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>
2026-04-12 21:38:18 +00:00
copilot-swe-agent[bot] 92b46d1bba Initial plan 2026-04-12 21:35:00 +00:00
+104 -5
View File
@@ -5,6 +5,7 @@ gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw, GLib from gi.repository import Gtk, Adw, GLib
import atexit import atexit
import os import os
import secrets
import subprocess import subprocess
import sys import sys
import threading 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: try:
logfile = open(LOG, "a") logfile = open(LOG, "a")
atexit.register(logfile.close) atexit.register(logfile.close)
@@ -135,6 +156,7 @@ class InstallerWindow(Adw.ApplicationWindow):
self.boot_size = None self.boot_size = None
self.data_disk = None self.data_disk = None
self.data_size = None self.data_size = None
self.free_password = None
# Root navigation view # Root navigation view
self.nav = Adw.NavigationView() self.nav = Adw.NavigationView()
@@ -970,6 +992,46 @@ class InstallerWindow(Adw.ApplicationWindow):
run_stream(["sudo", "nix", "--extra-experimental-features", "nix-command flakes", run_stream(["sudo", "nix", "--extra-experimental-features", "nix-command flakes",
"flake", "lock", "/mnt/etc/nixos"], buf) "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) GLib.idle_add(self.push_complete)
# ── Complete ─────────────────────────────────────────────────────────── # ── Complete ───────────────────────────────────────────────────────────
@@ -979,14 +1041,51 @@ class InstallerWindow(Adw.ApplicationWindow):
status = Adw.StatusPage() status = Adw.StatusPage()
status.set_title("Installation Complete!") status.set_title("Installation Complete!")
status.set_description("Rebooting…") status.set_description("Before rebooting, write down your login password.")
status.set_vexpand(True) status.set_icon_name("dialog-password-symbolic")
outer.append(status) 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"<span font_family='monospace' size='xx-large' weight='bold'>"
f"{GLib.markup_escape_text(self.free_password)}</span>"
)
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(
"<span foreground='#e8a838' size='medium' weight='bold'>"
"⚠ 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.</span>"
)
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 return False
# ── Error screen ─────────────────────────────────────────────────────── # ── Error screen ───────────────────────────────────────────────────────