Proof Portal

Project overview

Omarchy

ProbeLabs65 findings · 95 requirements

A proof layer — requirements, tests and verified fixes — for two of Omarchy's subsystems: the application menu (launcher scripts, QML model, JSONC config, search and selection) and the lock screen (lock scripts, QML, PAM authentication). Scope is deliberately limited to those components of omacom/omarchy; the rest of the distribution is not covered.

Inbox
ReportR-1029GitHub issuePublicFixed

Lock screen keeps fingerprint PAM armed for the whole lock, wedging USB imaging readers

Reported by voglster · filed Sep 3, 2026 · open at source

Decided: Duplicate — decided by agents · verified 4 days ago.

This defect is tracked under R-443. This report describes the same defect, so it is folded under it in the inbox.

Earliest report of this defect: R-1029 (filed Sep 3, 2026).

Decided by agents · verified· Oct 9, 2026

At a glance

1 · What is claimed?
  • ConfirmedReproducible: the lock re-arms the fingerprint scan after every attempt for the whole lock, also while the displays are blank (250 ms after a miss; error streaks back off to a 40 s cap)
  • Not confirmedAffects the lock component
3 · Is it reproducible?
An engine-executed run exited 1 and showed the defect signal. See reproducer
4 · What remains unclear?
  • Open question for maintainers: Open decision: when should the lock screen scan for a fingerprint, and should retries end? Options: (A) keep the current behaviour (scan for the whole lock, also while the displays are blank; 250 ms after a miss, error backoff to 40 s, no total budget); (B) start no scan while the displays are blank and re-arm on wake (PRs #12420, #7179); (C) a total attempt or time budget per lock, value chosen by the maintainers (#10078's ask, PR #10082).
  • Open question for maintainers: The decision is the Omarchy maintainers'; a draft requirement listing these options sits on this report's branch (report/cmulr91vo0k9f1gw499ic4jis), and approving one option = merging that draft into the baseline with the chosen text.
  • Affects the lock component — no requirement of the lock component is confirmed as violated
5 · What happens next?
Decided by agents and verified by a second, independent agent — see the decision.

Open question — draft spec awaiting a decision

Draft — awaiting a decisionSW-REQ-261005-CXRPcomponent lockrefines When the user locks the session, the system shall engage… SYS-REQ-260912-T0XP

When shall the lock screen scan for a fingerprint, and shall retries end?

What the code does today

(upstream quattro 035ce29f, after upstream #7158) after an attempt that reached the reader and did not match, the next attempt is armed in 250 ms (FingerprintModel.js:3, Service.qml:420); attempts that do not reach the reader or end in a fast error back off 1 s doubling to a 40 s cap (FingerprintModel.js:7-23); runBlank() leaves a pending retry running (Service.qml:294-298) and startFingerprint() does not check displaysBlank (Service.qml:357-375), so scanning goes on while the displays are blank, for as long as the session is locked.

Options (listed neutrally — the choice is the maintainers')
  1. AKeep the current behaviour
  2. BThe lock service shall start no fingerprint scan while the displays are blank and shall re-arm the reader on wake
  3. CA total attempt or time budget per lock, value chosen by the maintainers

Approving this draft means choosing one option; its text then replaces this description.

Why this is an open question

Raised by omacom/omarchy#9913 (proof inbox R-1029) and omacom/omarchy#12202 (proof inbox R-443); the retry-budget half of omacom/omarchy#10078 asks the same. Validations: report/cmulr91vo0k9f1gw499ic4jis (rval-745154880ad90c47c99d5d68a7d1114c) and report/cmulr8r7a0iwl1gw4uqg4huff (rval-4da3f7f4c6cb4bb2c50649cba8ac53f4). #12202's premise (no new PAM session after a miss) does not hold at this commit; #9913's flat 250 ms retry on errors was replaced by #7158's backoff. Open upstream PRs #12420 and #7179 implement option B; PR #10082 proposes option C.

Proposed on this report's branch @ a24dc58395 · specs/software/requirements/SW-REQ-261005-CXRP.req.yaml · not part of the audited spec until approved and merged

Written after the validation was sealed at 4b21f9d4a7: the seal covers the reproducer and the observed behaviour, not this proposed text.

Original report

Lock screen keeps fingerprint PAM armed for the whole lock, wedging USB imaging readersrevision 1 · captured 2026-09-28 21:24 UTC via poll

What happens

The lock screen arms fingerprint PAM for the entire duration of the lock. On a USB imaging reader (DigitalPersona U.are.U 4500, 05ba:000a, libfprint uru4000) this drives libfprint's duty-cycle governor into its cutoff after roughly three minutes, and the reader stops responding for the rest of the lock. Unlocking then requires the password, and the sensor illuminator stays lit the whole time the machine is idle.

Two separate problems combine:

1. Fingerprint PAM is armed for the whole lock, not just when someone is there.

shell/plugins/lock/Service.qml starts fingerprint verification on onSecureStateChanged (L235) and keeps it running until unlock. The existing comment at L428 states this intent directly:

// Only a password check in flight should hold the display up. The
// fingerprint PAM stays armed for the whole lock, so gating on
// `authenticating` here would keep the panel lit until unlock.

That is fine for a laptop match-on-chip sensor, which idles cheaply while waiting. An imaging reader instead illuminates and captures continuously. libfprint 1.94 models device duty cycle in software (FP_TEMPERATURE_COLD/WARM/HOT — an estimate, not a hardware sensor) and refuses to verify once the estimate reaches HOT:

fprintd[...]: Device reported an error during verify: Device disabled to prevent overheating.

The estimate only decays while the device is idle, and a whole-lock arm never provides idle time, so it cannot recover while locked. Nothing is physically hot — this is purely libfprint's governor.

2. `fingerprintRetryTimer` hot-loops on hard device errors.

interval: 250 (L358), restarted unconditionally on any failure — handleFingerprintFinished (L226) and onError (L352). Once the device returns the error above, each verify fails instantly, so the 250 ms retry becomes a spin loop with no backoff and no failure cap.

Measured on one 22-minute lock: 2,445 failed verifies, ~36 per 10 seconds, sustained until the password was entered.

This second issue is device-independent — spinning a 250 ms retry against a hard device error is undesirable on any hardware.

Expected

The reader should not be scanned while the displays are blanked and nobody can present a finger, and a hard device error should back off rather than spin.

Steps to reproduce

Requires a libfprint imaging reader (this was a DigitalPersona U.are.U 4500; likely any uru4000-class device).

  1. Enrol a finger and configure lock-screen fingerprint (omarchy setup security fingerprint).
  2. Lock with Super+Ctrl+L and leave the machine for more than ~3 minutes.
  3. Return and touch the reader.

Observed: the sensor stays illuminated the entire time; touching does nothing; the password is required. journalctl fills with Device disabled to prevent overheating.

Leaving the reader idle for ~3 minutes lets the estimate decay and it verifies normally again, which confirms the device itself is healthy — fprintd-verify returns verify-match.

Suggested fix

Tie the fingerprint scan to display wake rather than to the lock as a whole, and back off on repeated failures. I have this running locally as a fork of the lock plugin:

  • runBlank() closes the fingerprint window (aborts the PAM context); runWake() opens it. Locked with monitors off means no scanning at all.
  • A bounded scan-window timer as a backstop.
  • Exponential backoff on retry (250 ms doubling to a 10 s cap) replacing the flat restart.

Result on the same hardware: 2 PAM sessions instead of 2,445 failed verifies, zero overheating errors, and unlock 2.4 s after nudging the mouse.

Happy to open a PR if the approach looks right — I did not want to presume the design, since gating on wake changes behaviour for match-on-chip readers too (they would also stop scanning while blanked, which seems desirable but is a behaviour change).

System details

Omarchy4.0.1-1
Kernel7.1.9-arch1-2
CPUAMD Ryzen AI MAX+ 395 w/ Radeon 8060S
GPUAMD Strix Halo [1002:1586] (rev c1)
libfprint1.94.100-1
fprintd1.94.5-2
ReaderDigitalPersona U.are.U 4500, USB 05ba:000a, driver uru4000

Most recent change to the file: #6817 ("Blank the lock screen while the fingerprint reader waits").

View raw text
## What happens

The lock screen arms fingerprint PAM for the entire duration of the lock. On a USB *imaging* reader (DigitalPersona U.are.U 4500, `05ba:000a`, libfprint `uru4000`) this drives libfprint's duty-cycle governor into its cutoff after roughly three minutes, and the reader stops responding for the rest of the lock. Unlocking then requires the password, and the sensor illuminator stays lit the whole time the machine is idle.

Two separate problems combine:

**1. Fingerprint PAM is armed for the whole lock, not just when someone is there.**

`shell/plugins/lock/Service.qml` starts fingerprint verification on `onSecureStateChanged` (L235) and keeps it running until unlock. The existing comment at L428 states this intent directly:

```qml
// Only a password check in flight should hold the display up. The
// fingerprint PAM stays armed for the whole lock, so gating on
// `authenticating` here would keep the panel lit until unlock.
```

That is fine for a laptop match-on-chip sensor, which idles cheaply while waiting. An imaging reader instead illuminates and captures continuously. libfprint 1.94 models device duty cycle in software (`FP_TEMPERATURE_COLD/WARM/HOT` — an estimate, not a hardware sensor) and refuses to verify once the estimate reaches HOT:

```
fprintd[...]: Device reported an error during verify: Device disabled to prevent overheating.
```

The estimate only decays while the device is idle, and a whole-lock arm never provides idle time, so it cannot recover while locked. Nothing is physically hot — this is purely libfprint's governor.

**2. `fingerprintRetryTimer` hot-loops on hard device errors.**

`interval: 250` (L358), restarted unconditionally on any failure — `handleFingerprintFinished` (L226) and `onError` (L352). Once the device returns the error above, each verify fails *instantly*, so the 250 ms retry becomes a spin loop with no backoff and no failure cap.

Measured on one 22-minute lock: **2,445 failed verifies**, ~36 per 10 seconds, sustained until the password was entered.

This second issue is device-independent — spinning a 250 ms retry against a hard device error is undesirable on any hardware.

## Expected

The reader should not be scanned while the displays are blanked and nobody can present a finger, and a hard device error should back off rather than spin.

## Steps to reproduce

Requires a libfprint imaging reader (this was a DigitalPersona U.are.U 4500; likely any `uru4000`-class device).

1. Enrol a finger and configure lock-screen fingerprint (`omarchy setup security fingerprint`).
2. Lock with Super+Ctrl+L and leave the machine for more than ~3 minutes.
3. Return and touch the reader.

Observed: the sensor stays illuminated the entire time; touching does nothing; the password is required. `journalctl` fills with `Device disabled to prevent overheating.`

Leaving the reader idle for ~3 minutes lets the estimate decay and it verifies normally again, which confirms the device itself is healthy — `fprintd-verify` returns `verify-match`.

## Suggested fix

Tie the fingerprint scan to display wake rather than to the lock as a whole, and back off on repeated failures. I have this running locally as a fork of the lock plugin:

- `runBlank()` closes the fingerprint window (aborts the PAM context); `runWake()` opens it. Locked with monitors off means no scanning at all.
- A bounded scan-window timer as a backstop.
- Exponential backoff on retry (250 ms doubling to a 10 s cap) replacing the flat restart.

Result on the same hardware: 2 PAM sessions instead of 2,445 failed verifies, zero overheating errors, and unlock 2.4 s after nudging the mouse.

Happy to open a PR if the approach looks right — I did not want to presume the design, since gating on wake changes behaviour for match-on-chip readers too (they would also stop scanning while blanked, which seems desirable but is a behaviour change).

## System details

| | |
|---|---|
| Omarchy | 4.0.1-1 |
| Kernel | 7.1.9-arch1-2 |
| CPU | AMD Ryzen AI MAX+ 395 w/ Radeon 8060S |
| GPU | AMD Strix Halo `[1002:1586]` (rev c1) |
| libfprint | 1.94.100-1 |
| fprintd | 1.94.5-2 |
| Reader | DigitalPersona U.are.U 4500, USB `05ba:000a`, driver `uru4000` |

Most recent change to the file: #6817 ("Blank the lock screen while the fingerprint reader waits").

Validation

AI validator claude-code (claude-opus-5.5) · evaluated revision 1 at product commit 1e43373287af · sealed 2026-10-05T14:30:13Z

Validator's summary (commentary, not evidence)

Validation of omacom/omarchy#9913 on quattro-proof 1e433732 (upstream quattro 035ce29f product code plus the proof layer). The report's first point holds: the lock keeps fingerprint PAM armed for the whole lock, including while the displays are blank. Its second point (a flat 250 ms retry with no backoff spinning on hard device errors) no longer holds: upstream #7158 replaced it with a backoff. At this commit (upstream #7158 merged, which is in upstream quattro 035ce29f), the real Service.qml retry policy is: after an attempt that reached the reader and did not match, the next attempt is armed in 250 ms (FingerprintModel.js:3 and Service.qml:420 upstream); attempts that never reach the reader or end in a fast error back off 1, 2, 4 ... 32 s and then 40 s (FingerprintModel.js:7-23); runBlank() leaves a pending retry running (Service.qml:294-298) and a miss while the displays are blank arms the next attempt in 250 ms (startFingerprint, Service.qml:357-375, has no displaysBlank check); retries continue for as long as the session stays locked. SW-REQ-261004-296X covers the unavailable report and SW-REQ-260912-ND55 says the armed fingerprint PAM must not hold the display lit; no requirement says whether the reader is scanned while the displays are blank or whether retries have a total budget. That is an open specification question for the maintainers. Open upstream PRs #12420 and #7179 propose not scanning while blank. a withheld known issue (fixed) was examined: the start-failure path, not this one.

  • ConfirmedReproducible

    Reproducible: the lock re-arms the fingerprint scan after every attempt for the whole lock, also while the displays are blank (250 ms after a miss; error streaks back off to a 40 s cap)

    Engine: engine-executed run exited 1 and showed the defect signal

    Evidence: file test/reports/report-cmulr91vo0k9f1gw499ic4jis.sh

    Validator's rationale (commentary): Observed with the real Service.qml: runBlank() leaves the pending retry running and a miss with displaysBlank=true arms the next attempt in 250 ms. The second half of the report (no backoff) is not reproduced: 9 error attempts back off 1000 ... 40000 ms.

  • Not confirmedAffects a componentlock

    Affects the lock component

    Engine: no requirement of the lock component is confirmed as violated

Engine reference checks at 4b21f9d4a7d6 (12/12 ok)
  • okcommit4b21f9d4a7d6e8e1429d072dfa3b3d643510e68d
  • okbase commit1e43373287afbd6dca4674ad463d5b5db7812358
  • okbase ancestry1e43373287afbd6dca4674ad463d5b5db7812358
  • okproduct code untouchedthis report's reproducer branch
  • okbranchthis report's reproducer branch
  • okfiletest/reports/report-cmulr91vo0k9f1gw499ic4jis.qml
  • okfiletest/reports/report-cmulr91vo0k9f1gw499ic4jis.sh
  • okrequirementSW-REQ-260912-41VV
  • okrequirementSW-REQ-260912-ND55
  • okrequirementSW-REQ-261004-296X
  • okcomponentlock
  • okknown issuewithheld

The engine's own check, at validation time, that the commits, the reproducer branch (touching only reproducer files), the reproducer files and every mapped requirement / component / known issue really exist.

Validator-authored hypothesis. Each claim's verification is engine-computed: confirmed only from an engine-executed reproducer run recorded in this record, refuted when recorded evidence contradicts it. Prose is commentary. The disposition of the report is a human decision.

Reproducer

engine-runexit 1 · expected exit 0 · defect exit 1 · 665 ms · 2026-10-05T14:30:12Z→ showed the defect
test/reports/report-cmulr91vo0k9f1gw499ic4jis.shon this report's reproducer branch @ 4b21f9d4a7+ test/reports/report-cmulr91vo0k9f1gw499ic4jis.qml
bash test/reports/report-cmulr91vo0k9f1gw499ic4jis.sh

Setup failure: exit 2 means the reproducer could not set up (e.g. the product did not build) — such a run decides nothing.

Defect signal: output contains AS-REPORTED:

Matched lines (engine, over the full output)
AS-REPORTED: the lock re-arms the fingerprint scan after every miss, also while the displays are blank (250 ms after a miss; error streaks back off to a 40 s cap), for as long as the session is locked
Observed output · 762 bytes
Observed on this tree (quickshell 0.3.1, lock plugin shell/plugins/lock):
  - after one attempt that reached the reader and did not match: the next attempt is armed in 250 ms
  - runBlank() leaves the pending retry running; with displaysBlank=true a miss arms the next attempt in 250 ms
  - 9 unreached/error attempts in a row arm the next attempt after [1000,2000,4000,8000,16000,32000,40000,40000,40000] ms (unavailable notice: true)
  - after them the retry is still armed and the lock is still requested (true): retries go on while the session stays locked
AS-REPORTED: the lock re-arms the fingerprint scan after every miss, also while the displays are blank (250 ms after a miss; error streaks back off to a 40 s cap), for as long as the session is locked

Needs: linux · quickshell (0.3.x) and jq on PATH; a sway binary for a private headless compositor (PROOF_ENV_DIR/sysroot/usr/bin/sway, else sway on PATH); the script exits 2 with SETUP: otherwise. Nothing to build: the QML is loaded straight from the worktree · ran on linux/amd64

Linked

Decision

Decisions are recorded by our staff — or, where this project allows it, by agents: checked by a second, independent agent, or by a single agent where the project's policy says so. Any decision reached so far is shown below.

  • DuplicateR-443Decided by agents · verified · 2026-10-09 09:20 UTC