nixosModules.settings builds a router from a JSON file: the router
options as they are, plus host name, time zone, SSH keys and
stateVersion; the module system validates it. lib.mkRouterSystem builds
such a system, lib.mkConfigDir the configuration directory installed to
/var/lib/router/config (a flake pinned to this router source, the
settings and an optional local.nix). router-apply builds it and switches
with commit-confirm.
The device must be able to rebuild without internet, so the packages the
rebuilt derivations need are part of the system; they were found by
diffing the derivation graphs of typical changes against the installed
system.
checks.apply installs such a router, boots it offline, checks that the
unchanged settings rebuild to the installed system, applies and
confirms a change, and rolls back an unconfirmed one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
- wireguard.<n>.sourceAddresses: source for router-originated traffic
into a tunnel without an address of its own (PreferredSource on the
routes); checked at build time.
- examples/home: antigua's routing role ported to the router module
(PPPoE with IPv6 inside the session, LAN with ULA and reservation,
local DNS and forward zones, the freifunk/family/mobile tunnels with
explicit zone rules, ddclient following the PPP device).
nixosConfigurations.home, packages.installer-home and a flake check
that builds it.
- PLAN.md: the remaining antigua gaps and the open question about
services that stay behind the router.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
DNS and DHCP (needed to replace a dnsmasq-based router):
- router.dns.domain/hosts/overrides/forwardZones: authoritative local
domain, split-horizon host records with reverse entries, wildcard
overrides and per-zone forwarders (private zones skip DNSSEC).
- lans.<n>.dhcp.reservations: Kea host reservations, published in DNS.
- Tunnels with dns = true admit what their peers route, so a
site-to-site peer without a transfer address can use the resolver.
Installer:
- router-install wipes a disk, creates an ESP and an ext4 root and
installs a prebuilt system with its secrets; nothing is built on the
box. installed-layout is the shared disk layout contract.
- lib.mkInstaller builds an ISO for a given nixosSystem;
packages.installer-example is the example's ISO.
Covered by the dns and installer VM tests (the latter installs onto an
empty disk and boots it under UEFI), wireguard DNS and assertion cases.
PLAN.md records the remaining gaps for replacing the hand-written router.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- Allow the WAN link to also carry LANs when the uplink is PPPoE on a
VLAN with IPv6 off; only the PPP device counts as an uplink then, so
LAN traffic can never match WAN rules.
- Add router.rollback: router-arm / router-confirm / router-rollback
plus `nix run .#deploy`, a commit-confirm deployment flow.
- Wait for the LAN devices instead of the uplink in networkd
wait-online, so an unplugged uplink cannot block boot or activation.
- Add examples/desktop (single NIC, PPPoE on VLAN 1001) as
nixosConfigurations.example, built as a flake check.
Covered by the onestick and rollback VM tests and new assertion cases.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add an IPv4 helper lib (parse, netmask, cidr, overlap) and derive
router.lans.<name>.ipv4.subnet from address and prefixLength instead of
requiring it twice. New assertions reject invalid addresses, DHCP pools that
are reversed, outside the subnet or contain the router address, and
overlapping LANs.
Add `lib` unit tests and an `assertions` check that evaluates broken configs
and compares the exact error messages.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
router.* schema (wan, lans, dns.forwarders) compiled to systemd-networkd,
a generated nftables ruleset with NAT, Kea DHCPv4 and Unbound, plus build-time
assertions against lock-out and interface conflicts.
tests/basic.nix is a three-node VM test (upstream/router/client) covering WAN
DHCP, LAN DHCP and DNS, NAT, WAN-deny and LAN SSH/ping. `nix run .#lab` boots
the same topology in tmux with one console pane per VM and the driver REPL.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>