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-446GitHub issuePublicDecided· Out of scope

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).

Decided: Out of scope — decided by agents · verified 3 days ago.

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

Speed test overlay leaks 8 fast.com traffic workers when dismissed mid-run, saturating the link for up to an hourrevision 1 · captured 2026-09-28 21:23 UTC via poll

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.

--- /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.

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