Merge remote-tracking branch 'origin/main' into main
All checks were successful
Check / eval (push) Successful in 3m2s
Lock bump / bump (push) Successful in 6m30s

This commit is contained in:
2026-07-12 20:21:23 +01:00
26 changed files with 891 additions and 42 deletions

View File

@@ -167,6 +167,7 @@ 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)
# nomarchy.hardware.fingerprint.parallel = false; # sequential prompt instead
```
### Enroll (menu or CLI)
@@ -190,6 +191,21 @@ fprintd-list "$USER"
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.
### 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.
@@ -438,7 +454,7 @@ Full tables: [README § options](../README.md). Hardware-shaped surface:
| `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.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) |

View File

@@ -269,6 +269,9 @@ default yet.
on whenever fprintd is enabled; Nomarchy forces PAM to follow the
`pam` flag, but only after a **system** rebuild. Verify with
`grep pam_fprintd /etc/pam.d/sudo` (should be empty when pam is off).
With pam on, the prompt accepts password *or* finger in parallel by
default (`fingerprint.parallel = false` for stock sequential) — see
HARDWARE.md §5.
- **Browser profiles:** Nomarchy does not manage Chromium/Firefox state.
Bookmarks/extensions live under `~/.config/chromium` (or
`~/.config/google-chrome` / ungoogled paths if that was your previous

View File

@@ -30,8 +30,10 @@ boreal` (or any preset). If a switch failed halfway, the state file is written
`home-manager switch --flake ~/.nomarchy` (or `nomarchy-home`).
Your flake checkout is a git repo, and with auto-commit enabled every
apply is a commit: `git -C ~/.nomarchy log` to see what changed,
`git revert` the culprit, then `nomarchy-home`.
apply is a commit — and every `nomarchy-pull`/`-rebuild`/`-home` first
sweeps pending hand edits into one, so history mirrors your generations:
`git -C ~/.nomarchy log` to see what changed, `git revert` the culprit,
then `nomarchy-home`.
## 2. The desktop won't start at all

View File

@@ -402,6 +402,68 @@ Design/decision records and a running log of shipped work (items marked
built-in. Rules in `modules/nixos/dock-audio-rules.nix`;
`checks.dock-audio` pure + conf-file VM. Real hotplug V3 in
HARDWARE-QUEUE.
✓ **Dock audio round 2 — auto-follow watcher (2026-07-12):** the T14s
dock test showed priority rules alone can't deliver "just works":
WirePlumber's *stored* default (default-nodes state, written by any
past explicit device pick — every real machine has one) outranks
`priority.session`, so the default never moves on plug. Fix:
`nomarchy-dock-audio` (modules/home/dock-audio.nix,
`nomarchy.dockAudio.enable`, default on) — a `pactl subscribe` watcher
that `set-default-sink`s new dock-class sinks (regex data imported
from dock-audio-rules.nix, single source) + toasts; startup sweep for
login-while-docked (only when the default isn't already dock-class, so
restarts don't override a manual pick). EasyEffects needs nothing: EE 8
`useDefaultOutputDevice` defaults true and verified live — with a
stream playing, EE's output links moved to a fake dock-named null sink
and back on unload. Unplug = WirePlumber priority fallback to built-in.
Docked menu rows shipped alongside: Display gains "Laptop screen off ·
everything → <ext>" (live-only `keyword monitor <eDP>,disable` — a
persisted disable could black-screen an undocked boot), "Screen on ·
<name>", "Move workspace · → next monitor"
(`movecurrentworkspacetomonitor +1`), "Swap workspaces"
(`swapactiveworkspaces`, exactly-2 gate); Audio gains a self-gated
"Send output → <dock sink>" quick row (the watcher's manual override).
Real-dock V3 for both in HARDWARE-QUEUE.
✓ **Undock blackout rescue (2026-07-12):** Bernardo asked whether the
panel comes back if you undock while "Laptop screen off" — VM answer
(tools/monitor-fallback.nix, full softGL desktop): **no**, Hyprland
0.55.4 leaves zero active outputs and never re-enables a soft-disabled
monitor. Fix: `rescue_blackout` in the display hotplug watcher
(hyprland.nix) — on monitorremoved with zero active outputs,
immediately (and with retries) `keyword monitor <name>,preferred,auto,
auto` every disabled output + toast; independent of the profile
auto-switch gate (safety invariant, not a preference). Sharp edge
found while testing: the zero-output state itself crashes Hyprland in
the VM ~4/5 runs (ABRT in aquamarine CBackend::dispatchIdle, ~0.5s —
faster than any userland rescue; control run with the panel active
survives 5/5, so it's the state, not headless removal; monitor-
disconnect crashes are a known upstream class). Posture: rescue ships
as defense-in-depth (recovers every surviving case; worst case on real
DRM is session-to-greeter on the re-lit panel, not a black brick);
V3 decides if the menu row needs a mirror-based rework. Also fixed in
the rescue: `grep -c .` exits 1 at count 0 — an `||`-guard on that
pipeline silently disabled the rescue exactly when it mattered.
✓ **Parallel fingerprint-or-password (2026-07-12, Bernardo promoted
from PROPOSED):** `nomarchy.hardware.fingerprint.parallel` (default
**true** — the better UX is what opting into fingerprint PAM gets you;
`false` restores stock sequential pam_fprintd). Implementation:
packaged `pam-fprint-grosshack` v0.3.0 (pkgs/, GitLab pin — the
field-standard fprintd fork; stock PAM cannot express parallel
factors, linux-pam#301; the cleaner-concept `pam-any` stays the
fallback candidate if grosshack fights greetd/hyprlock). Source
reviewed before packaging: every failure path returns
PAM_AUTHINFO_UNAVAIL (falls through to password) and a typed password
is only ferried via PAM_AUTHTOK to the stock
`auth sufficient pam_unix.so … try_first_pass` — the hack never
validates passwords itself, so password login cannot be locked out by
it. Wiring reuses stock fprintd's rule slot (mkForce'd modulePath) so
ordering is inherited, not recomputed. checks.hardware-toggles grew to
three nodes: parallel default (stack shape + password-works/
wrong-password-fails with no reader), parallel=false (stock
pam_fprintd), pam=false (neither). Real-reader race → HARDWARE-QUEUE
(AMD dev box). Accepted quirks (documented in the package): process-
wide SIGUSR1 handler, pthread_cancel'd prompt on fingerprint win,
prompt buffer not zeroized.
✓ **neon-glass finish (#88, `VISION § D`):** rewrote whole-swap
`rofi.rasi` to rofi-native `@color` glass panel (cyan rim, soft
selection wash — same hex-alpha glass pattern as Boreal); btop title/
@@ -938,10 +1000,21 @@ Design/decision records and a running log of shipped work (items marked
shrinks the security-patch latency the single-input model otherwise imposes
(users are gated on the maintainer for nixpkgs CVEs), since a fix is usually
already eval/build/VM-tested and one hardware promotion away from `v1` —
worth fast-laning lock-only/security bumps ahead of feature batches. The repo
has **no CI today** (manual `nix flake update` only). Explicitly *not* a
binary cache: compile-from-source is a deliberate values call (Gentoo-style),
so this automates the *config/lock* channel, not artifact distribution.
worth fast-laning lock-only/security bumps ahead of feature batches.
**Shipped since:** `.gitea/workflows/check.yml` (eval tier on every main
push) and `bump.yml` (Monday 05:17 UTC scheduled lock bump, eval + V1
build gate, auto-lands on green — first landing 8fded63, 2026-07-06).
2026-07-12: both gates moved from one big `nix flake check --no-build`
(~6.0 GB peak RSS — OOM'd the 4 GB VPS, then could never fit the
runner's 2 GB container cap) to `tools/ci-eval.sh`, one output per nix
process. **nomarchy-live is excluded by decision** (Bernardo, same
day): the ISO is built/distributed manually, so its guard rides that
manual flow — and its eval alone is 2.69 GB, the one output that can't
fit the cap (Actions run 315: everything else green, it OOM-killed).
With it out, the worst CI output is 0.99 GB — no server change needed. Explicitly
*not* a binary cache: compile-from-source is a deliberate values call
(Gentoo-style), so this automates the *config/lock* channel, not
artifact distribution.
- ✓ **Opt-in auto-commit of state mutations (in-flake state, Phase 4):**
`settings.autoCommit` (menu: **System Auto-commit**, self-gated on the
flake being a git repo) makes every `apply`/`set` also `git commit`
@@ -960,6 +1033,18 @@ Design/decision records and a running log of shipped work (items marked
`get` now prints booleans JSON-style (`true`, not Python's `True`) —
which also un-sticks the System menu's "Auto timezone (on/off)" label,
whose `= true` comparison could never match before.
- ✓ **Auto-commit sweep before switch (2026-07-12):** the pathspec limit
above left hand edits (system.nix/home.nix, lock bumps) forever-dirty
even with `settings.autoCommit` on. Now `nomarchy-pull`/`-rebuild`/
`-home` run `nomarchy-autocommit` (internal, in nomarchy-lifecycle)
first: same live-read flag, commits *everything* dirty with an honest
`nomarchy: auto-commit before <pull|rebuild|home switch>` message whose
body lists the swept files — so `git log` mirrors the generation list
and hand edits still never ride a settings-named commit. Same fallback
identity; never fatal (`|| true` at the call sites — a git hiccup can't
block a rebuild); before the ff-only pull, it also un-dirties the tree.
Guarded by `checks.lifecycle-autocommit` (sandbox-repo round trip of
the real binary: sweep, no-op on clean/off/non-repo).
- **"nomarchy" control center:** a single TUI/GUI front-end over the common
toggles — theme, power profile, opt-in services, display, DND — built on
the same `nomarchy-theme-sync` / `nomarchy.*` surface the menu already

View File

@@ -105,6 +105,11 @@ unattended env it uses is in the script, and the same flow works
interactively from the live terminal. If the desktop comes up unthemed,
read `/var/log/nomarchy-hm-preactivate.log` on the installed system.
Two full-desktop VM harnesses (softGL Hyprland, too heavy for `checks.*`)
live next to the scripts: `tools/theme-shot.nix` (themed-desktop
screenshots) and `tools/monitor-fallback.nix` (undock blackout rescue —
disabled panel must re-enable when the last active output disappears).
## 5. VM-specific gotchas
- **No KVM** (e.g. nested without acceleration): everything works but