Files
Nomarchy/docs/HARDWARE.md
Bernardo Magri ad6b76e1eb feat(hints): #73 fingerprint + doctor MOTD/first-boot tips
#43 shipped Firmware MOTD + control-center --first-boot tip. Residual:
- MOTD always lists nomarchy-doctor; Fingerprint line only when
  nomarchy.hardware.fingerprint.enable (no nag without a reader).
- first-boot tips self-gate on fprintd-list / nomarchy-doctor (same
  gum style as Firmware). No dismiss state file — first-boot is once.

V0 flake check --no-build + bash -n; V1 build control-center + eval
MOTD with/without fingerprint.enable. V3 enroll stays hardware-queue.
2026-07-10 09:39:10 +01:00

437 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`](../agent/BACKLOG.md) (PROPOSED Hardware
> product). Product framing: [`VISION.md`](VISION.md) § A. Design history:
> [`ROADMAP.md`](ROADMAP.md). Docs map: [`README.md`](README.md).
> Migration: [`MIGRATION.md`](MIGRATION.md).
## 1. Architecture (three layers)
Nomarchy does **not** reimplement [nixos-hardware](https://github.com/NixOS/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 |
|------------|---------|--------|
| WiFi / BT firmware blobs | `hardware.enableRedistributableFirmware` | iwlwifi, ath, rtw, brcm, SOF, … |
| NetworkManager | on | WiFi 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:
1. Probes CPU vendor, GPUs (`lspci`), battery, SSD, fingerprint USB VIDs,
RGB+IR webcam names, NPU PCI class.
2. Looks up DMI `sys_vendor` × `product_name` in a ~60-entry model table
(Framework, Dell XPS/Latitude, ThinkPad, Surface, ASUS ROG, Apple T2,
System76).
3. Emits `MODULE …` lines → `hardwareProfile` list in `flake.nix`.
4. Emits `NOMARCHY hardware.*=…` → active + commented blocks in
`system.nix`.
5. 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
```nix
# modules/nixos/default.nix
services.fwupd.enable = lib.mkDefault true;
```
fwupd talks to the [LVFS](https://fwupd.org/): 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)
```sh
# 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-*` and
`nixos-generate-config` once redistributable firmware is on — not a
separate `nomarchy.*` toggle.
- **GPU GuC/HuC (Intel i915):** `nomarchy.hardware.intel.guc`
`i915.enable_guc=3`. Installer turns this **off** when the bound driver
is `xe` (GuC is default there; the param is ignored).
- **`hardware.enableAllFirmware`:** not set. Oddball non-redistributable
WiFi 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:
```nix
nomarchy.hardware.fingerprint.enable = true; # services.fprintd
# nomarchy.hardware.fingerprint.pam = true; # login + sudo (opt-in)
```
### Enroll (menu or CLI)
**Shipped #55:** System Fingerprint (self-gated when `fprintd-list` is
on PATH) — Enroll / List / Verify / Delete all, plus **Use for login**
which writes `settings.fingerprint.pam` and applies on the next
`sys-rebuild` (option default follows theme-state.json).
**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.
```sh
# CLI still works:
fprintd-enroll
fprintd-list "$USER"
# or: System Fingerprint Use for login (on) → sys-rebuild
```
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.
### 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
1. Confirm `common-gpu-nvidia` (and often `common-gpu-intel` or
`common-gpu-amd`) are in `hardwareProfile`.
2. Read the relevant nixos-hardware module and [NixOS NVIDIA wiki](https://wiki.nixos.org/wiki/Nvidia).
3. Add plain NixOS options in `system.nix`, for example (illustrative —
hardware-specific):
```nix
# 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
```
4. 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):** 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). A full `nomarchy.hardware.nvidia.*` stack stays optional and
needs a maintainer + hardware-queue coverage.
## 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 / `settings.power.batteryChargeLimit` | System (sysfs apply is separate backlog) |
| 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 **WirePlumbers
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](ROADMAP.md).
**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 applicable
- `common-pc` or `common-pc-laptop` + optional `common-pc-ssd`
- `nomarchy.hardware.intel` or `.amd` when 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
1. List candidates:
```sh
nix eval github:NixOS/nixos-hardware#nixosModules \
--apply builtins.attrNames
# or: the ISO ships hardware-modules.txt for the same list
```
2. Set in your flake (installer already uses a list):
```nix
hardwareProfile = [
"common-cpu-amd"
"common-gpu-amd"
"common-pc-laptop"
"common-pc-ssd"
"lenovo-thinkpad-t14-amd-gen3" # if it matches
];
```
3. `sudo nixos-rebuild switch --flake ~/.nomarchy#default`
### Template / migration (no installer)
See [MIGRATION.md](MIGRATION.md): reuse `hardware-configuration.nix`, set
`hardwareProfile`, uncomment `nomarchy.hardware` / power in `system.nix`.
Post-install re-probe (**shipped #58**):
```sh
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. Adding your model to the distro
For contributors (and power users who will PR):
1. On the machine:
```sh
cat /sys/class/dmi/id/sys_vendor
cat /sys/class/dmi/id/product_name
```
2. Find a matching attr in
[nixos-hardware](https://github.com/NixOS/nixos-hardware) (or add one
upstream first).
3. Append to `pkgs/nomarchy-install/hardware-db.sh` in `HARDWARE_DB`:
```bash
"VendorRegex|Product regex|nixos-hardware-module-name"
```
First match wins — put specific lines above broad fallbacks.
4. Verify the name exists in the **pinned** nixos-hardware input
(`mkFlake` will throw if not). A CI check that every DB name ∈
`nixosModules` is queued so lock bumps cannot silently break installs.
5. Optional: note on-hardware QA steps in `agent/HARDWARE-QUEUE.md`.
## 10. Doctor and hardware health (current vs target)
**Today** (`nomarchy-doctor`): failed units, disk space, theme-state
validity/git, generation age, snapper timer. **No** WiFi, GPU, fwupd,
or fingerprint checks.
**Target checks** (queued, all read-only, each failure prints one fix
command — same doctor contract):
| 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 writable | power docs |
## 11. Option quick reference
Full tables: [README § options](../README.md). 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` | fprintd + PAM |
| `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) |
## 12. Ease summary
| Situation | Effort |
|-----------|--------|
| Known model in DB (Framework, many ThinkPads, …) | Low — install and go |
| Unknown modern Intel/AMD laptop | Lowmedium — 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 |
## 13. 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 |