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.
Bar widgets cannot resolve their own service when a cloned/third-party bar is active
Reported by Olinkan · filed Oct 3, 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
What happened
A bar widget that owns a service cannot reach that service, so its bar button silently does nothing — but only when the bar in use is a cloned / third-party bar (omarchy plugin clone omarchy.bar). With the stock first-party bar the same plugin works.
Reproduced with `omasettings` (io.github.twiking.omasettings, kinds bar-widget + service):
- Clicking the gear in the bar does nothing. No window, no error.
omarchy-shell omasettings showopens the window fine.hyprctl layersconfirms noomasettingslayer is created by the click.
No QML error is logged, which is what makes it hard to diagnose.
Root cause
A bar widget reaches its service through `bar.shell`, and bar.shell is the scope of the bar plugin, not the widget's:
configureBar()injects the bar root'sshellwithshell.pluginShellFor(manifest)— wheremanifestis the bar's manifest (shell.qml:221).pluginShellFor()returns the full host shell only for first-party manifests; for anything else it returns a scopedPluginShellApibound to the bar's own id (shell.qml:739).- That scoped api resolves services through
pluginServiceFor(key, requestedId), which is gated bypluginOwnsTarget(pluginId, requestedId)— a strict identity check (resolveEnabledId(requestedId) === caller) (shell.qml:385-394).
So with a cloned bar, the widget asking for its own service is asking through the bar's scope, and pluginOwnsTarget("valiant.bar", "io.github.twiking.omasettings") is false → serviceFor() returns null → the widget's toggle() is a no-op.
Panels are unaffected because the host injects each panel's own scope on load:
if ("shell" in item) item.shell = shell.pluginShellFor(panelEntry.manifest) // shell.qml:1342Bar widgets get bar.shell (the bar's scope) instead, so they have no equivalent path to their own service.
Steps to reproduce
omarchy plugin clone omarchy.bar <your-clone>and set it as the active bar inshell.json("bar": { "id": "<your-clone>" }).- Install a plugin that is both a
bar-widgetand aservice, whose widget looks its service up withbar.shell.serviceFor(<its own id>)—omasettingsis one. - Click its bar button.
Nothing happens. With the stock omarchy.bar the same plugin opens normally.
Expected
A bar widget should be able to resolve its own service regardless of which bar is active — the same guarantee panels already have.
Suggested direction
The bar hands each widget slot a reference to itself (bar: root). One option is for the bar to also hand each slot a scope for the widget's own plugin id, the way the panel loader already does, so bar.shell.serviceFor(self) resolves. A smaller alternative is to let a scoped api resolve a target when the caller is the active bar — a bar is already trusted with pluginHasBarCapabilities for other reasons — though that is a broader grant than the current identity check.
Happy to send a PR if there's appetite for either shape.
Workaround
Call the plugin's IPC target directly instead of going through the scoped api:
o.bind("SUPER + SHIFT + S", "OmaSettings", "omarchy-shell omasettings toggle")The IPC target is not permission-scoped, so it works.
System
- Omarchy
4.0.4-1 - Hyprland config: Lua (
hyprland.lua), v0.56.2 - Bar:
valiant.bar, a clone ofomarchy.bar - Wayland-only, NVIDIA GTX 970
- Reproduced on Omarchy's own
qs/Quickshell shell; noomarchy debugsubcommand exists in 4.0.4 so the log is not attached.
View raw text
## What happened
A **bar widget** that owns a service cannot reach that service, so its bar button silently does nothing — but only when the bar in use is a **cloned / third-party bar** (`omarchy plugin clone omarchy.bar`). With the stock first-party bar the same plugin works.
Reproduced with [`omasettings`](https://github.com/twiking/omasettings) (`io.github.twiking.omasettings`, kinds `bar-widget` + `service`):
- Clicking the gear in the bar does nothing. No window, no error.
- `omarchy-shell omasettings show` opens the window fine.
- `hyprctl layers` confirms no `omasettings` layer is created by the click.
No QML error is logged, which is what makes it hard to diagnose.
## Root cause
A bar widget reaches its service through **`bar.shell`**, and `bar.shell` is the scope of the **bar plugin**, not the widget's:
- `configureBar()` injects the bar root's `shell` with `shell.pluginShellFor(manifest)` — where `manifest` is the **bar's** manifest (`shell.qml:221`).
- `pluginShellFor()` returns the **full host shell** only for first-party manifests; for anything else it returns a scoped `PluginShellApi` bound to the bar's own id (`shell.qml:739`).
- That scoped api resolves services through `pluginServiceFor(key, requestedId)`, which is gated by `pluginOwnsTarget(pluginId, requestedId)` — a strict identity check (`resolveEnabledId(requestedId) === caller`) (`shell.qml:385-394`).
So with a cloned bar, the widget asking for its own service is asking *through the bar's scope*, and `pluginOwnsTarget("valiant.bar", "io.github.twiking.omasettings")` is false → `serviceFor()` returns `null` → the widget's `toggle()` is a no-op.
Panels are unaffected because the host injects each panel's **own** scope on load:
```qml
if ("shell" in item) item.shell = shell.pluginShellFor(panelEntry.manifest) // shell.qml:1342
```
Bar widgets get `bar.shell` (the bar's scope) instead, so they have no equivalent path to their own service.
## Steps to reproduce
1. `omarchy plugin clone omarchy.bar <your-clone>` and set it as the active bar in `shell.json` (`"bar": { "id": "<your-clone>" }`).
2. Install a plugin that is **both** a `bar-widget` and a `service`, whose widget looks its service up with `bar.shell.serviceFor(<its own id>)` — `omasettings` is one.
3. Click its bar button.
Nothing happens. With the stock `omarchy.bar` the same plugin opens normally.
## Expected
A bar widget should be able to resolve **its own** service regardless of which bar is active — the same guarantee panels already have.
## Suggested direction
The bar hands each widget slot a reference to itself (`bar: root`). One option is for the bar to also hand each slot a scope for the widget's own plugin id, the way the panel loader already does, so `bar.shell.serviceFor(self)` resolves. A smaller alternative is to let a scoped api resolve a target when the caller is the **active bar** — a bar is already trusted with `pluginHasBarCapabilities` for other reasons — though that is a broader grant than the current identity check.
Happy to send a PR if there's appetite for either shape.
## Workaround
Call the plugin's IPC target directly instead of going through the scoped api:
```lua
o.bind("SUPER + SHIFT + S", "OmaSettings", "omarchy-shell omasettings toggle")
```
The IPC target is not permission-scoped, so it works.
## System
- Omarchy `4.0.4-1`
- Hyprland config: Lua (`hyprland.lua`), v0.56.2
- Bar: `valiant.bar`, a clone of `omarchy.bar`
- Wayland-only, NVIDIA GTX 970
- Reproduced on Omarchy's own `qs`/Quickshell shell; no `omarchy debug` subcommand exists in 4.0.4 so the log is not attached.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 20:57 UTC