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.
Speed test overlay leaks 8 fast.com traffic workers when dismissed mid-run, saturating the link for up to an hour
Reported by Marjinoz · filed Sep 16, 2026Closed upstream on 2026-10-12 by the bot omarchybot, as a duplicate (target not recorded).
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
System details: AMD Ryzen 7 7800X3D 8-Core Processor, NVIDIA RTX 4090, Omarchy 4.0.4, Quickshell 0.3.1, wired 300/300 (Altibox)
Summary
Dismissing the Speed Test overlay (omarchy.speedtest) while a run is in progress leaks all 8 omarchy-network-speedtest traffic workers. They keep looping 64 MB uploads (or downloads) against fast.com until the signed URLs expire (~1 hour), saturating the link the whole time. On a 300/300 line this gave ~250 Mbit/s of background upload, ~100 ms latency to the LAN gateway and a download test dropping from ~250 to ~57 Mbit/s, which shows up as constant buffering in every video player.
Omarchy 4.0.4 (bin/omarchy-network-speedtest and shell/plugins/panels/speedtest/Panel.qml identical to current quattro).
Reproduce
- Open the speed test from the menu (Tests → Network Speed Test).
- Close the overlay while the dials are still moving (within the ~10 s run).
ps -eo pid,ppid,etimes,args | grep '[o]marchy-network-speedtest'shows 8 workers re-parented tosystemd --user, each with acurl ... nflxvideo.net/speedtestchild, andip -s linkkeeps showing the link saturated.
Cause
shell.hide() in shell/shell.qml calls the panel's close() (which sets Process.running = false, i.e. SIGTERM, asynchronously) and then drops the id from openPanelIds in the same call. That flips the panel Loader inactive and destroys the panel item immediately. Quickshell's Process destructor sends SIGKILL to a still-running child (src/io/process.cpp: this->process->kill()), so the bash script dies before its trap cleanup EXIT can run, and the background traffic_worker subshells are orphaned.
Verified directly: kill -TERM on the script cleans up (trap runs), kill -KILL leaves all 8 workers running. The panel's comment "dismissing it stops the traffic" is therefore not true when dismissed mid-run.
Fix
Make the workers not depend on the parent's EXIT trap: each worker polls the parent PID and kills its own transfer as soon as the parent is gone. Tested: SIGTERM, SIGKILL during upload and SIGKILL during download all leave zero orphans and zero residual traffic within 0.2 s.
--- /tmp/claude-1000/-home-marius-Work/15298556-b7d6-4dd1-a091-840a1ece0280/scratchpad/omarchy-network-speedtest.orig 2026-09-17 00:04:39.556017029 +0200
+++ /tmp/claude-1000/-home-marius-Work/15298556-b7d6-4dd1-a091-840a1ece0280/scratchpad/omarchy-network-speedtest 2026-09-17 00:04:39.567481790 +0200
@@ -67,23 +67,37 @@
trap cleanup EXIT
# Round-robin across the returned URLs so we spread load across Netflix OCA nodes.
+#
+# Workers outlive their parent when it is SIGKILLed (the EXIT trap never runs
+# then; Quickshell's Process destructor does exactly that when the speed test
+# overlay is dismissed mid-run), so each worker polls the parent and tears its
+# transfer down within a beat of the parent going away instead of saturating
+# the link until the fast.com URLs expire.
+parent_pid=$$
+
traffic_worker() {
local urls=("$@")
local url_count=${#urls[@]}
local idx=$RANDOM
- if [[ $direction == "down" ]]; then
- while true; do
- url=${urls[$((idx % url_count))]}
- curl -fsS -o /dev/null "$url" 2>/dev/null || return
- idx=$((idx + 1))
- done
- else
- while true; do
- url=${urls[$((idx % url_count))]}
- dd if=/dev/zero bs=1M count=64 2>/dev/null | curl -fsS -o /dev/null -X POST --data-binary @- "$url" 2>/dev/null || return
- idx=$((idx + 1))
+ local url pid
+ while kill -0 "$parent_pid" 2>/dev/null; do
+ url=${urls[$((idx % url_count))]}
+ if [[ $direction == "down" ]]; then
+ curl -fsS -o /dev/null "$url" 2>/dev/null &
+ else
+ dd if=/dev/zero bs=1M count=64 2>/dev/null | curl -fsS -o /dev/null -X POST --data-binary @- "$url" 2>/dev/null &
+ fi
+ pid=$!
+ while kill -0 "$pid" 2>/dev/null; do
+ if ! kill -0 "$parent_pid" 2>/dev/null; then
+ kill "$pid" 2>/dev/null
+ return
+ fi
+ sleep 0.2
done
- fi
+ wait "$pid" 2>/dev/null || return
+ idx=$((idx + 1))
+ done
}
for (( i = 0; i < parallel; i++ )); doAn additional, shell-side improvement would be for hide() to wait for the panel's process to exit before unloading the Loader, but the script-side fix also covers shell restarts and crashes.
PR with the fix: coming right after this issue.
View raw text
**System details:** AMD Ryzen 7 7800X3D 8-Core Processor, NVIDIA RTX 4090, Omarchy 4.0.4, Quickshell 0.3.1, wired 300/300 (Altibox)
## Summary
Dismissing the Speed Test overlay (`omarchy.speedtest`) while a run is in progress leaks all 8 `omarchy-network-speedtest` traffic workers. They keep looping 64 MB uploads (or downloads) against fast.com until the signed URLs expire (~1 hour), saturating the link the whole time. On a 300/300 line this gave ~250 Mbit/s of background upload, ~100 ms latency to the LAN gateway and a download test dropping from ~250 to ~57 Mbit/s, which shows up as constant buffering in every video player.
Omarchy 4.0.4 (`bin/omarchy-network-speedtest` and `shell/plugins/panels/speedtest/Panel.qml` identical to current `quattro`).
## Reproduce
1. Open the speed test from the menu (Tests → Network Speed Test).
2. Close the overlay while the dials are still moving (within the ~10 s run).
3. `ps -eo pid,ppid,etimes,args | grep '[o]marchy-network-speedtest'` shows 8 workers re-parented to `systemd --user`, each with a `curl ... nflxvideo.net/speedtest` child, and `ip -s link` keeps showing the link saturated.
## Cause
`shell.hide()` in `shell/shell.qml` calls the panel's `close()` (which sets `Process.running = false`, i.e. SIGTERM, asynchronously) and then drops the id from `openPanelIds` in the same call. That flips the panel `Loader` inactive and destroys the panel item immediately. Quickshell's `Process` destructor sends SIGKILL to a still-running child (`src/io/process.cpp`: `this->process->kill()`), so the bash script dies before its `trap cleanup EXIT` can run, and the background `traffic_worker` subshells are orphaned.
Verified directly: `kill -TERM` on the script cleans up (trap runs), `kill -KILL` leaves all 8 workers running. The panel's comment "dismissing it stops the traffic" is therefore not true when dismissed mid-run.
## Fix
Make the workers not depend on the parent's EXIT trap: each worker polls the parent PID and kills its own transfer as soon as the parent is gone. Tested: SIGTERM, SIGKILL during upload and SIGKILL during download all leave zero orphans and zero residual traffic within 0.2 s.
```diff
--- /tmp/claude-1000/-home-marius-Work/15298556-b7d6-4dd1-a091-840a1ece0280/scratchpad/omarchy-network-speedtest.orig 2026-09-17 00:04:39.556017029 +0200
+++ /tmp/claude-1000/-home-marius-Work/15298556-b7d6-4dd1-a091-840a1ece0280/scratchpad/omarchy-network-speedtest 2026-09-17 00:04:39.567481790 +0200
@@ -67,23 +67,37 @@
trap cleanup EXIT
# Round-robin across the returned URLs so we spread load across Netflix OCA nodes.
+#
+# Workers outlive their parent when it is SIGKILLed (the EXIT trap never runs
+# then; Quickshell's Process destructor does exactly that when the speed test
+# overlay is dismissed mid-run), so each worker polls the parent and tears its
+# transfer down within a beat of the parent going away instead of saturating
+# the link until the fast.com URLs expire.
+parent_pid=$$
+
traffic_worker() {
local urls=("$@")
local url_count=${#urls[@]}
local idx=$RANDOM
- if [[ $direction == "down" ]]; then
- while true; do
- url=${urls[$((idx % url_count))]}
- curl -fsS -o /dev/null "$url" 2>/dev/null || return
- idx=$((idx + 1))
- done
- else
- while true; do
- url=${urls[$((idx % url_count))]}
- dd if=/dev/zero bs=1M count=64 2>/dev/null | curl -fsS -o /dev/null -X POST --data-binary @- "$url" 2>/dev/null || return
- idx=$((idx + 1))
+ local url pid
+ while kill -0 "$parent_pid" 2>/dev/null; do
+ url=${urls[$((idx % url_count))]}
+ if [[ $direction == "down" ]]; then
+ curl -fsS -o /dev/null "$url" 2>/dev/null &
+ else
+ dd if=/dev/zero bs=1M count=64 2>/dev/null | curl -fsS -o /dev/null -X POST --data-binary @- "$url" 2>/dev/null &
+ fi
+ pid=$!
+ while kill -0 "$pid" 2>/dev/null; do
+ if ! kill -0 "$parent_pid" 2>/dev/null; then
+ kill "$pid" 2>/dev/null
+ return
+ fi
+ sleep 0.2
done
- fi
+ wait "$pid" 2>/dev/null || return
+ idx=$((idx + 1))
+ done
}
for (( i = 0; i < parallel; i++ )); do
```
An additional, shell-side improvement would be for `hide()` to wait for the panel's process to exit before unloading the Loader, but the script-side fix also covers shell restarts and crashes.
PR with the fix: coming right after this issue.
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 14:38 UTC