Add a generic installer that asks for the basics and builds offline

The installer ISO no longer needs a prebuilt system: `router-setup` asks
for the disk, the WAN and LAN interfaces, DHCP or PPPoE (optionally on a
VLAN), host name, LAN address and a password, writes settings.json and
the secrets, builds that router offline (the ISO carries a reference
router system whose closure has everything the build needs) and installs
it with `router-install --system`.

The password is used for the web UI and, hashed, as root's console
password (routerSettings sets root's hashedPasswordFile; it applies when
the account is created at installation).

checks.setup answers router-setup on the console and checks the installed
box: its settings, the web UI and console login with that password, and
a rebuild that reproduces the installed system.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
This commit is contained in:
2026-10-29 09:19:14 +01:00
co-authored by Claude Opus 5.5
parent 5ddff44a35
commit f5909fbdcb
7 changed files with 468 additions and 17 deletions
+62 -4
View File
@@ -21,6 +21,8 @@
# The installer pieces; `lib.mkInstaller` wires them up.
installer = ./modules/installer/install-script.nix;
installer-iso = ./modules/installer/iso.nix;
# The generic installer's `router-setup` (see lib.setupModule).
setup = ./modules/installer/setup.nix;
# Appliance disk image: dm-verity store, volatile root, state on /var.
image = ./modules/image;
# Configuration from a settings file (JSON), rebuilt on the device.
@@ -64,7 +66,16 @@
}:
pkgs.runCommand "router-config" { } ''
mkdir -p $out
cat > $out/flake.nix <<'EOF'
cp ${self.lib.configFlake pkgs} $out/flake.nix
cp ${settings} $out/settings.json
${pkgs.lib.optionalString (local != null) "cp ${local} $out/local.nix"}
'';
# The configuration directory's flake.nix (shared by mkConfigDir and
# router-setup, so both build exactly the same system).
lib.configFlake =
pkgs:
pkgs.writeText "flake.nix" ''
{
inputs.nixos-router.url = "path:${self.outPath}";
outputs =
@@ -76,11 +87,58 @@
};
};
}
EOF
cp ${settings} $out/settings.json
${pkgs.lib.optionalString (local != null) "cp ${local} $out/local.nix"}
'';
# The generic installer: `router-setup` asks for the basics, builds
# the router offline and installs it; the rest is done in the web UI.
# nix build .#installer
# router-setup's configuration: the flake template and the reference
# system whose closure makes the offline build possible. `local` is an
# extra module that the installed systems will import as well.
lib.setupModule =
{
local ? null,
}:
{ pkgs, ... }:
{
imports = [ self.nixosModules.setup ];
router.setup = {
flake = self.lib.configFlake pkgs;
reference =
(self.lib.mkRouterSystem {
settings = builtins.toFile "settings.json" (
builtins.toJSON {
router = {
wan.interface = "eth0";
lans.lan = {
interface = "eth1";
internet = true;
ipv4.address = "192.168.1.1";
dhcp.pool = {
start = "192.168.1.100";
end = "192.168.1.200";
};
};
};
}
);
# A path, not a derivation: the module system would read its attributes.
modules = pkgs.lib.optional (local != null) "${local}";
}).config.system.build.toplevel;
};
};
# The generic installer ISO:
# nix build .#installer
nixosConfigurations.installer = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.installer-iso
(self.lib.setupModule { })
];
};
packages.x86_64-linux.installer = self.nixosConfigurations.installer.config.system.build.isoImage;
# Installer ISO for a given nixosSystem:
# nix build .#installer-example (see packages)
lib.mkInstaller =