From e81529c1bbefba6c2155fdea2cc44bf85191599d Mon Sep 17 00:00:00 2001 From: Bernardo Magri Date: Fri, 10 Jul 2026 08:45:33 +0100 Subject: [PATCH] =?UTF-8?q?feat(menu):=20#55=20System=20=E2=80=BA=20Finger?= =?UTF-8?q?print=20enroll/list/PAM?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Self-gated System › Fingerprint when fprintd is on PATH: enroll, list, verify, delete-all (terminal), and Use for login writing settings.fingerprint.pam (hardware.nix default from theme-state). Close #55. Verified: V1 (HM menu rebuild). V3 pending: real-reader enroll (HARDWARE-QUEUE). --- agent/HARDWARE-QUEUE.md | 5 ++++ docs/HARDWARE.md | 25 ++++++++-------- modules/home/rofi.nix | 61 +++++++++++++++++++++++++++++++++++++- modules/nixos/hardware.nix | 22 ++++++++++++-- 4 files changed, 96 insertions(+), 17 deletions(-) diff --git a/agent/HARDWARE-QUEUE.md b/agent/HARDWARE-QUEUE.md index 872cd2c..cda153d 100644 --- a/agent/HARDWARE-QUEUE.md +++ b/agent/HARDWARE-QUEUE.md @@ -8,6 +8,11 @@ the failure as a NOW bug in BACKLOG.md. Machines: the **AMD dev box** QA machine), the **T14s** (webcam case). ## Any machine (dev box is fine) +- [ ] **#55 fingerprint enroll on real reader** — with + `nomarchy.hardware.fingerprint.enable` and a physical reader: System › + Fingerprint › Enroll a finger; List shows it; Verify succeeds; optional + Use for login (on) → `sys-rebuild` → login/sudo accept fingerprint. + Menu surface is V1/V2 without a reader (self-gate + dry paths). - [ ] **#60 non-BAT* battery name (if available)** — on a machine whose system battery is **not** named `BAT*` (e.g. `CMB0`): confirm charge-limit oneshot writes the threshold, System › Battery limit diff --git a/docs/HARDWARE.md b/docs/HARDWARE.md index 2530ad8..a65351e 100644 --- a/docs/HARDWARE.md +++ b/docs/HARDWARE.md @@ -163,27 +163,26 @@ nomarchy.hardware.fingerprint.enable = true; # services.fprintd # nomarchy.hardware.fingerprint.pam = true; # login + sudo (opt-in) ``` -### Enroll (CLI today) +### 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). ```sh -fprintd-enroll # as your user +# CLI still works: +fprintd-enroll fprintd-list "$USER" -# then, if you want login/sudo: -# uncomment fingerprint.pam in system.nix → sys-rebuild +# 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. +until a finger is enrolled. Full enroll on a real reader is V3/hardware. -### Gaps +### Doctor -- No System menu “Fingerprint” row (enroll / list / delete). -- No “use for login” toggle that writes state + rebuild note. -- Doctor does not check reader presence or enroll status. - -**Product direction (queued):** when `fprintd` is active, a self-gated -menu module for enroll/list; optional PAM toggle via state + rebuild -hint (same pattern as Bluetooth in Control Center). +`nomarchy-doctor` reports fprintd unit + enroll status when present. ## 6. NVIDIA (deep dive) diff --git a/modules/home/rofi.nix b/modules/home/rofi.nix index c31260e..f783594 100644 --- a/modules/home/rofi.nix +++ b/modules/home/rofi.nix @@ -702,6 +702,62 @@ ${themeRows} fi printf "\nEnter to close."; read -r _' ;; + fingerprint) + # System ▸ Fingerprint — enroll/list/delete + optional PAM for + # login/sudo (HARDWARE.md §5, BACKLOG #55). Self-gated on fprintd + # CLI (services.fprintd). Enroll is interactive → terminal. + command -v fprintd-list >/dev/null 2>&1 \ + || { notify-send "Fingerprint" "fprintd not available (nomarchy.hardware.fingerprint.enable?)."; exit 0; } + pam=$(nomarchy-theme-sync get settings.fingerprint.pam 2>/dev/null || echo false) + case "$pam" in true|True) pam_label="Use for login (on)" ;; *) pam_label="Use for login (off)" ;; esac + choice=$( { + row "Enroll finger" preferences-desktop-user-password + row "List enrolled" view-list + row "Verify" dialog-password + row "Delete all" edit-delete + row "$pam_label" system-lock-screen + back + } | rofi -dmenu -show-icons -p Fingerprint) || exit 0 + case "$choice" in + "$BACK") exec "$0" system ;; + *Enroll*) + exec ${cfg.terminal} -e sh -c ' + echo "== Enroll fingerprint for $USER ==" + fprintd-enroll || true + printf "\nEnter to close."; read -r _' ;; + *List*) + exec ${cfg.terminal} -e sh -c ' + echo "== Enrolled fingers for $USER ==" + fprintd-list "$USER" || true + printf "\nEnter to close."; read -r _' ;; + *Verify*) + exec ${cfg.terminal} -e sh -c ' + echo "== Verify fingerprint for $USER ==" + fprintd-verify || true + printf "\nEnter to close."; read -r _' ;; + *"Delete all"*) + exec ${cfg.terminal} -e sh -c ' + echo "== Delete all fingerprints for $USER ==" + printf "Type yes to delete every enrolled finger: " + read -r ans + case "$ans" in + yes) fprintd-delete "$USER" || true ;; + *) echo "Cancelled." ;; + esac + printf "\nEnter to close."; read -r _' ;; + *"Use for login"*) + case "$pam" in true|True) new=false ;; *) new=true ;; esac + if [ "$new" = true ]; then + if fprintd-list "$USER" 2>/dev/null | grep -qiE 'no fingers enrolled|No devices available'; then + notify-send "Fingerprint" "Enroll a finger first, then enable login." + exit 0 + fi + fi + nomarchy-theme-sync --quiet set settings.fingerprint.pam "$new" --no-switch + notify-send "Fingerprint" "Use for login: $new — applies on next sys-rebuild." + ;; + esac ;; + controlcenter) # Nomarchy TUI Control Center. exec ${cfg.terminal} -e nomarchy-control-center ;; @@ -811,6 +867,8 @@ ${themeRows} # Firmware/LVFS updates — self-gated on fwupd (default-on, but off # on VMs/headless via services.fwupd.enable=false), like Printers. command -v fwupdmgr >/dev/null 2>&1 && row "Firmware" firmware-manager + # Fingerprint — self-gated on fprintd CLI (services.fprintd; BACKLOG #55). + command -v fprintd-list >/dev/null 2>&1 && row "Fingerprint" preferences-desktop-user-password command -v nomarchy-control-center >/dev/null 2>&1 && row "Control Center" preferences-desktop if has_system_battery && command -v powerprofilesctl >/dev/null 2>&1; then row "Power profile" preferences-system-power @@ -835,6 +893,7 @@ ${themeRows} *Rollback*) exec "$0" rollback ;; *Doctor*) exec "$0" doctor ;; *Firmware*) exec "$0" firmware ;; + *Fingerprint*) exec "$0" fingerprint ;; *"Control Center"*) exec "$0" controlcenter ;; *"Power profile"*) exec "$0" power-profile ;; *"Battery limit"*) exec "$0" batterylimit ;; @@ -904,7 +963,7 @@ ${themeRows} esac ;; *) - echo "usage: nomarchy-menu [lookfeel|tools|system|power|power-profile|powermgmt|batterylimit|theme|clipboard|calc|files|emoji|web|network|bluetooth|audio|display|display-profile|printers|capture|colorpicker|keybinds|ask|dnd|nightlight|autotimezone|autocommit|vpn|snapshot|doctor|firmware|controlcenter|rollback]" >&2 + echo "usage: nomarchy-menu [lookfeel|tools|system|power|power-profile|powermgmt|batterylimit|theme|clipboard|calc|files|emoji|web|network|bluetooth|audio|display|display-profile|printers|capture|colorpicker|keybinds|ask|dnd|nightlight|autotimezone|autocommit|vpn|snapshot|doctor|firmware|fingerprint|controlcenter|rollback]" >&2 exit 64 ;; esac ''; diff --git a/modules/nixos/hardware.nix b/modules/nixos/hardware.nix index 289df34..d5d9754 100644 --- a/modules/nixos/hardware.nix +++ b/modules/nixos/hardware.nix @@ -15,6 +15,13 @@ let cfg = config.nomarchy.hardware; + # Fingerprint PAM can follow theme-state.json (menu toggle → next + # sys-rebuild), same bridge as autoTimezone (BACKLOG #55). + hwState = + if config.nomarchy.system.stateFile != null + then builtins.fromJSON (builtins.readFile config.nomarchy.system.stateFile) + else { }; + pamFromState = (hwState.settings or { }).fingerprint.pam or false; in { options.nomarchy.hardware = { @@ -92,9 +99,18 @@ in a fingerprint reader via fprintd (the installer turns this on when it detects a known reader). Enroll with `fprintd-enroll`''; - pam = lib.mkEnableOption '' - using the fingerprint for login and sudo (PAM). Opt-in — password-only - stays the default for the cautious; enroll a finger first''; + pam = lib.mkOption { + type = lib.types.bool; + default = pamFromState; + defaultText = lib.literalExpression + "(settings.fingerprint.pam from theme-state.json) or false"; + description = '' + Use the fingerprint for login and sudo (PAM). Opt-in — password-only + stays the default for the cautious; enroll a finger first. Defaults + from theme-state.json `settings.fingerprint.pam` (System › Fingerprint + menu) when set; otherwise false. + ''; + }; }; npu.enable = lib.mkEnableOption ''