Proof Portal
Omarchy
ProbeLabs65 findings · 95 requirementsA 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.
Notifications never render: `screenObscured` is permanently true because `firstPartyServiceFor("omarchy.lock")` always returns null
Reported by rugobal · filed Sep 26, 2026 · open at source
At a glance
- 1 · What is claimed?
- Only the report text below — no validator has restated it as checkable claims yet.
- 2 · What does it affect?
- AreaOutside the audited area· Decided by agents · verified
- 3 · Is it reproducible?
- No validation has been run yet.
- 4 · What remains unclear?
- Not validated yet: no claims, no reproducer.
- 5 · What happens next?
- Decided by agents and verified by a second, independent agent — see the decision.
Original report
Summary
Notification toasts never render on this install (Omarchy 4.0.3-4). The daemon receives notifications, but the toast overlay window is never mapped, Super + Shift + Alt + , (history replay) shows nothing, and toast state files pile up in ~/.local/state/omarchy/notifications/ instead of being archived to history/.
The notifications service hides its popup window while it believes the screen may be obscured:
// shell/plugins/notifications/Service.qml:60
readonly property bool screenObscured:
!idleService
|| !idleService.screensaverStateKnown
|| idleService.screensaverWindowCount > 0
|| !lockService // <- permanently true
|| lockService.lockedlockService is shell.firstPartyServiceFor("omarchy.lock"), which can never return anything but null:
shell/plugins/lock/manifest.jsondeclaresomarchy.capabilities: ["authentication"]ShellRoot.ensureService()keeps authentication services out of the public service map entirely — "Never publish lock/polkit through ShellRoot._services" (it only doesAuthServiceStore.put(key, inst))ShellRoot.firstPartyServiceFor()only looks in_services, soserviceFor("omarchy.lock")is alwaysnull.pluginFirstPartyServiceFor()additionally restricts to["omarchy.idle", "omarchy.media", "omarchy.nightlight", "omarchy.notifications"], so scoped plugin shells can't see it either.
So !lockService is always true → screenObscured is always true → popupWindow.visible (Service.qml:972) is always false and the toast expiry timer (ticking, Service.qml:1028) never runs. This looks unconditional for the stock shell, so it should affect every install with the lock plugin enabled.
Expected
Toasts appear in the top-right for their normal lifetime, then get archived to ~/.local/state/omarchy/notifications/history/.
Actual
Nothing is ever visible. hyprctl layers never shows an omarchy-notifications layer, and ~/.local/state/omarchy/notifications/*.json accumulates popup files that never expire (a 12-minute-old toast was still "on screen" during testing).
Steps to reproduce
omarchy notification send "repro" "toast should appear"- No toast appears in the top-right corner
hyprctl layers | grep omarchy-notifications→ emptyls ~/.local/state/omarchy/notifications/*.json→ the toast's popup file exists and never moves tohistory/- Every other
screenObscuredterm is verifiably false:
omarchy-shell lock isLocked→falseomarchy-shell idle status→"screensaverWindows": 0; journal hasomarchy idle ... screensaver-state-ready: 0 window(s)- no
notifications.json(dndoff)
→ the !lockService term is the only one that can hold the guard.
Suggested fix
Treat a missing lock service as "not locked" instead of "screen obscured":
readonly property bool screenObscured:
!idleService
|| !idleService.screensaverStateKnown
|| idleService.screensaverWindowCount > 0
|| (lockService && lockService.locked)(If the notifications plugin is meant to see lock state again, omarchy.lock would need to be exposed through a non-authentication surface — but the guard change above is sufficient, since the lock service is unreachable by design.)
System details
- Omarchy 4.0.3-4 (Try Omarchy QEMU guest, single monitor, Hyprland)
- Debug log: https://logs.omarchy.org/3R5Kkh (expires in 24h — happy to re-upload)
Happy to test a fix and report back.
View raw text
## Summary
Notification toasts never render on this install (Omarchy 4.0.3-4). The daemon receives notifications, but the toast overlay window is never mapped, `Super + Shift + Alt + ,` (history replay) shows nothing, and toast state files pile up in `~/.local/state/omarchy/notifications/` instead of being archived to `history/`.
The notifications service hides its popup window while it believes the screen may be obscured:
```qml
// shell/plugins/notifications/Service.qml:60
readonly property bool screenObscured:
!idleService
|| !idleService.screensaverStateKnown
|| idleService.screensaverWindowCount > 0
|| !lockService // <- permanently true
|| lockService.locked
```
`lockService` is `shell.firstPartyServiceFor("omarchy.lock")`, which can never return anything but `null`:
- `shell/plugins/lock/manifest.json` declares `omarchy.capabilities: ["authentication"]`
- `ShellRoot.ensureService()` keeps authentication services out of the public service map entirely — "Never publish lock/polkit through ShellRoot._services" (it only does `AuthServiceStore.put(key, inst)`)
- `ShellRoot.firstPartyServiceFor()` only looks in `_services`, so `serviceFor("omarchy.lock")` is always `null`. `pluginFirstPartyServiceFor()` additionally restricts to `["omarchy.idle", "omarchy.media", "omarchy.nightlight", "omarchy.notifications"]`, so scoped plugin shells can't see it either.
So `!lockService` is always `true` → `screenObscured` is always `true` → `popupWindow.visible` (`Service.qml:972`) is always `false` and the toast expiry timer (`ticking`, `Service.qml:1028`) never runs. This looks unconditional for the stock shell, so it should affect every install with the lock plugin enabled.
## Expected
Toasts appear in the top-right for their normal lifetime, then get archived to `~/.local/state/omarchy/notifications/history/`.
## Actual
Nothing is ever visible. `hyprctl layers` never shows an `omarchy-notifications` layer, and `~/.local/state/omarchy/notifications/*.json` accumulates popup files that never expire (a 12-minute-old toast was still "on screen" during testing).
## Steps to reproduce
1. `omarchy notification send "repro" "toast should appear"`
2. No toast appears in the top-right corner
3. `hyprctl layers | grep omarchy-notifications` → empty
4. `ls ~/.local/state/omarchy/notifications/*.json` → the toast's popup file exists and never moves to `history/`
5. Every other `screenObscured` term is verifiably false:
- `omarchy-shell lock isLocked` → `false`
- `omarchy-shell idle status` → `"screensaverWindows": 0`; journal has `omarchy idle ... screensaver-state-ready: 0 window(s)`
- no `notifications.json` (`dnd` off)
→ the `!lockService` term is the only one that can hold the guard.
## Suggested fix
Treat a missing lock service as "not locked" instead of "screen obscured":
```qml
readonly property bool screenObscured:
!idleService
|| !idleService.screensaverStateKnown
|| idleService.screensaverWindowCount > 0
|| (lockService && lockService.locked)
```
(If the notifications plugin is meant to see lock state again, `omarchy.lock` would need to be exposed through a non-authentication surface — but the guard change above is sufficient, since the lock service is unreachable by design.)
## System details
- Omarchy 4.0.3-4 (Try Omarchy QEMU guest, single monitor, Hyprland)
- Debug log: https://logs.omarchy.org/3R5Kkh (expires in 24h — happy to re-upload)
Happy to test a fix and report back.
Validation
Not checked yet. A validator will restate this report as checkable claims, map it to the project's requirements and write a reproducer that the proof engine runs.
Linked
Not linked to any known issue, other report or change yet.
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.
- Out of scopeDecided by agents · verified · 2026-10-09 02:13 UTC