feat(idle): unlock by fingerprint, and say so — hyprlock never used the PAM flag
All checks were successful
Check / eval (push) Successful in 2m59s

Bernardo tested the lock screen after turning `fingerprint.pam` on: sudo took
a finger, hyprlock did nothing. The PAM stack was not the problem — grosshack
sits in /etc/pam.d/hyprlock at order 11400 exactly like sudo's. hyprlock just
never asks it: its PAM conversation runs only on submit, so a module that
wants to prompt *while* polling the reader never gets a turn. hyprlock has its
own fprintd-over-D-Bus backend instead, behind `auth:fingerprint:enabled`,
which nothing in the distro ever set. So `fingerprint.pam = true` promised
"login + sudo" and silently skipped the surface you meet most often.

The second half is the one worth having: enabling the backend alone yields a
reader that works while the field still reads "password…". $FPRINTPROMPT is
the ONLY slot hyprlock renders the ready/scanning/failed messages into, and
ours hardcoded the placeholder — proven on hardware, where a test config with
the backend on unlocked by finger and said nothing whatsoever. A feature that
works and cannot be discovered fails VISION's "discoverable without reading
the README" either way, so the placeholder now carries $FPRINTPROMPT and the
messages mirror sudo's "Enter Password or Place finger".

New `nomarchy.idle.fingerprint` (default false), mirroring
`nomarchy.hardware.fingerprint.pam` the way `nomarchy.keyboard.layout` mirrors
`services.xserver.xkb.layout`: hyprlock is configured in standalone Home
Manager, which has no `osConfig` to derive the NixOS side from. Opt-in rather
than implied, because with no reader hyprlock advertises a scan that cannot
happen. Documented in the template (SoT for opt-in comments) and README —
option-docs caught the missing row, which is the check doing its job.

Verified V2: nix flake check --no-build; checks.option-docs passes (failed
first with "undocumented option", as it should). V3 pending: Bernardo applies
it and confirms the field reads "password… or scan finger" and both factors
work — the mechanism itself is already proven on his reader via a test config.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 12:58:53 +01:00
parent 625b7e38a3
commit e2de9062d8
4 changed files with 46 additions and 1 deletions

View File

@@ -43,7 +43,15 @@ in
check_color = rgb c.warn;
fail_color = rgb c.bad;
rounding = t.ui.rounding;
placeholder_text = "<i>password</i>";
# $FPRINTPROMPT is the ONLY place hyprlock renders the fingerprint
# ready/scanning/failed messages — there is no implicit slot for
# them. Enabling the backend without this yields a reader that
# silently works while the field still says "password…", i.e. a
# feature nobody can find (VISION: fingerprint discoverable without
# reading the README). Verified on hardware 2026-07-14: unlock by
# finger worked and said nothing at all.
placeholder_text =
if cfg.idle.fingerprint then "<i>$FPRINTPROMPT</i>" else "<i>password</i>";
}];
label = [{
@@ -56,6 +64,19 @@ in
halign = "center";
valign = "center";
}];
} // lib.optionalAttrs cfg.idle.fingerprint {
# hyprlock does NOT take a fingerprint through PAM: its PAM stack runs
# only on submit, so a parallel module never gets to poll the reader.
# This is a separate backend of its own, talking to fprintd over
# D-Bus, and `nomarchy.hardware.fingerprint.pam` does not reach it —
# which is why finger-unlock at the lock screen did nothing at all
# until this option existed, while sudo took a finger happily.
# The wording mirrors sudo's "Enter Password or Place finger" prompt.
auth.fingerprint = {
enabled = true;
ready_message = "password or scan finger";
present_message = "scanning finger";
};
};
};

View File

@@ -367,6 +367,23 @@ in
dockAudio.enable = lib.mkEnableOption "settled PipeWire/WirePlumber reprobe and automatic default-output switch to an available dock/monitor sink (HDMI/DisplayPort/USB) on fresh display hotplug, with a toast and journal result; a later manual choice sticks until the next plug" // { default = true; };
firstBootWelcome.enable = lib.mkEnableOption "one dismissible \"you're set\" toast on the first session (menu/themes/keys + network pointer); marker is settings.firstBootShown in the flake checkout" // { default = true; };
idle.enable = lib.mkEnableOption "hyprlock + hypridle (idle lock, display off, suspend)" // { default = true; };
idle.fingerprint = lib.mkEnableOption ''
unlocking the lock screen with a fingerprint as well as the password,
and saying so on the input field.
Mirrors `nomarchy.hardware.fingerprint.pam` in system.nix keep the two
matched, the same way `nomarchy.keyboard.layout` matches
`services.xserver.xkb.layout`. It cannot be derived: hyprlock is
configured here, in standalone Home Manager, which has no `osConfig` to
read the NixOS side from.
Deliberately separate from the PAM flag rather than implied by it:
hyprlock does NOT unlock by fingerprint through PAM. Its PAM stack only
runs on submit, so a parallel module never gets to poll hyprlock has
its own fprintd-over-D-Bus backend instead, and this is the switch for
it. With no reader present it advertises a scan that cannot happen, so
it stays opt-in
'';
yazi.enable = lib.mkEnableOption "the yazi TUI file manager, themed with a curated plugin set" // { default = true; };
osd.enable = lib.mkEnableOption "swayosd on-screen display for volume/brightness/mute" // { default = true; };
shell.enable = lib.mkEnableOption "the zsh shell experience (starship prompt, bat/eza/zoxide)" // { default = true; };