Squashed commit of the following:

commit bf4873eb012235b4d82cd79c4f508064ead301ab
Author: Joao Porta <jpedro.porta@gmail.com>
Date:   Fri Jul 17 17:24:26 2026 -0300

    wake on lan
This commit is contained in:
2026-07-17 17:25:16 -03:00
parent 85d5fa453a
commit cdbad8910c
8 changed files with 243 additions and 10 deletions
+2
View File
@@ -14,6 +14,7 @@
../../modules/nixos/steam
../../modules/nixos/awsvpn
../../modules/nixos/tailscale
../../modules/nixos/wake-on-lan
];
# Bootloader
@@ -115,6 +116,7 @@
hyprland.enable = true;
steam.enable = true;
tailscale.enable = true;
wake-on-lan.enable = true;
};
##------------------------------
+5 -10
View File
@@ -20,36 +20,31 @@
size = 10.3;
normal = {
family = "BerkeleyMono Nerd Font Mono";
style = "Regular";
};
bold = {
family = "BerkeleyMono Nerd Font Mono";
style = "ExtraBold";
};
italic = {
family = "BerkeleyMono Nerd Font Mono";
style = "Oblique";
};
bold_italic = {
family = "BerkeleyMono Nerd Font Mono";
style = "ExtraBold Oblique";
};
};
window = {
decorations = "None";
opacity = 0.8;
blur = true;
blur = false;
dynamic_padding = true;
padding = {
x = 10;
y = 16;
};
};
keyboard = {
bindings = [
{
key = "Return";
mods = "Shift";
chars = "\u001B\r";
}
];
};
};
};
};
+71
View File
@@ -0,0 +1,71 @@
{
config,
lib,
pkgs,
...
}:
let
cfg = config.custom.wake-on-lan;
iface = "enp14s0";
mac = "d8:43:ae:5a:ae:12";
wake-jpporta-nixos = pkgs.writeShellScriptBin "wake-jpporta-nixos" ''
exec ${pkgs.wakeonlan}/bin/wakeonlan -i "''${1:-255.255.255.255}" ${mac}
'';
in
{
options.custom.wake-on-lan = {
enable = lib.mkEnableOption "Wake-on-LAN on the wired NIC (magic-packet mode)";
};
config = lib.mkIf cfg.enable {
# Keep `wol g` applied on the NIC after the link is up (boot path).
systemd.services."wol-${iface}" = {
description = "Enable Wake-on-LAN (magic packet) on ${iface}";
wantedBy = [ "multi-user.target" ];
wants = [
"network-online.target"
"NetworkManager.service"
];
after = [
"network-online.target"
"NetworkManager.service"
"NetworkManager-wait-online.service"
];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${pkgs.ethtool}/bin/ethtool -s ${iface} wol g";
};
};
# Re-apply `wol g` after resume from suspend / hibernate so NetworkManager
# cannot silently drop the WOL flag on link renegotiation.
systemd.services."wol-${iface}-resume" = {
description = "Re-apply Wake-on-LAN (magic packet) on ${iface} after resume";
wantedBy = [
"suspend.target"
"hibernate.target"
"hybrid-sleep.target"
];
after = [
"systemd-suspend.service"
"systemd-hibernate.service"
"systemd-hybrid-sleep.service"
];
serviceConfig = {
Type = "oneshot";
ExecStart = "${pkgs.ethtool}/bin/ethtool -s ${iface} wol g";
};
};
environment.systemPackages = [
pkgs.ethtool
pkgs.wakeonlan
wake-jpporta-nixos
];
};
}
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-07-17
+59
View File
@@ -0,0 +1,59 @@
## Context
`jpporta-nixos` is a desktop machine with a wired Ethernet interface (`enp14s0`, MAC `d8:43:ae:5a:ae:12`) managed by NetworkManager. The user wants to power the machine on remotely via Wake-on-LAN so it can be reached over Tailscale + SSH without manual intervention.
WOL requires three layers of configuration:
1. **Firmware (BIOS/UEFI):** "Wake on LAN" must be enabled on the NIC, and the machine must remain in a low-power state after shutdown (most desktop boards default to this; some servers default to "power on after AC loss" which would defeat the purpose).
2. **Kernel/driver (NixOS):** The NIC driver must be told to keep listening for magic packets while the system is off. For most Intel NICs this means `ethtool -s <iface> wol g` (magic packet mode).
3. **Network:** Magic packets are sent to the layer-2 broadcast address (`ff:ff:ff:ff:ff:ff`) and rely on the NIC matching its own MAC. The host cannot route or filter them above the NIC, so no userspace daemon is required to *receive* a packet. A userspace tool (`wakeonlan`) is required to *send* one.
NixOS already provides `networking.wakeOnLan.enable`, but that option is hard-coded to `eth0` and is incompatible with predictable interface names (`enp14s0`). The NixOS-supported path for a named, predictable interface is a custom systemd unit that invokes `ethtool` against the specific interface. This is the approach used here because it survives reboots, resumes, and NetworkManager reconnects.
## Goals / Non-Goals
**Goals:**
- Persist `wol g` on `enp14s0` across reboots and resume-from-suspend.
- Provide `ethtool` and `wakeonlan` on `$PATH` so the user can inspect and send magic packets.
- Provide a convenience script `wake-jpporta-nixos` that sends a WOL packet to this machine's MAC.
- Wrap the configuration in a reusable module (`custom.wake-on-lan.enable`) consistent with existing patterns.
- Document the out-of-band BIOS / router configuration steps.
**Non-Goals:**
- Setting BIOS options (firmware is out of reach from NixOS).
- Configuring router port-forwarding for WOL-over-internet (router is out of scope).
- Listening for / relaying WOL packets on this host — that is the NIC's job once `wol g` is set.
- Changing the network interface naming scheme or replacing NetworkManager.
## Decisions
1. **Per-interface systemd unit over `networking.wakeOnLan.enable`.**
- The top-level `networking.wakeOnLan.enable` is hard-coded to `eth0`, which doesn't match the predictable interface name `enp14s0` used here.
- Alternative considered: `networking.interfaces.enp14s0.wakeOnLan.enable = true`. This option does exist in modern NixOS, but on this machine it interacts awkwardly with NetworkManager (which re-loads NIC settings on reconnect) and the underlying implementation still relies on an ethtool invocation that may be racy with NetworkManager bringing the link up.
- **Decision:** Use a dedicated systemd `oneshot` service with a `network-online.target` dependency. This makes the WOL setting idempotent, runs after NetworkManager is up, and can also be triggered on resume-from-suspend by adding it to the `suspend.target` resume path. Concretely we ship a unit `wol-enp14s0.service` that runs `ethtool -s enp14s0 wol g`.
2. **Package set: `ethtool` + `iputils` (which provides `wakeonlan`).**
- `pkgs.ethtool` is the standard tool for inspecting and setting NIC driver options, including WOL modes.
- `pkgs.iputils` provides `wakeonlan`, the simplest CLI for sending a magic packet to a target MAC.
- Alternative considered: `pkgs.wol` (the separate, older `wol` program). `wakeonlan` from iputils is more widely used, has a simpler interface (`wakeonlan <mac>`), and is what most WOL guides assume.
3. **Convenience wrapper script `wake-jpporta-nixos`.**
- Hard-codes the MAC of this machine's wired NIC (`d8:43:ae:5a:ae:12`) into a small `writeShellScriptBin`-packaged script.
- Rationale: avoids having to remember or look up the MAC every time. The wrapper accepts an optional broadcast IP argument so the user can target `192.168.0.255` from off-LAN once the router is configured to forward UDP 9 to that broadcast.
4. **Module pattern: `modules/nixos/wake-on-lan/default.nix` with `custom.wake-on-lan.enable`.**
- Mirrors the existing pattern used by `modules/nixos/tailscale/` and `modules/nixos/hyprland/`.
- Defaults to disabled. Host (`jpporta-nixos`) opts in via `custom.wake-on-lan.enable = true`.
## Risks / Trade-offs
- **[Risk]** `ethtool -s ... wol g` only takes effect while the kernel NIC driver is loaded. Some BIOS/firmware combinations fully power down the NIC on shutdown, making WOL impossible regardless of the OS setting.
- **Mitigation:** Document this clearly in the change notes. The user must verify with `ethtool enp14s0` after a `systemctl poweroff` that `Supports Wake-on: pumbg` and `Wake-on: g` are still present.
- **[Risk]** Some desktop boards default to "Power on after AC loss = Power on" which would make the machine boot after any power outage regardless of WOL — undermining the use case.
- **Mitigation:** Document the BIOS setting ("After Power Loss" → "Power Off" or "Stay Off") in the change notes.
- **[Risk]** NetworkManager may re-apply link settings on resume, overwriting `wol g`.
- **Mitigation:** The systemd unit is wired to run on resume via a `path`/`service` pair (or a manual `systemctl restart wol-enp14s0.service` hook). For the initial implementation we trigger it on boot and rely on the user to re-run if it ever drifts; the follow-up task in `tasks.md` adds the resume hook.
- **[Trade-off]** Hard-coding the MAC and interface name in the Nix module means the module is specific to `jpporta-nixos`. We accept this trade-off because the module is only enabled on this host and the alternative (parameterizing) would add ceremony for a single user.
+27
View File
@@ -0,0 +1,27 @@
## Why
The user wants to power on `jpporta-nixos` remotely via Wake-on-LAN (WOL). This is useful when the machine is shut down but the user wants to access it from another device (e.g., over Tailscale + SSH) without manually pressing the power button. Currently the machine has no WOL configuration: nothing keeps the NIC in a low-power listening state, and no tooling exists on the host to inspect or send WOL packets.
## What Changes
- Enable Wake-on-LAN on the wired Ethernet interface (`enp14s0`) so the NIC keeps listening for magic packets while the system is powered off.
- Add `ethtool` so the user can inspect the current WOL settings and supported modes on the NIC.
- Add `wol` (the `iputils`-style `wakeonlan` command) so WOL packets can be sent from this host to other machines, and from other machines back to this one (using its MAC `d8:43:ae:5a:ae:12`).
- Add a small helper script (`wake-jpporta-nixos`) that sends a WOL magic packet to this machine's MAC address, so it can be invoked by name.
- Document the manual BIOS-level steps that are required for WOL to work (these cannot be set from NixOS).
## Capabilities
### New Capabilities
- `wake-on-lan`: Configures Wake-on-LAN on the host's wired NIC and provides tooling/scripts to inspect and send magic packets.
### Modified Capabilities
- None
## Impact
- **Code:** A new optional module `modules/nixos/wake-on-lan/` will be added and imported into `hosts/jpporta-nixos/configuration.nix`. The module will expose a `custom.wake-on-lan.enable` toggle consistent with the project's existing module pattern (see `modules/nixos/tailscale/`).
- **System:** A systemd unit will be installed that runs `ethtool -s enp14s0 wol g` on boot and on resume, persisting the WOL setting across reboots and sleep states.
- **User:** Two new commands will be on `$PATH`: `ethtool` and `wakeonlan`, plus a convenience wrapper `wake-jpporta-nixos`.
- **Hardware:** Requires BIOS-level "Wake on LAN" to be enabled on the motherboard and a stable AC power source. The user must configure these out-of-band.
- **Network:** To wake the machine from outside the LAN, the router must forward UDP port 9 (or 7) to the broadcast address. This is a router-side concern, not configurable from NixOS.
@@ -0,0 +1,51 @@
## ADDED Requirements
### Requirement: Persistent Wake-on-LAN on the wired NIC
The system SHALL keep the wired Ethernet interface (`enp14s0`) configured to wake on magic-packet while powered off.
#### Scenario: WOL applied on boot
- **WHEN** the system finishes booting
- **THEN** `ethtool enp14s0` SHALL report `Wake-on: g`
#### Scenario: WOL applied on resume from suspend
- **WHEN** the system resumes from suspend
- **THEN** `ethtool enp14s0` SHALL report `Wake-on: g`
### Requirement: Wake-on-LAN tooling available
The system SHALL provide `ethtool` and `wakeonlan` on `$PATH` so the user can inspect and trigger WOL.
#### Scenario: Inspect WOL state
- **WHEN** the user runs `ethtool enp14s0`
- **THEN** the command SHALL exit 0
- **AND** the output SHALL include `Supports Wake-on:` and `Wake-on:` lines
#### Scenario: Send a magic packet
- **WHEN** the user runs `wakeonlan <MAC>` (or `wake-jpporta-nixos`)
- **THEN** the command SHALL exit 0
- **AND** a UDP magic-packet SHALL be broadcast to `255.255.255.255:9`
### Requirement: Convenience wrapper to wake this host
The system SHALL provide a `wake-jpporta-nixos` command that sends a WOL magic packet to this machine's wired NIC MAC.
#### Scenario: Wake this machine from LAN
- **WHEN** the user runs `wake-jpporta-nixos`
- **THEN** the host's NIC SHALL receive the magic packet
- **AND** the system SHALL begin booting
#### Scenario: Wake this machine through a custom broadcast address
- **WHEN** the user runs `wake-jpporta-nixos 192.168.0.255`
- **THEN** the magic packet SHALL be sent to the `192.168.0.255` broadcast address
- **AND** the host SHALL begin booting
### Requirement: Reusable opt-in module
The system SHALL expose a `custom.wake-on-lan.enable` option that, when enabled, configures all of the above.
#### Scenario: Module disabled
- **WHEN** `custom.wake-on-lan.enable` is `false` or unset
- **THEN** the system SHALL NOT install the WOL systemd unit
- **AND** the system SHALL NOT install the `ethtool`, `wakeonlan`, or `wake-jpporta-nixos` packages
#### Scenario: Module enabled
- **WHEN** `custom.wake-on-lan.enable` is `true`
- **THEN** the WOL systemd unit SHALL be active
- **AND** the `wake-jpporta-nixos` command SHALL be on `$PATH`
+26
View File
@@ -0,0 +1,26 @@
## 1. Create the WOL NixOS module
- [x] 1.1 Create `modules/nixos/wake-on-lan/default.nix` with a `custom.wake-on-lan.enable` option.
- [x] 1.2 Define a systemd `oneshot` service `wol-enp14s0.service` that runs `ethtool -s enp14s0 wol g`.
- [x] 1.3 Wire the unit's `after`/`wants` to `network-online.target` and `NetworkManager.service` so it runs after the link is up.
- [x] 1.4 Add the unit to the resume path so `wol g` is re-applied after suspend/resume (use `systemd-suspend.service` or a `path` unit hook).
## 2. Add packages and the convenience wrapper
- [x] 2.1 Add `pkgs.ethtool` and `pkgs.wakeonlan` to `environment.systemPackages` when the module is enabled. *(Note: `pkgs.iputils` does not provide `wakeonlan` in nixpkgs — see implementation note.)*
- [x] 2.2 Create a `wake-jpporta-nixos` shell script via `pkgs.writeShellScriptBin` that invokes `wakeonlan -i ${1:-255.255.255.255} d8:43:ae:5a:ae:12`.
- [x] 2.3 Add the wrapper to `environment.systemPackages`.
## 3. Wire the module into the host
- [x] 3.1 Add `../../modules/nixos/wake-on-lan` to `imports` in `hosts/jpporta-nixos/configuration.nix`.
- [x] 3.2 Add `custom.wake-on-lan.enable = true;` under the existing `custom = { ... };` block in the host config.
## 4. Verify and document
- [x] 4.1 Rebuild the system with `sudo nixos-rebuild switch --flake .#jpporta-nixos`.
- [x] 4.2 Run `ethtool enp14s0` and confirm `Supports Wake-on:` lists `g` and `Wake-on: g`.
- [x] 4.3 Run `systemctl status wol-enp14s0.service` and confirm the unit is `active (exited)`.
- [x] 4.4 Run `wake-jpporta-nixos` once while the machine is up to confirm the script runs without error (it will be a no-op when the host is already up).
- [x] 4.5 Shut the machine down, then run `wake-jpporta-nixos` from another machine on the same LAN and confirm `jpporta-nixos` powers on.
- [x] 4.6 Document the BIOS-level prerequisite ("Wake on LAN" enabled, "After Power Loss" → Power Off / Stay Off) in the change notes or a README so it's not lost.