Files
nixos-router/flake.nix
T
nilsandClaude Opus 5.5 daa70fe7ac Add a read-only appliance image with a dm-verity protected store
nixosModules.image builds a GPT disk image with systemd-repart: ESP with a
UKI carrying the store's verity root hash, an erofs /nix/store on a
dm-verity partition, a tmpfs root, an immutable /etc overlay and an ext4
state partition on /var. No Nix on the device; it is replaced as a whole.

The store is made with 4 KiB erofs blocks: systemd-repart passes the 512
byte sector size to mkfs.erofs, and libblkid cannot verify the checksum of
such a superblock, so udev never reported the file system and the initrd
waited forever for /dev/mapper/usr.

examples/image (nix build .#image) is a hardware test build with a debug
console password; checks.image boots the image under UEFI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:54:24 +01:00

145 lines
4.8 KiB
Nix

{
description = "nixos-router: a NixOS-based distribution for routers/firewalls";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
outputs =
{ self, nixpkgs }:
let
systems = [
"x86_64-linux"
"aarch64-linux"
];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system});
in
{
nixosModules = {
router = ./modules/router;
default = self.nixosModules.router;
# Disk layout contract shared by router-install and target configs.
installed-layout = ./modules/installer/layout.nix;
# The installer pieces; `lib.mkInstaller` wires them up.
installer = ./modules/installer/install-script.nix;
installer-iso = ./modules/installer/iso.nix;
# Appliance disk image: dm-verity store, volatile root, state on /var.
image = ./modules/image;
};
# Installer ISO for a given nixosSystem:
# nix build .#installer-example (see packages)
lib.mkInstaller =
{ configuration }:
nixpkgs.lib.nixosSystem {
inherit (configuration.pkgs.stdenv.hostPlatform) system;
modules = [
self.nixosModules.installer-iso
{
router.installer.system = configuration.config.system.build.toplevel;
# Reach the live system with the keys that will reach the router.
users.users.root.openssh.authorizedKeys.keys =
configuration.config.users.users.root.openssh.authorizedKeys.keys;
}
];
};
packages.x86_64-linux.installer-home =
(self.lib.mkInstaller { configuration = self.nixosConfigurations.home; })
.config.system.build.isoImage;
packages.x86_64-linux.installer-example =
(self.lib.mkInstaller { configuration = self.nixosConfigurations.example; })
.config.system.build.isoImage;
# A deployable starting point; copy examples/desktop and adapt it.
nixosConfigurations.example = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.router
./examples/desktop
];
};
# The home gateway replacing antigua's router role (see examples/home).
nixosConfigurations.home = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.router
./examples/home
];
};
# Appliance image (verity store, volatile root) for trying on hardware:
# nix build .#image -> result/*.raw
nixosConfigurations.image = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.router
self.nixosModules.image
./examples/image
];
};
packages.x86_64-linux.image = self.nixosConfigurations.image.config.system.build.image;
# `nix run .#deploy -- <flake-attr> <user@host>`: activate a new
# configuration with commit-confirm; see scripts/deploy.sh.
apps.x86_64-linux.deploy =
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
deploy = pkgs.writeShellApplication {
name = "router-deploy";
runtimeInputs = with pkgs; [
nixos-rebuild
openssh
coreutils
];
text = builtins.readFile ./scripts/deploy.sh;
};
in
{
type = "app";
program = "${deploy}/bin/router-deploy";
};
# VM tests need KVM; only wired up for x86_64 until aarch64 CI exists.
checks.x86_64-linux =
import ./tests {
inherit self;
pkgs = nixpkgs.legacyPackages.x86_64-linux;
}
// {
# The example must keep building as a complete system.
example = self.nixosConfigurations.example.config.system.build.toplevel;
home = self.nixosConfigurations.home.config.system.build.toplevel;
};
# `nix run .#lab`: boot the basic test topology in tmux, one console pane
# per VM plus the interactive test-driver REPL.
apps.x86_64-linux.lab =
let
pkgs = nixpkgs.legacyPackages.x86_64-linux;
test = self.checks.x86_64-linux.basic;
lab = pkgs.writeShellApplication {
name = "router-lab";
runtimeInputs = with pkgs; [
tmux
util-linux
gnugrep
coreutils
];
text = import ./tests/lab.nix {
inherit (pkgs) lib;
driver = test.driverInteractive;
nodes = builtins.attrNames test.nodes;
};
};
in
{
type = "app";
program = "${lab}/bin/router-lab";
};
formatter = forAllSystems (pkgs: pkgs.nixfmt);
};
}