Bernardo, post-reboot: "Use for login" was the wrong question. Whether the
finger works is one decision, not two, and whether login prompts at all is
a different decision that was never in the menu.
System › Fingerprint is now a single Fingerprint (on/off) switch, leading
the menu with enroll/list/verify/delete as the plumbing behind it. It
writes the one settings.fingerprint.pam key, and modules/home/idle.nix now
defaults idle.fingerprint from that same key — so the lock screen and
login/sudo move together instead of drifting apart the way they did until
e2de906. nomarchy-fingerprint does the two rebuilds this needs (sudo
system for PAM, home switch for hyprlock) and refuses to turn on with no
finger enrolled.
System › Auto-login is new (nomarchy-autologin), and it is what decides
whether anything is asked at boot: auto-login on means no prompt whatever
the fingerprint switch says; off means the greeter asks, for a password or
a finger. Installer-seeded ON for LUKS machines — the passphrase already
gates the disk — and off without it, where the greeter is the only thing
between power-on and the desktop.
Both had to become state-owned to be toggleable at all, which surfaced two
real bugs:
* nomarchy.system.greeter.autoLogin defaulted from
`config.nomarchy.settings…` — an attribute that exists ONLY on the Home
Manager side. On NixOS it is absent and `or null` swallowed the error,
so the default silently evaluated to null on every machine ever built.
That is why the installer baked a Nix line: the state path never
worked. Now read via theme-state-read.nix (the hardware.nix/timezone.nix
pattern) and mkDefault'd, so the menu owns it and a hand-set line still
pins it. Two more options read the same phantom bridge — BACKLOG #116.
* `theme-sync get` printed Python's "None" for a JSON null, so every
`case … null)` a caller writes would miss. Now prints "null", as the
comment above it already promised for booleans.
The installer seeds the state instead of emitting the system.nix line,
because that line outranks the state and would strand the toggle.
V1 (V3 pending: HARDWARE-QUEUE). nix flake check --no-build, installer-
safety and option-docs all pass. Proved by eval/build, not assumed: a state
carrying autoLogin yields greetd initial_session {"user":"bernardo"}, the
template state (no autoLogin) yields none, and a hand-set null beats a state
that says otherwise; a state with only fingerprint.pam=true — nothing set by
hand — renders the hyprlock auth.fingerprint block; both new tools pass
bash -n and land in systemPackages (nomarchy-fingerprint only with a
reader); the patcher writes settings.greeter.autoLogin and no system.nix
line; and the get round trip prints null, so the menu reads "Auto-login
(off)" where it would have read "(on)".
The reader itself, the two rebuilds, and the reboot are hardware — queued.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
23 KiB
Hardware support
How Nomarchy enables CPUs, GPUs, laptops, firmware, and peripherals — and what to do when your machine is not in the happy path.
Queue:
agent/BACKLOG.md(PROPOSED › Hardware product). Product framing:VISION.md§ A. Design history:ROADMAP.md. Docs map:README.md. Migration:MIGRATION.md.
1. Architecture (three layers)
Nomarchy does not reimplement nixos-hardware. It stacks:
┌─────────────────────────────────────────────────────────────────────┐
│ A. Always-on desktop floor │
│ modules/nixos/default.nix + power.nix + audio/BT │
│ redistributable firmware · fwupd · NM · PipeWire · PPD · ddcci │
└───────────────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────────────▼─────────────────────────────────────┐
│ B. nixos-hardware profiles (via mkFlake hardwareProfile) │
│ common-cpu-* · common-gpu-* · common-pc(-laptop|-ssd) · model │
│ microcode · GPU media · fstrim · vendor/model quirks │
└───────────────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────────────▼─────────────────────────────────────┐
│ C. nomarchy.hardware.* (modules/nixos/hardware.nix) │
│ gap above commons: GuC · amd-pstate · VA-API env · fprintd · │
│ IR-webcam hide · ROCm/NPU/latestKernel · I2C/DDC/CI │
└─────────────────────────────────────────────────────────────────────┘
| Layer | Who turns it on | Where it lives |
|---|---|---|
| A | Distro defaults (mkDefault) |
Any machine importing nomarchy.nixosModules.nomarchy |
| B | Installer DMI/lspci → hardwareProfile = [ … ] |
Downstream flake.nix (mkFlake) |
| C | Installer probes → system.nix, or hand-edit |
Downstream system.nix |
hardware-configuration.nix (from nixos-generate-config) still owns
initrd modules, filesystems, and the usual generate-config flags. The
installer writes a real one; the template ships a placeholder you must
replace.
Unknown hardwareProfile names fail at eval with Levenshtein
suggestions (lib.nix) — never silently ignored.
2. What works without thinking about it
These ship on every Nomarchy system unless you override them:
| Capability | Default | Notes |
|---|---|---|
| Wi‑Fi / BT firmware blobs | hardware.enableRedistributableFirmware |
iwlwifi, ath, rtw, brcm, SOF, … |
| NetworkManager | on | Wi‑Fi UI: System › Network |
| PipeWire + WirePlumber | on | |
| Bluetooth + blueman | on | System › Bluetooth |
| power-profiles-daemon | on | Menu + Waybar; TLP via nomarchy.system.power.backend = "tlp" |
| External monitor brightness | nomarchy.hardware.i2c.ddcci on |
DDC/CI → backlight devices |
| Firmware updates (LVFS) | services.fwupd.enable |
Never auto-flashes — see §4 |
| SMART / UPower / pcscd (FIDO) | on | |
| earlyoom | on | Process-level OOM, not cgroup kill of the whole session |
Not default-on (need detect or uncomment): fprintd, GuC/HuC,
amd-pstate, ROCm, NPU, thermald, charge limit, snapper (installer enables
snapper), latestKernel.
3. Install path (best experience)
nomarchy-install sources pkgs/nomarchy-install/hardware-db.sh and:
- Probes CPU vendor, GPUs (
lspci), battery, SSD, fingerprint USB VIDs, RGB+IR webcam names, NPU PCI class. - Looks up DMI
sys_vendor×product_namein a ~60-entry model table (Framework, Dell XPS/Latitude, ThinkPad, Surface, ASUS ROG, Apple T2, System76). - Emits
MODULE …lines →hardwareProfilelist inflake.nix. - Emits
NOMARCHY hardware.*=…→ active + commented blocks insystem.nix. - Asks you to confirm profiles (or pick manually from the full
nixos-hardware attr list baked into the ISO). Override with
NOMARCHY_HW=auto|none|"mod1 mod2".
Live ISO is intentionally generic (no model profile): broad GPU
modules as available, redistributable firmware on, no baked
nomarchy.hardware.*. Tuning happens at install time.
Out of scope for the installer today: aarch64 (Pi, Snapdragon X), Steam Deck (Jovian), Apple Silicon (T2 Intel Macs only in the DB).
4. Firmware updates (deep dive)
What we ship
# modules/nixos/default.nix
services.fwupd.enable = lib.mkDefault true;
fwupd talks to the LVFS: UEFI capsule (BIOS), some SSDs, docks, Thunderbolt controllers, peripherals. It only refreshes metadata on its own. Applying an update is always an explicit user action.
What you do today (CLI)
# After first boot (and occasionally later):
fwupdmgr refresh # optional; daemon often has metadata
fwupdmgr get-devices # what LVFS can see
fwupdmgr get-updates # pending
fwupdmgr update # apply (may need reboot / battery / AC)
Disable on VMs/headless: services.fwupd.enable = false; in system.nix.
Why this is under-discovered
| Present | Missing |
|---|---|
| Daemon + package | Doctor check (“updates available”) |
| README one-liner | Waybar / updates panel integration |
System ▸ Firmware menu (nomarchy-menu firmware) |
|
| MOTD + first-boot tip (#43) |
System ▸ Firmware (shipped): a self-gated System-submenu row (present
whenever fwupdmgr is on PATH, i.e. services.fwupd.enable, default-on)
that opens a terminal and runs fwupdmgr refresh → get-updates →
confirm → fwupdmgr update. It never auto-flashes — fwupdmgr update
confirms each device and prompts for the reboot a capsule needs; pillar 1
(rock-stable) forbids silent BIOS writes.
Hints (#43 / #73): MOTD cheat-sheet line + nomarchy-control-center --first-boot tip when fwupdmgr is on PATH. Fingerprint / doctor tips
follow the same pattern (fingerprint MOTD only when
nomarchy.hardware.fingerprint.enable).
Still queued: a Doctor “updates available” check, and Waybar/updates-panel integration.
Thunderbolt
Firmware for TB devices may appear via LVFS. There is no first-class
services.hardware.bolt / security-level UI in Nomarchy — use plain
NixOS options if you need bolt.
Microcode / kernel firmware
- CPU microcode: from nixos-hardware
common-cpu-*andnixos-generate-configonce redistributable firmware is on — not a separatenomarchy.*toggle. - GPU GuC/HuC (Intel i915):
nomarchy.hardware.intel.guc→i915.enable_guc=3. Installer turns this off when the bound driver isxe(GuC is default there; the param is ignored). hardware.enableAllFirmware: not set. Oddball non-redistributable Wi‑Fi still needs a manual NixOS fix (or a broader policy decision).
5. Fingerprint (deep dive)
Detect → enable
Installer scans USB vendor IDs common to libfprint (Goodix, Synaptics,
Elan, …) via lsusb or /sys/bus/usb/.../idVendor. On hit:
nomarchy.hardware.fingerprint.enable = true; # services.fprintd
# nomarchy.hardware.fingerprint.pam = true; # login + sudo (opt-in)
# nomarchy.hardware.fingerprint.parallel = false; # sequential prompt instead
Enroll (menu or CLI)
Shipped #55: System › Fingerprint (self-gated when fprintd-list is
on PATH) — a single Fingerprint (on/off) switch, plus Enroll / List /
Verify / Delete all (all usable while it's off — turning it on needs an
enrolled finger). The switch writes settings.fingerprint.pam, the one
state key behind both halves of "use my finger": login/sudo PAM here,
and the lock-screen unlock in modules/home/idle.nix. It runs
nomarchy-fingerprint, which sudos a system rebuild and then a home
switch, because the two halves live in different configurations.
It does not decide whether login prompts at all: auto-login skips the greeter entirely, so this adds the finger to the prompts that actually happen — sudo, the lock screen, and the greeter only when auto-login is off.
Hints (#73): MOTD line when fingerprint.enable is on; first-boot
tip when fprintd-list is on PATH (SUPER+M → System › Fingerprint /
fprintd-enroll). No permanent nag without a reader.
# CLI still works:
fprintd-enroll
fprintd-list "$USER"
nomarchy-fingerprint toggle # or on | off | status — what the menu row runs
PAM stays opt-in on purpose: password-only remains the cautious default until a finger is enrolled. Full enroll on a real reader is V3/hardware.
Auto-login
settings.greeter.autoLogin (System › Auto-login, or nomarchy-autologin [toggle|on|off|status]) decides whether boot goes straight to the desktop
or stops at the greeter. The installer seeds it ON for LUKS-encrypted
machines — the disk passphrase already gates the machine, so a greeter
password is a second prompt for the same thing — and leaves it off
without LUKS, where the greeter is the only thing between power-on and the
desktop. It's baked into greetd at system rebuild, so the change shows on
the next boot.
Auto-login and fingerprint are independent, and auto-login wins at boot: with it on you are never asked for anything at startup, whatever the fingerprint switch says. Turn auto-login off and the greeter asks — for a password, or a password or finger when fingerprint is on.
Both are in-flake state rather than lines in system.nix on purpose: a
hand-set nomarchy.system.greeter.autoLogin (or
nomarchy.hardware.fingerprint.pam) outranks the state and pins the
setting, leaving the menu toggle unable to move it. That's the escape
hatch, not the default — leave those lines commented to use the menu.
Parallel prompt (password or finger, whichever first)
With fingerprint.pam on, sudo/login/hyprlock show one prompt that
accepts either factor — type the password or touch the sensor
(2026-07-12; pam-fprint-grosshack, an fprintd fork, since stock PAM
can't express parallel factors). This is the default;
fingerprint.parallel = false restores stock pam_fprintd's sequential
wait-for-the-reader-then-password. Lockout safety is structural: the
module never validates passwords itself — a typed password is handed to
the normal pam_unix rule, and every failure (no reader, fprintd hung,
timeout) falls through to password, so finger auth can only ever add a
way in. Verify the wiring with
grep pam_fprintd_grosshack /etc/pam.d/sudo. Known cosmetic quirk: after
a fingerprint win a leftover password prompt may linger on the console.
Doctor
nomarchy-doctor reports fprintd unit + enroll status when present.
6. NVIDIA (deep dive)
What happens at install
lspci → NVIDIA ⇒ MODULE common-gpu-nvidia
That is the entire Nomarchy surface. Unfree packages are allowed
(allowUnfree = true in the module and in mkFlake), so the proprietary
path from nixos-hardware can evaluate.
There is no nomarchy.hardware.nvidia.* for:
- hybrid / PRIME (Intel+NVIDIA, AMD+NVIDIA)
- open vs closed kernel module
- power management / udev / suspend quirks
- CUDA / container toolkit packages
What a hybrid laptop user must do today
- Confirm
common-gpu-nvidia(and oftencommon-gpu-intelorcommon-gpu-amd) are inhardwareProfile. - Read the relevant nixos-hardware module and NixOS NVIDIA wiki.
- Add plain NixOS options in
system.nix, for example (illustrative — hardware-specific):
# Example only — verify against your generation and nixos-hardware module.
# hardware.nvidia.prime = { ... };
# hardware.nvidia.powerManagement.enable = true;
# hardware.nvidia.open = false; # or true for open modules on newer cards
- Rebuild the system. Home Manager does not own the driver.
Live ISO note
The ISO lists nouveau among available initrd modules and does not
force-load every GPU driver (avoids multi-driver panics). Proprietary
NVIDIA on the live session is not the product focus; installed systems
use the profile.
Product direction (shipped #59; first-class wrappers deferred for v1):
when common-gpu-nvidia is in hardwareProfile, the installer emits a
commented system.nix block with PRIME / power / open-module pointers
(same pattern as ROCm / NPU comments). No nomarchy.hardware.nvidia.*
stack for v1 — hybrid PRIME remains wiki/plain NixOS until a maintainer
commits to hardware testing.
7. Day-2: “my laptop is mostly fine, make it fully fine”
| Goal | How | Rebuild? |
|---|---|---|
| Power profile (perf/balanced/saver) | System menu / Waybar | No (PPD D-Bus) |
| Charge limit 80% | Menu / CC — live sysfs + state; boot oneshot re-applies | No rebuild for live; rebuild bakes Nix option |
| Thermald (Intel) | Installer on for Intel laptops; else power.thermal.enable |
System |
| Fingerprint enroll | fprintd-enroll |
No |
| Fingerprint login | fingerprint.pam = true |
System |
| ROCm / Intel compute | Uncomment opt-ins in system.nix |
System (large) |
| NPU driver | Uncomment npu (+ often latestKernel) |
System; userspace BYO |
| Newer kernel for brand-new silicon | nomarchy.hardware.latestKernel = true |
System |
| Hide IR “dark camera” | Installer or camera.hideIrSensor |
System |
| Firmware | fwupdmgr update |
Reboot if capsule requires |
| Model quirks missing | Set better hardwareProfile (see §8) |
System |
| OpenRGB / printing / Steam | nomarchy.services.* |
System |
Dual-sensor webcam / IR hide
Many dual-sensor modules (e.g. ThinkPad T14s) expose colour + IR as two identically named “Integrated Camera” nodes. Picking the IR node yields a black/dark greyscale frame — the classic “my webcam is dark” symptom.
nomarchy.hardware.camera.hideIrSensor (installer-on when RGB+IR names
are detected; overridable irMatch) drops the IR node from WirePlumber’s
v4l2 monitor so native PipeWire pickers only offer the colour camera.
The kernel /dev/video* node stays open, so Howdy-style face unlock still
works. Design history: ROADMAP § Webcam.
What it does not cover: apps that list cameras via libcamera or
the xdg-desktop-portal / Flatpak camera path still see both
sensors — a Flatpak Zoom (or similar) user can still pick the black IR
“camera”. That is intentional: a blanket libcamera disable would risk
external USB cams that need the libcamera path, and surgical internal-only
libcamera rules could not match early enough (only device.api binds
before the WirePlumber monitor rule).
Workaround: prefer the colour device in the picker (or any non-IR name). Apps that default to the first v4l2 colour source without a picker are fine.
Further engineering (needs T14s-class hardware): (b) a WirePlumber
libcamera monitor rule disabling GREY-only nodes; (c) a libcamera/udev
quirk at the libcamera layer. Neither is implemented — the recommended
path is document-only until those can be verified on real dual-sensor
hardware (see agent/HARDWARE-QUEUE.md › T14s).
Theme switches never touch drivers (Home Manager only).
8. Unsupported or unlisted machines
Still good floor
Even with no DMI hit you usually get:
common-cpu-{intel,amd}common-gpu-{intel,amd,nvidia}as applicablecommon-pcorcommon-pc-laptop+ optionalcommon-pc-ssdnomarchy.hardware.intelor.amdwhen vendor matches- Layer A (firmware, audio, BT, PPD, fwupd)
You lose model-specific EC/suspend/keyboard/audio quirks that only exist in a named nixos-hardware module.
Pick a profile after install
-
List candidates:
nix eval github:NixOS/nixos-hardware#nixosModules \ --apply builtins.attrNames # or: the ISO ships hardware-modules.txt for the same list -
Set in your flake (installer already uses a list):
hardwareProfile = [ "common-cpu-amd" "common-gpu-amd" "common-pc-laptop" "common-pc-ssd" "lenovo-thinkpad-t14-amd-gen3" # if it matches ]; -
sudo nixos-rebuild switch --flake ~/.nomarchy#default
Template / migration (no installer)
See MIGRATION.md: reuse hardware-configuration.nix, set
hardwareProfile, uncomment nomarchy.hardware / power in system.nix.
Post-install re-probe (shipped #58):
nomarchy-detect-hw # human report + suggested snippets
nomarchy-detect-hw --raw # MODULE / NOMARCHY / DETAIL protocol lines
Prints suggested hardwareProfile and system.nix lines; does not
rewrite the flake. Paste after review, then sudo nixos-rebuild switch.
9. Install-tested machines (hall of fame)
Models that have seen a real Nomarchy session (live ISO, install, or day-to-day QA) — not a guarantee of every feature, just a public seed list. DB coverage (hardware-db.sh) is broader; this table is the smaller “someone actually sat at it” set.
| Model | DMI (sys_vendor × product_name / version) |
Typical hardwareProfile |
Last noted | Notes |
|---|---|---|---|---|
| Lenovo ThinkPad T14s Gen 4 (AMD) | LENOVO × 21F8CTO1WW / ThinkPad T14s Gen 4 |
common AMD laptop + SSD (plus nomarchy.hardware.amd / fingerprint / IR cam as probed) |
2026-07 | Maintainer day-to-day + HARDWARE-QUEUE (IR portal, fingerprint, NPU). Dual-sensor webcam is the motivating IR case. |
| Dell Latitude 5410 | Dell Latitude 5410 (Intel) | common Intel laptop + SSD (+ GuC when enabled) | 2026-07 | Primary Intel hardware-QA host (session, themes, polkit, bar). |
| Dell Latitude 5310 | Dell Latitude 5310 (Intel) | same family as 5410 | 2026-07 | Charge-limit / power QA sibling (see HARDWARE-QUEUE). |
Send your DMI line
If you install Nomarchy on a machine and it works (or fails in an interesting way), open a PR that:
- Adds a row to the table above (model, DMI, profile, date, short notes).
- Optionally adds a
HARDWARE_DBline inpkgs/nomarchy-install/hardware-db.shwhen nixos-hardware has a matching module (§10).
Quick DMI grab:
printf '%s | %s | %s\n' \
"$(cat /sys/class/dmi/id/sys_vendor)" \
"$(cat /sys/class/dmi/id/product_name)" \
"$(cat /sys/class/dmi/id/product_version)"
nomarchy-detect-hw # suggested MODULE / NOMARCHY lines
10. Adding your model to the distro
For contributors (and power users who will PR):
-
On the machine:
cat /sys/class/dmi/id/sys_vendor cat /sys/class/dmi/id/product_name -
Find a matching attr in nixos-hardware (or add one upstream first).
-
Append to
pkgs/nomarchy-install/hardware-db.shinHARDWARE_DB:"VendorRegex|Product regex|nixos-hardware-module-name"First match wins — put specific lines above broad fallbacks.
-
Verify the name exists in the pinned nixos-hardware input (
mkFlakewill throw if not). A CI check that every DB name ∈nixosModulesis queued so lock bumps cannot silently break installs. -
Optional: note on-hardware QA steps in
agent/HARDWARE-QUEUE.md. Install-tested status goes in the hall-of-fame table (§9).
11. Doctor and hardware health (current vs target)
Today (nomarchy-doctor): failed units, disk space, theme-state
validity/git, generation age, snapper; hardware section self-gates per
machine (NetworkManager, audio sink, GPU smoke, fprintd, fwupd, charge
limit, battery health, hibernate/zram). All read-only; each ✖ prints one
fix command.
| Check | Pass condition | Suggested fix line |
|---|---|---|
| NetworkManager | device connected or wifi radio on | open System › Network |
| Audio sink | default sink exists | System › Audio |
| GPU accel | glxinfo/vainfo smoke (if installed) |
check hardwareProfile / drivers |
| Fingerprint | if USB VID matched / fprintd unit | fprintd-enroll or enable option |
| fwupd | daemon active; optional “updates pending” warn | fwupdmgr get-updates |
| Battery threshold | if laptop + limit set, sysfs reports it | power docs / restart charge-limit unit |
| Battery health | cycle_count and/or charge|energy_full÷design % when sysfs exports them | report-only (warn if <70% of design) |
| First-boot pre-activate | installer log present + no HM generation | home-manager switch --flake ~/.nomarchy -b bak |
| Hibernate / zram | disk swap + resume=; zram active | docs/MIGRATION.md → Enabling hibernation |
12. Option quick reference
Full tables: README § options. Hardware-shaped surface:
| Option | Role |
|---|---|
mkFlake.hardwareProfile |
nixos-hardware name or list |
nomarchy.hardware.intel.enable / .guc / .computeRuntime |
Intel gap layer |
nomarchy.hardware.amd.enable / .pstate / .vaapi / .rocm.* |
AMD gap layer |
nomarchy.hardware.fingerprint.enable / .pam / .parallel |
fprintd + PAM (parallel password-or-finger prompt by default) |
nomarchy.hardware.npu.enable |
in-kernel NPU only |
nomarchy.hardware.latestKernel |
linuxPackages_latest |
nomarchy.hardware.camera.hideIrSensor / .irMatch |
dual-sensor webcams (v4l2 only; §7) |
nomarchy.hardware.i2c.enable / .ddcci |
I2C + external backlight |
nomarchy.system.power.* |
PPD/TLP, laptop, thermald, charge limit |
nomarchy.system.bluetooth.enable |
BT stack |
services.fwupd.enable |
LVFS (native NixOS, default on) |
13. Ease summary
| Situation | Effort |
|---|---|
| Known model in DB (Framework, many ThinkPads, …) | Low — install and go |
| Unknown modern Intel/AMD laptop | Low–medium — commons cover a lot |
| Firmware updates | Medium — CLI only until product work lands |
| Fingerprint for login | Medium — enroll CLI + PAM uncomment |
| Hybrid NVIDIA | High — plain NixOS/wiki territory |
| ROCm / NPU userspace | High — opt-in + expert |
| Contribute a new DMI line | Medium for someone who can PR |
14. Related files
| Path | Role |
|---|---|
modules/nixos/hardware.nix |
nomarchy.hardware.* |
modules/nixos/power.nix |
PPD/TLP, charge limit, thermald |
modules/nixos/default.nix |
firmware, fwupd, ddcci default |
pkgs/nomarchy-install/hardware-db.sh |
DMI DB + probes |
pkgs/nomarchy-install/nomarchy-install.sh |
writes flake + system.nix |
lib.nix |
hardwareProfile resolution |
templates/downstream/system.nix |
commented opt-ins |
agent/HARDWARE-QUEUE.md |
on-machine V3 checks |