Web UI: dark/light themes (appliance layout, sidebar, switch checkboxes),
theme preference on the Advanced page, Changes page with the pending diff.
Interface names: router.interfaces.<name>.mac, lans.<l>.deviceName and
pppoe.vlanName; a rename is staged for the next reboot by router-apply.
Remote access: an MCP server (POST /mcp) and an OpenAPI REST API
(/api/v1, /api/openapi.json) with the same tokens, scopes, limits and audit
log; tools to read status, settings, leases, logs (get_logs), and to edit
and check the saved settings (never applied). Tokens on the API access page.
Firewall: a reserved `local` zone (the router itself) as the destination of
rules, so the web UI or SSH can be allowed from the WAN.
PLAN.md lists what is still left.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
router.firewall.nat replaces the dnat/toPort/hairpin fields of the filter
rules. Filter rules gain natOnly, and may use wan with log and rate limit
in the web UI. The Firewall page edits the NAT table and can add the
matching allow rule. Settings schema 1 (migrations/1.jq) converts old
forwards.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
The DHCP page edits the reservations per LAN (checked like the module's
assertions) and offers active clients for reservation. The web UI checks
that need no installed system moved from apply.nix into webui.nix (one
node, under a minute); apply.nix shares one apply for the pool change and
a reservation.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AeFqV9dZrK6nipHkPfyM4F
router-webui (Go, standard library only) edits settings.json: host
name, WAN (DHCP or PPPoE), LANs with DHCP, and the raw JSON for the
rest; keys the forms do not know are kept. It applies and confirms by
starting router-apply.service and router-confirm.service, which a
switch never restarts, and shows the build log and the rollback
countdown. HTTPS with a self-signed certificate, a PBKDF2 password
hash, CSRF tokens and an Origin check; reachable from management LANs
only (new router.managementPorts).
router-apply records the settings it built and router-confirm keeps
them as settings.confirmed.json (new rollback.onConfirm hook), so the
UI can tell unapplied changes and discard them.
checks.apply now also logs in from a LAN client, discards the change
left by the rollback, edits the DHCP pool, applies and confirms.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>